diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..2af0068 --- /dev/null +++ b/TODO.md @@ -0,0 +1,3 @@ +- Defination vs Reference (A seperate def call) +- Called by (which function calls this or reference the variable) +- inline edit in float window (Almost done) diff --git a/lua/navigator.lua b/lua/navigator.lua index b62356f..e70fe10 100644 --- a/lua/navigator.lua +++ b/lua/navigator.lua @@ -9,13 +9,10 @@ _NgConfigValues = { -- -- your on_attach will be called at end of navigator on_attach -- end, sumneko_root_path = vim.fn.expand("$HOME") .. "/github/sumneko/lua-language-server", - sumneko_binary = vim.fn.expand("$HOME") .. "/github/sumneko/lua-language-server/bin/macOS/lua-language-server", - code_action_prompt = { - enable = true, - sign = true, - sign_priority = 40, - virtual_text = true - } + sumneko_binary = vim.fn.expand("$HOME") .. + "/github/sumneko/lua-language-server/bin/macOS/lua-language-server", + treesitter_call_tree = true, + code_action_prompt = {enable = true, sign = true, sign_priority = 40, virtual_text = true} } vim.cmd("command! -nargs=0 LspLog call v:lua.open_lsp_log()") @@ -23,27 +20,21 @@ vim.cmd("command! -nargs=0 LspRestart call v:lua.reload_lsp()") local extend_config = function(opts) opts = opts or {} - if next(opts) == nil then - return - end + if next(opts) == nil then return end for key, value in pairs(opts) do if _NgConfigValues[key] == nil then error(string.format("[] Key %s not valid", key)) return end if type(M.config_values[key]) == "table" then - for k, v in pairs(value) do - _NgConfigValues[key][k] = v - end + for k, v in pairs(value) do _NgConfigValues[key][k] = v end else _NgConfigValues[key] = value end end end -M.config_values = function() - return _NgConfigValues -end +M.config_values = function() return _NgConfigValues end M.setup = function(cfg) extend_config(cfg) diff --git a/lua/navigator/definition.lua b/lua/navigator/definition.lua index 88b3dad..90de38d 100644 --- a/lua/navigator/definition.lua +++ b/lua/navigator/definition.lua @@ -5,8 +5,16 @@ local gui = require "navigator.gui" local log = util.log local TextView = require("guihua.textview") -- callback for lsp definition, implementation and declaration handler -local function definition_hdlr(_, _, locations, _, bufnr) +local function definition_hdlr(err, _, locations, _, bufnr) -- log(locations) + if err ~= nil then + print(err) + return + end + if type(locations) == "number" then + log(locations) + err("unable to handle request") + end if locations == nil or vim.tbl_isempty(locations) then print "Definition not found" return @@ -38,9 +46,7 @@ local function def_preview(timeout_ms) -- result = {vim.tbl_deep_extend("force", {}, unpack(result))} -- log("def-preview", result) for key, value in pairs(result) do - if result[key] ~= nil then - table.insert(data, value.result[1]) - end + if result[key] ~= nil then table.insert(data, value.result[1]) end end local range = data[1].targetRange or data[1].range @@ -49,13 +55,9 @@ local function def_preview(timeout_ms) row = math.max(row - 3, 1) local delta = range.start.line - row + 1 local uri = data[1].uri or data[1].targetUri - if not uri then - return - end + if not uri then return end local bufnr = vim.uri_to_bufnr(uri) - if not vim.api.nvim_buf_is_loaded(bufnr) then - vim.fn.bufload(bufnr) - end + if not vim.api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) end -- TODO: 12 should be an option local definition = vim.api.nvim_buf_get_lines(bufnr, row, range["end"].line + 12, false) local def_line = vim.api.nvim_buf_get_lines(bufnr, range.start.line, range.start.line + 1, false) diff --git a/lua/navigator/gui.lua b/lua/navigator/gui.lua index 417afa0..b4e2fcf 100644 --- a/lua/navigator/gui.lua +++ b/lua/navigator/gui.lua @@ -2,72 +2,78 @@ local M = {} local ListView = require "guihua.listview" local TextView = require "guihua.textview" local util = require "navigator.util" -local log = require "navigator.util".log -local verbose = require "navigator.util".verbose +local log = require"navigator.util".log +local verbose = require"navigator.util".verbose local api = vim.api function M.new_preview(opts) - return TextView:new( - { - loc = "top_center", - rect = { - height = opts.preview_heigh or 12, - width = opts.width or 100, - pos_x = opts.pos_x or 0, - pos_y = opts.pos_y or 4 - }, - -- data = display_data, - relative = opts.relative, - data = opts.items, - syntax = opts.syntax, - enter = opts.enter or false, - hl_line = opts.hl_line - } - ) + return TextView:new({ + loc = "top_center", + rect = { + height = opts.height, -- opts.preview_heigh or 12, -- TODO 12 + width = opts.width or 100, + pos_x = opts.pos_x or 0, + pos_y = opts.pos_y or 4 + }, + -- data = display_data, + relative = opts.relative, + -- data = opts.items, -- either items or uri + uri = opts.uri, + syntax = opts.syntax, + enter = opts.enter or false, + range = opts.range, + display_range = opts.display_range, + hl_line = opts.hl_line + }) end -function M._preview_location(opts) --location, width, pos_x, pos_y - local uri = opts.location.targetUri or opts.location.uri +function M._preview_location(opts) -- location, width, pos_x, pos_y + local uri = opts.location.uri if opts.uri == nil then log("invalid/nil uri ") return end local bufnr = vim.uri_to_bufnr(uri) - if not api.nvim_buf_is_loaded(bufnr) then - vim.fn.bufload(bufnr) - end + if not api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) end -- - local range = opts.location.targetRange or opts.location.range - if range.start == nil then - print("error invalid range") - return - end - if range.start.line == nil then - range.start.line = range["end"].line - 1 - opts.lnum = range["end"].line + 1 - log(opts) - end - if range["end"].line == nil then - range["end"].line = range.start.line + 1 - opts.lnum = range.start.line + 1 - log(opts) - end - local contents = api.nvim_buf_get_lines(bufnr, range.start.line, (range["end"].line or 1) + 10, false) + local display_range = opts.location.range + -- if range.start == nil then + -- print("error invalid range") + -- return + -- end + -- if range.start.line == nil then + -- range.start.line = range["end"].line - 1 + -- opts.lnum = range["end"].line + 1 + -- log(opts) + -- end + -- if range["end"].line == nil then + -- range["end"].line = range.start.line + 1 + -- opts.lnum = range.start.line + 1 + -- log(opts) + -- end + -- TODO: preview height + -- local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line, false) -- - local syntax = api.nvim_buf_get_option(bufnr, "syntax") - if syntax == nil or #syntax < 1 then - syntax = api.nvim_buf_get_option(bufnr, "ft") - end + local syntax = api.nvim_buf_get_option(bufnr, "ft") + if syntax == nil or #syntax < 1 then syntax = "c" end - verbose(syntax, contents) - local win_opts = {syntax = syntax, width = opts.width, pos_x = opts.offset_x or 0, pos_y = opts.offset_y or 10} - win_opts.items = contents - win_opts.hl_line = opts.lnum - range.start.line - if win_opts.hl_line < 0 then - win_opts.hl_line = 1 - end - verbose(opts.lnum, range.start.line, win_opts.hl_line) + -- verbose(syntax, contents) + local win_opts = { + syntax = syntax, + width = opts.width, + height = display_range['end'].line - display_range.start.line + 1, + pos_x = opts.offset_x or 0, + pos_y = opts.offset_y or 10, + range = opts.range, + display_range = display_range, + uri = uri, + allow_edit = true + } + -- win_opts.items = contents + win_opts.hl_line = opts.lnum - display_range.start.line + if win_opts.hl_line < 0 then win_opts.hl_line = 1 end + verbose(opts.lnum, opts.range.start.line, win_opts.hl_line) local w = M.new_preview(win_opts) return w @@ -75,15 +81,14 @@ end function M.preview_uri(opts) -- uri, width, line, col, offset_x, offset_y local line_beg = opts.lnum - 1 - if line_beg >= 2 then - line_beg = line_beg - 2 - end - local loc = {uri = opts.uri, targetRange = {start = {line = line_beg}}} - -- TODO: options for 8 - loc.targetRange["end"] = {line = opts.lnum + 8} + if line_beg >= 2 then line_beg = line_beg - 2 end + local loc = {uri = opts.uri, range = {start = {line = line_beg}}} + + -- TODO: preview height + loc.range["end"] = {line = opts.lnum + 12} opts.location = loc - -- log("uri", opts.uri, opts.lnum, opts.location) + log("uri", opts.uri, opts.lnum, opts.location) return M._preview_location(opts) end @@ -92,69 +97,129 @@ function M.new_list_view(opts) local items = opts.items local data = {} - if opts.rawdata then - data = items - else - data = require "guihua.util".aggregate_filename(items, opts) - end + local wwidth = api.nvim_get_option("columns") local width = math.min(opts.width or config.width or 120, math.floor(wwidth * 0.8)) local wheight = config.height or math.floor(api.nvim_get_option("lines") * 0.8) local prompt = opts.prompt or false + opts.width = width + if opts.rawdata then + data = items + else + data = require"guihua.util".prepare_for_render(items, opts) + end + if data and not vim.tbl_isempty(data) then -- replace -- TODO: 10 vimrc opt - if #data > 10 and opts.prompt == nil then - prompt = true - end + if #data > 10 and opts.prompt == nil then prompt = true end local height = math.min(#data, math.floor(wheight / 2)) - local offset_y = height - if prompt then - offset_y = offset_y + 1 - end - return ListView:new( - { - loc = "top_center", - prompt = prompt, - relative = opts.relative, - style = opts.style, - api = opts.api, - rect = { - height = height, + local offset_y = height + 2 -- style shadow took 2 lines + if prompt then offset_y = offset_y + 1 end + return ListView:new({ + loc = "top_center", + prompt = prompt, + relative = opts.relative, + style = opts.style, + api = opts.api, + rect = {height = height, width = width, pos_x = 0, pos_y = 0}, + ft = opts.ft or 'guihua', + -- data = display_data, + data = data, + on_confirm = opts.on_confirm or function(pos) + if pos == 0 then pos = 1 end + local l = data[pos] + if l.filename ~= nil then + verbose("openfile ", l.filename, l.lnum, l.col) + util.open_file_at(l.filename, l.lnum, l.col) + end + end, + on_move = opts.on_move or function(pos) + if pos == 0 then pos = 1 end + local l = data[pos] + verbose("on move", pos, l) + verbose("on move", pos, l.text or l, l.uri, l.filename) + -- todo fix + if l.uri == nil then l.uri = "file:///" .. l.filename end + return M.preview_uri({ + uri = l.uri, width = width, - pos_x = 0, - pos_y = 0 - }, - -- data = display_data, - data = data, - on_confirm = opts.on_confirm or function(pos) - if pos == 0 then - pos = 1 - end - local l = data[pos] - if l.filename ~= nil then - verbose("openfile ", l.filename, l.lnum, l.col) - util.open_file_at(l.filename, l.lnum, l.col) - end - end, - on_move = opts.on_move or function(pos) - if pos == 0 then - pos = 1 - end - local l = data[pos] - verbose("on move", pos, l.text or l, l.uri, l.filename) - -- todo fix - if l.uri == nil then - l.uri = "file:///" .. l.filename - end - return M.preview_uri( - {uri = l.uri, width = width, lnum = l.lnum, col = l.col, offset_x = 0, offset_y = offset_y} - ) - end - } - ) + lnum = l.lnum, + col = l.col, + range = l.range, + offset_x = 0, + offset_y = offset_y, + border = "double" + }) + end + }) end end return M + +-- Doc + +--[[ + -- each item should look like this + -- update if API changes + { + call_by = { }, + col = 40, + display_filename = "./curry.js", + filename = "/Users/ray.xu/lsp_test/js/curry.js", + lnum = 4, + range = { + end = { + character = 46, + line = 3 -- note: C index + }, + start = { + character = 39, + line = 3 + } + }, + rpath = "js/curry.js", + text = " (sum, element, index) => (sum += element * vector2[index]),", + uri = "file:///Users/ray.xu/lsp_test/js/curry.js" + } + --]] + +-- on move item: +--[[ + + call_by = { { + kind = " ", + node_scope = { + end = { + character = 1, + line = 7 + }, + start = { + character = 0, + line = 0 + } + }, + node_text = "curriedDot", + type = "var" + } }, + col = 22, + display_filename = "./curry.js", + filename = "/Users/ray.xu/lsp_test/js/curry.js", + lnum = 4, + range = { + end = { + character = 26, + line = 3 + }, + start = { + character = 21, + line = 3 + } + }, + rpath = "js/curry.js", + text = " 4: (sum, element, index) => (sum += element * vector   curriedDot()", + uri = "file:///Users/ray.xu/lsp_test/js/curry.js" +-- +]] diff --git a/lua/navigator/hierarchy.lua b/lua/navigator/hierarchy.lua index f359a91..2186b62 100644 --- a/lua/navigator/hierarchy.lua +++ b/lua/navigator/hierarchy.lua @@ -1,15 +1,17 @@ local gui = require "navigator.gui" local util = require "navigator.util" local log = util.log -local partial = util.partial +local partial = util.partial local lsphelper = require "navigator.lspwrapper" local cwd = vim.fn.getcwd(0) local M = {} -local function call_hierarchy_handler(direction, err, _, result, _, _, error_message) +local function call_hierarchy_handler(direction, err, _, result, _, _, + error_message) -- log('call_hierarchy') - assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags") + assert(#vim.lsp.buf_get_clients() > 0, + "Must have a client running to use lsp_tags") if err ~= nil then print("ERROR: " .. error_message) return @@ -21,24 +23,22 @@ local function call_hierarchy_handler(direction, err, _, result, _, _, error_mes local call_hierarchy_item = call_hierarchy_call[direction] local kind = ' ' if call_hierarchy_item.kind then - kind = require'navigator.lspclient.lspkind'.symbol_kind(call_hierarchy_item.kind) .. ' ' + kind = require'navigator.lspclient.lspkind'.symbol_kind( + call_hierarchy_item.kind) .. ' ' end for _, range in pairs(call_hierarchy_call.fromRanges) do local filename = assert(vim.uri_to_fname(call_hierarchy_item.uri)) local display_filename = filename:gsub(cwd .. "/", "./", 1) - table.insert( - items, - { - uri = call_hierarchy_item.uri, - filename = filename, - -- display_filename = filename:gsub(cwd .. "/", "./", 1), - display_filename = call_hierarchy_item.detail or display_filename, - text = kind .. call_hierarchy_item.name, - range = range, - lnum = range.start.line, - col = range.start.character - } - ) + table.insert(items, { + uri = call_hierarchy_item.uri, + filename = filename, + -- display_filename = filename:gsub(cwd .. "/", "./", 1), + display_filename = call_hierarchy_item.detail or display_filename, + text = kind .. call_hierarchy_item.name, + range = range, + lnum = range.start.line, + col = range.start.character + }) end end return items @@ -47,38 +47,45 @@ end local call_hierarchy_handler_from = partial(call_hierarchy_handler, "from") local call_hierarchy_handler_to = partial(call_hierarchy_handler, "to") -local function incoming_calls_handler(bang, err, method, result, client_id, bufnr) - assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags") - local results = call_hierarchy_handler_from(err, method, result, client_id, bufnr, "Incoming calls not found") - gui.new_list_view({items = results, api = ' '}) +local function incoming_calls_handler(bang, err, method, result, client_id, + bufnr) + assert(#vim.lsp.buf_get_clients() > 0, + "Must have a client running to use lsp_tags") + local results = call_hierarchy_handler_from(err, method, result, client_id, + bufnr, "Incoming calls not found") + + local ft = vim.api.nvim_buf_get_option(bufnr, "ft") + gui.new_list_view({items = results, ft = ft, api = ' '}) end -local function outgoing_calls_handler(bang, err, method, result, client_id, bufnr) - local results = call_hierarchy_handler_to(err, method, result, client_id, bufnr, "Outgoing calls not found") +local function outgoing_calls_handler(bang, err, method, result, client_id, + bufnr) + local results = call_hierarchy_handler_to(err, method, result, client_id, + bufnr, "Outgoing calls not found") - gui.new_list_view({items =results, api = ' '}) - --fzf_locations(bang, "", "Outgoing Calls", results, false) + local ft = vim.api.nvim_buf_get_option(bufnr, "ft") + gui.new_list_view({items = results, ft = ft, api = ' '}) + -- fzf_locations(bang, "", "Outgoing Calls", results, false) end - function M.incoming_calls(bang, opts) - assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags") - if not lsphelper.check_capabilities("call_hierarchy") then - return - end + assert(#vim.lsp.buf_get_clients() > 0, + "Must have a client running to use lsp_tags") + if not lsphelper.check_capabilities("call_hierarchy") then return end local params = vim.lsp.util.make_position_params() - util.call_sync("callHierarchy/incomingCalls", params, opts, partial(incoming_calls_handler, bang)) + util.call_sync("callHierarchy/incomingCalls", params, opts, + partial(incoming_calls_handler, bang)) end function M.outgoing_calls(bang, opts) - assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags") - if not lsphelper.check_capabilities("call_hierarchy") then - return - end + assert(#vim.lsp.buf_get_clients() > 0, + "Must have a client running to use lsp_tags") + if not lsphelper.check_capabilities("call_hierarchy") then return end local params = vim.lsp.util.make_position_params() - util.call_sync("callHierarchy/outgoingCalls", params, opts, partial(outgoing_calls_handler, bang)) + util.call_sync("callHierarchy/outgoingCalls", params, opts, + partial(outgoing_calls_handler, bang)) end M.incoming_calls_call = partial(M.incoming_calls, 0) diff --git a/lua/navigator/implementation.lua b/lua/navigator/implementation.lua index 4c8f36f..3a5b150 100644 --- a/lua/navigator/implementation.lua +++ b/lua/navigator/implementation.lua @@ -17,20 +17,18 @@ end local function implementation_handler(bang, err, method, result, client_id, bufnr) local results = location_handler(err, method, result, client_id, bufnr, "Implementation not found") - gui.new_list_view({items =results, api = 'Implementation'}) + local ft = vim.api.nvim_buf_get_option(bufnr, "ft") + gui.new_list_view({items = results, ft = ft, api = 'Implementation'}) end function M.implementation(bang, opts) - if not lsphelper.check_capabilities("implementation") then - return - end + if not lsphelper.check_capabilities("implementation") then return end local params = vim.lsp.util.make_position_params() + util.call_sync("textDocument/implementation", params, opts, partial(implementation_handler, bang)) end - - M.implementation_call = partial(M.implementation, 0) M.implementation_handler = partial(implementation_handler, 0) diff --git a/lua/navigator/lspclient/attach.lua b/lua/navigator/lspclient/attach.lua index baa62ba..ec39633 100644 --- a/lua/navigator/lspclient/attach.lua +++ b/lua/navigator/lspclient/attach.lua @@ -3,6 +3,7 @@ local lsp = require("vim.lsp") local util = require "navigator.util" local log = util.log +local verbose = util.verbose local diagnostic_map = function(bufnr) local opts = {noremap = true, silent = true} @@ -11,37 +12,41 @@ end local M = {} M.on_attach = function(client, bufnr) - log("attaching") + local uri = vim.uri_from_bufnr(bufnr) - local hassig, sig = pcall(require, "lsp_signature") - if hassig then - sig.on_attach() + log("loading for ft ", ft, uri) + if uri == 'file://' or uri == 'file:///' then + log("skip loading for ft ", ft, uri) + return end + log("attaching", bufnr) + verbose(client) + local hassig, sig = pcall(require, "lsp_signature") + if hassig then sig.on_attach() end diagnostic_map(bufnr) -- lspsaga - require "navigator.lspclient.highlight".add_highlight() + require"navigator.lspclient.highlight".add_highlight() api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.vim.lsp.omnifunc") - require("navigator.lspclient.mapping").setup({client = client, bufnr = bufnr, cap = client.resolved_capabilities}) + require("navigator.lspclient.mapping").setup({ + client = client, + bufnr = bufnr, + cap = client.resolved_capabilities + }) if client.resolved_capabilities.document_highlight then require("navigator.dochighlight").documentHighlight() end - require "navigator.lspclient.lspkind".init() - + require"navigator.lspclient.lspkind".init() local capabilities = vim.lsp.protocol.make_client_capabilities() capabilities.textDocument.completion.completionItem.snippetSupport = true - local config = require "navigator".config_value - if config ~= nil and config.on_attach ~= nil then - config.on_attach(client, bufnr) - end + local config = require"navigator".config_value + if config ~= nil and config.on_attach ~= nil then config.on_attach(client, bufnr) end end -M.setup = function(cfg) - return M -end +M.setup = function(cfg) return M end return M diff --git a/lua/navigator/lspclient/clients.lua b/lua/navigator/lspclient/clients.lua index b160b5c..e6174fb 100644 --- a/lua/navigator/lspclient/clients.lua +++ b/lua/navigator/lspclient/clients.lua @@ -1,29 +1,26 @@ -- todo allow config passed in -local log = require "navigator.util".log -local verbose = require "navigator.util".verbose +local log = require"navigator.util".log +local verbose = require"navigator.util".verbose _Loading = false if packer_plugins ~= nil then -- packer installed - local loader = require "packer".loader - if not packer_plugins["neovim/nvim-lspconfig"] or not packer_plugins["neovim/nvim-lspconfig"].loaded then - loader("nvim-lspconfig") - end + local loader = require"packer".loader + if not packer_plugins["neovim/nvim-lspconfig"] or + not packer_plugins["neovim/nvim-lspconfig"].loaded then loader("nvim-lspconfig") end if not packer_plugins["ray-x/guihua.lua"] or not packer_plugins["guihua.lua"].loaded then loader("guihua.lua") - -- if lazyloading + -- if lazyloading end end local has_lsp, lspconfig = pcall(require, "lspconfig") -if not has_lsp then - error("loading lsp config") -end +if not has_lsp then error("loading lsp config") end local highlight = require "navigator.lspclient.highlight" local util = lspconfig.util -local config = require "navigator".config_values() +local config = require"navigator".config_values() local cap = vim.lsp.protocol.make_client_capabilities() local on_attach = require("navigator.lspclient.attach").on_attach @@ -32,6 +29,37 @@ local on_attach = require("navigator.lspclient.attach").on_attach -- lua setup local sumneko_root_path = config.sumneko_root_path local sumneko_binary = config.sumneko_binary +local library = {} + +local path = vim.split(package.path, ";") + +table.insert(path, "lua/?.lua") +table.insert(path, "lua/?/init.lua") + +local function add(lib) + for _, p in pairs(vim.fn.expand(lib, false, true)) do + p = vim.loop.fs_realpath(p) + library[p] = true + end +end + +-- add runtime +add("$VIMRUNTIME") + +-- add your config +local home = vim.fn.expand("$HOME") +if vim.fn.isdirectory(home .. "/.config/nvim") then add(home .. "/.config/nvim") end + +-- add plugins it may be very slow to add all in path +-- if vim.fn.isdirectory(home .. "/.config/share/nvim/site/pack/packer") then +-- add(home .. "/.local/share/nvim/site/pack/packer/opt/*") +-- add(home .. "/.local/share/nvim/site/pack/packer/start/*") +-- end + +library[vim.fn.expand("$VIMRUNTIME/lua")] = true +library[vim.fn.expand("$VIMRUNTIME/lua/vim")] = true +library[vim.fn.expand("$VIMRUNTIME/lua/vim/lsp")] = true +-- [vim.fn.expand("~/repos/nvim/lua")] = true local setups = { gopls = { @@ -40,11 +68,8 @@ local setups = { filetypes = {"go", "gomod"}, message_level = vim.lsp.protocol.MessageType.Error, cmd = { - "gopls", - -- share the gopls instance if there is one already - "-remote=auto", - --[[ debug options ]] - -- + "gopls", -- share the gopls instance if there is one already + "-remote=auto", --[[ debug options ]] -- -- "-logfile=auto", -- "-debug=:0", "-remote.debug=:0" @@ -73,10 +98,7 @@ local setups = { }, clangd = { cmd = { - "clangd", - "--background-index", - "--suggest-missing-includes", - "--clang-tidy", + "clangd", "--background-index", "--suggest-missing-includes", "--clang-tidy", "--header-insertion=iwyu" }, filetypes = {"c", "cpp", "objc", "objcpp"}, @@ -87,7 +109,8 @@ local setups = { }, rust_analyzer = { root_dir = function(fname) - return util.root_pattern("Cargo.toml", "rust-project.json", ".git")(fname) or util.path.dirname(fname) + return util.root_pattern("Cargo.toml", "rust-project.json", ".git")(fname) or + util.path.dirname(fname) end, filetypes = {"rust"}, message_level = vim.lsp.protocol.MessageType.error, @@ -105,7 +128,7 @@ local setups = { on_attach = function(client, bufnr) client.resolved_capabilities.execute_command = true highlight.diagnositc_config_sign() - require "sqls".setup {picker = "telescope"} -- or default + require"sqls".setup {picker = "telescope"} -- or default end, settings = { cmd = {"sqls", "-config", "$HOME/.config/sqls/config.yml"} @@ -133,25 +156,16 @@ local setups = { diagnostics = { enable = true, -- Get the language server to recognize the `vim` global - globals = { - "vim", - "describe", - "it", - "before_each", - "after_each", - "teardown", - "pending" - } + globals = {"vim", "describe", "it", "before_each", "after_each", "teardown", "pending"} }, + completion = {callSnippet = "Both"}, workspace = { -- Make the server aware of Neovim runtime files - library = { - [vim.fn.expand("$VIMRUNTIME/lua")] = true, - [vim.fn.expand("$VIMRUNTIME/lua/vim")] = true, - [vim.fn.expand("$VIMRUNTIME/lua/vim/lsp")] = true - -- [vim.fn.expand("~/repos/nvim/lua")] = true - } - } + library = library, + maxPreload = 256, + preloadFileSize = 50000 + }, + telemetry = {enable = false} } } }, @@ -172,49 +186,17 @@ local setups = { init_options = { compilationDatabaseDirectory = "build", root_dir = [[ util.root_pattern("compile_commands.json", "compile_flags.txt", "CMakeLists.txt", "Makefile", ".git") or util.path.dirname ]], - index = { - threads = 2 - }, - clang = { - excludeArgs = {"-frounding-math"} - } + index = {threads = 2}, + clang = {excludeArgs = {"-frounding-math"}} } } } local servers = { - "angularls", - "gopls", - "tsserver", - "flow", - "bashls", - "dockerls", - "julials", - "pyls", - "pyright", - "jedi_language_server", - "jdtls", - "sumneko_lua", - "vimls", - "html", - "jsonls", - "solargraph", - "cssls", - "yamlls", - "clangd", - "ccls", - "sqls", - "denols", - "dartls", - "dotls", - "kotlin_language_server", - "nimls", - "intelephense", - "vuels", - "phpactor", - "omnisharp", - "r_language_server", - "rust_analyzer", + "angularls", "gopls", "tsserver", "flow", "bashls", "dockerls", "julials", "pyls", "pyright", + "jedi_language_server", "jdtls", "sumneko_lua", "vimls", "html", "jsonls", "solargraph", "cssls", + "yamlls", "clangd", "ccls", "sqls", "denols", "dartls", "dotls", "kotlin_language_server", + "nimls", "intelephense", "vuels", "phpactor", "omnisharp", "r_language_server", "rust_analyzer", "terraformls" } @@ -232,16 +214,12 @@ local function load_cfg(ft, client, cfg, loaded) local should_load = false if lspft ~= nil and #lspft > 0 then - for _, value in ipairs(lspft) do - if ft == value then - should_load = true - end - end + for _, value in ipairs(lspft) do if ft == value then should_load = true end end if should_load then for _, c in pairs(loaded) do if client == c then -- loaded - log(client, "already been loaded for", ft, loaded) + verbose(client, "already been loaded for", ft, loaded) return end end @@ -258,35 +236,27 @@ local function wait_lsp_startup(ft, retry) local loaded = {} for i = 1, 2 do for _, client in ipairs(clients) do - if client ~= nil then - table.insert(loaded, client.name) - end + 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 - if not retry or ft == nil then - return - end + if not retry or ft == nil then return end -- local timer = vim.loop.new_timer() local i = 0 - vim.wait( - 1000, - 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 + vim.wait(1000, 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. + verbose("active", #clients, i) _Loading = false - end, - 200 - ) + return true + end + _Loading = false + end, 200) end end @@ -294,39 +264,36 @@ vim.cmd([[autocmd FileType * lua require'navigator.lspclient.clients'.setup()]]) local function setup(user_opts) verbose(debug.traceback()) - if _Loading == true then + if lspconfig == nil then + error("lsp-config need installed and enabled") return end + + if _Loading == true then return end local ft = vim.bo.filetype - if ft == nil then - ft = vim.api.nvim_buf_get_option(0, "filetype") - end + if ft == nil then ft = vim.api.nvim_buf_get_option(0, "filetype") end if ft == nil or ft == "" then log("nil filetype") return end - log("loading for ft ", ft) local retry = true local disable_ft = { - "NvimTree", - "guihua", - "clap_input", - "clap_spinner", - "vista", - "TelescopePrompt", - "csv", - "txt", - "markdown", - "defx" + "NvimTree", "guihua", "clap_input", "clap_spinner", "vista", "vista_kind", "TelescopePrompt", + "csv", "txt", "markdown", "defx" } for i = 1, #disable_ft do if ft == disable_ft[i] then + log("navigator disabled for ft", ft) return end end - if lspconfig == nil then - error("lsp-config need installed and enabled") + local bufnr = vim.fn.bufnr() + local uri = vim.uri_from_bufnr(bufnr) + + log("loading for ft ", ft, uri) + if uri == 'file://' or uri == 'file:///' then + log("skip loading for ft ", ft, uri) return end diff --git a/lua/navigator/lspclient/lspkind.lua b/lua/navigator/lspclient/lspkind.lua index c8051ec..7cf37dc 100644 --- a/lua/navigator/lspclient/lspkind.lua +++ b/lua/navigator/lspclient/lspkind.lua @@ -23,37 +23,13 @@ local kind_symbols = { Struct = " ", Event = "ﳅ", Operator = "", - TypeParameter = "", + TypeParameter = " ", Default = "" } local CompletionItemKind = { - " ", - "𝔉 ", - "ⓕ ", - " ", - "ﴲ ", - " ", - " ", - "ﰮ ", - " ", - " ", - " ", - " ", - "𝕰 ", - " ", - "﬌ ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - "ﳅ ", - " ", - " ", - " " + " ", "𝔉 ", "ⓕ ", " ", "ﴲ ", " ", " ", "ﰮ ", " ", " ", " ", " ", "𝕰 ", " ", + "﬌ ", " ", " ", " ", " ", " ", " ", " ", "ﳅ ", " ", " ", " " } -- A symbol kind. @@ -87,18 +63,14 @@ local SymbolKind = { } local SymbolItemKind = { - " ", " ", " ", " ", "פּ ", "ƒ ", " ", "ﴲ ", " ", "𝕰 ", "蘒", " ", " ", " ", " ", " ", " ", " ", " ", " ", "ﳠ ", " ", " ", "ﳅ ", " ", " ", " " } + " ", " ", " ", " ", "פּ ", "ƒ ", " ", "ﴲ ", " ", "𝕰 ", "蘒", " ", " ", " ", " ", + " ", " ", " ", " ", " ", "ﳠ ", " ", " ", "ﳅ ", " ", " ", " " +} local lspkind = {} -function lspkind.comp_kind(kind) - return CompletionItemKind[kind] or "" -end +function lspkind.comp_kind(kind) return CompletionItemKind[kind] or "" end -function lspkind.symbol_kind(kind) - return SymbolItemKind[kind] or "" -end +function lspkind.symbol_kind(kind) return SymbolItemKind[kind] or "" end -function lspkind.init() - require('vim.lsp.protocol').CompletionItemKind = CompletionItemKind -end +function lspkind.init() require('vim.lsp.protocol').CompletionItemKind = CompletionItemKind end return lspkind diff --git a/lua/navigator/lspclient/mapping.lua b/lua/navigator/lspclient/mapping.lua index 47d9341..96fdd11 100644 --- a/lua/navigator/lspclient/mapping.lua +++ b/lua/navigator/lspclient/mapping.lua @@ -3,7 +3,8 @@ local function set_keymap(...) vim.api.nvim_set_keymap(...) end local event_hdlrs = { {ev = "BufWritePre", func = "diagnostic.set_loclist({open_loclist = false})"}, - {ev = "CursorHold", func = "document_highlight()"}, {ev = "CursorHoldI", func = "document_highlight()"}, + {ev = "CursorHold", func = "document_highlight()"}, + {ev = "CursorHoldI", func = "document_highlight()"}, {ev = "CursorMoved", func = "clear_references()"} } @@ -11,13 +12,15 @@ local key_maps = { {key = "gr", func = "references()"}, {mode = "i", key = "", func = "signature_help()"}, {key = "gs", func = "signature_help()"}, {key = "g0", func = "document_symbol()"}, {key = "gW", func = "workspace_symbol()"}, {key = "", func = "definition()"}, - {key = "gD", func = "declaration()"}, {key = "gp", func = "require('navigator.definition').definition_preview()"}, + {key = "gD", func = "declaration()"}, + {key = "gp", func = "require('navigator.definition').definition_preview()"}, {key = "gT", func = "require('navigator.treesitter').buf_ts()"}, {key = "GT", func = "require('navigator.treesitter').bufs_ts()"}, {key = "K", func = "hover()"}, - {key = "ga", mode = "n", func = "code_action()"}, {key = "ga", mode = "v", func = "range_code_action()"}, - {key = "re", func = "rename()"}, {key = "gi", func = "incoming_calls()"}, - {key = "go", func = "outgoing_calls()"}, {key = "gi", func = "implementation()"}, - {key = "gt", func = "type_definition()"}, {key = "gL", func = "diagnostic.show_line_diagnostics()"}, + {key = "ga", mode = "n", func = "code_action()"}, + {key = "ga", mode = "v", func = "range_code_action()"}, {key = "re", func = "rename()"}, + {key = "gi", func = "incoming_calls()"}, {key = "go", func = "outgoing_calls()"}, + {key = "gi", func = "implementation()"}, {key = "gt", func = "type_definition()"}, + {key = "gL", func = "diagnostic.show_line_diagnostics()"}, {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()"}, @@ -77,7 +80,9 @@ local function set_mapping(user_opts) vim.cmd([[autocmd BufWritePre lua vim.lsp.buf.formatting()]]) end -- if user_opts.cap.document_range_formatting then - if range_fmt then buf_set_keymap("v", "ff", "lua vim.lsp.buf.range_formatting()", opts) end + if range_fmt then + buf_set_keymap("v", "ff", "lua vim.lsp.buf.range_formatting()", opts) + end end local function set_event_handler(user_opts) @@ -94,7 +99,8 @@ local function set_event_handler(user_opts) else f = "lua vim.lsp.buf." .. value.func end - local cmd = "autocmd FileType " .. file_types .. " autocmd nvim_lsp_autos " .. value.ev .. " silent! " .. f + local cmd = "autocmd FileType " .. file_types .. " autocmd nvim_lsp_autos " .. value.ev .. + " silent! " .. f vim.api.nvim_command(cmd) end vim.api.nvim_command([[augroup END]]) @@ -110,8 +116,10 @@ function M.setup(user_opts) set_event_handler(user_opts) local cap = user_opts.cap or {} if cap.call_hierarchy or cap.callHierarchy then - vim.lsp.handlers["callHierarchy/incomingCalls"] = require"navigator.hierarchy".incoming_calls_handler - vim.lsp.handlers["callHierarchy/outgoingCalls"] = require"navigator.hierarchy".outgoing_calls_handler + vim.lsp.handlers["callHierarchy/incomingCalls"] = + require"navigator.hierarchy".incoming_calls_handler + vim.lsp.handlers["callHierarchy/outgoingCalls"] = + require"navigator.hierarchy".outgoing_calls_handler end vim.lsp.handlers["textDocument/references"] = require"navigator.reference".reference_handler @@ -122,15 +130,21 @@ function M.setup(user_opts) vim.lsp.handlers["textDocument/declaration"] = require"navigator.definition".declaration_handler end - vim.lsp.handlers["textDocument/typeDefinition"] = require"navigator.definition".typeDefinition_handler - vim.lsp.handlers["textDocument/implementation"] = require"navigator.implementation".implementation_handler + vim.lsp.handlers["textDocument/typeDefinition"] = + require"navigator.definition".typeDefinition_handler + vim.lsp.handlers["textDocument/implementation"] = + require"navigator.implementation".implementation_handler - vim.lsp.handlers["textDocument/documentSymbol"] = require"navigator.symbols".document_symbol_handler + vim.lsp.handlers["textDocument/documentSymbol"] = + require"navigator.symbols".document_symbol_handler vim.lsp.handlers["workspace/symbol"] = require"navigator.symbols".workspace_symbol_handler - vim.lsp.handlers["textDocument/publishDiagnostics"] = require"navigator.diagnostics".diagnostic_handler + vim.lsp.handlers["textDocument/publishDiagnostics"] = + require"navigator.diagnostics".diagnostic_handler local hassig, sig = pcall(require, "lsp_signature") - if not hassig then vim.lsp.handlers["textDocument/signatureHelp"] = require"navigator.signature".signature_handler end + 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 diff --git a/lua/navigator/lspwrapper.lua b/lua/navigator/lspwrapper.lua index 8139ca4..c8d5088 100644 --- a/lua/navigator/lspwrapper.lua +++ b/lua/navigator/lspwrapper.lua @@ -2,10 +2,18 @@ local M = {} local util = require "navigator.util" local gutil = require "guihua.util" local lsp = require "vim.lsp" -local log = require "navigator.util".log -local symbol_kind = require "navigator.lspclient.lspkind".symbol_kind +local api = vim.api +local log = require"navigator.util".log +local verbose = require"navigator.util".verbose +local symbol_kind = require"navigator.lspclient.lspkind".symbol_kind local cwd = vim.fn.getcwd(0) cwd = gutil.add_pec(cwd) +ts_nodes = {} +ts_nodes_time = {} +local ts_enabled, ts_locals = pcall(require, "nvim-treesitter.locals") + +local calltree_enabled = require"navigator".config_values().treesitter_call_tree + function M.lines_from_locations(locations, include_filename) local fnamemodify = (function(filename) if include_filename then @@ -17,10 +25,8 @@ function M.lines_from_locations(locations, include_filename) local lines = {} for _, loc in ipairs(locations) do - table.insert( - lines, - (fnamemodify(loc["filename"]) .. loc["lnum"] .. ":" .. loc["col"] .. ": " .. vim.trim(loc["text"])) - ) + table.insert(lines, (fnamemodify(loc["filename"]) .. loc["lnum"] .. ":" .. loc["col"] .. ": " .. + vim.trim(loc["text"]))) end return lines @@ -35,11 +41,9 @@ function M.symbols_to_items(result) item.kind = result[i].kind local kind = symbol_kind(item.kind) - item.name = result[i].name --symbol name + item.name = result[i].name -- symbol name item.text = result[i].name - if kind ~= nil then - item.text = kind .. ": " .. item.text - end + if kind ~= nil then item.text = kind .. ": " .. item.text end item.filename = vim.uri_to_fname(item.uri) item.display_filename = item.filename:gsub(cwd .. "/", "./", 1) @@ -48,9 +52,7 @@ function M.symbols_to_items(result) end item.lnum = item.range.start.line + 1 - if item.containerName ~= nil then - item.text = " " .. item.containerName .. item.text - end + if item.containerName ~= nil then item.text = " " .. item.containerName .. item.text end table.insert(locations, item) end end @@ -63,9 +65,7 @@ local function extract_result(results_lsp) if results_lsp then local results = {} for _, server_results in pairs(results_lsp) do - if server_results.result then - vim.list_extend(results, server_results.result) - end + if server_results.result then vim.list_extend(results, server_results.result) end end return results @@ -78,9 +78,7 @@ function M.check_capabilities(feature, client_id) local supported_client = false for _, client in pairs(clients) do supported_client = client.resolved_capabilities[feature] - if supported_client then - goto continue - end + if supported_client then goto continue end end ::continue:: @@ -99,7 +97,8 @@ end function M.call_sync(method, params, opts, handler) params = params or {} opts = opts or {} - local results_lsp, err = lsp.buf_request_sync(0, method, params, opts.timeout or vim.g.navtator_timeout) + local results_lsp, err = lsp.buf_request_sync(0, method, params, + opts.timeout or vim.g.navtator_timeout or 1000) handler(err, method, extract_result(results_lsp), nil, nil) end @@ -110,44 +109,86 @@ function M.call_async(method, params, handler) util.show(...) handler(...) end - return lsp.buf_request(0, method, params, callback) + return lsp.buf_request(0, method, params, callback) -- results_lsp, canceller end +local function ts_functions(uri) + if not ts_enabled or not calltree_enabled then return nil end + local ts_func = require"navigator.treesitter".buf_func + local bufnr = vim.uri_to_bufnr(uri) + local x = os.clock() + if ts_nodes[uri] ~= nil then + local t = ts_nodes_time[uri] + local fname = vim.uri_to_fname(uri) + local modified = vim.fn.getftime(fname) + if modified < t then return ts_nodes[uri] end + end + local unload = false + if not api.nvim_buf_is_loaded(bufnr) then + log("! load buf !", uri, bufnr) + vim.fn.bufload(bufnr) + unload = true + end + + local funcs = ts_func(bufnr) + if unload then + local cmd = string.format("bd %d", bufnr) + log(cmd) + -- vim.cmd(cmd) -- todo: not sure if it is needed + end + ts_nodes[uri] = funcs + ts_nodes_time[uri] = os.time() + verbose(funcs) + log(string.format("elapsed time: %.4f\n", os.clock() - x)) + return funcs +end + +local function find_ts_func_by_range(funcs, range) + if funcs == nil or range == nil then return nil end + local result = {} + verbose(funcs, range) + for _, value in pairs(funcs) do + local func_range = value.node_scope + -- note treesitter is C style + if func_range and func_range.start.line + 1 <= range.start.line and func_range['end'].line + 1 >= + range['end'].line then table.insert(result, value) end + end + return result +end + function M.locations_to_items(locations) if not locations or vim.tbl_isempty(locations) then print("list not avalible") return end + local width = 4 local items = {} -- lsp.util.locations_to_items(locations) -- items and locations may not matching - table.sort( - locations, - function(i, j) - if i.uri == j.uri then - if i.range and i.range.start then - return i.range.start.line < j.range.start.line - end - return false - else - return i.uri < j.uri - end + table.sort(locations, function(i, j) + if i.uri == j.uri then + if i.range and i.range.start then return i.range.start.line < j.range.start.line end + return false + else + return i.uri < j.uri end - ) + end) for i, loc in ipairs(locations) do local item = lsp.util.locations_to_items({loc})[1] item.uri = locations[i].uri + local funcs = ts_functions(item.uri) item.range = locations[i].range item.filename = assert(vim.uri_to_fname(item.uri)) local filename = item.filename:gsub(cwd .. "/", "./", 1) item.display_filename = filename or item.filename - + item.call_by = find_ts_func_by_range(funcs, item.range) item.rpath = util.get_relative_path(cwd, item.filename) table.insert(items, item) + width = math.max(width, #item.text) end - return items + return items, width + 15 end function M.symbol_to_items(locations) @@ -158,20 +199,15 @@ function M.symbol_to_items(locations) local items = {} -- lsp.util.locations_to_items(locations) -- items and locations may not matching - table.sort( - locations, - function(i, j) - if i.uri == j.uri then - if i.range and i.range.start then - return i.range.start.line < j.range.start.line - end - return false - else - return i.uri < j.uri - end + table.sort(locations, function(i, j) + if i.uri == j.uri then + if i.range and i.range.start then return i.range.start.line < j.range.start.line end + return false + else + return i.uri < j.uri end - ) - for i, loc in ipairs(locations) do + end) + for i, _ in ipairs(locations) do local item = {} -- lsp.util.locations_to_items({loc})[1] item.uri = locations[i].uri item.range = locations[i].range diff --git a/lua/navigator/reference.lua b/lua/navigator/reference.lua index 678502c..9c57b02 100644 --- a/lua/navigator/reference.lua +++ b/lua/navigator/reference.lua @@ -3,39 +3,51 @@ local log = util.log local lsphelper = require "navigator.lspwrapper" local gui = require "navigator.gui" local lsp = require "navigator.lspwrapper" -local verbose = require "navigator.util".verbose +local verbose = require"navigator.util".verbose -- local log = util.log -- local partial = util.partial -- local cwd = vim.fn.getcwd(0) -- local lsphelper = require "navigator.lspwrapper" local locations_to_items = lsphelper.locations_to_items ---vim.api.nvim_set_option("navtator_options", {width = 90, height = 60, location = require "navigator.location".center}) +-- 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 function ref_hdlr(err, api, locations, num, bufnr) local opts = {} -- log("arg1", arg1) -- log(api) -- log(locations) -- log("num", num) -- log("bfnr", bufnr) + if err ~= nil then + print('ref callback error, lsp may not ready', err) + return + end + if type(locations) ~= 'table' then + log("arg1", arg1) + log(api) + log(locations) + log("num", num) + log("bfnr", bufnr) + error(locations) + end if locations == nil or vim.tbl_isempty(locations) then print "References not found" return end - verbose(locations) - local items = locations_to_items(locations) - gui.new_list_view({items = items, api = "Reference"}) + local items, width = locations_to_items(locations) + local ft = vim.api.nvim_buf_get_option(bufnr, "ft") + + local wwidth = vim.api.nvim_get_option("columns") + width = math.min(width + 24 or 120, math.floor(wwidth * 0.8)) + gui.new_list_view({items = items, ft = ft, width = width, 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} - return lsp.call_async(method[1], ref_params, ref_hdlr) -- return asyncresult, canceller + lsp.call_async("textDocument/references", ref_params, ref_hdlr) -- return asyncresult, canceller + -- lsp.call_async("textDocument/definition", 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} diff --git a/lua/navigator/symbols.lua b/lua/navigator/symbols.lua index ddd7167..2934071 100644 --- a/lua/navigator/symbols.lua +++ b/lua/navigator/symbols.lua @@ -1,10 +1,10 @@ local gui = require "navigator.gui" local M = {} -local log = require "navigator.util".log +local log = require"navigator.util".log local lsphelper = require "navigator.lspwrapper" local locations_to_items = lsphelper.locations_to_items -local clone = require "guihua.util".clone -local symbol_kind = require "navigator.lspclient.lspkind".symbol_kind +local clone = require"guihua.util".clone +local symbol_kind = require"navigator.lspclient.lspkind".symbol_kind local symbols_to_items = lsphelper.symbols_to_items -- function M.document_symbols(opts) @@ -46,7 +46,8 @@ function M.workspace_symbols(opts) local params = vim.lsp.util.make_position_params() params.context = {includeDeclaration = true} params.query = opts.prompt or "" - local results_lsp = vim.lsp.buf_request_sync(0, "workspace/symbol", params, lspopts.timeout or 15000) + local results_lsp = vim.lsp.buf_request_sync(0, "workspace/symbol", params, + lspopts.timeout or 15000) if not results_lsp or vim.tbl_isempty(results_lsp) then print(bufnr, "symbol not found for buf") return @@ -54,16 +55,18 @@ function M.workspace_symbols(opts) -- result_lsp local result = {} for i = 1, #results_lsp do - if results_lsp[i] ~= nil and results_lsp[i].result ~= nil and #results_lsp[i].result > 0 then - result = results_lsp[i].result - end + if results_lsp[i] ~= nil and results_lsp[i].result ~= nil and + #results_lsp[i].result > 0 then result = results_lsp[i].result end end local items = symbols_to_items(result) -- log(#items, items[1]) + local ft = vim.api.nvim_buf_get_option(0, "ft") + if #items > 0 then lspopts.items = items + lspopts.ft = ft gui.new_list_view(lspopts) else print("symbols not found") @@ -71,9 +74,7 @@ function M.workspace_symbols(opts) end function M.document_symbol_handler(err, _, result, _, bufnr) - if err then - print(bufnr, "failed to get document symbol") - end + if err then print(bufnr, "failed to get document symbol") end if not result or vim.tbl_isempty(result) then print(bufnr, "symbol not found for buf") @@ -94,9 +95,7 @@ function M.document_symbol_handler(err, _, result, _, bufnr) item.uri = uri item.selectionRange = result[i].selectionRange item.detail = result[i].detail or "" - if item.detail == "()" then - item.detail = "func" - end + if item.detail == "()" then item.detail = "func" end item.lnum = result[i].range.start.line + 1 item.text = "[" .. kind .. "]" .. item.detail .. " " .. item.name @@ -116,14 +115,23 @@ function M.document_symbol_handler(err, _, result, _, bufnr) child.uri = uri child.lnum = c.range.start.line + 1 child.detail = c.detail or "" - child.text = "  [" .. ckind .. "] " .. child.detail .. " " .. child.name + child.text = "  [" .. ckind .. "] " .. child.detail .. " " .. + child.name table.insert(locations, child) end end end + + local ft = vim.api.nvim_buf_get_option(bufnr, "ft") -- verbose(locations) -- local items = locations_to_items(locations) - gui.new_list_view({items = locations, prompt = true, rawdata = true, api = " "}) + gui.new_list_view({ + items = locations, + prompt = true, + rawdata = true, + ft = ft, + api = " " + }) -- if locations == nil or vim.tbl_isempty(locations) then -- print "References not found" @@ -147,9 +155,7 @@ function M.document_symbol_handler(err, _, result, _, bufnr) end function M.workspace_symbol_handler(err, _, result, _, bufnr) - if err then - print(bufnr, "failed to get workspace symbol") - end + if err then print(bufnr, "failed to get workspace symbol") end if not result or vim.tbl_isempty(result) then print(bufnr, "symbol not found for buf") return @@ -170,7 +176,15 @@ function M.workspace_symbol_handler(err, _, result, _, bufnr) -- table.insert(locations, item) -- end -- local items = locations_to_items(locations) - gui.new_list_view({items = items, prompt = true, rowdata = true, api = " "}) + + local ft = vim.api.nvim_buf_get_option(bufnr, "ft") + gui.new_list_view({ + items = items, + prompt = true, + ft = ft, + rowdata = true, + api = " " + }) -- if locations == nil or vim.tbl_isempty(locations) then -- print "References not found" diff --git a/lua/navigator/treesitter.lua b/lua/navigator/treesitter.lua index 6a435c3..f087996 100644 --- a/lua/navigator/treesitter.lua +++ b/lua/navigator/treesitter.lua @@ -1,5 +1,9 @@ local gui = require "navigator.gui" -local ts_locals = require "nvim-treesitter.locals" + +local ok, ts_locals = pcall(require, "nvim-treesitter.locals") + +if not ok then error("treesitter not installed") end + local parsers = require "nvim-treesitter.parsers" local ts_utils = require "nvim-treesitter.ts_utils" local utils = require "nvim-treesitter.utils" @@ -70,26 +74,47 @@ local function prepare_node(node, kind) return matches end -local function get_smallest_context(source) - local scopes = ts_locals.get_scopes() +local function get_var_context(source) + local sbl, sbc, sel, sec = source:range() local current = source + local result = current + local next = ts_utils.get_next_node(source) + local parent = current:parent() - while current ~= nil and not vim.tbl_contains(scopes, current) do - current = current:parent() - log(current) - log(current.node) - log(current:range()) - -- local nod = prepare_node(current) - -- log(nod) + if next == nil or parent == nil then return end + if next:type() == "function" or next:type() == "arrow_function" then + log(current:type(), current:range()) + return parent + else + return source end - return current or nil + -- while current ~= nil do + -- log(current:type(), current:range()) + -- if current:type() == "variable_declarator" or current:type() == "function_declaration" then + -- return current + -- end + -- -- local bl, bc, el, ec = current:range() + -- -- if bl == sbl and bc == sbc and el >= sel and ec >= sec then result = current end + -- current = current:parent() + -- end + -- log(current) +end + +local function get_smallest_context(source) + local scopes = ts_locals.get_scopes() + local current = source + while current ~= nil and not vim.tbl_contains(scopes, current) do current = current:parent() end + log(current) + if current ~= nil then return current end + return get_var_context(source) + -- if source:type() == "identifier" then return get_var_context(source) end end local lsp_reference = require"navigator.dochighlight".goto_adjent_reference function M.goto_adjacent_usage(bufnr, delta) local opt = {forward = true} - log(delta) + -- log(delta) if delta < 0 then opt = {forward = false} end bufnr = bufnr or api.nvim_get_current_buf() local node_at_point = ts_utils.get_node_at_cursor() @@ -132,7 +157,13 @@ local function get_all_nodes(bufnr, filter, summary) -- Force some types to act like they are parents -- instead of neighbors of the next nodes. - local containers = {["function"] = true, ["type"] = true, ["method"] = true} + local containers = { + ["function"] = true, + ["arrow_function"] = true, + ["type"] = true, + ["class"] = true, + ["method"] = true + } -- Step 2 find correct completions local length = 10 local parents = {} -- stack of nodes a clever algorithm from treesiter refactor @Santos Gallegos @@ -156,20 +187,28 @@ local function get_all_nodes(bufnr, filter, summary) item.kind = node.kind item.type = node.type local tsdata = node.def - local scope = get_smallest_context(tsdata) + + log(item.type, tsdata:type()) if node.def == nil then goto continue end - local start_line_node, _, _ = tsdata:start() - item.range = ts_utils.node_to_lsp_range(tsdata) item.node_text = ts_utils.get_node_text(tsdata, bufnr)[1] + + local scope = get_smallest_context(tsdata) + if scope ~= nil then + -- it is strange.. + log(item.node_text, item.kind, item.type) + item.node_scope = ts_utils.node_to_lsp_range(scope) + end if filter ~= nil and not filter[item.type] then goto continue end if summary then - table.insert(all_nodes, item) + if item.node_scope ~= nil then table.insert(all_nodes, item) end goto continue end - item.node_scope = scope + item.range = ts_utils.node_to_lsp_range(tsdata) + local start_line_node, _, _ = tsdata:start() 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.full_text = vim.trim(api.nvim_buf_get_lines(bufnr, start_line_node, start_line_node + 1, + false)[1] or "") item.uri = uri item.name = node.node_text item.filename = fname @@ -180,24 +219,44 @@ local function get_all_nodes(bufnr, filter, summary) 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) + item.text = string.format(" %s %s%-10s\t %s", item.kind, indent, item.node_text, + item.full_text) if #item.text > length then length = #item.text end table.insert(all_nodes, item) ::continue:: end end + verbose(all_nodes) return all_nodes, length end -function M.buf_func() - if ts_locals == nil then +function M.buf_func(bufnr) + if not ok or ts_locals == nil then error("treesitter not loaded") return end - local bufnr = api.nvim_get_current_buf() - local all_nodes, width = get_all_nodes(bufnr, {["function"] = true, ["method"] = true}, true) - -- log(all_nodes, width) + bufnr = bufnr or api.nvim_get_current_buf() + local all_nodes, width = get_all_nodes(bufnr, { + ["function"] = true, + ["var"] = true, + ["method"] = true, + ["class"] = true, + ["type"] = true + }, true) + table.sort(all_nodes, function(i, j) + if i.range and j.range then + if i.range.start.line == j.range.start.line then + return i.range['end'].line < j.range['end'].line + else + return i.range.start.line < j.range.start.line + end + end + return false + end) + + verbose(all_nodes, width) + return all_nodes end @@ -212,7 +271,14 @@ function M.buf_ts() local all_nodes, width = get_all_nodes(bufnr) local ft = vim.api.nvim_buf_get_option(bufnr, "ft") - gui.new_list_view({items = all_nodes, prompt = true, ft = ft, rawdata = true, width = width + 10, api = "🎄"}) + gui.new_list_view({ + items = all_nodes, + prompt = true, + ft = ft, + rawdata = true, + width = width + 10, + api = "🎄" + }) end function M.bufs_ts() @@ -239,7 +305,13 @@ function M.bufs_ts() verbose(ts_opened) local ft = vim.api.nvim_buf_get_option(0, "ft") - gui.new_list_view({items = ts_opened, prompt = true, ft = ft, width = max_length + 10, api = "🎄"}) + gui.new_list_view({ + items = ts_opened, + prompt = true, + ft = ft, + width = max_length + 10, + api = "🎄" + }) end end diff --git a/lua/navigator/util.lua b/lua/navigator/util.lua index 1abff5c..824fca4 100644 --- a/lua/navigator/util.lua +++ b/lua/navigator/util.lua @@ -1,10 +1,7 @@ -- retreives data form file -- and line to highlight -- Some of function copied from https://github.com/RishabhRD/nvim-lsputils - -local M = { - log_path = vim.lsp.get_log_path() -} +local M = {log_path = vim.lsp.get_log_path()} function M.get_data_from_file(filename, startLine) local displayLine if startLine < 3 then @@ -28,10 +25,7 @@ function M.get_data_from_file(filename, startLine) startLine = startLine + 1 end end - return { - data = data, - line = displayLine - } + return {data = data, line = displayLine} end function M.get_base(path) @@ -47,9 +41,7 @@ end local function getDir(path) local data = {} local len = #path - if len <= 1 then - return nil - end + if len <= 1 then return nil end local last_index = 1 for i = 2, len do local cur_char = path:sub(i, i) @@ -68,35 +60,22 @@ function M.get_relative_path(base_path, my_path) local base_len = #base_data local my_len = #my_data - if base_len > my_len then - return my_path - end + if base_len > my_len then return my_path end - if base_data[1] ~= my_data[1] then - return my_path - end + if base_data[1] ~= my_data[1] then return my_path end local cur = 0 for i = 1, base_len do - if base_data[i] ~= my_data[i] then - break - end + if base_data[i] ~= my_data[i] then break end cur = i end local data = "" - for i = cur + 1, my_len do - data = data .. my_data[i] .. "/" - end + for i = cur + 1, my_len do data = data .. my_data[i] .. "/" end data = data .. M.get_base(my_path) return data end -local default_config = { - plugin = "navigator", - use_console = false, - use_file = true, - level = "debug" -} +local default_config = {plugin = "navigator", use_console = false, use_file = true, level = "error"} M._log = require("guihua.log").new({level = default_config.level}, true) @@ -104,24 +83,16 @@ M._log = require("guihua.log").new({level = default_config.level}, true) M.log = M._log.info M.verbose = M._log.debug -function M.fmt(...) - M._log.fmt_info(...) -end +function M.fmt(...) M._log.fmt_info(...) end function M.split(inputstr, sep) - if sep == nil then - sep = "%s" - end + if sep == nil then sep = "%s" end local t = {} - for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do - table.insert(t, str) - end + for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do table.insert(t, str) end return t end -function M.trim_space(s) - return s:match("^%s*(.-)%s*$") -end +function M.trim_space(s) return s:match("^%s*(.-)%s*$") end function M.quickfix_extract(line) -- check if it is a line of file pos been selected @@ -137,7 +108,10 @@ function M.quickfix_extract(line) M.log(line) return nil end - local location = {uri = "file:///" .. sep[1], range = {start = {line = sep[2] - 3 > 0 and sep[2] - 3 or 1}}} + local location = { + uri = "file:///" .. sep[1], + range = {start = {line = sep[2] - 3 > 0 and sep[2] - 3 or 1}} + } location.range["end"] = {line = sep[2] + 15} return location end @@ -156,9 +130,7 @@ function M.getArgs(inputstr) return cmd, t end -function M.p(t) - print(vim.inspect(t)) -end +function M.p(t) print(vim.inspect(t)) end function M.printError(msg) vim.cmd("echohl ErrorMsg") @@ -176,18 +148,14 @@ function M.open_log() vim.cmd("edit " .. path) end -function table.pack(...) - return {n = select("#", ...), ...} -end +function table.pack(...) return {n = select("#", ...), ...} end function M.show(...) local string = "" local args = table.pack(...) - for i = 1, args.n do - string = string .. tostring(args[i]) .. "\t" - end + for i = 1, args.n do string = string .. tostring(args[i]) .. "\t" end return string .. "\n" end @@ -197,48 +165,26 @@ function M.split2(s, sep) sep = sep or " " local pattern = string.format("([^%s]+)", sep) - string.gsub( - s, - pattern, - function(c) - fields[#fields + 1] = c - end - ) + string.gsub(s, pattern, function(c) fields[#fields + 1] = c end) return fields end -M.open_file = function(filename) - vim.api.nvim_command(string.format("e! %s", filename)) -end +M.open_file = function(filename) vim.api.nvim_command(string.format("e! %s", filename)) end M.open_file_at = function(filename, line, col) vim.api.nvim_command(string.format("e! +%s %s", line, filename)) col = col or 1 - vim.api.nvim_command(string.format("normal! %dl", col-1)) + vim.api.nvim_command(string.format("normal! %dl", col - 1)) end -function M.exists(var) - for k, _ in pairs(_G) do - if k == var then - return true - end - end -end +function M.exists(var) for k, _ in pairs(_G) do if k == var then return true end end end -function M.partial(func, arg) - return (function(...) - return func(arg, ...) - end) -end +function M.partial(func, arg) return (function(...) return func(arg, ...) end) end local exclude_ft = {"scrollbar", "help", "NvimTree"} function M.exclude(fname) - for i = 1, #exclude_ft do - if string.find(fname, exclude_ft[i]) then - return true - end - end + for i = 1, #exclude_ft do if string.find(fname, exclude_ft[i]) then return true end end return false end @@ -250,33 +196,28 @@ 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 + 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}) + 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 + 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 + -- 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 + for bufnr in pairs(bufs) do M.clear_buf(bufnr) end bufs = {} end