From d1d39668027248fa7d4adea8b5dda3ac350b4a2c Mon Sep 17 00:00:00 2001 From: ray-x Date: Sat, 24 Apr 2021 12:38:47 +1000 Subject: [PATCH] treesitter support and rust client support --- README.md | 10 ++- lua/navigator/lspclient/clients.lua | 114 +++++++++++++------------ lua/navigator/lspclient/mapping.lua | 2 + lua/navigator/symbols.lua | 50 +++++------ lua/navigator/treesitter.lua | 126 ++++++++++++++++++++++++++++ lua/navigator/util.lua | 28 ++++--- 6 files changed, 237 insertions(+), 93 deletions(-) create mode 100644 lua/navigator/treesitter.lua diff --git a/README.md b/README.md index 50f0f69..61c0d00 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Navigator -GUI for Neovim (nightly) built-in LSP with a collection of most used LSP/treesitter functions. -Easy code navigation. +GUI for Neovim (nightly) built-in LSP with a collection of most used LSP/🌲Treesitter functions. +Easy code navigation through LSP symbols and 🌲Treesitter symbols. # Features: @@ -18,6 +18,7 @@ After installed a handful of lsp plugins, I still got ~500 loc for lsp and still to tune the plugins to fit my requirements. The plugin help user setup lspconfig with only a few lines of codes. Seconde reason is that lots of plugins serve as an enhance version of quickfix, lots of improvement was made by [lspsaga](https://github.com/glepnir/lspsaga.nvim), from which, the plugin was inspired. +It also the first plugin, IMO, that allows you to search in all treesitter symbols in the workspace. # Similar projects / special mentions: @@ -90,6 +91,11 @@ Use or `:q!` to kill the floating window, to move and to o ![code preview](https://github.com/ray-x/files/blob/master/img/navigator/preview_with_hl.jpg?raw=true) +### Treesitter symbol + +Treetsitter symbols in all buffers +![treesitter](https://github.com/ray-x/files/blob/master/img/navigator/treesitter.jpg?raw=true) + ### Call hierarchy (incomming/outgoing) ![incomming](https://github.com/ray-x/files/blob/master/img/navigator/incomming.jpg?raw=true) diff --git a/lua/navigator/lspclient/clients.lua b/lua/navigator/lspclient/clients.lua index 1031e60..2d39ce0 100644 --- a/lua/navigator/lspclient/clients.lua +++ b/lua/navigator/lspclient/clients.lua @@ -2,24 +2,26 @@ local lspconfig = nil local lsp_status = nil +if not packer_plugins["neovim/nvim-lspconfig"] or not packer_plugins["neovim/nvim-lspconfig"].loaded then + vim.cmd [[packadd nvim-lspconfig]] +end if not packer_plugins["nvim-lua/lsp-status.nvim"] or not packer_plugins["lsp-status.nvim"].loaded then vim.cmd [[packadd lsp-status.nvim]] - lsp_status = require("lsp-status") -- if lazyloading - vim.cmd [[packadd nvim-lspconfig]] - lspconfig = require "lspconfig" end +lspconfig = require "lspconfig" +lsp_status = require("lsp-status") local cap = vim.lsp.protocol.make_client_capabilities() local on_attach = require("navigator.lspclient.attach").on_attach local lsp_status_cfg = { - status_symbol = "", - indicator_errors = "", --'', - indicator_warnings = "", --'', + status_symbol = " ", + indicator_errors = " ", --' ', + indicator_warnings = " ", --' ', indicator_info = "﯎", - --'', + --' ', indicator_hint = "💡", - indicator_ok = "", + indicator_ok = " ", --'✔️', spinner_frames = {"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"}, select_symbol = function(cursor_pos, symbol) @@ -102,6 +104,22 @@ local clang_cfg = { on_attach(client) end } +local rust_cfg = { + settings = { + ["rust-analyzer"] = { + assist = { + importMergeBehavior = "last", + importPrefix = "by_self", + }, + cargo = { + loadOutDirsFromCheck = true + }, + procMacro = { + enable = true + }, + } + } +} local sqls_cfg = { on_attach = function(client, bufnr) @@ -111,7 +129,7 @@ local sqls_cfg = { require "sqls".setup {picker = "telescope"} -- or default end, settings = { - cmd = {"sqls", "-config", "$HOME/.config/sqls/config.yml"}, + cmd = {"sqls", "-config", "$HOME/.config/sqls/config.yml"} -- alterantively: -- connections = { -- { @@ -119,13 +137,6 @@ local sqls_cfg = { -- datasourcename = 'host=127.0.0.1 port=5432 user=postgres password=password dbname=user_db sslmode=disable', -- }, -- }, - workspace = { - library = { - -- this loads the `lua` files from nvim into the runtime. - [vim.fn.expand("$vimruntime/lua")] = true, - [vim.fn.expand("~/repos/nvim/lua")] = true - } - } } } -- lua setup @@ -168,34 +179,41 @@ local lua_cfg = { } } } +local servers = { + "gopls", + "tsserver", + "flow", + "bashls", + "dockerls", + "pyls", + "jdtls", + "sumneko_lua", + "vimls", + "html", + "jsonls", + "cssls", + "yamlls", + "clangd", + "sqls", + "denols", + "dartls", + "dotls", + "kotlin_language_server", + "nimls", + "phpactor", + "r_language_server", + "rust_analyzer", + "terraformls" +} local function lsp_status_setup() - local servers = { - "gopls", - "tsserver", - "flow", - "bashls", - "dockerls", - "pyls", - "sumneko_lua", - "vimls", - "html", - "jsonls", - "cssls", - "yamlls", - "clangd", - "sqls" - } + if lsp_status ~= nil then + lsp_status.register_progress() - for _, lspclient in ipairs(servers) do - if lsp_status ~= nl then - lsp_status.register_progress() - - lsp_status.config(lsp_status_cfg) - end - require "utils.highlight".diagnositc_config_sign() - require "utils.highlight".add_highlight() + lsp_status.config(lsp_status_cfg) end + require "utils.highlight".diagnositc_config_sign() + require "utils.highlight".add_highlight() end local function setup(user_opts) @@ -206,7 +224,7 @@ local function setup(user_opts) lsp_status_setup() - for _, lspclient in ipairs({"tsserver", "bashls", "flow", "dockerls", "vimls", "html", "jsonls", "cssls", "yamlls"}) do + for _, lspclient in ipairs(servers) do lspconfig[lspclient].setup { message_level = vim.lsp.protocol.MessageType.error, log_level = vim.lsp.protocol.MessageType.error, @@ -218,20 +236,10 @@ local function setup(user_opts) lspconfig.gopls.setup(golang_setup) lspconfig.sqls.setup(sqls_cfg) - require "lspconfig".sumneko_lua.setup(lua_cfg) + lspconfig.sumneko_lua.setup(lua_cfg) lspconfig.clangd.setup(clang_cfg) - servers = { - "dockerls", - "bashls", - "rust_analyzer", - "pyls" - } - for _, server in ipairs(servers) do - lspconfig[server].setup { - on_attach = on_attach - } - end + lspconfig.rust_analyzer.setup(rust_cfg) end return {setup = setup, cap = cap} diff --git a/lua/navigator/lspclient/mapping.lua b/lua/navigator/lspclient/mapping.lua index 642c6f5..97a7e91 100644 --- a/lua/navigator/lspclient/mapping.lua +++ b/lua/navigator/lspclient/mapping.lua @@ -19,6 +19,8 @@ local key_maps = { {key = "", func = "definition()"}, {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()"}, diff --git a/lua/navigator/symbols.lua b/lua/navigator/symbols.lua index b592b0e..ddd7167 100644 --- a/lua/navigator/symbols.lua +++ b/lua/navigator/symbols.lua @@ -7,31 +7,31 @@ 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) - assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running") - opts = opts or {} - local params = vim.lsp.util.make_position_params() - params.context = {includeDeclaration = true} - params.query = "" - local results_lsp = vim.lsp.buf_request_sync(0, "textDocument/documentSymbol", params, opts.timeout or 3000) - local locations = {} - log(results_lsp) - for _, server_results in pairs(results_lsp) do - if server_results.result then - vim.list_extend(locations, vim.lsp.util.symbols_to_items(server_results.result) or {}) - end - end - local lines = {} - - for _, loc in ipairs(locations) do - table.insert(lines, string.format("%s:%s:%s", loc.filename, loc.lnum, loc.text)) - end - if #lines > 0 then - gui.new_list_view({data = lines}) - else - print("symbols not found") - end -end +-- function M.document_symbols(opts) +-- assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running") +-- opts = opts or {} +-- local params = vim.lsp.util.make_position_params() +-- params.context = {includeDeclaration = true} +-- params.query = "" +-- local results_lsp = vim.lsp.buf_request_sync(0, "textDocument/documentSymbol", params, opts.timeout or 3000) +-- local locations = {} +-- log(results_lsp) +-- for _, server_results in pairs(results_lsp) do +-- if server_results.result then +-- vim.list_extend(locations, vim.lsp.util.symbols_to_items(server_results.result) or {}) +-- end +-- end +-- local lines = {} +-- +-- for _, loc in ipairs(locations) do +-- table.insert(lines, string.format("%s:%s:%s", loc.filename, loc.lnum, loc.text)) +-- end +-- if #lines > 0 then +-- gui.new_list_view({data = lines}) +-- else +-- print("symbols not found") +-- end +-- end function M.workspace_symbols(opts) opts = opts or {} diff --git a/lua/navigator/treesitter.lua b/lua/navigator/treesitter.lua new file mode 100644 index 0000000..d81c62c --- /dev/null +++ b/lua/navigator/treesitter.lua @@ -0,0 +1,126 @@ +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 api = vim.api +local M = {} + +local cwd = vim.fn.getcwd(0) +local log = require "navigator.util".log + +local match_kinds = { + var = "👹", -- Vampaire + method = "🍔", -- mac + ["function"] = "🤣", -- Fun + parameter = "", -- Pi + associated = "🤝", + namespace = "🚀", + type = " ", + field = "🏈" +} + +local get_icon = function(kind) + if match_kinds[kind] == nil then + return "🌲" + else + return match_kinds[kind] + end +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 + return current or nil +end + +local function prepare_node(node, kind) + local matches = {} + + if node.node then + table.insert(matches, {kind = get_icon(kind), def = node.node}) + else + for name, item in pairs(node) do + vim.list_extend(matches, prepare_node(item, name)) + end + end + return matches +end + +local function get_all_nodes(bufnr) + bufnr = bufnr or 0 + if not parsers.has_parser() then + print("ts not loaded") + end + local fname = vim.fn.expand("%:p:f") + local uri = vim.uri_from_fname(fname) + if bufnr ~= 0 then + uri = vim.uri_from_bufnr(bufnr) + fname = vim.uri_to_fname(uri) + end + local display_filename = fname:gsub(cwd .. "/", "./", 1) + + + local all_nodes = {} + -- Support completion-nvim customized label map + local customized_labels = vim.g.completion_customize_lsp_label or {} + + -- Step 2 find correct completions + for _, def in ipairs(ts_locals.get_definitions(bufnr)) do + local nodes = prepare_node(def) + local item = {} + for _, node in ipairs(nodes) do + item.tsdata = node.def or {} + 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] + 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 + item.name = node.node_text + item.filename = fname + item.display_filename = display_filename + item.lnum = item.range.start.line + 1 + item.text = string.format("[%s %10s]\t🧩 %s", item.kind, item.node_text, item.full_text) + table.insert(all_nodes, item) + end + end + return all_nodes +end + +function M.buf_ts() + local all_nodes = get_all_nodes() + gui.new_list_view({items = all_nodes, prompt = true, rawdata = true, api = "🎄"}) +end +local exclude_ft = {'scroll', 'help', 'NvimTree'} +local function exclude(fname) + for i = 1, #exclude_ft do + if string.find(fname, exclude_ft[i]) then return true end + end + return false +end +function M.bufs_ts() + local bufs = vim.api.nvim_list_bufs() + local ts_opened = {} + for _, buf in ipairs(bufs) do + local bname = vim.fn.bufname(buf) + if #bname > 0 and not exclude(bname) then + if vim.api.nvim_buf_is_loaded(buf) then + local all_nodes = get_all_nodes(buf) + if all_nodes ~= nil then + vim.list_extend(ts_opened, all_nodes) + end + end + end + end + if #ts_opened > 1 then + log(ts_opened) + gui.new_list_view({items = ts_opened, prompt = true, api = "🎄"}) + end +end + +return M diff --git a/lua/navigator/util.lua b/lua/navigator/util.lua index 6e3f79c..5e6648b 100644 --- a/lua/navigator/util.lua +++ b/lua/navigator/util.lua @@ -45,19 +45,21 @@ function M.get_base(path) end local function getDir(path) - local data = {} - local len = #path - if len <= 1 then return nil end - local last_index = 1 - for i = 2, len do - local cur_char = path:sub(i,i) - if cur_char == '/' then - local my_data = path:sub(last_index + 1, i - 1) - table.insert(data, my_data) - last_index = i - end + local data = {} + local len = #path + if len <= 1 then + return nil + end + local last_index = 1 + for i = 2, len do + local cur_char = path:sub(i, i) + if cur_char == "/" then + local my_data = path:sub(last_index + 1, i - 1) + table.insert(data, my_data) + last_index = i end - return data + end + return data end function M.get_relative_path(base_path, my_path) @@ -96,7 +98,7 @@ local default_config = { level = "info" } -M._log = require('guihua.log').new({level='info'}, true) +M._log = require("guihua.log").new({level = "info"}, true) -- add log to you lsp.log M.log = M._log.info