document symbol jump

neovim_0_5
ray-x 3 years ago
parent 554b546a67
commit fc759b4e8a

@ -9,7 +9,7 @@ Easy code navigation through LSP and 🌲🏡Treesitter symbols, diagnostic erro
- Unorthodox UI with floating windows
- Async request with lsp.buf_request for reference search
- Treesitter symbol search. It is handy for large filas (Some of LSP e.g. sumneko_lua, there is a 100kb file size limition?)
- fzy search with Lua-JIT
- FZY search with Lua-JIT
- Better navigation for diagnostic errors, Navigate through all files/buffers that contain errors/warnings
- Grouping references/implementation/incomming/outgoing based on file names.
- Nerdfont, emoji for LSP and Treesitter kind
@ -120,6 +120,7 @@ colorscheme: [aurora](https://github.com/ray-x/aurora)
![reference](https://github.com/ray-x/files/blob/master/img/navigator/ref.gif?raw=true)
### Document Symbol
![document symbol](https://github.com/ray-x/files/blob/master/img/navigator/doc_symbol.gif?raw=true)
@ -128,6 +129,12 @@ colorscheme: [aurora](https://github.com/ray-x/aurora)
![workspace symbol](https://github.com/ray-x/files/blob/master/img/navigator/workspace_symbol.gif?raw=true)
# Current symbol highlight and jump backword/forward between symbols
Document highlight provided by LSP.
Jump between symbols between symbols with treesitter
![doc jump](https://github.com/ray-x/files/blob/master/img/navigator/doc_hl_jump.gif?raw=true)
### Diagnostic
Diagnostic in single bufer

@ -0,0 +1,118 @@
local util = require "navigator.util"
local log = util.log
local api = vim.api
local references = {}
-- returns r1 < r2 based on start of range
local function before(r1, r2)
if r1.start.line < r2.start.line then
return true
end
if r2.start.line < r1.start.line then
return false
end
if r1.start.character < r2.start.character then
return true
end
return false
end
local function handle_document_highlight(_, _, result, _, bufnr, _)
if not bufnr then
return
end
if type(result) ~= "table" then
vim.lsp.util.buf_clear_references(bufnr)
return
end
table.sort(
result,
function(a, b)
return before(a.range, b.range)
end
)
references[bufnr] = result
end
-- modify from vim-illuminate
local function goto_adjent_reference(opt)
log(opt)
opt = vim.tbl_extend("force", {forward = true, wrap = true}, opt or {})
local bufnr = vim.api.nvim_get_current_buf()
local refs = references[bufnr]
if not refs or #refs == 0 then
return nil
end
local next = nil
local nexti = nil
local crow, ccol = unpack(vim.api.nvim_win_get_cursor(0))
local crange = {start = {line = crow - 1, character = ccol}}
for i, ref in ipairs(refs) do
local range = ref.range
if opt.forward then
if before(crange, range) and (not next or before(range, next)) then
next = range
nexti = i
end
else
if before(range, crange) and (not next or before(next, range)) then
next = range
nexti = i
end
log(nexti, next)
end
end
if not next and opt.wrap then
nexti = opt.reverse and #refs or 1
next = refs[nexti].range
end
log(next)
vim.api.nvim_win_set_cursor(0, {next.start.line + 1, next.start.character})
return next
end
local function documentHighlight()
api.nvim_exec(
[[
hi LspReferenceRead cterm=bold gui=Bold ctermbg=yellow guibg=purple4
hi LspReferenceText cterm=bold gui=Bold ctermbg=red guibg=gray27
hi LspReferenceWrite cterm=bold gui=Bold,Italic ctermbg=red guibg=MistyRose
augroup lsp_document_highlight
autocmd! * <buffer>
autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
augroup END
]],
false
)
vim.lsp.handlers["textDocument/documentHighlight"] = function(_, _, result, _, bufnr)
if not result then
return
end
bufnr = api.nvim_get_current_buf()
vim.lsp.util.buf_clear_references(bufnr)
vim.lsp.util.buf_highlight_references(bufnr, result)
bufnr = bufnr or 0
if type(result) ~= "table" then
vim.lsp.util.buf_clear_references(bufnr)
return
end
table.sort(
result,
function(a, b)
return before(a.range, b.range)
end
)
references[bufnr] = result
end
end
return {
documentHighlight = documentHighlight,
goto_adjent_reference = goto_adjent_reference,
handle_document_highlight = handle_document_highlight
}

@ -9,29 +9,6 @@ local diagnostic_map = function(bufnr)
api.nvim_buf_set_keymap(bufnr, "n", "]O", ":lua vim.lsp.diagnostic.set_loclist()<CR>", opts)
end
local M = {}
local function documentHighlight()
api.nvim_exec(
[[
hi LspReferenceRead cterm=bold gui=Bold ctermbg=yellow guibg=DarkOrchid3
hi LspReferenceText cterm=bold gui=Bold ctermbg=red guibg=gray27
hi LspReferenceWrite cterm=bold gui=Bold,Italic ctermbg=red guibg=MistyRose
augroup lsp_document_highlight
autocmd! * <buffer>
autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
augroup END
]],
false
)
vim.lsp.handlers["textDocument/documentHighlight"] = function(_, _, result, _)
if not result then
return
end
bufnr = api.nvim_get_current_buf()
vim.lsp.util.buf_clear_references(bufnr)
vim.lsp.util.buf_highlight_references(bufnr, result)
end
end
M.on_attach = function(client, bufnr)
log("attaching")
@ -46,19 +23,21 @@ M.on_attach = function(client, bufnr)
api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.vim.lsp.omnifunc")
-- https://github.com/fsouza
require("navigator.lspclient.mapping").setup({client = client, bufnr = bufnr, cap = client.resolved_capabilities})
if client.resolved_capabilities.document_highlight then
documentHighlight()
require("navigator.dochighlight").documentHighlight()
end
require("navigator.lspclient.mapping").setup({client = client, bufnr = bufnr, cap = client.resolved_capabilities})
require "navigator.lspclient.lspkind".init()
vim.cmd [[silent! packadd vim-illuminate]]
local hasilm, ilm = pcall(require, "illuminate") -- package.loaded["illuminate"]
if hasilm then
ilm.on_attach(client)
if not package.loaded["illuminate"] then
vim.cmd [[silent! packadd vim-illuminate]]
local hasilm, ilm = pcall(require, "illuminate") -- package.loaded["illuminate"]
if hasilm then
ilm.on_attach(client)
end
end
require "navigator.lspclient.lspkind".init()
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.textDocument.completion.completionItem.snippetSupport = true

@ -2,6 +2,8 @@
local log = require "navigator.util".log
local verbose = require "navigator.util".verbose
_Loading = false
if packer_plugins ~= nil then
if not packer_plugins["neovim/nvim-lspconfig"] or not packer_plugins["neovim/nvim-lspconfig"].loaded then
vim.cmd [[packadd nvim-lspconfig]]
@ -240,15 +242,52 @@ local function load_cfg(ft, client, cfg, loaded)
end
end
lspconfig[client].setup(cfg)
log(client, "loading for", ft, cfg)
log(client, "loading for", ft)
end
end
-- need to verify the lsp server is up
end
local function wait_lsp_startup(ft)
local clients = vim.lsp.get_active_clients() or {}
local loaded = {}
for _, client in ipairs(clients) do
if client ~= nil then
table.insert(loaded, client.name)
end
end
for _, lspclient in ipairs(servers) do
local cfg = setups[lspclient] or default_cfg
load_cfg(ft, lspclient, cfg, loaded)
end
--
local timer = vim.loop.new_timer()
local i = 0
timer:start(
200,
200,
function()
clients = vim.lsp.get_active_clients() or {}
i = i + 1
if i > 5 or #clients > 0 then
timer:close() -- Always close handles to avoid leaks.
log("active", #clients, i)
_Loading = false
return true
end
_Loading = false
end
)
end
vim.cmd([[autocmd filetype * lua require'navigator.lspclient.clients'.setup()]]) -- BufWinEnter BufNewFile,BufRead ?
local function setup(user_opts)
verbose(debug.traceback())
if _Loading == true then
return
end
if lspconfig == nil then
error("lsp-config need installed and enabled")
@ -267,35 +306,7 @@ local function setup(user_opts)
return
end
for i = 1, 2 do
local clients = vim.lsp.get_active_clients() or {}
local loaded = {}
for _, client in ipairs(clients) do
if client ~= nil then
table.insert(loaded, client.name)
end
end
for _, lspclient in ipairs(servers) do
local cfg = setups[lspclient] or default_cfg
load_cfg(ft, lspclient, cfg, loaded)
end
local timer = vim.loop.new_timer()
local i = 0
-- Waits 20ms, then repeats every 20ms until lsp is loaded.
timer:start(
20,
20,
function()
local clients = vim.lsp.get_active_clients() or {}
if i > 20 or #clients > 0 then
timer:close() -- Always close handles to avoid leaks.
log("active", #clients)
return true
end
i = i + 1
end
)
end
_Loading = true
wait_lsp_startup(ft)
end
return {setup = setup, cap = cap}

@ -36,6 +36,8 @@ local key_maps = {
{key = "gG", func = "require('navigator.diagnostics').show_diagnostic()"},
{key = "]d", func = "diagnostic.goto_next()"},
{key = "[d", func = "diagnostic.goto_prev()"},
{key = "]r", func = "require('navigator.treesitter').goto_next_usage()"},
{key = "[r", func = "require('navigator.treesitter').goto_previous_usage()"},
{key = "<C-LeftMouse>", func = "definition()"},
{key = "g<LeftMouse>", func = "implementation()"}
}
@ -160,6 +162,7 @@ function M.setup(user_opts)
if not hassig then
vim.lsp.handlers["textDocument/signatureHelp"] = require "navigator.signature".signature_handler
end
-- vim.lsp.handlers["textDocument/hover"] = require 'navigator.hover'.hover_handler
end

@ -2,7 +2,7 @@ local util = require "navigator.util"
local log = util.log
local lsphelper = require "navigator.lspwrapper"
local gui = require "navigator.gui"
local lsp = require 'navigator.lspwrapper'
local lsp = require "navigator.lspwrapper"
local verbose = require "navigator.util".verbose
-- local log = util.log
-- local partial = util.partial
@ -12,8 +12,6 @@ local locations_to_items = lsphelper.locations_to_items
--vim.api.nvim_set_option("navtator_options", {width = 90, height = 60, location = require "navigator.location".center})
-- local options = vim.g.navtator_options or {width = 60, height = 40, location = location.center}
local function ref_hdlr(arg1, api, locations, num, bufnr)
local opts = {}
-- log("arg1", arg1)
@ -27,17 +25,17 @@ local function ref_hdlr(arg1, api, locations, num, bufnr)
end
verbose(locations)
local items = locations_to_items(locations)
gui.new_list_view({items = items, api = 'Reference'})
gui.new_list_view({items = items, api = "Reference"})
end
local async_reference_request = function()
local method = {"textDocument/references"}
local ref_params = vim.lsp.util.make_position_params()
ref_params.context = {includeDeclaration = true;}
ref_params.context = {includeDeclaration = true}
return lsp.call_async(method[1], ref_params, ref_hdlr) -- return asyncresult, canceller
end
return { reference_handler = ref_hdlr,
show_reference = async_reference_request
return {
reference_handler = ref_hdlr,
show_reference = async_reference_request
}

@ -2,6 +2,7 @@ local gui = require "navigator.gui"
local ts_locals = require "nvim-treesitter.locals"
local parsers = require "nvim-treesitter.parsers"
local ts_utils = require "nvim-treesitter.ts_utils"
local utils = require "nvim-treesitter.utils"
local api = vim.api
local util = require "navigator.util"
local M = {}
@ -37,22 +38,28 @@ local function get_definitions(bufnr)
local nodes_set = {}
for _, loc in ipairs(local_nodes) do
if loc.definition then
ts_locals.recurse_local_nodes(loc.definition, function(_, node, _, match)
-- lua doesn't compare tables by value,
-- use the value from byte count instead.
local _, _, start = node:start()
nodes_set[start] = {node = node, type = match or ""}
end)
ts_locals.recurse_local_nodes(
loc.definition,
function(_, node, _, match)
-- lua doesn't compare tables by value,
-- use the value from byte count instead.
local _, _, start = node:start()
nodes_set[start] = {node = node, type = match or ""}
end
)
end
end
-- Sort by order of appearance.
local definition_nodes = vim.tbl_values(nodes_set)
table.sort(definition_nodes, function (a, b)
local _, _, start_a = a.node:start()
local _, _, start_b = b.node:start()
return start_a < start_b
end)
table.sort(
definition_nodes,
function(a, b)
local _, _, start_a = a.node:start()
local _, _, start_b = b.node:start()
return start_a < start_b
end
)
return definition_nodes
end
@ -80,6 +87,41 @@ local function prepare_node(node, kind)
return matches
end
local lsp_reference = require "navigator.dochighlight".goto_adjent_reference
function M.goto_adjacent_usage(bufnr, delta)
local opt = {forward = true}
log(delta)
if delta < 0 then
opt = {forward = false}
end
local bufnr = bufnr or api.nvim_get_current_buf()
local node_at_point = ts_utils.get_node_at_cursor()
if not node_at_point then
lsp_reference(opt)
return
end
local def_node, scope = ts_locals.find_definition(node_at_point, bufnr)
local usages = ts_locals.find_usages(def_node, scope, bufnr)
local index = utils.index_of(usages, node_at_point)
if not index then
lsp_reference(opt)
return
end
local target_index = (index + delta + #usages - 1) % #usages + 1
ts_utils.goto_node(usages[target_index])
end
function M.goto_next_usage(bufnr)
return M.goto_adjacent_usage(bufnr, 1)
end
function M.goto_previous_usage(bufnr)
return M.goto_adjacent_usage(bufnr, -1)
end
local function get_all_nodes(bufnr)
bufnr = bufnr or 0
if not parsers.has_parser() then
@ -130,8 +172,10 @@ local function get_all_nodes(bufnr)
item.kind = node.kind
item.node_scope = get_smallest_context(item.tsdata)
local start_line_node, _, _ = item.tsdata:start()
item.node_text = ts_utils.get_node_text(item.tsdata, bufnr)[1]
if item.node_text == '_' then goto continue end
item.node_text = ts_utils.get_node_tex(item.tsdata, bufnr)[1]
if item.node_text == "_" then
goto continue
end
item.full_text = vim.trim(api.nvim_buf_get_lines(bufnr, start_line_node, start_line_node + 1, false)[1] or "")
item.range = ts_utils.node_to_lsp_range(item.tsdata)
item.uri = uri
@ -141,8 +185,10 @@ local function get_all_nodes(bufnr)
item.lnum, item.col, _ = def.node:start()
item.lnum = item.lnum + 1
item.col = item.col + 1
local indent=""
if #parents > 1 then indent = string.rep(' ', #parents - 1) .. '' end
local indent = ""
if #parents > 1 then
indent = string.rep(" ", #parents - 1) .. ""
end
item.text = string.format("%s%s%-10s\t🧩 %s", item.kind, indent, item.node_text, item.full_text)
if #item.text > length then

@ -95,7 +95,7 @@ local default_config = {
plugin = "navigator",
use_console = false,
use_file = true,
level = "info"
level = "error"
}
M._log = require("guihua.log").new({level = default_config.level}, true)
@ -240,4 +240,42 @@ function M.exclude(fname)
return false
end
--- virtual text
-- name space search
local nss
local api = vim.api
local bufs
function M.set_virt_eol(bufnr, lnum, chunks, priority, id)
if nss == nil then
nss = api.nvim_create_namespace("navigator_search")
end
bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr
bufs[bufnr] = true
-- id may be nil
return api.nvim_buf_set_extmark(bufnr, nss, lnum, -1, {id = id, virt_text = chunks, priority = priority})
end
function M.clear_buf(bufnr)
if not bufnr then
return
end
bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr
if bufs[bufnr] then
if api.nvim_buf_is_valid(bufnr) then
api.nvim_buf_clear_namespace(bufnr, nss, 0, -1)
-- nvim_buf_del_extmark
end
bufs[bufnr] = nil
end
end
function M.clear_all_buf()
for bufnr in pairs(bufs) do
M.clear_buf(bufnr)
end
bufs = {}
end
return M

Loading…
Cancel
Save