feat(api): Get symbol and breadcrumb at location
Currently the implementation is very limited. Ref: #24 - Outline must be open and have been loaded for it to work (requires lazy loading or headless loading of Sidebar) - Empty string returned if cursor is not in any symbol ('closest' symbol not yet supported) - Line column not used - Returning concatenated symbol names rather than a list of tables with node info (requires a refactor of outline.SymbolNode type) - Subject to config.symbols.filter and folds (requires finding hover list somewhere outside of writer.make_outline)
This commit is contained in:
@@ -120,13 +120,14 @@ end
|
||||
function M.open_outline(opts)
|
||||
local tab = vim.api.nvim_get_current_tabpage()
|
||||
local sidebar = M.sidebars[tab]
|
||||
M.current = sidebar
|
||||
|
||||
if not sidebar then
|
||||
sidebar = Sidebar:new()
|
||||
M.sidebars[tab] = sidebar
|
||||
end
|
||||
|
||||
M.current = sidebar
|
||||
|
||||
return sidebar:open(opts)
|
||||
end
|
||||
|
||||
@@ -135,6 +136,85 @@ function M.is_focus_in_outline()
|
||||
return M._sidebar_do('has_focus')
|
||||
end
|
||||
|
||||
M.has_focus = M.is_focus_in_outline
|
||||
|
||||
---@return boolean
|
||||
function M.is_open()
|
||||
local sidebar = M._get_sidebar()
|
||||
if sidebar then
|
||||
return sidebar.view:is_open()
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---@param sidebar outline.Sidebar
|
||||
---@param opts outline.BreadcrumbOpts
|
||||
---@return string
|
||||
local function _get_breadcrumb(sidebar, opts)
|
||||
local list = sidebar.hovered
|
||||
-- TODO: Use cursor column in writer.make_outline to prevent
|
||||
-- siblings being included
|
||||
local names = {}
|
||||
for _, node in ipairs(list) do
|
||||
if node.depth > opts.depth then
|
||||
break
|
||||
end
|
||||
names[node.depth] = node.name
|
||||
end
|
||||
|
||||
return table.concat(names, opts.sep)
|
||||
end
|
||||
|
||||
---@param opts outline.BreadcrumbOpts?
|
||||
---@return string? breadcrumb nil if outline not open
|
||||
function M.get_breadcrumb(opts)
|
||||
local sidebar = M._get_sidebar()
|
||||
|
||||
if not sidebar then
|
||||
return
|
||||
end
|
||||
|
||||
opts = opts or {}
|
||||
opts.sep = opts.sep or ' > '
|
||||
if not opts.depth or opts.depth < 1 then
|
||||
opts.depth = math.huge
|
||||
end
|
||||
|
||||
return _get_breadcrumb(sidebar, opts)
|
||||
end
|
||||
|
||||
---@param opts outline.SymbolOpts
|
||||
---@return string?
|
||||
function M.get_symbol(opts)
|
||||
local sidebar = M._get_sidebar()
|
||||
if not sidebar then
|
||||
return
|
||||
end
|
||||
|
||||
opts = opts or {}
|
||||
local depth = opts.depth
|
||||
if not opts.depth or opts.depth < 1 then
|
||||
depth = math.huge
|
||||
end
|
||||
|
||||
if not utils.table_has_content(sidebar.hovered) then
|
||||
return ""
|
||||
end
|
||||
|
||||
local kind
|
||||
if opts.kind then
|
||||
kind = require('outline.symbols').str_to_kind[opts.kind]
|
||||
end
|
||||
|
||||
for i = #sidebar.hovered, 1, -1 do
|
||||
local node = sidebar.hovered[i]
|
||||
if node.depth <= depth and (not kind or node.kind == kind) then
|
||||
return node.name
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
---Handle follow cursor command with bang
|
||||
local function _cmd_follow_cursor(opts)
|
||||
M.follow_cursor({ focus_outline = not opts.bang })
|
||||
|
||||
@@ -28,12 +28,7 @@ end
|
||||
---@param result table
|
||||
local function convert_symbols(result)
|
||||
local s = {}
|
||||
local kinds_index = {}
|
||||
-- create a inverse indexing of symbols.kind
|
||||
local symbols = require('outline.symbols')
|
||||
for k, v in pairs(symbols.kinds) do
|
||||
kinds_index[v] = k
|
||||
end
|
||||
local kinds_index = require('outline.symbol').str_to_kind
|
||||
-- rebuild coc.nvim symbol list hierarchy according to the 'level' key
|
||||
for _, value in pairs(result) do
|
||||
value.children = {}
|
||||
|
||||
@@ -17,6 +17,7 @@ local Sidebar = {}
|
||||
---@field view outline.View
|
||||
---@field items outline.SymbolNode[]
|
||||
---@field flats outline.FlatSymbolNode[]
|
||||
---@field hovered outline.FlatSymbolNode[]
|
||||
---@field original_cursor string
|
||||
---@field code outline.SidebarCodeState
|
||||
---@field autocmds { [integer]: integer } winnr to autocmd id
|
||||
@@ -27,6 +28,7 @@ function Sidebar:new()
|
||||
code = { buf = 0, win = 0 },
|
||||
items = {},
|
||||
flats = {},
|
||||
hovered = {},
|
||||
autocmds = {},
|
||||
original_cursor = vim.o.guicursor,
|
||||
}, { __index = Sidebar })
|
||||
@@ -94,7 +96,7 @@ function Sidebar:initial_handler(response, opts)
|
||||
self.items = items
|
||||
|
||||
local current
|
||||
self.flats, current = writer.make_outline(self.view.bufnr, items, self.code.win)
|
||||
self.flats, current, self.hovered = writer.make_outline(self.view.bufnr, items, self.code.win)
|
||||
|
||||
self:update_cursor_pos(current)
|
||||
|
||||
@@ -261,7 +263,7 @@ end
|
||||
---@param set_cursor_to_node outline.SymbolNode|outline.FlatSymbolNode?
|
||||
function Sidebar:_update_lines(update_cursor, set_cursor_to_node)
|
||||
local current
|
||||
self.flats, current =
|
||||
self.flats, current, self.hovered =
|
||||
writer.make_outline(self.view.bufnr, self.items, self.code.win, set_cursor_to_node)
|
||||
if update_cursor ~= false then
|
||||
self:update_cursor_pos(current)
|
||||
|
||||
@@ -39,6 +39,12 @@ M.kinds = {
|
||||
[255] = 'Macro',
|
||||
}
|
||||
|
||||
-- inverse indexing of symbols.kind
|
||||
M.str_to_kind = {}
|
||||
for k, v in pairs(M.kinds) do
|
||||
M.str_to_kind[v] = k
|
||||
end
|
||||
|
||||
---@param kind string|integer
|
||||
function M.icon_from_kind(kind)
|
||||
local kindstr = kind
|
||||
|
||||
@@ -93,3 +93,11 @@
|
||||
---@class outline.OutlineOpts
|
||||
---@field focus_outline boolean? Whether to focus on outline of after some operation. If nil, defaults to true
|
||||
---@field split_command string?
|
||||
|
||||
---@class outline.BreadcrumbOpts
|
||||
---@field depth integer?
|
||||
---@field sep string?
|
||||
|
||||
---@class outline.SymbolOpts
|
||||
---@field depth integer?
|
||||
---@field kind string?
|
||||
|
||||
@@ -68,16 +68,17 @@ end
|
||||
---@param items outline.SymbolNode[] Tree of symbols after being parsed by parser.parse_result
|
||||
---@param codewin integer code window
|
||||
---@param find_node outline.FlatSymbolNode|outline.SymbolNode? Find a given node rather than node matching cursor position in codewin
|
||||
---@return outline.FlatSymbolNode[],outline.FlatSymbolNode? flattened_items Empty table returned if bufnr is invalid
|
||||
---@return outline.FlatSymbolNode[],outline.FlatSymbolNode?,outline.FlatSymbolNode[]? flattened_items Empty table returned if bufnr is invalid
|
||||
function M.make_outline(bufnr, items, codewin, find_node)
|
||||
if not M.is_buffer_outline(bufnr) then
|
||||
return {}, nil
|
||||
return {}, nil, {}
|
||||
end
|
||||
local codebuf = vim.api.nvim_win_get_buf(codewin)
|
||||
-- 0-indexed
|
||||
local hovered_line = vim.api.nvim_win_get_cursor(codewin)[1] - 1
|
||||
-- Deepest matching node to put cursor on based on hovered line
|
||||
local put_cursor
|
||||
local hover_list = {}
|
||||
|
||||
clear_virt_text(bufnr)
|
||||
|
||||
@@ -144,6 +145,7 @@ function M.make_outline(bufnr, items, codewin, find_node)
|
||||
-- XXX: not setting for children, but it works because when unfold is called
|
||||
-- this function is called again anyway.
|
||||
node.hovered = true
|
||||
table.insert(hover_list, node)
|
||||
if not find_node then
|
||||
put_cursor = node
|
||||
end
|
||||
@@ -270,7 +272,7 @@ function M.make_outline(bufnr, items, codewin, find_node)
|
||||
end
|
||||
end
|
||||
|
||||
return flattened, put_cursor
|
||||
return flattened, put_cursor, hover_list
|
||||
end
|
||||
-- XXX: Is the performance tradeoff of calling `nvim_buf_set_lines` on each
|
||||
-- iteration worth it in order to put setting of highlights, details, and
|
||||
|
||||
Reference in New Issue
Block a user