Files
hop.nvim/lua/hop/window.lua
2023-08-23 10:56:45 +03:30

133 lines
4.0 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local hint = require('hop.hint')
---@class WindowContext
---@field hwin number
---@field cursor_pos any[]
---@field top_line number
---@field bot_line number
---@field win_width number
---@field col_offset number
---@class Context
---@field buffer_handle number
---@field contexts WindowContext[]
---@class LineContext
---@field line_nr number
---@field line string
local M = {}
-- get information about the window and the cursor
---@param win_handle number
---@return WindowContext
local function window_context(win_handle)
vim.api.nvim_set_current_win(win_handle)
local win_info = vim.fn.getwininfo(win_handle)[1]
local win_view = vim.fn.winsaveview()
local cursor_pos = vim.api.nvim_win_get_cursor(win_handle)
-- NOTE: due to an (unknown yet) bug in neovim, the sign_width is not correctly reported when shifting the window
-- view inside a non-wrap window, so we cant rely on this; for this reason, we have to implement a weird hack that
-- is going to disable the signs while hop is running (Im sorry); the state is restored after jump
-- local left_col_offset = win_info.variables.context.number_width + win_info.variables.context.sign_width
local win_width = nil
-- hack to get the left column offset in nowrap
if not vim.wo.wrap then
vim.api.nvim_win_set_cursor(win_handle, { cursor_pos[1], 0 })
local left_col_offset = vim.fn.wincol() - 1
vim.fn.winrestview(win_view)
win_width = win_info.width - left_col_offset
end
return {
hwin = win_handle,
cursor_pos = cursor_pos,
top_line = win_info.topline - 1,
bot_line = win_info.botline,
win_width = win_width,
col_offset = win_view.leftcol,
}
end
-- returns current window context or all visible windows context in multiwindow mode
---@param opts Options
---@return Context[]
function M.get_window_context(opts)
---@type Context[]
local contexts = {}
-- Generate contexts of windows
local cur_hwin = vim.api.nvim_get_current_win()
local cur_hbuf = vim.api.nvim_win_get_buf(cur_hwin)
contexts[1] = {
buffer_handle = cur_hbuf,
contexts = { window_context(cur_hwin) },
}
if not opts.multi_windows then
return contexts
end
-- Get the context for all the windows in current tab
for _, w in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
if vim.api.nvim_win_is_valid(w) and vim.api.nvim_win_get_config(w).relative == '' then
local b = vim.api.nvim_win_get_buf(w)
-- skips current window and excluded filetypes
if not (w == cur_hwin or vim.tbl_contains(opts.excluded_filetypes, vim.bo[b].filetype)) then
contexts[#contexts + 1] = {
buffer_handle = b,
contexts = { window_context(w) },
}
end
end
end
return contexts
end
-- Collect visible and unfold lines of window context
---@param buf_handle number
---@param context WindowContext
---@return LineContext[]
function M.get_lines_context(buf_handle, context)
---@type LineContext[]
local lines = {}
local lnr = context.top_line
while lnr < context.bot_line do -- top_line is inclusive and bot_line is exclusive
local fold_end = vim.api.nvim_win_call(context.hwin, function()
return vim.fn.foldclosedend(lnr + 1) -- `foldclosedend()` use 1-based line number
end)
if fold_end == -1 then -- line isn't folded
lines[#lines + 1] = {
line_nr = lnr,
line = vim.api.nvim_buf_get_lines(buf_handle, lnr, lnr + 1, false)[1], -- `nvim_buf_get_lines()` uses 0-based line index
}
lnr = lnr + 1
else
lnr = fold_end -- skip folded lines
end
end
return lines
end
-- Clip the window context based on the direction.
---@param context WindowContext
---@param direction HintDirection
function M.clip_window_context(context, direction)
-- everything after the cursor will be clipped.
if direction == hint.HintDirection.BEFORE_CURSOR then
context.bot_line = context.cursor_pos[1]
-- everything before the cursor will be clipped.
elseif direction == hint.HintDirection.AFTER_CURSOR then
context.top_line = context.cursor_pos[1] - 1
end
end
return M