fix/refactor(preview): Proper per-sidebar support & complete rewrite
This commit is contained in:
@@ -154,20 +154,18 @@ end
|
|||||||
function M.get_window_width()
|
function M.get_window_width()
|
||||||
if M.o.outline_window.relative_width then
|
if M.o.outline_window.relative_width then
|
||||||
return math.ceil(vim.o.columns * (M.o.outline_window.width / 100))
|
return math.ceil(vim.o.columns * (M.o.outline_window.width / 100))
|
||||||
else
|
|
||||||
return M.o.outline_window.width
|
|
||||||
end
|
end
|
||||||
|
return M.o.outline_window.width
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.get_preview_width()
|
function M.get_preview_width()
|
||||||
if M.o.preview_window.relative_width then
|
if M.o.preview_window.relative_width then
|
||||||
local relative_width = math.ceil(vim.o.columns * (M.o.preview_window.width / 100))
|
local relative_width = math.max(
|
||||||
|
math.ceil(vim.o.columns * (M.o.preview_window.width / 100)),
|
||||||
|
M.o.preview_window.min_width
|
||||||
|
)
|
||||||
|
|
||||||
if relative_width < M.o.preview_window.min_width then
|
return relative_width
|
||||||
return M.o.preview_window.min_width
|
|
||||||
else
|
|
||||||
return relative_width
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
return M.o.preview_window.width
|
return M.o.preview_window.width
|
||||||
end
|
end
|
||||||
@@ -332,6 +330,10 @@ function M.resolve_config()
|
|||||||
M.o.keymaps[action] = { keys }
|
M.o.keymaps[action] = { keys }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
----- PREVIEW -----
|
||||||
|
M.o.preview_window.width = M.get_preview_width()
|
||||||
|
----- WINDOW -----
|
||||||
|
M.o.outline_window.width = M.get_window_width()
|
||||||
end
|
end
|
||||||
|
|
||||||
---Ensure l is either table, false, or nil. If not, print warning using given
|
---Ensure l is either table, false, or nil. If not, print warning using given
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ function M.show_status()
|
|||||||
if not sidebar then
|
if not sidebar then
|
||||||
ctx.code_win_active = true
|
ctx.code_win_active = true
|
||||||
else
|
else
|
||||||
ctx.code_win_active = require('outline.preview').has_code_win(win)
|
ctx.code_win_active = sidebar:has_code_win()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,88 +1,67 @@
|
|||||||
local cfg = require('outline.config')
|
local cfg = require('outline.config')
|
||||||
local hover = require('outline.hover')
|
local providers = require('outline.providers')
|
||||||
local outline = require('outline')
|
|
||||||
|
|
||||||
local M = {}
|
local conf
|
||||||
|
|
||||||
local state = {
|
---@class outline.Preview
|
||||||
preview_buf = nil,
|
local Preview = {}
|
||||||
preview_win = nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
local function is_current_win_outline()
|
---@class outline.Preview
|
||||||
local curwin = vim.api.nvim_get_current_win()
|
---@field buf integer
|
||||||
return curwin == outline.current.view.win
|
---@field win integer
|
||||||
|
---@field width integer
|
||||||
|
---@field height integer
|
||||||
|
---@field outline_height integer
|
||||||
|
---@field s outline.Sidebar
|
||||||
|
|
||||||
|
---@param s outline.Sidebar
|
||||||
|
function Preview:new(s)
|
||||||
|
-- Config must have been setup when calling Preview:new
|
||||||
|
conf = cfg.o.preview_window
|
||||||
|
return setmetatable({
|
||||||
|
buf = nil,
|
||||||
|
win = nil,
|
||||||
|
s = s,
|
||||||
|
width = nil,
|
||||||
|
height = nil,
|
||||||
|
}, { __index = Preview })
|
||||||
end
|
end
|
||||||
|
|
||||||
local function has_code_win(winnr)
|
---Creates new preview window and sets the content. Calls setup and set_lines.
|
||||||
if not outline.current then
|
function Preview:create()
|
||||||
return false
|
self.buf = vim.api.nvim_create_buf(false, true)
|
||||||
end
|
vim.api.nvim_buf_attach(self.buf, false, {
|
||||||
winnr = winnr or outline.current.code.win
|
on_detach = function()
|
||||||
return vim.api.nvim_win_is_valid(winnr) and vim.api.nvim_buf_is_valid(outline.current.code.buf)
|
self.buf = nil
|
||||||
|
self.win = nil
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
-- FIXME: Re-calculate dimensions on update-preview, in case outline window
|
||||||
|
-- was resized between last preview and next preview?
|
||||||
|
self.outline_height = vim.api.nvim_win_get_height(self.s.view.win)
|
||||||
|
self.width = conf.width
|
||||||
|
self.height = math.max(math.ceil(self.outline_height / 2), conf.min_height)
|
||||||
|
self.win = vim.api.nvim_open_win(self.buf, false, {
|
||||||
|
relative = 'editor',
|
||||||
|
height = self.height,
|
||||||
|
width = self.width,
|
||||||
|
bufpos = { 0, 0 },
|
||||||
|
col = self:calc_col(),
|
||||||
|
row = self:calc_row(),
|
||||||
|
border = conf.border,
|
||||||
|
})
|
||||||
|
self:setup()
|
||||||
|
self:set_lines()
|
||||||
end
|
end
|
||||||
|
|
||||||
M.has_code_win = has_code_win
|
---Set up highlights, window, and buffer options
|
||||||
|
function Preview:setup()
|
||||||
|
vim.api.nvim_win_set_option(self.win, 'winhl', conf.winhl)
|
||||||
|
vim.api.nvim_win_set_option(self.win, 'winblend', conf.winblend)
|
||||||
|
|
||||||
---Get the correct column to place the floating window based on
|
local code_buf = self.s.code.buf
|
||||||
-- 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.win
|
|
||||||
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.win)[1] + offset
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_height()
|
|
||||||
return vim.api.nvim_win_get_height(outline.current.view.win)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_hovered_node()
|
|
||||||
local hovered_line = vim.api.nvim_win_get_cursor(outline.current.view.win)[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')
|
local ft = vim.api.nvim_buf_get_option(code_buf, 'filetype')
|
||||||
|
vim.api.nvim_buf_set_option(self.buf, 'syntax', ft)
|
||||||
vim.api.nvim_buf_set_option(state.preview_buf, 'syntax', ft)
|
|
||||||
|
|
||||||
local ts_highlight_fn = vim.treesitter.start
|
local ts_highlight_fn = vim.treesitter.start
|
||||||
if not _G._outline_nvim_has[8] then
|
if not _G._outline_nvim_has[8] then
|
||||||
@@ -91,75 +70,95 @@ local function setup_preview_buf()
|
|||||||
ts_highlight_fn = ts_highlight.attach
|
ts_highlight_fn = ts_highlight.attach
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
pcall(ts_highlight_fn, state.preview_buf, ft)
|
pcall(ts_highlight_fn, self.buf, ft)
|
||||||
|
|
||||||
vim.api.nvim_buf_set_option(state.preview_buf, 'bufhidden', 'delete')
|
vim.api.nvim_buf_set_option(self.buf, 'bufhidden', 'delete')
|
||||||
vim.api.nvim_win_set_option(state.preview_win, 'cursorline', true)
|
vim.api.nvim_win_set_option(self.win, 'cursorline', true)
|
||||||
update_preview(code_buf)
|
vim.api.nvim_buf_set_option(self.buf, 'modifiable', false)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function set_bg_hl()
|
---Get the correct column to place the floating window based on relative
|
||||||
vim.api.nvim_win_set_option(state.preview_win, 'winhl', cfg.o.preview_window.winhl)
|
---positions of the outline and the code window.
|
||||||
vim.api.nvim_win_set_option(state.preview_win, 'winblend', cfg.o.preview_window.winblend)
|
function Preview:calc_col()
|
||||||
end
|
---@type integer
|
||||||
|
local outline_winnr = self.s.view.win
|
||||||
|
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(self.s.code.win)[2]
|
||||||
|
|
||||||
local function show_preview()
|
-- TODO: What if code win is below/above outline instead?
|
||||||
if state.preview_win == nil and state.preview_buf == nil then
|
|
||||||
state.preview_buf = vim.api.nvim_create_buf(false, true)
|
local col = outline_col
|
||||||
vim.api.nvim_buf_attach(state.preview_buf, false, {
|
if outline_col > code_col then
|
||||||
on_detach = function()
|
col = col - self.width - 3
|
||||||
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
|
else
|
||||||
update_preview()
|
col = col + outline_width + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return col
|
||||||
|
end
|
||||||
|
|
||||||
|
---Get the vertically center-aligned row for preview window
|
||||||
|
function Preview:calc_row()
|
||||||
|
local offset = math.floor((self.outline_height - self.height) / 2) - 1
|
||||||
|
return vim.api.nvim_win_get_position(self.s.view.win)[1] + offset
|
||||||
|
end
|
||||||
|
|
||||||
|
---Set and update preview buffer content
|
||||||
|
function Preview:set_lines()
|
||||||
|
-- TODO: Editable, savable buffer in the preview like VS Code for quick
|
||||||
|
-- edits? It can be like LSP. Trigger preview to open, trigger again to focus
|
||||||
|
-- (so buffer can be edited).
|
||||||
|
-- This can be achieved by simply opening the buffer from inside the preview
|
||||||
|
-- window.
|
||||||
|
-- This also removes the need of manually setting highlights, treesitter etc.
|
||||||
|
-- The preview window will look exactly the same as in the code window.
|
||||||
|
local node = self.s:_current_node()
|
||||||
|
if not node then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local lines = vim.api.nvim_buf_get_lines(self.s.code.buf, 0, -1, false)
|
||||||
|
|
||||||
|
if self.buf ~= nil then
|
||||||
|
vim.api.nvim_buf_set_option(self.buf, 'modifiable', true)
|
||||||
|
vim.api.nvim_buf_set_lines(self.buf, 0, -1, false, lines)
|
||||||
|
vim.api.nvim_buf_set_option(self.buf, 'modifiable', false)
|
||||||
|
vim.api.nvim_win_set_cursor(self.win, { node.line + 1, node.character })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.show()
|
---Create or update preview
|
||||||
if not is_current_win_outline() or #vim.api.nvim_list_wins() < 2 then
|
function Preview:show()
|
||||||
|
if not self.s:has_focus() or #vim.api.nvim_list_wins() < 2 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
show_preview()
|
if self.buf and self.win then
|
||||||
set_bg_hl()
|
self:set_lines()
|
||||||
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
|
else
|
||||||
M.show()
|
self:create()
|
||||||
|
end
|
||||||
|
|
||||||
|
if conf.open_hover_on_preview then
|
||||||
|
providers.action(self.s, 'show_hover', { self.s })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
function Preview:close()
|
||||||
|
-- TODO: Why was this in symbols-outline.nvim?
|
||||||
|
-- if self.s:has_code_win() then
|
||||||
|
if self.win ~= nil and vim.api.nvim_win_is_valid(self.win) then
|
||||||
|
vim.api.nvim_win_close(self.win, true)
|
||||||
|
end
|
||||||
|
-- end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Preview:toggle()
|
||||||
|
if self.win ~= nil then
|
||||||
|
self:close()
|
||||||
|
else
|
||||||
|
self:show()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Preview
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
local View = require('outline.view')
|
local View = require('outline.view')
|
||||||
|
local Preview = require('outline.preview')
|
||||||
local cfg = require('outline.config')
|
local cfg = require('outline.config')
|
||||||
local folding = require('outline.folding')
|
local folding = require('outline.folding')
|
||||||
local parser = require('outline.parser')
|
local parser = require('outline.parser')
|
||||||
@@ -24,10 +25,12 @@ local Sidebar = {}
|
|||||||
---@field code outline.SidebarCodeState
|
---@field code outline.SidebarCodeState
|
||||||
---@field autocmds { [integer]: integer } winnr to autocmd id
|
---@field autocmds { [integer]: integer } winnr to autocmd id
|
||||||
---@field provider outline.Provider?
|
---@field provider outline.Provider?
|
||||||
|
---@field preview outline.Preview
|
||||||
|
|
||||||
function Sidebar:new()
|
function Sidebar:new()
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
view = View:new(),
|
view = View:new(),
|
||||||
|
preview = Preview:new(),
|
||||||
code = { buf = 0, win = 0 },
|
code = { buf = 0, win = 0 },
|
||||||
items = {},
|
items = {},
|
||||||
flats = {},
|
flats = {},
|
||||||
@@ -136,7 +139,7 @@ function Sidebar:setup_keymaps()
|
|||||||
goto_and_close = { '_goto_and_close', {} },
|
goto_and_close = { '_goto_and_close', {} },
|
||||||
down_and_jump = { '_move_and_jump', { 'down' } },
|
down_and_jump = { '_move_and_jump', { 'down' } },
|
||||||
up_and_jump = { '_move_and_jump', { 'up' } },
|
up_and_jump = { '_move_and_jump', { 'up' } },
|
||||||
toggle_preview = { require('outline.preview').toggle, {} },
|
toggle_preview = { function() self.preview:toggle() end, {} },
|
||||||
fold_toggle = { '_toggle_fold', {} },
|
fold_toggle = { '_toggle_fold', {} },
|
||||||
fold = { '_set_folded', { true } },
|
fold = { '_set_folded', { true } },
|
||||||
unfold = { '_set_folded', { false } },
|
unfold = { '_set_folded', { false } },
|
||||||
@@ -163,12 +166,12 @@ function Sidebar:setup_buffer_autocmd()
|
|||||||
if cfg.o.preview_window.auto_preview then
|
if cfg.o.preview_window.auto_preview then
|
||||||
vim.api.nvim_create_autocmd('CursorMoved', {
|
vim.api.nvim_create_autocmd('CursorMoved', {
|
||||||
buffer = 0,
|
buffer = 0,
|
||||||
callback = require('outline.preview').show,
|
callback = function() self.preview:show() end,
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
vim.api.nvim_create_autocmd('CursorMoved', {
|
vim.api.nvim_create_autocmd('CursorMoved', {
|
||||||
buffer = 0,
|
buffer = 0,
|
||||||
callback = require('outline.preview').close,
|
callback = function() self.preview:close() end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
if cfg.o.outline_window.auto_jump then
|
if cfg.o.outline_window.auto_jump then
|
||||||
@@ -465,6 +468,10 @@ function Sidebar:_set_all_folded(folded, nodes)
|
|||||||
self:_update_lines(true, current)
|
self:_update_lines(true, current)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Sidebar:has_code_win()
|
||||||
|
return self.code.win and self.code.buf and self.code.win ~= 0 and self.code.buf ~= 0 and vim.api.nvim_win_is_valid(self.code.win) and vim.api.nvim_buf_is_valid(self.code.buf)
|
||||||
|
end
|
||||||
|
|
||||||
---@see outline.follow_cursor
|
---@see outline.follow_cursor
|
||||||
---@param opts outline.OutlineOpts?
|
---@param opts outline.OutlineOpts?
|
||||||
---@return boolean ok
|
---@return boolean ok
|
||||||
@@ -473,7 +480,7 @@ function Sidebar:follow_cursor(opts)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if require('outline.preview').has_code_win(self.code.win) then
|
if self:has_code_win() then
|
||||||
self:_highlight_current_item(self.code.win, true)
|
self:_highlight_current_item(self.code.win, true)
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
@@ -516,6 +523,7 @@ function Sidebar:open(opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not self.view:is_open() then
|
if not self.view:is_open() then
|
||||||
|
self.preview.s = self
|
||||||
self.provider = providers.find_provider()
|
self.provider = providers.find_provider()
|
||||||
if not self.provider then
|
if not self.provider then
|
||||||
utils.echo('No providers found for current buffer')
|
utils.echo('No providers found for current buffer')
|
||||||
@@ -531,6 +539,7 @@ end
|
|||||||
function Sidebar:close()
|
function Sidebar:close()
|
||||||
local code_win = self.code.win
|
local code_win = self.code.win
|
||||||
self.view:close()
|
self.view:close()
|
||||||
|
self.preview:close()
|
||||||
vim.fn.win_gotoid(code_win)
|
vim.fn.win_gotoid(code_win)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -547,7 +556,7 @@ end
|
|||||||
---@see outline.focus_code
|
---@see outline.focus_code
|
||||||
---@return boolean ok
|
---@return boolean ok
|
||||||
function Sidebar:focus_code()
|
function Sidebar:focus_code()
|
||||||
if require('outline.preview').has_code_win(self.code.win) then
|
if self:has_code_win() then
|
||||||
vim.fn.win_gotoid(self.code.win)
|
vim.fn.win_gotoid(self.code.win)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -557,7 +566,7 @@ end
|
|||||||
---@see outline.focus_toggle
|
---@see outline.focus_toggle
|
||||||
---@return boolean ok
|
---@return boolean ok
|
||||||
function Sidebar:focus_toggle()
|
function Sidebar:focus_toggle()
|
||||||
if self.view:is_open() and require('outline.preview').has_code_win(self.code.win) then
|
if self.view:is_open() and self:has_code_win() then
|
||||||
local winid = vim.fn.win_getid()
|
local winid = vim.fn.win_getid()
|
||||||
if winid == self.code.win then
|
if winid == self.code.win then
|
||||||
vim.fn.win_gotoid(self.view.win)
|
vim.fn.win_gotoid(self.view.win)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function View:setup_view(split_command)
|
|||||||
vim.cmd(split_command)
|
vim.cmd(split_command)
|
||||||
|
|
||||||
-- resize to a % of the current window size
|
-- resize to a % of the current window size
|
||||||
vim.cmd('vertical resize ' .. cfg.get_window_width())
|
vim.cmd('vertical resize ' .. cfg.o.outline_window.width)
|
||||||
|
|
||||||
-- get current (outline) window and attach our buffer to it
|
-- get current (outline) window and attach our buffer to it
|
||||||
self.win = vim.api.nvim_get_current_win()
|
self.win = vim.api.nvim_get_current_win()
|
||||||
|
|||||||
Reference in New Issue
Block a user