1

Regenerate nvim config

This commit is contained in:
2024-06-02 03:29:20 +02:00
parent 75eea0c030
commit ef2e28883d
5576 changed files with 604886 additions and 503 deletions

View File

@ -0,0 +1,754 @@
local actions = {}
local function fix_end_character_position(bufnr, name_range_or_scope)
if name_range_or_scope["end"].character == 0 and (name_range_or_scope["end"].line - name_range_or_scope["start"].line) > 0 then
name_range_or_scope["end"].line = name_range_or_scope["end"].line - 1
name_range_or_scope["end"].character = string.len(vim.api.nvim_buf_get_lines(bufnr, name_range_or_scope["end"].line - 1, name_range_or_scope["end"].line, false)[1])
end
end
function actions.close()
local callback = function(display)
display:close()
vim.api.nvim_win_set_cursor(display.for_win, display.start_cursor)
end
return {
callback = callback,
description = "Close Navbuddy"
}
end
function actions.next_sibling()
local callback = function(display)
if display.focus_node.next == nil then
return
end
for _ = 1, vim.v.count1 do
local next_node = display.focus_node.next
if next_node == nil then
break
end
display.focus_node = next_node
end
display:redraw()
end
return {
callback = callback,
description = "Move down to next node"
}
end
function actions.previous_sibling()
local callback = function(display)
if display.focus_node.prev == nil then
return
end
for _ = 1, vim.v.count1 do
local prev_node = display.focus_node.prev
if prev_node == nil then
break
end
display.focus_node = prev_node
end
display:redraw()
end
return {
callback = callback,
description = "Move up to previous node"
}
end
function actions.parent()
local callback = function(display)
if display.focus_node.parent.is_root then
return
end
local parent_node = display.focus_node.parent
display.focus_node = parent_node
display:redraw()
end
return {
callback = callback,
description = "Move left to parent level"
}
end
function actions.children()
local callback = function(display)
if display.focus_node.children == nil then
actions.select().callback(display)
return
end
local child_node
if display.focus_node.memory then
child_node = display.focus_node.children[display.focus_node.memory]
else
child_node = display.focus_node.children[1]
end
display.focus_node = child_node
display:redraw()
end
return {
callback = callback,
description = "Move right to child node level"
}
end
function actions.root()
local callback = function(display)
if display.focus_node.parent.is_root then
return
end
while not display.focus_node.parent.is_root do
display.focus_node.parent.memory = display.focus_node.index
display.focus_node = display.focus_node.parent
end
display:redraw()
end
return {
callback = callback,
description = "Move to top most node"
}
end
function actions.select()
local callback = function(display)
display:close()
fix_end_character_position(display.for_buf, display.focus_node.name_range)
fix_end_character_position(display.for_buf, display.focus_node.scope)
-- to push location to jumplist:
-- move display to start_cursor, set mark ', then move to new location
vim.api.nvim_win_set_cursor(display.for_win, display.start_cursor)
vim.api.nvim_command("normal! m'")
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.name_range["start"].line, display.focus_node.name_range["start"].character }
)
if display.config.source_buffer.reorient == "smart" then
local total_lines = display.focus_node.scope["end"].line - display.focus_node.scope["start"].line + 1
if total_lines >= vim.api.nvim_win_get_height(display.for_win) then
vim.api.nvim_command("normal! zt")
else
local mid_line =
bit.rshift(display.focus_node.scope["start"].line + display.focus_node.scope["end"].line, 1)
vim.api.nvim_win_set_cursor(display.for_win, { mid_line, 0 })
vim.api.nvim_command("normal! zz")
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.name_range["start"].line, display.focus_node.name_range["start"].character }
)
end
elseif display.config.source_buffer.reorient == "mid" then
vim.api.nvim_command("normal! zz")
elseif display.config.source_buffer.reorient == "top" then
vim.api.nvim_command("normal! zt")
end
end
return {
callback = callback,
description = "Select and Goto current node"
}
end
function actions.yank_name()
local callback = function(display)
display:close()
fix_end_character_position(display.for_buf, display.focus_node.name_range)
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.name_range["start"].line, display.focus_node.name_range["start"].character }
)
vim.api.nvim_command("normal! v")
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.name_range["end"].line, display.focus_node.name_range["end"].character - 1 }
)
vim.api.nvim_command('normal! "+y')
end
return {
callback = callback,
description = "Yank node name"
}
end
function actions.yank_scope()
local callback = function(display)
display:close()
fix_end_character_position(display.for_buf, display.focus_node.scope)
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.scope["start"].line, display.focus_node.scope["start"].character }
)
vim.api.nvim_command("normal! v")
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.scope["end"].line, display.focus_node.scope["end"].character - 1 }
)
vim.api.nvim_command('normal! "+y')
end
return {
callback = callback,
description = "Yank node scope"
}
end
function actions.visual_name()
local callback = function(display)
display:close()
fix_end_character_position(display.for_buf, display.focus_node.name_range)
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.name_range["start"].line, display.focus_node.name_range["start"].character }
)
vim.api.nvim_command("normal! v")
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.name_range["end"].line, display.focus_node.name_range["end"].character - 1 }
)
end
return {
callback = callback,
description = "Visual select node name"
}
end
function actions.visual_scope()
local callback = function(display)
display:close()
fix_end_character_position(display.for_buf, display.focus_node.scope)
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.scope["start"].line, display.focus_node.scope["start"].character }
)
vim.api.nvim_command("normal! v")
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.scope["end"].line, display.focus_node.scope["end"].character - 1 }
)
end
return {
callback = callback,
description = "Visual select node scope"
}
end
function actions.insert_name()
local callback = function(display)
display:close()
fix_end_character_position(display.for_buf, display.focus_node.name_range)
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.name_range["start"].line, display.focus_node.name_range["start"].character }
)
vim.api.nvim_feedkeys("i", "n", false)
end
return {
callback = callback,
description = "Insert node name"
}
end
function actions.insert_scope()
local callback = function(display)
display:close()
fix_end_character_position(display.for_buf, display.focus_node.scope)
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.scope["start"].line, display.focus_node.scope["start"].character }
)
vim.api.nvim_feedkeys("i", "n", false)
end
return {
callback = callback,
description = "Insert node scope"
}
end
function actions.append_name()
local callback = function(display)
display:close()
fix_end_character_position(display.for_buf, display.focus_node.name_range)
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.name_range["end"].line, display.focus_node.name_range["end"].character - 1 }
)
vim.api.nvim_feedkeys("a", "n", false)
end
return {
callback = callback,
description = "Append node name"
}
end
function actions.append_scope()
local callback = function(display)
display:close()
fix_end_character_position(display.for_buf, display.focus_node.scope)
if
string.len(
vim.api.nvim_buf_get_lines(
display.for_buf,
display.focus_node.scope["end"].line - 1,
display.focus_node.scope["end"].line,
false
)[1]
) == display.focus_node.scope["end"].character
then
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.scope["end"].line, display.focus_node.scope["end"].character }
)
else
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.scope["end"].line, display.focus_node.scope["end"].character - 1 }
)
end
vim.api.nvim_feedkeys("a", "n", false)
end
return {
callback = callback,
description = "Append node scope"
}
end
function actions.rename()
local callback = function(display)
display:close()
vim.lsp.buf.rename()
end
return {
callback = callback,
description = "Rename"
}
end
function actions.delete()
local callback = function(display)
actions.visual_scope().callback(display)
vim.api.nvim_command("normal! d")
end
return {
callback = callback,
description = "Delete"
}
end
function actions.fold_create()
local callback = function(display)
if vim.o.foldmethod ~= "manual" then
vim.notify("Fold create action works only when foldmethod is 'manual'", vim.log.levels.ERROR)
return
end
fix_end_character_position(display.for_buf, display.focus_node.scope)
display.state.leaving_window_for_action = true
vim.api.nvim_set_current_win(display.for_win)
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.scope["start"].line, display.focus_node.scope["start"].character }
)
vim.api.nvim_command("normal! v")
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.scope["end"].line, display.focus_node.scope["end"].character - 1 }
)
vim.api.nvim_command("normal! zf")
vim.api.nvim_set_current_win(display.mid.winid)
display.state.leaving_window_for_action = false
end
return {
callback = callback,
description = "Create fold"
}
end
function actions.fold_delete()
local callback = function(display)
if vim.o.foldmethod ~= "manual" then
vim.notify("Fold delete action works only when foldmethod is 'manual'", vim.log.levels.ERROR)
return
end
fix_end_character_position(display.for_buf, display.focus_node.scope)
display.state.leaving_window_for_action = true
vim.api.nvim_set_current_win(display.for_win)
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.scope["start"].line, display.focus_node.scope["start"].character }
)
vim.api.nvim_command("normal! v")
vim.api.nvim_win_set_cursor(
display.for_win,
{ display.focus_node.scope["end"].line, display.focus_node.scope["end"].character - 1 }
)
pcall(vim.api.nvim_command, "normal! zd")
vim.api.nvim_set_current_win(display.mid.winid)
display.state.leaving_window_for_action = false
end
return {
callback = callback,
description = "Delete fold"
}
end
function actions.comment()
local callback = function(display)
local status_ok, comment = pcall(require, "Comment.api")
if not status_ok then
vim.notify("Comment.nvim not found", vim.log.levels.ERROR)
return
end
fix_end_character_position(display.for_buf, display.focus_node.scope)
display.state.leaving_window_for_action = true
vim.api.nvim_set_current_win(display.for_win)
vim.api.nvim_buf_set_mark(
display.for_buf,
"<",
display.focus_node.scope["start"].line,
display.focus_node.scope["start"].character,
{}
)
vim.api.nvim_buf_set_mark(
display.for_buf,
">",
display.focus_node.scope["end"].line,
display.focus_node.scope["end"].character,
{}
)
comment.locked("toggle.linewise")("v")
vim.api.nvim_set_current_win(display.mid.winid)
display.state.leaving_window_for_action = false
end
return {
callback = callback,
description = "Comment"
}
end
local function swap_nodes(for_buf, nodeA, nodeB)
-- nodeA
-- ^
-- |
-- v
-- nodeB
fix_end_character_position(for_buf, nodeA.scope)
fix_end_character_position(for_buf, nodeB.scope)
if nodeA.scope["end"].line >= nodeB.scope["start"].line and nodeA.parent == nodeB.parent then
vim.notify("Cannot swap!", vim.log.levels.ERROR)
return
end
local nodeA_text = vim.api.nvim_buf_get_lines(for_buf, nodeA.scope["start"].line-1, nodeA.scope["end"].line-1+1, false)
local mid_text = vim.api.nvim_buf_get_lines(for_buf, nodeA.scope["end"].line-1+1, nodeB.scope["start"].line-1, false)
local nodeB_text = vim.api.nvim_buf_get_lines(for_buf, nodeB.scope["start"].line-1, nodeB.scope["end"].line-1+1, false)
local start_line = nodeA.scope["start"].line-1
local nodeA_line_cnt = nodeA.scope["end"].line + 1 - nodeA.scope["start"].line
local mid_line_cnt = nodeB.scope["start"].line - nodeA.scope["end"].line - 1
local nodeB_line_cnt = nodeB.scope["end"].line + 1 - nodeB.scope["start"].line
-- Swap pointers
nodeA.next = nodeB.next
nodeB.next = nodeA
nodeB.prev = nodeA.prev
nodeA.prev = nodeB
-- Swap index
local nodeB_index = nodeB.index
nodeB.index = nodeA.index
nodeA.index = nodeB_index
-- Swap in parent's children array
local parent = nodeA.parent
parent.children[nodeA.index] = nodeA
parent.children[nodeB.index] = nodeB
-- Adjust line numbers
nodeA.scope["start"].line = nodeA.scope["start"].line + nodeB_line_cnt + mid_line_cnt
nodeA.scope["end"].line = nodeA.scope["end"].line + nodeB_line_cnt + mid_line_cnt
nodeA.name_range["start"].line = nodeA.name_range["start"].line + nodeB_line_cnt + mid_line_cnt
nodeA.name_range["end"].line = nodeA.name_range["end"].line + nodeB_line_cnt + mid_line_cnt
nodeB.scope["start"].line = nodeB.scope["start"].line - nodeA_line_cnt - mid_line_cnt
nodeB.scope["end"].line = nodeB.scope["end"].line - nodeA_line_cnt - mid_line_cnt
nodeB.name_range["start"].line = nodeB.name_range["start"].line - nodeA_line_cnt - mid_line_cnt
nodeB.name_range["end"].line = nodeB.name_range["end"].line - nodeA_line_cnt - mid_line_cnt
-- Set lines
vim.api.nvim_buf_set_lines(for_buf, start_line, start_line + nodeB_line_cnt, false, nodeB_text)
vim.api.nvim_buf_set_lines(for_buf, start_line + nodeB_line_cnt, start_line + nodeB_line_cnt + mid_line_cnt, false, mid_text)
vim.api.nvim_buf_set_lines(for_buf, start_line + nodeB_line_cnt + mid_line_cnt, start_line + nodeB_line_cnt + mid_line_cnt + nodeA_line_cnt, false, nodeA_text)
end
function actions.move_down()
local callback = function(display)
if display.focus_node.next == nil then
return
end
swap_nodes(display.for_buf, display.focus_node, display.focus_node.next)
display:redraw()
end
return {
callback = callback,
description = "Move code block down"
}
end
function actions.move_up()
local callback = function(display)
if display.focus_node.prev == nil then
return
end
swap_nodes(display.for_buf, display.focus_node.prev, display.focus_node)
display:redraw()
end
return {
callback = callback,
description = "Move code block up"
}
end
function actions.toggle_preview()
local callback = function(display)
if vim.api.nvim_win_get_buf(display.right.winid) == display.right.bufnr then
display:show_preview()
else
display:hide_preview()
end
end
return {
callback = callback,
description = "Show preview of current node"
}
end
function actions.vsplit()
local callback = function(display)
actions.close().callback(display)
vim.api.nvim_command("vsplit")
display.for_win = vim.api.nvim_get_current_win()
actions.select().callback(display)
vim.api.nvim_command("normal! zv")
end
return {
callback = callback,
description = "Open selected node in a vertical split"
}
end
function actions.hsplit()
local callback = function(display)
actions.close().callback(display)
vim.api.nvim_command("split")
display.for_win = vim.api.nvim_get_current_win()
actions.select().callback(display)
vim.api.nvim_command("normal! zv")
end
return {
callback = callback,
description = "Open selected node in a horizontal split"
}
end
function actions.telescope(opts)
local callback = function(display)
local status_ok, _ = pcall(require, "telescope")
if not status_ok then
vim.notify("telescope.nvim not found", vim.log.levels.ERROR)
return
end
local navic = require("nvim-navic.lib")
local pickers = require("telescope.pickers")
local entry_display = require("telescope.pickers.entry_display")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
local t_actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local displayer = entry_display.create({
separator = " ",
items = {
{ width = 14 },
{ remaining = true },
},
})
local function make_display(entry)
local node = entry.value
local kind = navic.adapt_lsp_num_to_str(node.kind)
local kind_hl = "Navbuddy"..kind
local name_hl = "NavbuddyNormalFloat"
local columns = {
{ string.lower(kind), kind_hl },
{ node.name, name_hl},
}
return displayer(columns)
end
local function make_entry(node)
return {
value = node,
display = make_display,
name = node.name,
ordinal = string.lower(navic.adapt_lsp_num_to_str(node.kind)).." "..node.name,
lnum = node.name_range["start"].line,
col = node.name_range["start"].character,
bufnr = display.for_buf,
filename = vim.api.nvim_buf_get_name(display.for_buf),
}
end
display:close()
pickers.new(opts, {
prompt_title = "Fuzzy Search",
finder = finders.new_table({
results = display.focus_node.parent.children,
entry_maker = make_entry
}),
sorter = conf.generic_sorter(opts),
previewer = conf.qflist_previewer(opts),
attach_mappings = function(prompt_bufnr, _)
t_actions.select_default:replace(function()
local selection = action_state.get_selected_entry()
display.focus_node = selection.value
t_actions.close(prompt_bufnr)
end)
t_actions.close:enhance({
post = function()
display = require("nvim-navbuddy.display"):new(display)
end
})
return true
end,
}):find()
end
return {
callback = callback,
description = "Fuzzy search current level with telescope"
}
end
function actions.help()
local callback = function(display)
display:close()
local nui_popup = require("nui.popup")
local help_popup = nui_popup({
relative = "editor",
position = display.config.window.position,
size = display.config.window.size,
enter = true,
focusable = true,
border = display.config.window.border,
win_options = {
winhighlight = "Normal:NavbuddyNormalFloat,FloatBorder:NavbuddyFloatBorder",
},
buf_options = {
modifiable = false,
},
})
local function quit_help()
help_popup:unmount()
require("nvim-navbuddy.display"):new(display)
end
help_popup:map("n", "q", quit_help)
help_popup:map("n", "<esc>", quit_help)
help_popup:mount()
local max_keybinding_len = 0
for k, _ in pairs(display.config.mappings) do
max_keybinding_len = math.max(#k, max_keybinding_len)
end
local lines = {}
for k, v in pairs(display.config.mappings) do
local text = " " .. k .. string.rep(" ", max_keybinding_len - #k) .. " | " .. v.description
table.insert(lines, text)
end
table.sort(lines)
table.insert(lines, 1, " Navbuddy Mappings" .. string.rep(" ", math.max(1, vim.api.nvim_win_get_width(help_popup.winid) - 18*2)) .. "press 'q' to exit ")
table.insert(lines, 2, string.rep("-", vim.api.nvim_win_get_width(help_popup.winid)))
vim.api.nvim_buf_set_option(help_popup.bufnr, "modifiable", true)
vim.api.nvim_buf_set_lines(help_popup.bufnr, 0, -1, false, lines)
vim.api.nvim_buf_set_option(help_popup.bufnr, "modifiable", false)
vim.api.nvim_buf_add_highlight(
help_popup.bufnr,
-1,
"NavbuddyFunction",
0,
0,
-1
)
for i = 2, #lines do
vim.api.nvim_buf_add_highlight(
help_popup.bufnr,
-1,
"NavbuddyKey",
i - 1,
0,
max_keybinding_len + 3
)
end
end
return {
callback = callback,
description = "Show mappings"
}
end
return actions

View File

@ -0,0 +1,405 @@
local navic = require("nvim-navic.lib")
local nui_popup = require("nui.popup")
local nui_layout = require("nui.layout")
local nui_text = require("nui.text")
local ui = require("nvim-navbuddy.ui")
local ns = vim.api.nvim_create_namespace("nvim-navbuddy")
local function clear_buffer(buf)
vim.api.nvim_win_set_buf(buf.winid, buf.bufnr)
vim.api.nvim_win_set_option(buf.winid, "signcolumn", "no")
vim.api.nvim_win_set_option(buf.winid, "foldlevel", 100)
vim.api.nvim_win_set_option(buf.winid, "wrap", true)
vim.api.nvim_buf_set_option(buf.bufnr, "modifiable", true)
vim.api.nvim_buf_set_lines(buf.bufnr, 0, -1, false, {})
vim.api.nvim_buf_set_option(buf.bufnr, "modifiable", false)
for _, extmark in ipairs(vim.api.nvim_buf_get_extmarks(buf.bufnr, ns, 0, -1, {})) do
vim.api.nvim_buf_del_extmark(buf.bufnr, ns, extmark[1])
end
end
local function fill_buffer(buf, node, config)
local cursor_pos = vim.api.nvim_win_get_cursor(buf.winid)
clear_buffer(buf)
local parent = node.parent
local lines = {}
for _, child_node in ipairs(parent.children) do
local text = " " .. config.icons[child_node.kind] .. child_node.name
table.insert(lines, text)
end
vim.api.nvim_buf_set_option(buf.bufnr, "modifiable", true)
vim.api.nvim_buf_set_lines(buf.bufnr, 0, -1, false, lines)
vim.api.nvim_buf_set_option(buf.bufnr, "modifiable", false)
if cursor_pos[1] ~= node.index then
cursor_pos[1] = node.index
end
for i, child_node in ipairs(parent.children) do
local hl_group = "Navbuddy" .. navic.adapt_lsp_num_to_str(child_node.kind)
vim.api.nvim_buf_add_highlight(
buf.bufnr,
ns,
hl_group,
i - 1,
0,
-1
)
if config.node_markers.enabled then
vim.api.nvim_buf_set_extmark(buf.bufnr, ns, i - 1, #lines[i], {
virt_text = { {
child_node.children ~= nil and config.node_markers.icons.branch
or i == cursor_pos[1] and config.node_markers.icons.leaf_selected
or config.node_markers.icons.leaf,
i == cursor_pos[1] and { "NavbuddyCursorLine", hl_group } or hl_group,
} },
virt_text_pos = "right_align",
virt_text_hide = false,
})
end
end
vim.api.nvim_buf_add_highlight(buf.bufnr, ns, "NavbuddyCursorLine", cursor_pos[1] - 1, 0, -1)
vim.api.nvim_buf_set_extmark(buf.bufnr, ns, cursor_pos[1] - 1, #lines[cursor_pos[1]], {
end_row = cursor_pos[1],
hl_eol = true,
hl_group = "NavbuddyCursorLine" .. navic.adapt_lsp_num_to_str(node.kind),
})
vim.api.nvim_win_set_cursor(buf.winid, cursor_pos)
end
local display = {}
function display:new(obj)
ui.highlight_setup(obj.config)
-- Object
setmetatable(obj, self)
self.__index = self
local config = obj.config
-- NUI elements
local left_popup = nui_popup({
focusable = false,
border = config.window.sections.left.border or ui.get_border_chars(config.window.border, "left"),
win_options = {
winhighlight = "Normal:NavbuddyNormalFloat,FloatBorder:NavbuddyFloatBorder",
},
buf_options = {
modifiable = false,
},
})
local mid_popup = nui_popup({
enter = true,
border = config.window.sections.mid.border or ui.get_border_chars(config.window.border, "mid"),
win_options = {
winhighlight = "Normal:NavbuddyNormalFloat,FloatBorder:NavbuddyFloatBorder",
scrolloff = config.window.scrolloff
},
buf_options = {
modifiable = false,
},
})
local lsp_name = {
bottom = nui_text("[" .. obj.lsp_name .. "]", "NavbuddyFloatBorder"),
bottom_align = "right",
}
if
config.window.sections.right.border == "none"
or config.window.border == "none"
or config.window.sections.right.border == "shadow"
or config.window.border == "shadow"
or config.window.sections.right.border == "solid"
or config.window.border == "solid"
then
lsp_name = nil
end
local right_popup = nui_popup({
focusable = false,
border = {
style = config.window.sections.right.border or ui.get_border_chars(config.window.border, "right"),
text = lsp_name,
},
win_options = {
winhighlight = "Normal:NavbuddyNormalFloat,FloatBorder:NavbuddyFloatBorder",
scrolloff = 0,
},
buf_options = {
modifiable = false,
},
})
local layout = nui_layout(
{
relative = "editor",
position = config.window.position,
size = config.window.size,
},
nui_layout.Box({
nui_layout.Box(left_popup, { size = config.window.sections.left.size }),
nui_layout.Box(mid_popup, { size = config.window.sections.mid.size }),
nui_layout.Box(right_popup, { grow = 1 }),
}, { dir = "row" })
)
obj.layout = layout
obj.left = left_popup
obj.mid = mid_popup
obj.right = right_popup
obj.state = {
leaving_window_for_action = false,
leaving_window_for_reorientation = false,
closed = false,
-- user_gui_cursor = nil,
source_buffer_scrolloff = nil
}
-- Set filetype
vim.api.nvim_buf_set_option(obj.mid.bufnr, "filetype", "Navbuddy")
-- Hidden cursor
if obj.state.user_gui_cursor == nil then
obj.state.user_gui_cursor = vim.api.nvim_get_option("guicursor")
end
obj.state.user_gui_cursor = vim.api.nvim_get_option("guicursor")
if obj.state.user_gui_cursor ~= "" then
vim.api.nvim_set_option("guicursor", "a:NavbuddyCursor")
end
-- User Scrolloff
if config.source_buffer.scrolloff then
obj.state.source_buffer_scrolloff = vim.api.nvim_get_option("scrolloff")
vim.api.nvim_set_option("scrolloff", config.source_buffer.scrolloff)
end
-- Autocmds
local augroup = vim.api.nvim_create_augroup("Navbuddy", { clear = false })
vim.api.nvim_clear_autocmds({ buffer = obj.mid.bufnr })
vim.api.nvim_create_autocmd("CursorMoved", {
group = augroup,
buffer = obj.mid.bufnr,
callback = function()
local cursor_pos = vim.api.nvim_win_get_cursor(obj.mid.winid)
if obj.focus_node ~= obj.focus_node.parent.children[cursor_pos[1]] then
obj.focus_node = obj.focus_node.parent.children[cursor_pos[1]]
obj:redraw()
end
obj.focus_node.parent.memory = obj.focus_node.index
obj:clear_highlights()
obj:focus_range()
end,
})
vim.api.nvim_create_autocmd("BufLeave", {
group = augroup,
buffer = obj.mid.bufnr,
callback = function()
if
obj.state.leaving_window_for_action == false
and obj.state.leaving_window_for_reorientation == false
and obj.state.closed == false
then
obj:close()
end
end,
})
vim.api.nvim_create_autocmd("CmdlineEnter", {
group = augroup,
buffer = obj.mid.bufnr,
callback = function()
vim.api.nvim_set_option("guicursor", obj.state.user_gui_cursor)
end
})
vim.api.nvim_create_autocmd("CmdlineLeave", {
group = augroup,
buffer = obj.mid.bufnr,
callback = function()
if obj.state.user_gui_cursor ~= "" then
vim.api.nvim_set_option("guicursor", "a:NavbuddyCursor")
end
end,
})
-- Mappings
for i, v in pairs(config.mappings) do
obj.mid:map("n", i,
function()
v.callback(obj)
end,
{ nowait=true })
end
-- Display
layout:mount()
obj:redraw()
obj:focus_range()
return obj
end
function display:focus_range()
local ranges = nil
if vim.deep_equal(self.focus_node.scope, self.focus_node.name_range) then
ranges = { { "NavbuddyScope", self.focus_node.scope } }
else
ranges = { { "NavbuddyScope", self.focus_node.scope }, { "NavbuddyName", self.focus_node.name_range } }
end
if self.config.source_buffer.highlight then
for _, v in ipairs(ranges) do
local highlight, range = unpack(v)
if range["start"].line == range["end"].line then
vim.api.nvim_buf_add_highlight(
self.for_buf,
ns,
highlight,
range["start"].line - 1,
range["start"].character,
range["end"].character
)
else
vim.api.nvim_buf_add_highlight(
self.for_buf,
ns,
highlight,
range["start"].line - 1,
range["start"].character,
-1
)
vim.api.nvim_buf_add_highlight(
self.for_buf,
ns,
highlight,
range["end"].line - 1,
0,
range["end"].character
)
for i = range["start"].line, range["end"].line - 2, 1 do
vim.api.nvim_buf_add_highlight(self.for_buf, ns, highlight, i, 0, -1)
end
end
end
end
if self.config.source_buffer.follow_node then
self:reorient(self.for_win, self.config.source_buffer.reorient)
end
end
function display:reorient(ro_win, reorient_method)
vim.api.nvim_win_set_cursor(ro_win, { self.focus_node.name_range["start"].line, self.focus_node.name_range["start"].character })
self.state.leaving_window_for_reorientation = true
vim.api.nvim_set_current_win(ro_win)
if reorient_method == "smart" then
local total_lines = self.focus_node.scope["end"].line - self.focus_node.scope["start"].line + 1
if total_lines >= vim.api.nvim_win_get_height(ro_win) then
vim.api.nvim_command("normal! zt")
else
local mid_line = bit.rshift(self.focus_node.scope["start"].line + self.focus_node.scope["end"].line, 1)
vim.api.nvim_win_set_cursor(ro_win, { mid_line, 0 })
vim.api.nvim_command("normal! zz")
vim.api.nvim_win_set_cursor(
ro_win,
{ self.focus_node.name_range["start"].line, self.focus_node.name_range["start"].character }
)
end
elseif reorient_method == "mid" then
vim.api.nvim_command("normal! zz")
elseif reorient_method == "top" then
vim.api.nvim_command("normal! zt")
end
vim.api.nvim_set_current_win(self.mid.winid)
self.state.leaving_window_for_reorientation = false
end
function display:show_preview()
vim.api.nvim_win_set_buf(self.right.winid, self.for_buf)
vim.api.nvim_win_set_option(self.right.winid, 'winhighlight', 'Normal:NavbuddyNormalFloat,FloatBorder:NavbuddyFloatBorder')
vim.api.nvim_win_set_option(self.right.winid, "signcolumn", "no")
vim.api.nvim_win_set_option(self.right.winid, "foldlevel", 100)
vim.api.nvim_win_set_option(self.right.winid, "wrap", false)
self:reorient(self.right.winid, "smart")
end
function display:hide_preview()
vim.api.nvim_win_set_buf(self.right.winid, self.right.bufnr)
local node = self.focus_node
if node.children then
if node.memory then
fill_buffer(self.right, node.children[node.memory], self.config)
else
fill_buffer(self.right, node.children[1], self.config)
end
else
clear_buffer(self.right)
end
end
function display:clear_highlights()
vim.api.nvim_buf_clear_highlight(self.for_buf, ns, 0, -1)
end
function display:redraw()
local node = self.focus_node
fill_buffer(self.mid, node, self.config)
local preview_method = self.config.window.sections.right.preview
if preview_method == "always" then
self:show_preview()
else
if node.children then
if node.memory then
fill_buffer(self.right, node.children[node.memory], self.config)
else
fill_buffer(self.right, node.children[1], self.config)
end
else
if preview_method == "leaf" then
self:show_preview()
else
clear_buffer(self.right)
end
end
end
if node.parent.is_root then
clear_buffer(self.left)
else
fill_buffer(self.left, node.parent, self.config)
end
end
function display:close()
self.state.closed = true
vim.api.nvim_set_option("guicursor", self.state.user_gui_cursor)
if self.state.source_buffer_scrolloff then
vim.api.nvim_set_option("scrolloff", self.state.source_buffer_scrolloff)
end
self.layout:unmount()
self:clear_highlights()
end
return display

View File

@ -0,0 +1,435 @@
local navic = require("nvim-navic.lib")
local nui_menu = require("nui.menu")
local display = require("nvim-navbuddy.display")
local actions = require("nvim-navbuddy.actions")
local config = {
window = {
border = "single",
size = "60%",
position = "50%",
scrolloff = nil,
sections = {
left = {
size = "20%",
},
mid = {
size = "40%",
},
right = {
preview = "leaf",
},
},
},
node_markers = {
enabled = true,
icons = {
leaf = " ",
leaf_selected = "",
branch = "",
},
},
icons = {
[1] = "󰈙 ", -- File
[2] = "", -- Module
[3] = "󰌗 ", -- Namespace
[4] = "", -- Package
[5] = "󰌗 ", -- Class
[6] = "󰆧 ", -- Method
[7] = "", -- Property
[8] = "", -- Field
[9] = "", -- Constructor
[10] = "󰕘", -- Enum
[11] = "󰕘", -- Interface
[12] = "󰊕 ", -- Function
[13] = "󰆧 ", -- Variable
[14] = "󰏿 ", -- Constant
[15] = "", -- String
[16] = "󰎠 ", -- Number
[17] = "", -- Boolean
[18] = "󰅪 ", -- Array
[19] = "󰅩 ", -- Object
[20] = "󰌋 ", -- Key
[21] = "󰟢 ", -- Null
[22] = "", -- EnumMember
[23] = "󰌗 ", -- Struct
[24] = "", -- Event
[25] = "󰆕 ", -- Operator
[26] = "󰊄 ", -- TypeParameter
[255] = "󰉨 ", -- Macro
},
use_default_mappings = true,
mappings = {
["<esc>"] = actions.close(),
["q"] = actions.close(),
["j"] = actions.next_sibling(),
["k"] = actions.previous_sibling(),
["h"] = actions.parent(),
["l"] = actions.children(),
["0"] = actions.root(),
["v"] = actions.visual_name(),
["V"] = actions.visual_scope(),
["y"] = actions.yank_name(),
["Y"] = actions.yank_scope(),
["i"] = actions.insert_name(),
["I"] = actions.insert_scope(),
["a"] = actions.append_name(),
["A"] = actions.append_scope(),
["r"] = actions.rename(),
["d"] = actions.delete(),
["f"] = actions.fold_create(),
["F"] = actions.fold_delete(),
["c"] = actions.comment(),
["<enter>"] = actions.select(),
["o"] = actions.select(),
["J"] = actions.move_down(),
["K"] = actions.move_up(),
["s"] = actions.toggle_preview(),
["<C-v>"] = actions.vsplit(),
["<C-s>"] = actions.hsplit(),
["t"] = actions.telescope({
layout_strategy = "horizontal",
layout_config = {
height = 0.60,
width = 0.60,
prompt_position = "top",
preview_width = 0.50,
},
}),
["g?"] = actions.help(),
},
lsp = {
auto_attach = false,
preference = nil,
},
source_buffer = {
follow_node = true,
highlight = true,
reorient = "smart",
scrolloff = nil,
},
custom_hl_group = nil,
}
setmetatable(config.icons, {
__index = function()
return "? "
end,
})
local navbuddy_attached_clients = {}
-- @Private Methods
local function choose_lsp_menu(for_buf, make_request)
local style = nil
if config.window.border ~= nil and config.window.border ~= "None" then
style = config.window.border
else
style = "single"
end
local min_width = 23
local lines = {}
for _, v in ipairs(navbuddy_attached_clients[for_buf]) do
min_width = math.max(min_width, #v.name)
table.insert(lines, nui_menu.item(v.id .. ":" .. v.name))
end
local min_height = #lines
local menu = nui_menu({
relative = "editor",
position = "50%",
border = {
style = style,
text = {
top = "[Choose LSP Client]",
top_align = "center",
},
},
}, {
lines = lines,
min_width = min_width,
min_height = min_height,
keymap = {
focus_next = { "j", "<Down>", "<Tab>" },
focus_prev = { "k", "<Up>", "<S-Tab>" },
close = { "<Esc>", "q", "<C-c>" },
submit = { "<CR>", "<Space>", "l" },
},
on_close = function() end,
on_submit = function(item)
local id = tonumber(string.match(item.text, "%d+"))
for _, check_client in ipairs(navbuddy_attached_clients[for_buf]) do
if id == check_client.id then
make_request(check_client)
return
end
end
end,
})
menu:mount()
end
local function request(for_buf, handler)
local function make_request(client)
navic.request_symbol(for_buf, function(bufnr, symbols)
navic.update_data(bufnr, symbols)
navic.update_context(bufnr)
local context_data = navic.get_context_data(bufnr)
local curr_node = context_data[#context_data]
handler(for_buf, curr_node, client.name)
end, client)
end
if navbuddy_attached_clients[for_buf] == nil then
vim.notify("No lsp servers attached", vim.log.levels.ERROR)
elseif #navbuddy_attached_clients[for_buf] == 1 then
make_request(navbuddy_attached_clients[for_buf][1])
elseif config.lsp.preference ~= nil then
local found = false
for _, preferred_lsp in ipairs(config.lsp.preference) do
for _, attached_lsp in ipairs(navbuddy_attached_clients[for_buf]) do
if preferred_lsp == attached_lsp.name then
navbuddy_attached_clients[for_buf] = { attached_lsp }
found = true
make_request(attached_lsp)
break
end
end
if found then
break
end
end
if not found then
choose_lsp_menu(for_buf, make_request)
end
else
choose_lsp_menu(for_buf, make_request)
end
end
local function handler(bufnr, curr_node, lsp_name)
if curr_node.is_root then
if curr_node.children then
local curr_line = vim.api.nvim_win_get_cursor(0)[1]
local closest_dist = math.abs(curr_line - curr_node.children[1].scope["start"].line)
local closest_node = curr_node.children[1]
for _, node in ipairs(curr_node.children) do
if math.abs(curr_line - node.scope["start"].line) < closest_dist then
closest_dist = math.abs(curr_line - node.scope["start"].line)
closest_node = node
end
end
curr_node = closest_node
else
return
end
end
display:new({
for_buf = bufnr,
for_win = vim.api.nvim_get_current_win(),
start_cursor = vim.api.nvim_win_get_cursor(vim.api.nvim_get_current_win()),
focus_node = curr_node,
config = config,
lsp_name = lsp_name,
})
end
-- @Public Methods
local M = {}
function M.open(bufnr)
bufnr = bufnr or vim.api.nvim_get_current_buf()
request(bufnr, handler)
end
function M.attach(client, bufnr)
if not client.server_capabilities.documentSymbolProvider then
if not vim.g.navbuddy_silence then
vim.notify(
'nvim-navbuddy: Server "' .. client.name .. '" does not support documentSymbols.',
vim.log.levels.ERROR
)
end
return
end
if navbuddy_attached_clients[bufnr] == nil then
navbuddy_attached_clients[bufnr] = {}
end
-- Check if already attached
for _, c in ipairs(navbuddy_attached_clients[bufnr]) do
if c.id == client.id then
return
end
end
-- Check for stopped lsp servers
for i, c in ipairs(navbuddy_attached_clients[bufnr]) do
if c.is_stopped then
table.remove(navbuddy_attached_clients[bufnr], i)
end
end
table.insert(navbuddy_attached_clients[bufnr], client)
local navbuddy_augroup = vim.api.nvim_create_augroup("navbuddy", { clear = false })
vim.api.nvim_clear_autocmds({
buffer = bufnr,
group = navbuddy_augroup,
})
vim.api.nvim_create_autocmd("BufDelete", {
callback = function()
navic.clear_buffer_data(bufnr)
navbuddy_attached_clients[bufnr] = nil
end,
group = navbuddy_augroup,
buffer = bufnr,
})
vim.api.nvim_create_autocmd("LspDetach", {
callback = function()
if navbuddy_attached_clients[bufnr] ~= nil then
for i, c in ipairs(navbuddy_attached_clients[bufnr]) do
if c.id == client.id then
table.remove(navbuddy_attached_clients[bufnr], i)
break
end
end
if #navbuddy_attached_clients[bufnr] == 0 then
navbuddy_attached_clients[bufnr] = nil
end
end
end,
group = navbuddy_augroup,
buffer = bufnr,
})
vim.api.nvim_buf_create_user_command(bufnr, "Navbuddy", function()
M.open(bufnr)
end, {})
end
function M.setup(user_config)
if user_config ~= nil then
if user_config.window ~= nil then
config.window = vim.tbl_deep_extend("keep", user_config.window, config.window)
end
-- If one is set, default for others should be none
if
config.window.sections.left.border ~= nil
or config.window.sections.mid.border ~= nil
or config.window.sections.right.border ~= nil
then
config.window.sections.left.border = config.window.sections.left.border or "none"
config.window.sections.mid.border = config.window.sections.mid.border or "none"
config.window.sections.right.border = config.window.sections.right.border or "none"
end
if user_config.node_markers ~= nil then
config.node_markers = vim.tbl_deep_extend("keep", user_config.node_markers, config.node_markers)
end
if user_config.icons ~= nil then
for k, v in pairs(user_config.icons) do
if navic.adapt_lsp_str_to_num(k) then
config.icons[navic.adapt_lsp_str_to_num(k)] = v
end
end
end
if user_config.use_default_mappings ~= nil then
config.use_default_mappings = user_config.use_default_mappings
end
if user_config.mappings ~= nil then
if config.use_default_mappings then
config.mappings = vim.tbl_deep_extend("keep", user_config.mappings, config.mappings)
else
config.mappings = user_config.mappings
end
end
if user_config.lsp ~= nil then
config.lsp = vim.tbl_deep_extend("keep", user_config.lsp, config.lsp)
end
if user_config.source_buffer ~= nil then
config.source_buffer = vim.tbl_deep_extend("keep", user_config.source_buffer, config.source_buffer)
end
if user_config.custom_hl_group ~= nil then
config.custom_hl_group = user_config.custom_hl_group
end
end
if config.lsp.auto_attach == true then
local navbuddy_augroup = vim.api.nvim_create_augroup("navbuddy", { clear = false })
vim.api.nvim_clear_autocmds({
group = navbuddy_augroup,
})
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
local bufnr = args.buf
if args.data == nil and args.data.client_id == nil then
return
end
local client = vim.lsp.get_client_by_id(args.data.client_id)
if not client.server_capabilities.documentSymbolProvider then
return
end
M.attach(client, bufnr)
end,
})
--- Attach to already active clients.
local all_clients = vim.lsp.get_clients()
local supported_clients = vim.tbl_filter(function(client)
return client.server_capabilities.documentSymbolProvider
end, all_clients)
for _, client in ipairs(supported_clients) do
local buffers_of_client = vim.lsp.get_buffers_by_client_id(client.id)
for _, buffer_number in ipairs(buffers_of_client) do
M.attach(client, buffer_number)
end
end
end
end
return M

View File

@ -0,0 +1,196 @@
local navic = require("nvim-navic.lib")
local ui = {}
function ui.get_border_chars(style, section)
if style ~= "single" and style ~= "rounded" and style ~= "double" and style ~= "solid" then
return style
end
-- stylua: ignore
local border_chars = {
top_left = {
single = "",
rounded = "",
double = "",
solid = "",
},
top = {
single = "",
rounded = "",
double = "",
solid = "",
},
top_right = {
single = "",
rounded = "",
double = "",
solid = "",
},
right = {
single = "",
rounded = "",
double = "",
solid = "",
},
bottom_right = {
single = "",
rounded = "",
double = "",
solid = "",
},
bottom = {
single = "",
rounded = "",
double = "",
solid = "",
},
bottom_left = {
single = "",
rounded = "",
double = "",
solid = "",
},
left = {
single = "",
rounded = "",
double = "",
solid = "",
},
top_T = {
single = "",
rounded = "",
double = "",
solid = "",
},
bottom_T = {
single = "",
rounded = "",
double = "",
solid = "",
},
blank = "",
}
local border_chars_map = {
left = {
style = {
border_chars.top_left[style],
border_chars.top[style],
border_chars.top[style],
border_chars.blank,
border_chars.bottom[style],
border_chars.bottom[style],
border_chars.bottom_left[style],
border_chars.left[style],
},
},
mid = {
style = {
border_chars.top_T[style],
border_chars.top[style],
border_chars.top[style],
border_chars.blank,
border_chars.bottom[style],
border_chars.bottom[style],
border_chars.bottom_T[style],
border_chars.left[style],
},
},
right = {
border_chars.top_T[style],
border_chars.top[style],
border_chars.top_right[style],
border_chars.right[style],
border_chars.bottom_right[style],
border_chars.bottom[style],
border_chars.bottom_T[style],
border_chars.left[style],
},
}
return border_chars_map[section]
end
function ui.highlight_setup(config)
for lsp_num = 1, 26 do
local navbuddy_ok, _ =
pcall(vim.api.nvim_get_hl_by_name, "Navbuddy" .. navic.adapt_lsp_num_to_str(lsp_num), false)
local navic_ok, navic_hl =
pcall(vim.api.nvim_get_hl_by_name, "NavicIcons" .. navic.adapt_lsp_num_to_str(lsp_num), true)
if not navbuddy_ok and navic_ok then
navic_hl = navic_hl["foreground"]
vim.api.nvim_set_hl(0, "Navbuddy" .. navic.adapt_lsp_num_to_str(lsp_num), {
fg = navic_hl,
})
end
local ok, navbuddy_hl =
pcall(vim.api.nvim_get_hl_by_name, "Navbuddy" .. navic.adapt_lsp_num_to_str(lsp_num), true)
if ok then
navbuddy_hl = navbuddy_hl["foreground"]
local highlight
if config.custom_hl_group ~= nil then
highlight = { link = config.custom_hl_group }
else
highlight = { bg = navbuddy_hl }
end
vim.api.nvim_set_hl(0, "NavbuddyCursorLine" .. navic.adapt_lsp_num_to_str(lsp_num), highlight)
else
local _, normal_hl = pcall(vim.api.nvim_get_hl_by_name, "Normal", true)
normal_hl = normal_hl["foreground"]
vim.api.nvim_set_hl(0, "Navbuddy" .. navic.adapt_lsp_num_to_str(lsp_num), { fg = normal_hl })
local highlight
if config.custom_hl_group ~= nil then
highlight = { link = config.custom_hl_group }
else
highlight = { bg = normal_hl }
end
vim.api.nvim_set_hl(0, "NavbuddyCursorLine" .. navic.adapt_lsp_num_to_str(lsp_num), highlight)
end
end
local ok, _ = pcall(vim.api.nvim_get_hl_by_name, "NavbuddyCursorLine", false)
if not ok then
local highlight
if config.custom_hl_group ~= nil then
highlight = { link = config.custom_hl_group }
else
highlight = { reverse = true, bold = true }
end
vim.api.nvim_set_hl(0, "NavbuddyCursorLine", highlight)
end
ok, _ = pcall(vim.api.nvim_get_hl_by_name, "NavbuddyCursor", false)
if not ok then
vim.api.nvim_set_hl(0, "NavbuddyCursor", {
bg = "#000000",
blend = 100,
})
end
ok, _ = pcall(vim.api.nvim_get_hl_by_name, "NavbuddyName", false)
if not ok then
vim.api.nvim_set_hl(0, "NavbuddyName", { link = "IncSearch" })
end
ok, _ = pcall(vim.api.nvim_get_hl_by_name, "NavbuddyScope", false)
if not ok then
vim.api.nvim_set_hl(0, "NavbuddyScope", { link = "Visual" })
end
ok, _ = pcall(vim.api.nvim_get_hl_by_name, "NavbuddyFloatBorder", false)
if not ok then
vim.api.nvim_set_hl(0, "NavbuddyFloatBorder", { link = "FloatBorder" })
end
ok, _ = pcall(vim.api.nvim_get_hl_by_name, "NavbuddyNormalFloat", false)
if not ok then
vim.api.nvim_set_hl(0, "NavbuddyNormalFloat", { link = "NormalFloat" })
end
end
return ui