mirror of
https://github.com/smoka7/hop.nvim.git
synced 2026-07-21 11:46:49 -04:00
133 lines
4.0 KiB
Lua
133 lines
4.0 KiB
Lua
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 can’t rely on this; for this reason, we have to implement a weird hack that
|
||
-- is going to disable the signs while hop is running (I’m 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
|