16
README.md
16
README.md
@@ -43,6 +43,9 @@ local opts = {
|
|||||||
show_relative_numbers = false,
|
show_relative_numbers = false,
|
||||||
show_symbol_details = true,
|
show_symbol_details = true,
|
||||||
preview_bg_highlight = 'Pmenu',
|
preview_bg_highlight = 'Pmenu',
|
||||||
|
autofold_depth = nil,
|
||||||
|
auto_unfold_hover = true,
|
||||||
|
fold_markers = { '', '' },
|
||||||
keymaps = { -- These keymaps can be a string or a table for multiple keys
|
keymaps = { -- These keymaps can be a string or a table for multiple keys
|
||||||
close = {"<Esc>", "q"},
|
close = {"<Esc>", "q"},
|
||||||
goto_location = "<Cr>",
|
goto_location = "<Cr>",
|
||||||
@@ -51,6 +54,11 @@ local opts = {
|
|||||||
toggle_preview = "K",
|
toggle_preview = "K",
|
||||||
rename_symbol = "r",
|
rename_symbol = "r",
|
||||||
code_actions = "a",
|
code_actions = "a",
|
||||||
|
fold = "h",
|
||||||
|
unfold = "l",
|
||||||
|
fold_all = "W",
|
||||||
|
unfold_all = "E",
|
||||||
|
fold_reset = "R",
|
||||||
},
|
},
|
||||||
lsp_blacklist = {},
|
lsp_blacklist = {},
|
||||||
symbol_blacklist = {},
|
symbol_blacklist = {},
|
||||||
@@ -103,6 +111,9 @@ local opts = {
|
|||||||
| symbols | Icon and highlight config for symbol icons | table (dictionary) | scroll up |
|
| symbols | Icon and highlight config for symbol icons | table (dictionary) | scroll up |
|
||||||
| lsp_blacklist | Which lsp clients to ignore | table (array) | {} |
|
| lsp_blacklist | Which lsp clients to ignore | table (array) | {} |
|
||||||
| symbol_blacklist | Which symbols to ignore ([possible values](./lua/symbols-outline/symbols.lua)) | table (array) | {} |
|
| symbol_blacklist | Which symbols to ignore ([possible values](./lua/symbols-outline/symbols.lua)) | table (array) | {} |
|
||||||
|
| autofold_depth | Depth past which nodes will be folded by default | int | nil |
|
||||||
|
| auto_unfold_hover | Automatically unfold hovered symbol | boolean | true |
|
||||||
|
| fold_markers | Markers to denote foldable symbol's status | table (array) | { '', '' } |
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
@@ -123,6 +134,11 @@ local opts = {
|
|||||||
| K | Toggles the current symbol preview |
|
| K | Toggles the current symbol preview |
|
||||||
| r | Rename symbol |
|
| r | Rename symbol |
|
||||||
| a | Code actions |
|
| a | Code actions |
|
||||||
|
| h | Unfold symbol |
|
||||||
|
| l | Fold symbol |
|
||||||
|
| W | Fold all symbols |
|
||||||
|
| E | Unfold all symbols |
|
||||||
|
| R | Reset all folding |
|
||||||
| ? | Show help message |
|
| ? | Show help message |
|
||||||
|
|
||||||
## Highlights
|
## Highlights
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ local writer = require 'symbols-outline.writer'
|
|||||||
local config = require 'symbols-outline.config'
|
local config = require 'symbols-outline.config'
|
||||||
local utils = require 'symbols-outline.utils.init'
|
local utils = require 'symbols-outline.utils.init'
|
||||||
local View = require 'symbols-outline.view'
|
local View = require 'symbols-outline.view'
|
||||||
|
local folding = require 'symbols-outline.folding'
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local function setup_global_autocmd()
|
local function setup_global_autocmd()
|
||||||
if config.options.highlight_hovered_item then
|
if
|
||||||
|
config.options.highlight_hovered_item or config.options.auto_unfold_hover
|
||||||
|
then
|
||||||
vim.api.nvim_create_autocmd('CursorHold', {
|
vim.api.nvim_create_autocmd('CursorHold', {
|
||||||
pattern = '*',
|
pattern = '*',
|
||||||
callback = function()
|
callback = function()
|
||||||
@@ -63,6 +66,18 @@ local function wipe_state()
|
|||||||
M.state = { outline_items = {}, flattened_outline_items = {}, code_win = 0 }
|
M.state = { outline_items = {}, flattened_outline_items = {}, code_win = 0 }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function _update_lines()
|
||||||
|
M.state.flattened_outline_items = parser.flatten(M.state.outline_items)
|
||||||
|
writer.parse_and_write(M.view.bufnr, M.state.flattened_outline_items)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _merge_items(items)
|
||||||
|
utils.merge_items_rec(
|
||||||
|
{ children = items },
|
||||||
|
{ children = M.state.outline_items }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
local function __refresh()
|
local function __refresh()
|
||||||
if M.view:is_open() then
|
if M.view:is_open() then
|
||||||
local function refresh_handler(response)
|
local function refresh_handler(response)
|
||||||
@@ -71,12 +86,11 @@ local function __refresh()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local items = parser.parse(response)
|
local items = parser.parse(response)
|
||||||
|
_merge_items(items)
|
||||||
|
|
||||||
M.state.code_win = vim.api.nvim_get_current_win()
|
M.state.code_win = vim.api.nvim_get_current_win()
|
||||||
M.state.outline_items = items
|
|
||||||
M.state.flattened_outline_items = parser.flatten(items)
|
|
||||||
|
|
||||||
writer.parse_and_write(M.view.bufnr, M.state.flattened_outline_items)
|
_update_lines()
|
||||||
end
|
end
|
||||||
|
|
||||||
providers.request_symbols(refresh_handler)
|
providers.request_symbols(refresh_handler)
|
||||||
@@ -85,9 +99,13 @@ end
|
|||||||
|
|
||||||
M._refresh = utils.debounce(__refresh, 100)
|
M._refresh = utils.debounce(__refresh, 100)
|
||||||
|
|
||||||
local function goto_location(change_focus)
|
function M._current_node()
|
||||||
local current_line = vim.api.nvim_win_get_cursor(M.view.winnr)[1]
|
local current_line = vim.api.nvim_win_get_cursor(M.view.winnr)[1]
|
||||||
local node = M.state.flattened_outline_items[current_line]
|
return M.state.flattened_outline_items[current_line]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function goto_location(change_focus)
|
||||||
|
local node = M._current_node()
|
||||||
vim.api.nvim_win_set_cursor(
|
vim.api.nvim_win_set_cursor(
|
||||||
M.state.code_win,
|
M.state.code_win,
|
||||||
{ node.line + 1, node.character }
|
{ node.line + 1, node.character }
|
||||||
@@ -100,6 +118,45 @@ local function goto_location(change_focus)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function M._set_folded(folded, move_cursor, node_index)
|
||||||
|
local node = M.state.flattened_outline_items[node_index] or M._current_node()
|
||||||
|
local changed = (folded ~= folding.is_folded(node))
|
||||||
|
|
||||||
|
if folding.is_foldable(node) and changed then
|
||||||
|
node.folded = folded
|
||||||
|
|
||||||
|
if move_cursor then
|
||||||
|
vim.api.nvim_win_set_cursor(M.view.winnr, { node_index, 0 })
|
||||||
|
end
|
||||||
|
|
||||||
|
_update_lines()
|
||||||
|
elseif node.parent then
|
||||||
|
local parent_node =
|
||||||
|
M.state.flattened_outline_items[node.parent.line_in_outline]
|
||||||
|
|
||||||
|
if parent_node then
|
||||||
|
M._set_folded(
|
||||||
|
folded,
|
||||||
|
not parent_node.folded and folded,
|
||||||
|
parent_node.line_in_outline
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M._set_all_folded(folded, nodes)
|
||||||
|
nodes = nodes or M.state.outline_items
|
||||||
|
|
||||||
|
for _, node in ipairs(nodes) do
|
||||||
|
node.folded = folded
|
||||||
|
if node.children then
|
||||||
|
M._set_all_folded(folded, node.children)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
_update_lines()
|
||||||
|
end
|
||||||
|
|
||||||
function M._highlight_current_item(winnr)
|
function M._highlight_current_item(winnr)
|
||||||
local has_provider = providers.has_provider()
|
local has_provider = providers.has_provider()
|
||||||
|
|
||||||
@@ -127,26 +184,31 @@ function M._highlight_current_item(winnr)
|
|||||||
|
|
||||||
local hovered_line = vim.api.nvim_win_get_cursor(win)[1] - 1
|
local hovered_line = vim.api.nvim_win_get_cursor(win)[1] - 1
|
||||||
|
|
||||||
local nodes = {}
|
local leaf_node = nil
|
||||||
for index, value in ipairs(M.state.flattened_outline_items) do
|
|
||||||
|
local cb = function(value)
|
||||||
|
value.hovered = nil
|
||||||
|
|
||||||
if
|
if
|
||||||
value.line == hovered_line
|
value.line == hovered_line
|
||||||
or (hovered_line > value.range_start and hovered_line < value.range_end)
|
or (hovered_line > value.range_start and hovered_line < value.range_end)
|
||||||
then
|
then
|
||||||
value.line_in_outline = index
|
value.hovered = true
|
||||||
table.insert(nodes, value)
|
leaf_node = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- clear old highlight
|
utils.items_dfs(cb, M.state.outline_items)
|
||||||
ui.clear_hover_highlight(M.view.bufnr)
|
|
||||||
for _, value in ipairs(nodes) do
|
_update_lines()
|
||||||
ui.add_hover_highlight(
|
|
||||||
M.view.bufnr,
|
if leaf_node then
|
||||||
value.line_in_outline - 1,
|
for index, node in ipairs(M.state.flattened_outline_items) do
|
||||||
value.depth * 2
|
if node == leaf_node then
|
||||||
)
|
vim.api.nvim_win_set_cursor(M.view.winnr, { index, 1 })
|
||||||
vim.api.nvim_win_set_cursor(M.view.winnr, { value.line_in_outline, 1 })
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -168,7 +230,10 @@ local function setup_keymaps(bufnr)
|
|||||||
require('symbols-outline.hover').show_hover
|
require('symbols-outline.hover').show_hover
|
||||||
)
|
)
|
||||||
-- preview symbol
|
-- preview symbol
|
||||||
map(config.options.keymaps.toggle_preview, require('symbols-outline.preview').toggle)
|
map(
|
||||||
|
config.options.keymaps.toggle_preview,
|
||||||
|
require('symbols-outline.preview').toggle
|
||||||
|
)
|
||||||
-- rename symbol
|
-- rename symbol
|
||||||
map(
|
map(
|
||||||
config.options.keymaps.rename_symbol,
|
config.options.keymaps.rename_symbol,
|
||||||
@@ -188,6 +253,26 @@ local function setup_keymaps(bufnr)
|
|||||||
map(config.options.keymaps.close, function()
|
map(config.options.keymaps.close, function()
|
||||||
M.view:close()
|
M.view:close()
|
||||||
end)
|
end)
|
||||||
|
-- fold selection
|
||||||
|
map(config.options.keymaps.fold, function()
|
||||||
|
M._set_folded(true)
|
||||||
|
end)
|
||||||
|
-- unfold selection
|
||||||
|
map(config.options.keymaps.unfold, function()
|
||||||
|
M._set_folded(false)
|
||||||
|
end)
|
||||||
|
-- fold all
|
||||||
|
map(config.options.keymaps.fold_all, function()
|
||||||
|
M._set_all_folded(true)
|
||||||
|
end)
|
||||||
|
-- unfold all
|
||||||
|
map(config.options.keymaps.unfold_all, function()
|
||||||
|
M._set_all_folded(false)
|
||||||
|
end)
|
||||||
|
-- fold reset
|
||||||
|
map(config.options.keymaps.fold_reset, function()
|
||||||
|
M._set_all_folded(nil)
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function handler(response)
|
local function handler(response)
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ M.defaults = {
|
|||||||
show_symbol_details = true,
|
show_symbol_details = true,
|
||||||
preview_bg_highlight = 'Pmenu',
|
preview_bg_highlight = 'Pmenu',
|
||||||
winblend = 0,
|
winblend = 0,
|
||||||
|
autofold_depth = nil,
|
||||||
|
auto_unfold_hover = true,
|
||||||
|
fold_markers = { '', '' },
|
||||||
keymaps = { -- These keymaps can be a string or a table for multiple keys
|
keymaps = { -- These keymaps can be a string or a table for multiple keys
|
||||||
close = { '<Esc>', 'q' },
|
close = { '<Esc>', 'q' },
|
||||||
goto_location = '<Cr>',
|
goto_location = '<Cr>',
|
||||||
@@ -25,6 +28,11 @@ M.defaults = {
|
|||||||
rename_symbol = 'r',
|
rename_symbol = 'r',
|
||||||
code_actions = 'a',
|
code_actions = 'a',
|
||||||
show_help = '?',
|
show_help = '?',
|
||||||
|
fold = 'h',
|
||||||
|
unfold = 'l',
|
||||||
|
fold_all = 'W',
|
||||||
|
unfold_all = 'E',
|
||||||
|
fold_reset = 'R',
|
||||||
},
|
},
|
||||||
lsp_blacklist = {},
|
lsp_blacklist = {},
|
||||||
symbol_blacklist = {},
|
symbol_blacklist = {},
|
||||||
|
|||||||
27
lua/symbols-outline/folding.lua
Normal file
27
lua/symbols-outline/folding.lua
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
local M = {}
|
||||||
|
local config = require 'symbols-outline.config'
|
||||||
|
|
||||||
|
M.is_foldable = function(node)
|
||||||
|
return node.children and #node.children > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local get_default_folded = function(depth)
|
||||||
|
local fold_past = config.options.autofold_depth
|
||||||
|
if not fold_past then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return depth >= fold_past
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
M.is_folded = function(node)
|
||||||
|
if node.folded ~= nil then
|
||||||
|
return node.folded
|
||||||
|
elseif node.hovered and config.options.auto_unfold_hover then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return get_default_folded(node.depth)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -2,6 +2,7 @@ local symbols = require 'symbols-outline.symbols'
|
|||||||
local ui = require 'symbols-outline.ui'
|
local ui = require 'symbols-outline.ui'
|
||||||
local config = require 'symbols-outline.config'
|
local config = require 'symbols-outline.config'
|
||||||
local t_utils = require 'symbols-outline.utils.table'
|
local t_utils = require 'symbols-outline.utils.table'
|
||||||
|
local folding = require 'symbols-outline.folding'
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
@@ -9,8 +10,9 @@ local M = {}
|
|||||||
---@param result table The result from a language server.
|
---@param result table The result from a language server.
|
||||||
---@param depth number? The current depth of the symbol in the hierarchy.
|
---@param depth number? The current depth of the symbol in the hierarchy.
|
||||||
---@param hierarchy table? A table of booleans which tells if a symbols parent was the last in its group.
|
---@param hierarchy table? A table of booleans which tells if a symbols parent was the last in its group.
|
||||||
|
---@param parent table? A reference to the current symbol's parent in the function's recursion
|
||||||
---@return table
|
---@return table
|
||||||
local function parse_result(result, depth, hierarchy)
|
local function parse_result(result, depth, hierarchy, parent)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
|
|
||||||
for index, value in pairs(result) do
|
for index, value in pairs(result) do
|
||||||
@@ -24,14 +26,6 @@ local function parse_result(result, depth, hierarchy)
|
|||||||
-- whether this node is the last in its group
|
-- whether this node is the last in its group
|
||||||
local isLast = index == #result
|
local isLast = index == #result
|
||||||
|
|
||||||
local children = nil
|
|
||||||
if value.children ~= nil then
|
|
||||||
-- copy by value because we dont want it messing with the hir table
|
|
||||||
local child_hir = t_utils.array_copy(hir)
|
|
||||||
table.insert(child_hir, isLast)
|
|
||||||
children = parse_result(value.children, level + 1, child_hir)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- support SymbolInformation[]
|
-- support SymbolInformation[]
|
||||||
-- https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol
|
-- https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol
|
||||||
local selectionRange = value.selectionRange
|
local selectionRange = value.selectionRange
|
||||||
@@ -44,7 +38,7 @@ local function parse_result(result, depth, hierarchy)
|
|||||||
range = value.location.range
|
range = value.location.range
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(ret, {
|
local node = {
|
||||||
deprecated = value.deprecated,
|
deprecated = value.deprecated,
|
||||||
kind = value.kind,
|
kind = value.kind,
|
||||||
icon = symbols.icon_from_kind(value.kind),
|
icon = symbols.icon_from_kind(value.kind),
|
||||||
@@ -54,11 +48,23 @@ local function parse_result(result, depth, hierarchy)
|
|||||||
character = selectionRange.start.character,
|
character = selectionRange.start.character,
|
||||||
range_start = range.start.line,
|
range_start = range.start.line,
|
||||||
range_end = range['end'].line,
|
range_end = range['end'].line,
|
||||||
children = children,
|
|
||||||
depth = level,
|
depth = level,
|
||||||
isLast = isLast,
|
isLast = isLast,
|
||||||
hierarchy = hir,
|
hierarchy = hir,
|
||||||
})
|
parent = parent,
|
||||||
|
}
|
||||||
|
|
||||||
|
table.insert(ret, node)
|
||||||
|
|
||||||
|
local children = nil
|
||||||
|
if value.children ~= nil then
|
||||||
|
-- copy by value because we dont want it messing with the hir table
|
||||||
|
local child_hir = t_utils.array_copy(hir)
|
||||||
|
table.insert(child_hir, isLast)
|
||||||
|
children = parse_result(value.children, level + 1, child_hir, node)
|
||||||
|
end
|
||||||
|
|
||||||
|
node.children = children
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
@@ -139,14 +145,23 @@ function M.parse(response)
|
|||||||
return parse_result(sorted, nil, nil)
|
return parse_result(sorted, nil, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.flatten(outline_items, ret)
|
function M.flatten(outline_items, ret, depth)
|
||||||
|
depth = depth or 1
|
||||||
ret = ret or {}
|
ret = ret or {}
|
||||||
for _, value in ipairs(outline_items) do
|
for _, value in ipairs(outline_items) do
|
||||||
table.insert(ret, value)
|
table.insert(ret, value)
|
||||||
if value.children ~= nil then
|
value.line_in_outline = #ret
|
||||||
M.flatten(value.children, ret)
|
if value.children ~= nil and not folding.is_folded(value) then
|
||||||
|
M.flatten(value.children, ret, depth + 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- if depth == 1 then
|
||||||
|
-- for index, value in ipairs(ret) do
|
||||||
|
-- value.line_in_outline = index
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -155,7 +170,10 @@ function M.get_lines(flattened_outline_items)
|
|||||||
local hl_info = {}
|
local hl_info = {}
|
||||||
|
|
||||||
for node_line, node in ipairs(flattened_outline_items) do
|
for node_line, node in ipairs(flattened_outline_items) do
|
||||||
local line = t_utils.str_to_table(string.rep(' ', node.depth))
|
local depth = node.depth
|
||||||
|
local marker_space = (config.options.fold_markers and 1) or 0
|
||||||
|
|
||||||
|
local line = t_utils.str_to_table(string.rep(' ', depth + marker_space))
|
||||||
local running_length = 1
|
local running_length = 1
|
||||||
|
|
||||||
local function add_guide_hl(from, to)
|
local function add_guide_hl(from, to)
|
||||||
@@ -176,6 +194,21 @@ function M.get_lines(flattened_outline_items)
|
|||||||
-- i f index is last, add a bottom marker if current item is last,
|
-- i f index is last, add a bottom marker if current item is last,
|
||||||
-- else add a middle marker
|
-- else add a middle marker
|
||||||
elseif index == #line then
|
elseif index == #line then
|
||||||
|
-- add fold markers
|
||||||
|
if config.options.fold_markers and folding.is_foldable(node) then
|
||||||
|
if folding.is_folded(node) then
|
||||||
|
line[index] = config.options.fold_markers[1]
|
||||||
|
else
|
||||||
|
line[index] = config.options.fold_markers[2]
|
||||||
|
end
|
||||||
|
|
||||||
|
add_guide_hl(
|
||||||
|
running_length,
|
||||||
|
running_length + vim.fn.strlen(line[index]) - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
-- the root level has no vertical markers
|
||||||
|
elseif depth > 1 then
|
||||||
if node.isLast then
|
if node.isLast then
|
||||||
line[index] = ui.markers.bottom
|
line[index] = ui.markers.bottom
|
||||||
add_guide_hl(
|
add_guide_hl(
|
||||||
@@ -189,14 +222,18 @@ function M.get_lines(flattened_outline_items)
|
|||||||
running_length + vim.fn.strlen(ui.markers.middle) - 1
|
running_length + vim.fn.strlen(ui.markers.middle) - 1
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
-- else if the parent was not the last in its group, add a
|
-- else if the parent was not the last in its group, add a
|
||||||
-- vertical marker because there are items under us and we need
|
-- vertical marker because there are items under us and we need
|
||||||
-- to point to those
|
-- to point to those
|
||||||
elseif not node.hierarchy[index] then
|
elseif not node.hierarchy[index] and depth > 1 then
|
||||||
line[index] = ui.markers.vertical
|
line[index + marker_space] = ui.markers.vertical
|
||||||
add_guide_hl(
|
add_guide_hl(
|
||||||
running_length,
|
running_length - 1 + 2 * marker_space,
|
||||||
running_length + vim.fn.strlen(ui.markers.vertical) - 1
|
running_length
|
||||||
|
+ vim.fn.strlen(ui.markers.vertical)
|
||||||
|
- 1
|
||||||
|
+ 2 * marker_space
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -216,6 +253,8 @@ function M.get_lines(flattened_outline_items)
|
|||||||
local hl_end = #string_prefix + #node.icon
|
local hl_end = #string_prefix + #node.icon
|
||||||
local hl_type = config.options.symbols[symbols.kinds[node.kind]].hl
|
local hl_type = config.options.symbols[symbols.kinds[node.kind]].hl
|
||||||
table.insert(hl_info, { node_line, hl_start, hl_end, hl_type })
|
table.insert(hl_info, { node_line, hl_start, hl_end, hl_type })
|
||||||
|
|
||||||
|
node.prefix_length = #string_prefix + #node.icon + 1
|
||||||
end
|
end
|
||||||
return lines, hl_info
|
return lines, hl_info
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,15 +8,12 @@ function M.should_use_provider(bufnr)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function M.hover_info(_, _, on_info)
|
function M.hover_info(_, _, on_info)
|
||||||
on_info(
|
on_info(nil, {
|
||||||
nil,
|
|
||||||
{
|
|
||||||
contents = {
|
contents = {
|
||||||
kind = 'markdown',
|
kind = 'markdown',
|
||||||
contents = { 'No extra information availaible!' },
|
contents = { 'No extra information availaible!' },
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param on_symbols function
|
---@param on_symbols function
|
||||||
|
|||||||
@@ -23,15 +23,12 @@ function M.hover_info(bufnr, params, on_info)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not used_client then
|
if not used_client then
|
||||||
on_info(
|
on_info(nil, {
|
||||||
nil,
|
|
||||||
{
|
|
||||||
contents = {
|
contents = {
|
||||||
kind = 'markdown',
|
kind = 'markdown',
|
||||||
content = { 'No extra information availaible!' },
|
content = { 'No extra information availaible!' },
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
used_client.request('textDocument/hover', params, on_info, bufnr)
|
used_client.request('textDocument/hover', params, on_info, bufnr)
|
||||||
|
|||||||
@@ -38,4 +38,76 @@ function M.debounce(f, delay)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function M.items_dfs(callback, children)
|
||||||
|
for _, val in ipairs(children) do
|
||||||
|
callback(val)
|
||||||
|
|
||||||
|
if val.children then
|
||||||
|
M.items_dfs(callback, val.children)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Merges a symbol tree recursively, only replacing nodes
|
||||||
|
---which have changed. This will maintain the folding
|
||||||
|
---status of any unchanged nodes.
|
||||||
|
---@param new_node table New node
|
||||||
|
---@param old_node table Old node
|
||||||
|
---@param index? number Index of old_item in parent
|
||||||
|
---@param parent? table Parent of old_item
|
||||||
|
M.merge_items_rec = function(new_node, old_node, index, parent)
|
||||||
|
local failed = false
|
||||||
|
|
||||||
|
if not new_node or not old_node then
|
||||||
|
failed = true
|
||||||
|
else
|
||||||
|
for key, _ in pairs(new_node) do
|
||||||
|
if
|
||||||
|
vim.tbl_contains({
|
||||||
|
'parent',
|
||||||
|
'children',
|
||||||
|
'folded',
|
||||||
|
'hovered',
|
||||||
|
'line_in_outline',
|
||||||
|
'hierarchy',
|
||||||
|
}, key)
|
||||||
|
then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
if key == 'name' then
|
||||||
|
-- in the case of a rename, just rename the existing node
|
||||||
|
old_node['name'] = new_node['name']
|
||||||
|
else
|
||||||
|
if not vim.deep_equal(new_node[key], old_node[key]) then
|
||||||
|
failed = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if failed then
|
||||||
|
if parent and index then
|
||||||
|
parent[index] = new_node
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local next_new_item = new_node.children or {}
|
||||||
|
|
||||||
|
-- in case new children are created on a node which
|
||||||
|
-- previously had no children
|
||||||
|
if #next_new_item > 0 and not old_node.children then
|
||||||
|
old_node.children = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local next_old_item = old_node.children or {}
|
||||||
|
|
||||||
|
for i = 1, math.max(#next_new_item, #next_old_item) do
|
||||||
|
M.merge_items_rec(next_new_item[i], next_old_item[i], i, next_old_item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
local parser = require 'symbols-outline.parser'
|
local parser = require 'symbols-outline.parser'
|
||||||
local config = require 'symbols-outline.config'
|
local config = require 'symbols-outline.config'
|
||||||
|
local ui = require 'symbols-outline.ui'
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ function M.write_outline(bufnr, lines)
|
|||||||
vim.api.nvim_buf_set_option(bufnr, 'modifiable', false)
|
vim.api.nvim_buf_set_option(bufnr, 'modifiable', false)
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.add_highlights(bufnr, hl_info)
|
function M.add_highlights(bufnr, hl_info, nodes)
|
||||||
for _, line_hl in ipairs(hl_info) do
|
for _, line_hl in ipairs(hl_info) do
|
||||||
local line, hl_start, hl_end, hl_type = unpack(line_hl)
|
local line, hl_start, hl_end, hl_type = unpack(line_hl)
|
||||||
vim.api.nvim_buf_add_highlight(
|
vim.api.nvim_buf_add_highlight(
|
||||||
@@ -33,6 +34,8 @@ function M.add_highlights(bufnr, hl_info)
|
|||||||
hl_end
|
hl_end
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
M.add_hover_highlights(bufnr, nodes)
|
||||||
end
|
end
|
||||||
|
|
||||||
local ns = vim.api.nvim_create_namespace 'symbols-outline-virt-text'
|
local ns = vim.api.nvim_create_namespace 'symbols-outline-virt-text'
|
||||||
@@ -58,6 +61,30 @@ local function clear_virt_text(bufnr)
|
|||||||
vim.api.nvim_buf_clear_namespace(bufnr, -1, 0, -1)
|
vim.api.nvim_buf_clear_namespace(bufnr, -1, 0, -1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
M.add_hover_highlights = function(bufnr, nodes)
|
||||||
|
if not config.options.highlight_hovered_item then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- clear old highlight
|
||||||
|
ui.clear_hover_highlight(bufnr)
|
||||||
|
for _, node in ipairs(nodes) do
|
||||||
|
if not node.hovered then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
local marker_fac = (config.options.fold_markers and 1) or 0
|
||||||
|
if node.prefix_length then
|
||||||
|
ui.add_hover_highlight(
|
||||||
|
bufnr,
|
||||||
|
node.line_in_outline - 1,
|
||||||
|
node.prefix_length
|
||||||
|
)
|
||||||
|
end
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- runs the whole writing routine where the text is cleared, new data is parsed
|
-- runs the whole writing routine where the text is cleared, new data is parsed
|
||||||
-- and then written
|
-- and then written
|
||||||
function M.parse_and_write(bufnr, flattened_outline_items)
|
function M.parse_and_write(bufnr, flattened_outline_items)
|
||||||
@@ -66,7 +93,7 @@ function M.parse_and_write(bufnr, flattened_outline_items)
|
|||||||
|
|
||||||
clear_virt_text(bufnr)
|
clear_virt_text(bufnr)
|
||||||
local details = parser.get_details(flattened_outline_items)
|
local details = parser.get_details(flattened_outline_items)
|
||||||
M.add_highlights(bufnr, hl_info)
|
M.add_highlights(bufnr, hl_info, flattened_outline_items)
|
||||||
M.write_details(bufnr, details)
|
M.write_details(bufnr, details)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user