mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-24 06:20:05 +08:00
141 lines
4.2 KiB
Lua
141 lines
4.2 KiB
Lua
local hint = require'hop.hint'
|
||
|
||
local M = {}
|
||
|
||
local function window_context(win_handle, cursor_pos)
|
||
-- get a bunch of information about the window and the cursor
|
||
local win_info = vim.fn.getwininfo(win_handle)[1]
|
||
local win_view = vim.fn.winsaveview()
|
||
local top_line = win_info.topline - 1
|
||
local bot_line = win_info.botline
|
||
|
||
-- 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 = top_line,
|
||
bot_line = bot_line,
|
||
win_width = win_width,
|
||
col_offset = win_view.leftcol
|
||
}
|
||
end
|
||
|
||
-- Collect all multi-windows's context:
|
||
--
|
||
-- {
|
||
-- { -- context list that each contains one buffer
|
||
-- hbuf = <buf-handle>,
|
||
-- { -- windows list that display the same buffer
|
||
-- hwin = <win-handle>,
|
||
-- ...
|
||
-- },
|
||
-- ...
|
||
-- },
|
||
-- ...
|
||
-- }
|
||
function M.get_window_context(multi_windows)
|
||
local all_ctxs = {}
|
||
|
||
-- Generate contexts of windows
|
||
local cur_hwin = vim.api.nvim_get_current_win()
|
||
local cur_hbuf = vim.api.nvim_win_get_buf(cur_hwin)
|
||
all_ctxs[#all_ctxs + 1] = {
|
||
hbuf = cur_hbuf,
|
||
contexts = { window_context(cur_hwin, vim.api.nvim_win_get_cursor(cur_hwin)) },
|
||
}
|
||
|
||
if not multi_windows then
|
||
return all_ctxs
|
||
end
|
||
|
||
for _, w in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
|
||
local b = vim.api.nvim_win_get_buf(w)
|
||
if w ~= cur_hwin then
|
||
|
||
-- check duplicated buffers; the way this is done is by accessing all the already known contexts and checking that
|
||
-- the buffer we are accessing is already present in; if it is, we then append the window context to that buffer
|
||
local bctx = nil
|
||
for _, buffer_ctx in ipairs(all_ctxs) do
|
||
if b == buffer_ctx.hbuf then
|
||
bctx = buffer_ctx.contexts
|
||
break
|
||
end
|
||
end
|
||
|
||
if bctx then
|
||
bctx[#bctx + 1] = window_context(w, vim.api.nvim_win_get_cursor(w))
|
||
else
|
||
all_ctxs[#all_ctxs + 1] = {
|
||
hbuf = b,
|
||
contexts = { window_context(w, vim.api.nvim_win_get_cursor(w)) }
|
||
}
|
||
end
|
||
|
||
end
|
||
end
|
||
|
||
-- Move cursor back to current window
|
||
vim.api.nvim_set_current_win(cur_hwin)
|
||
|
||
return all_ctxs
|
||
end
|
||
|
||
-- Collect visible and unfold lines of window context
|
||
--
|
||
-- {
|
||
-- { line_nr = 0, line = "" }
|
||
-- }
|
||
function M.get_lines_context(buf_handle, context)
|
||
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
|
||
lines[#lines + 1] = {
|
||
line_nr = lnr,
|
||
line = vim.api.nvim_buf_get_lines(buf_handle, lnr, lnr + 1, false)[1], -- `nvim_buf_get_lines()` use 0-based line index
|
||
}
|
||
lnr = lnr + 1
|
||
else
|
||
lines[#lines + 1] = {
|
||
line_nr = lnr,
|
||
line = "",
|
||
}
|
||
lnr = fold_end
|
||
end
|
||
end
|
||
|
||
return lines
|
||
end
|
||
|
||
-- Clip the window context based on the direction.
|
||
--
|
||
-- If the direction is HintDirection.BEFORE_CURSOR, then everything after the cursor will be clipped.
|
||
-- If the direction is HintDirection.AFTER_CURSOR, then everything before the cursor will be clipped.
|
||
function M.clip_window_context(context, direction)
|
||
if direction == hint.HintDirection.BEFORE_CURSOR then
|
||
context.bot_line = context.cursor_pos[1] - 1
|
||
elseif direction == hint.HintDirection.AFTER_CURSOR then
|
||
context.top_line = context.cursor_pos[1] - 1
|
||
end
|
||
end
|
||
|
||
return M
|