Closes #37 Almost completely refactored the UI parts outline.nvim to use a Sidebar object that implements an outline window. In init.lua, we can then store a table of the outline for each tabpage ID. When tabs are closed the outline is closed and sidebar reset responsibly. This simplifies `init.lua` quite a lot, making it the highest level control center for the outline elements. All lua APIs and commands should work as before.
167 lines
4.6 KiB
Lua
167 lines
4.6 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.current.view.winnr
|
|
end
|
|
|
|
local function has_code_win(winnr)
|
|
if not outline.current then
|
|
return false
|
|
end
|
|
winnr = winnr or outline.current.code.win
|
|
return vim.api.nvim_win_is_valid(winnr)
|
|
and vim.api.nvim_buf_is_valid(outline.current.code.buf)
|
|
end
|
|
|
|
M.has_code_win = has_code_win
|
|
|
|
---Get the correct column to place the floating window based on
|
|
-- Relative positions of the outline and the code window.
|
|
---@param preview_width integer
|
|
local function get_col(preview_width)
|
|
---@type integer
|
|
local outline_winnr = outline.current.view.winnr
|
|
local outline_col = vim.api.nvim_win_get_position(outline_winnr)[2]
|
|
local outline_width = vim.api.nvim_win_get_width(outline_winnr)
|
|
local code_col = vim.api.nvim_win_get_position(outline.current.code.win)[2]
|
|
|
|
-- TODO: What if code win is below/above outline instead?
|
|
|
|
local col = outline_col
|
|
if outline_col > code_col then
|
|
col = col - preview_width - 3
|
|
else
|
|
col = col + outline_width + 1
|
|
end
|
|
|
|
return col
|
|
end
|
|
|
|
---@param preview_height integer
|
|
---@param outline_height integer
|
|
local function get_row(preview_height, outline_height)
|
|
local offset = math.floor((outline_height - preview_height) / 2) - 1
|
|
return vim.api.nvim_win_get_position(outline.current.view.winnr)[1] + offset
|
|
end
|
|
|
|
local function get_height()
|
|
return vim.api.nvim_win_get_height(outline.current.view.winnr)
|
|
end
|
|
|
|
local function get_hovered_node()
|
|
local hovered_line = vim.api.nvim_win_get_cursor(outline.current.view.winnr)[1]
|
|
local node = outline.current.flats[hovered_line]
|
|
return node
|
|
end
|
|
|
|
local function update_preview(code_buf)
|
|
code_buf = code_buf or outline.current.code.buf
|
|
|
|
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, false, 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 = outline.current.code.buf
|
|
local ft = vim.api.nvim_buf_get_option(code_buf, 'filetype')
|
|
|
|
vim.api.nvim_buf_set_option(state.preview_buf, 'syntax', ft)
|
|
|
|
local ts_highlight_fn = vim.treesitter.start
|
|
if not _G._outline_nvim_has[8] then
|
|
local ok, ts_highlight = pcall(require, 'nvim-treesitter.highlight')
|
|
if ok then
|
|
ts_highlight_fn = ts_highlight.attach
|
|
end
|
|
end
|
|
pcall(ts_highlight_fn, state.preview_buf, 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 width = cfg.get_preview_width()
|
|
local winheight = math.max(math.ceil(height / 2), cfg.o.preview_window.min_height)
|
|
state.preview_win = vim.api.nvim_open_win(state.preview_buf, false, {
|
|
relative = 'editor',
|
|
height = winheight,
|
|
width = width,
|
|
bufpos = { 0, 0 },
|
|
col = get_col(width),
|
|
-- Position preview window middle-aligned vertically
|
|
row = get_row(winheight, height),
|
|
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
|