Previously on each outline open, the `writer.make_outline` function might be called at least 4 times(!), after this refactor it will only be called once. And on update cursor autocmds, also called once (previously at least twice). behaviour: - Now the outline window focus and highlight can update on each cursor move (previously CursorHold, dependent on updatetime). This is now configurable as well. - During fold-all/unfold-all operations, now the cursor will remain on the same node (rather than same line in outline buffer). - The performance improvement is not significantly observable since even the old implementation can appear instant. One may even argue I am fixing a problem that did not exist, but implementation-wise it's just so much better now. config: - outline_window.auto_update_events, list of events to be passed to create_user_autocmd for updating cursor focus in outline, and updating outline items (refetching symbols), using keys cursor and items respectively. - outline_window.show_cursorline now supports 2 other string values: 'focus_in_outline'/'focus_in_code' which controls when to enable cursorline. Setting to true retains the default behaviour of always showing the cursorline. This was added because now that the cursor focus on the outline could change on each CursorMoved, the cursorline may pose to be qute attention-seeking during the outline cursor updates. Hence `focus_in_outline` is added so that when focus is in code, the cursorline for outline window is not shown. 'focus_in_code' is added so that a user who disabled highlight_hovered_item can keep track of position in outline when focus is in code, disabling cursorline when focus is in outline. At any given time, if hide cursor is enabled and show_cursorline is a string value, hiding of cursor will not be done if cursorline is not shown in the the given situation. implementation: - The reason for the improvement in performance as described in the first paragraph is due to merging of finding hover item and finding the deepest matched node to put cursor, into writer.make_outline. This done, when previously done in separate function, because after the separate function (namely _highlight_hovered_item) finishes, writer.make_outline is called *again* anyway. - Autocmds to update cursor position in outline is now done per buffer rather than global. Somehow the auto unfold and unfold depth options still work perfectly, for this we should thank simrat or which ever contributor that modularized the folding module and made it adaptable :)
158 lines
4.2 KiB
Lua
158 lines
4.2 KiB
Lua
local cfg = require('outline.config')
|
|
local hover = require('outline.hover')
|
|
local outline = require('outline')
|
|
|
|
local M = {}
|
|
|
|
local state = {
|
|
preview_buf = nil,
|
|
preview_win = nil,
|
|
}
|
|
|
|
local function is_current_win_outline()
|
|
local curwin = vim.api.nvim_get_current_win()
|
|
return curwin == outline.view.winnr
|
|
end
|
|
|
|
local function has_code_win()
|
|
local isWinValid = vim.api.nvim_win_is_valid(outline.state.code_win)
|
|
if not isWinValid then
|
|
return false
|
|
end
|
|
local bufnr = vim.api.nvim_win_get_buf(outline.state.code_win)
|
|
local isBufValid = vim.api.nvim_buf_is_valid(bufnr)
|
|
return isBufValid
|
|
end
|
|
|
|
M.has_code_win = has_code_win
|
|
|
|
local function get_width_offset()
|
|
---@type integer
|
|
local outline_winnr = outline.view.winnr
|
|
local width = cfg.get_preview_width() + 3
|
|
local has_numbers = vim.api.nvim_win_get_option(outline_winnr, 'number')
|
|
has_numbers = has_numbers or vim.api.nvim_win_get_option(outline_winnr, 'relativenumber')
|
|
|
|
if has_numbers then
|
|
width = width + 4
|
|
end
|
|
|
|
-- FIXME: use actual window position based on view rather than config
|
|
if cfg.o.outline_window.position == 'right' then
|
|
width = 0 - width
|
|
else
|
|
width = vim.api.nvim_win_get_width(outline_winnr) + 1
|
|
end
|
|
|
|
return width
|
|
end
|
|
|
|
local function get_height()
|
|
return vim.api.nvim_list_uis()[1].height
|
|
end
|
|
|
|
local function get_hovered_node()
|
|
local hovered_line = vim.api.nvim_win_get_cursor(outline.view.winnr)[1]
|
|
local node = outline.state.flattened_outline_items[hovered_line]
|
|
return node
|
|
end
|
|
|
|
local function update_preview(code_buf)
|
|
code_buf = code_buf or vim.api.nvim_win_get_buf(outline.state.code_win)
|
|
|
|
local node = get_hovered_node()
|
|
if not node then
|
|
return
|
|
end
|
|
local lines = vim.api.nvim_buf_get_lines(code_buf, 0, -1, false)
|
|
|
|
if state.preview_buf ~= nil then
|
|
vim.api.nvim_buf_set_lines(state.preview_buf, 0, -1, 0, lines)
|
|
vim.api.nvim_win_set_cursor(state.preview_win, { node.line + 1, node.character })
|
|
end
|
|
end
|
|
|
|
local function setup_preview_buf()
|
|
local code_buf = vim.api.nvim_win_get_buf(outline.state.code_win)
|
|
local ft = vim.api.nvim_buf_get_option(code_buf, 'filetype')
|
|
|
|
local function treesitter_attach()
|
|
local ts_highlight = require('nvim-treesitter.highlight')
|
|
|
|
ts_highlight.attach(state.preview_buf, ft)
|
|
end
|
|
|
|
-- user might not have tree sitter installed
|
|
pcall(treesitter_attach)
|
|
|
|
vim.api.nvim_buf_set_option(state.preview_buf, 'syntax', ft)
|
|
vim.api.nvim_buf_set_option(state.preview_buf, 'bufhidden', 'delete')
|
|
vim.api.nvim_win_set_option(state.preview_win, 'cursorline', true)
|
|
update_preview(code_buf)
|
|
end
|
|
|
|
local function set_bg_hl()
|
|
vim.api.nvim_win_set_option(state.preview_win, 'winhl', cfg.o.preview_window.winhl)
|
|
vim.api.nvim_win_set_option(state.preview_win, 'winblend', cfg.o.preview_window.winblend)
|
|
end
|
|
|
|
local function show_preview()
|
|
if state.preview_win == nil and state.preview_buf == nil then
|
|
state.preview_buf = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_buf_attach(state.preview_buf, false, {
|
|
on_detach = function()
|
|
state.preview_buf = nil
|
|
state.preview_win = nil
|
|
end,
|
|
})
|
|
local height = get_height()
|
|
local winheight = math.ceil(height / 2)
|
|
state.preview_win = vim.api.nvim_open_win(state.preview_buf, false, {
|
|
relative = 'win',
|
|
height = winheight,
|
|
width = cfg.get_preview_width(),
|
|
bufpos = { 0, 0 },
|
|
col = get_width_offset(),
|
|
-- Position preview window middle-aligned vertically
|
|
row = math.floor((height - winheight) / 2) - 1,
|
|
border = cfg.o.preview_window.border,
|
|
})
|
|
setup_preview_buf()
|
|
else
|
|
update_preview()
|
|
end
|
|
end
|
|
|
|
function M.show()
|
|
if not is_current_win_outline() or #vim.api.nvim_list_wins() < 2 then
|
|
return
|
|
end
|
|
|
|
show_preview()
|
|
set_bg_hl()
|
|
if cfg.o.preview_window.open_hover_on_preview then
|
|
hover.show_hover()
|
|
end
|
|
end
|
|
|
|
function M.close()
|
|
if has_code_win() then
|
|
if state.preview_win ~= nil and vim.api.nvim_win_is_valid(state.preview_win) then
|
|
vim.api.nvim_win_close(state.preview_win, true)
|
|
end
|
|
if state.hover_win ~= nil and vim.api.nvim_win_is_valid(state.hover_win) then
|
|
vim.api.nvim_win_close(state.hover_win, true)
|
|
end
|
|
end
|
|
end
|
|
|
|
function M.toggle()
|
|
if state.preview_win ~= nil then
|
|
M.close()
|
|
else
|
|
M.show()
|
|
end
|
|
end
|
|
|
|
return M
|