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:
19
README.md
19
README.md
@@ -790,6 +790,25 @@ require'outline'
|
|||||||
This is automatically called on events
|
This is automatically called on events
|
||||||
`outline_items.auto_update_events.refresh` from config.
|
`outline_items.auto_update_events.refresh` from config.
|
||||||
|
|
||||||
|
- **get_breadcrumb(opts)**
|
||||||
|
|
||||||
|
Return a string concatenated from hovered symbols hierarchy representing code
|
||||||
|
location.
|
||||||
|
|
||||||
|
Optional opts table fields:
|
||||||
|
- depth (nil): Maximum depth of the last symbol included. First item has depth 1.
|
||||||
|
Set to 0 or nil to include all
|
||||||
|
- sep (` > `): String for separator
|
||||||
|
|
||||||
|
- **get_symbol(opts)**
|
||||||
|
|
||||||
|
Return the symbol name of the deepest hovered symbol representing code
|
||||||
|
location.
|
||||||
|
|
||||||
|
Optional opts table fields:
|
||||||
|
- depth (nil): Maximum depth of the returned symbol
|
||||||
|
- kind (nil): Symbol kind to search for (string).
|
||||||
|
|
||||||
|
|
||||||
## Tips
|
## Tips
|
||||||
|
|
||||||
|
|||||||
@@ -120,13 +120,14 @@ end
|
|||||||
function M.open_outline(opts)
|
function M.open_outline(opts)
|
||||||
local tab = vim.api.nvim_get_current_tabpage()
|
local tab = vim.api.nvim_get_current_tabpage()
|
||||||
local sidebar = M.sidebars[tab]
|
local sidebar = M.sidebars[tab]
|
||||||
M.current = sidebar
|
|
||||||
|
|
||||||
if not sidebar then
|
if not sidebar then
|
||||||
sidebar = Sidebar:new()
|
sidebar = Sidebar:new()
|
||||||
M.sidebars[tab] = sidebar
|
M.sidebars[tab] = sidebar
|
||||||
end
|
end
|
||||||
|
|
||||||
|
M.current = sidebar
|
||||||
|
|
||||||
return sidebar:open(opts)
|
return sidebar:open(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -135,6 +136,85 @@ function M.is_focus_in_outline()
|
|||||||
return M._sidebar_do('has_focus')
|
return M._sidebar_do('has_focus')
|
||||||
end
|
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
|
---Handle follow cursor command with bang
|
||||||
local function _cmd_follow_cursor(opts)
|
local function _cmd_follow_cursor(opts)
|
||||||
M.follow_cursor({ focus_outline = not opts.bang })
|
M.follow_cursor({ focus_outline = not opts.bang })
|
||||||
|
|||||||
@@ -28,12 +28,7 @@ end
|
|||||||
---@param result table
|
---@param result table
|
||||||
local function convert_symbols(result)
|
local function convert_symbols(result)
|
||||||
local s = {}
|
local s = {}
|
||||||
local kinds_index = {}
|
local kinds_index = require('outline.symbol').str_to_kind
|
||||||
-- 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
|
|
||||||
-- rebuild coc.nvim symbol list hierarchy according to the 'level' key
|
-- rebuild coc.nvim symbol list hierarchy according to the 'level' key
|
||||||
for _, value in pairs(result) do
|
for _, value in pairs(result) do
|
||||||
value.children = {}
|
value.children = {}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ local Sidebar = {}
|
|||||||
---@field view outline.View
|
---@field view outline.View
|
||||||
---@field items outline.SymbolNode[]
|
---@field items outline.SymbolNode[]
|
||||||
---@field flats outline.FlatSymbolNode[]
|
---@field flats outline.FlatSymbolNode[]
|
||||||
|
---@field hovered outline.FlatSymbolNode[]
|
||||||
---@field original_cursor string
|
---@field original_cursor string
|
||||||
---@field code outline.SidebarCodeState
|
---@field code outline.SidebarCodeState
|
||||||
---@field autocmds { [integer]: integer } winnr to autocmd id
|
---@field autocmds { [integer]: integer } winnr to autocmd id
|
||||||
@@ -27,6 +28,7 @@ function Sidebar:new()
|
|||||||
code = { buf = 0, win = 0 },
|
code = { buf = 0, win = 0 },
|
||||||
items = {},
|
items = {},
|
||||||
flats = {},
|
flats = {},
|
||||||
|
hovered = {},
|
||||||
autocmds = {},
|
autocmds = {},
|
||||||
original_cursor = vim.o.guicursor,
|
original_cursor = vim.o.guicursor,
|
||||||
}, { __index = Sidebar })
|
}, { __index = Sidebar })
|
||||||
@@ -94,7 +96,7 @@ function Sidebar:initial_handler(response, opts)
|
|||||||
self.items = items
|
self.items = items
|
||||||
|
|
||||||
local current
|
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)
|
self:update_cursor_pos(current)
|
||||||
|
|
||||||
@@ -261,7 +263,7 @@ end
|
|||||||
---@param set_cursor_to_node outline.SymbolNode|outline.FlatSymbolNode?
|
---@param set_cursor_to_node outline.SymbolNode|outline.FlatSymbolNode?
|
||||||
function Sidebar:_update_lines(update_cursor, set_cursor_to_node)
|
function Sidebar:_update_lines(update_cursor, set_cursor_to_node)
|
||||||
local current
|
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)
|
writer.make_outline(self.view.bufnr, self.items, self.code.win, set_cursor_to_node)
|
||||||
if update_cursor ~= false then
|
if update_cursor ~= false then
|
||||||
self:update_cursor_pos(current)
|
self:update_cursor_pos(current)
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ M.kinds = {
|
|||||||
[255] = 'Macro',
|
[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
|
---@param kind string|integer
|
||||||
function M.icon_from_kind(kind)
|
function M.icon_from_kind(kind)
|
||||||
local kindstr = kind
|
local kindstr = kind
|
||||||
|
|||||||
@@ -93,3 +93,11 @@
|
|||||||
---@class outline.OutlineOpts
|
---@class outline.OutlineOpts
|
||||||
---@field focus_outline boolean? Whether to focus on outline of after some operation. If nil, defaults to true
|
---@field focus_outline boolean? Whether to focus on outline of after some operation. If nil, defaults to true
|
||||||
---@field split_command string?
|
---@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 items outline.SymbolNode[] Tree of symbols after being parsed by parser.parse_result
|
||||||
---@param codewin integer code window
|
---@param codewin integer code window
|
||||||
---@param find_node outline.FlatSymbolNode|outline.SymbolNode? Find a given node rather than node matching cursor position in codewin
|
---@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)
|
function M.make_outline(bufnr, items, codewin, find_node)
|
||||||
if not M.is_buffer_outline(bufnr) then
|
if not M.is_buffer_outline(bufnr) then
|
||||||
return {}, nil
|
return {}, nil, {}
|
||||||
end
|
end
|
||||||
local codebuf = vim.api.nvim_win_get_buf(codewin)
|
local codebuf = vim.api.nvim_win_get_buf(codewin)
|
||||||
-- 0-indexed
|
-- 0-indexed
|
||||||
local hovered_line = vim.api.nvim_win_get_cursor(codewin)[1] - 1
|
local hovered_line = vim.api.nvim_win_get_cursor(codewin)[1] - 1
|
||||||
-- Deepest matching node to put cursor on based on hovered line
|
-- Deepest matching node to put cursor on based on hovered line
|
||||||
local put_cursor
|
local put_cursor
|
||||||
|
local hover_list = {}
|
||||||
|
|
||||||
clear_virt_text(bufnr)
|
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
|
-- XXX: not setting for children, but it works because when unfold is called
|
||||||
-- this function is called again anyway.
|
-- this function is called again anyway.
|
||||||
node.hovered = true
|
node.hovered = true
|
||||||
|
table.insert(hover_list, node)
|
||||||
if not find_node then
|
if not find_node then
|
||||||
put_cursor = node
|
put_cursor = node
|
||||||
end
|
end
|
||||||
@@ -270,7 +272,7 @@ function M.make_outline(bufnr, items, codewin, find_node)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return flattened, put_cursor
|
return flattened, put_cursor, hover_list
|
||||||
end
|
end
|
||||||
-- XXX: Is the performance tradeoff of calling `nvim_buf_set_lines` on each
|
-- 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
|
-- iteration worth it in order to put setting of highlights, details, and
|
||||||
|
|||||||
Reference in New Issue
Block a user