diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..e623869 --- /dev/null +++ b/changelog.md @@ -0,0 +1,11 @@ +# Change Logs +## 2023-12-07 +- [x] Changed key maps + - Add fallback to key maps e.g. if `gD` does not find a type declaraion, it will fallback to `gD` + which is default key map go to global declaration + - `gt`: removed `gt` mapping and prefer `D` + +- [ ] Changed key maps + I plan to change the mapping for following key maps as it conflicts with other vim default key maps + - `gp`: changed to `gp` + - `gP`: changed to `gP` diff --git a/lua/navigator.lua b/lua/navigator.lua index 8862c98..f629546 100644 --- a/lua/navigator.lua +++ b/lua/navigator.lua @@ -35,7 +35,7 @@ _NgConfigValues = { enable = false, comment = true, -- ts fold text object max_lines_scan_comments = 2000, -- maximum lines to scan for comments - disable_filetypes = {'help', 'text', 'markdown'}, -- disable ts fold for specific filetypes + disable_filetypes = { 'help', 'text', 'markdown' }, -- disable ts fold for specific filetypes }, treesitter_analysis = true, -- treesitter variable context treesitter_navigation = true, -- bool|table @@ -242,7 +242,7 @@ M.deprecated = function(cfg) if cfg.lsp and cfg.lsp.sumneko_lua then warn('sumneko_lua option deprecated, refer to README for more details') end - if cfg.ts_fold ~= nil and type(cfg.ts_fold) == "boolean" then + if cfg.ts_fold ~= nil and type(cfg.ts_fold) == 'boolean' then warn('ts_fold option changed, refer to README for more details') cfg.ts_fold = { enable = cfg.ts_fold } end @@ -327,7 +327,6 @@ local extend_config = function(opts) -- if _NgConfigValues.sumneko_root_path or _NgConfigValues.sumneko_binary then -- vim.notify("Please put sumneko setup in lsp['lua_ls']", vim.log.levels.WARN) -- end - end M.config_values = function() @@ -381,7 +380,7 @@ M.setup = function(cfg) _NgConfigValues.loaded = true end - if _NgConfigValues.ts_fold.enable == true and not vim.tbl_contains(_NgConfigValues.ts_fold.disable_filetypes, vim.o.filetype) then + if _NgConfigValues.ts_fold.enable == true and not vim.tbl_contains(_NgConfigValues.ts_fold.disable_filetypes, vim.o.filetype) and not vim.wo.diff then require('navigator.foldts').on_attach() end diff --git a/lua/navigator/definition.lua b/lua/navigator/definition.lua index 3272a35..e13cb79 100644 --- a/lua/navigator/definition.lua +++ b/lua/navigator/definition.lua @@ -7,7 +7,6 @@ local trace = util.trace local TextView = require('guihua.textview') -- callback for lsp definition, implementation and declaration handler local definition_hdlr = function(err, locations, ctx, _) - -- log(locations) if err ~= nil then if tostring(err):find('no type definition') or tostring(err):find('invalid range') then vim.notify('Definition: ' .. tostring(err), vim.log.levels.DEBUG) @@ -18,11 +17,9 @@ local definition_hdlr = function(err, locations, ctx, _) vim.notify('Defination: ' .. tostring(err) .. vim.inspect(ctx), vim.log.levels.WARN) return end - if type(locations) == 'number' then + if locations == nil or vim.tbl_isempty(locations) or type(locations) == 'number' then log(locations) log('unable to handle request') - end - if locations == nil or vim.tbl_isempty(locations) then vim.notify('Definition not found') return end @@ -50,8 +47,9 @@ local definition_hdlr = function(err, locations, ctx, _) end end else - vim.lsp.util.jump_to_location(locations, oe) + return end + return true end local function get_symbol() @@ -67,10 +65,13 @@ local function def_preview(timeout_ms, method) if result == nil or vim.tbl_isempty(result) then vim.notify('No result found: ' .. method, vim.log.levels.WARN) - return nil + return end log(result) + if not vim.tbl_islist(result) then + return + end local data = {} -- result = {vim.tbl_deep_extend("force", {}, unpack(result))} -- log("def-preview", result) @@ -82,7 +83,7 @@ local function def_preview(timeout_ms, method) if vim.tbl_isempty(data) then vim.notify('No result found: ' .. method, vim.log.levels.WARN) - return nil + return end local range = data[1].targetRange or data[1].range or data[1].targetSelectionRange @@ -101,6 +102,9 @@ local function def_preview(timeout_ms, method) end local ok, parsers = pcall(require, 'nvim-treesitter.parsers') + if not ok then + return + end -- TODO: 32/64 should be an option local lines_num = 64 @@ -176,6 +180,7 @@ local function def_preview(timeout_ms, method) -- log(def_line[1], niddle) vim.fn.matchadd('Search', niddle) end + return true -- disable key-remap fallback -- TODO: -- https://github.com/oblitum/goyo.vim/blob/master/autoload/goyo.vim#L108-L135 end diff --git a/lua/navigator/lspclient/clients.lua b/lua/navigator/lspclient/clients.lua index ff8aeb2..1d53495 100644 --- a/lua/navigator/lspclient/clients.lua +++ b/lua/navigator/lspclient/clients.lua @@ -43,6 +43,8 @@ local disabled_ft = { 'notify', 'nofile', 'help', + 'dap-*', + 'dapui_*', '', } -- local cap = vim.lsp.protocol.make_client_capabilities() @@ -500,6 +502,12 @@ local function ft_disabled(ft) if ft == disabled_ft[i] then return true end + if disabled_ft[i]:find('*') then + local pattern = disabled_ft[i]:gsub('*', '') + if ft:find(pattern) then + return true + end + end end end local ft_map = { @@ -561,6 +569,7 @@ local function setup(user_opts) trace('navigator disabled for ft or it is loaded', ft) return end + if _NgConfigValues.lsp.servers then add_servers(_NgConfigValues.lsp.servers) _NgConfigValues.lsp.servers = nil diff --git a/lua/navigator/lspclient/inlay.lua b/lua/navigator/lspclient/inlay.lua new file mode 100644 index 0000000..21ab2fa --- /dev/null +++ b/lua/navigator/lspclient/inlay.lua @@ -0,0 +1,68 @@ +local log = require('vim.lsp.log') +local util = require('vim.lsp.util') +local api = vim.api +local bufstates = {} +return { + on_inlayhint = function(err, result, ctx, _) + if err then + if log.error() then + log.error('inlayhint', err) + end + return + end + local bufnr = assert(ctx.bufnr) + if util.buf_versions[bufnr] ~= ctx.version then + return + end + local client_id = ctx.client_id + if not result then + return + end + local bufstate = bufstates[bufnr] + if not bufstate or not bufstate.enabled then + return + end + if not (bufstate.client_hint and bufstate.version) then + bufstate.client_hint = vim.defaulttable() + bufstate.version = ctx.version + end + local hints_by_client = bufstate.client_hint + local client = assert(vim.lsp.get_client_by_id(client_id)) + + local new_hints_by_lnum = vim.defaulttable() + local num_unprocessed = #result + if num_unprocessed == 0 then + hints_by_client[client_id] = {} + bufstate.version = ctx.version + api.nvim__buf_redraw_range(bufnr, 0, -1) + return + end + + local lines = api.nvim_buf_get_lines(bufnr, 0, -1, false) + ---@param position lsp.Position + ---@return integer + local function pos_to_byte(position) + local col = position.character + if col > 0 then + local line = lines[position.line + 1] or '' + local ok, convert_result + ok, convert_result = pcall(util._str_byteindex_enc, line, col, client.offset_encoding) + if ok then + return convert_result + end + return math.min(#line, col) + end + return col + end + + for _, hint in ipairs(result) do + local lnum = hint.position.line + hint.position.character = pos_to_byte(hint.position) + table.insert(new_hints_by_lnum[lnum], hint) + end + + hints_by_client[client_id] = new_hints_by_lnum + bufstate.version = ctx.version + api.nvim__buf_redraw_range(bufnr, 0, -1) + end, +} diff --git a/lua/navigator/lspclient/mapping.lua b/lua/navigator/lspclient/mapping.lua index 7e98758..e6aed28 100644 --- a/lua/navigator/lspclient/mapping.lua +++ b/lua/navigator/lspclient/mapping.lua @@ -10,52 +10,69 @@ end if vim.diagnostic == nil then util.error('Please update nvim to 0.6.1+') end + +local function fallback_keymap(key) + -- when handler failed fallback to key + vim.schedule(function() + print('fallback to key', key) + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), 'n', true) + end) +end + +local function fallback_fn(key) + return function() + fallback_keymap(key) + end +end + local double = { '╔', '═', '╗', '║', '╝', '═', '╚', '║' } local single = { '╭', '─', '╮', '│', '╯', '─', '╰', '│' } +local remap = util.binding_remap -- stylua: ignore start local key_maps = { - { key = 'gr', func = require('navigator.reference').async_ref, desc = 'async_ref' }, - { key = 'gr', func = require('navigator.reference').reference, desc = 'reference' }, -- reference deprecated - { mode = 'i', key = '', func = vim.lsp.buf.signature_help, desc = 'signature_help' }, - { key = '', func = vim.lsp.buf.signature_help, desc = 'signature_help' }, - { key = 'g0', func = require('navigator.symbols').document_symbols, desc = 'document_symbols' }, - { key = 'gW', func = require('navigator.workspace').workspace_symbol_live, desc = 'workspace_symbol_live' }, - { key = '', func = require('navigator.definition').definition, desc = 'definition' }, - { key = 'gd', func = require('navigator.definition').definition, desc = 'definition' }, - { key = 'gD', func = vim.lsp.buf.declaration, desc = 'declaration' }, - { key = 'gt', func = vim.lsp.buf.type_definition, desc = 'type_definition' }, - { key = 'gp', func = require('navigator.definition').definition_preview, desc = 'definition_preview' }, - { key = 'gP', func = require('navigator.definition').type_definition_preview, desc = 'type_definition_preview' }, - { key = 'gt', func = require('navigator.treesitter').buf_ts, desc = 'buf_ts' }, - { key = 'gT', func = require('navigator.treesitter').bufs_ts, desc = 'bufs_ts' }, - { key = 'ct', func = require('navigator.ctags').ctags, desc = 'ctags' }, - { key = 'ca', mode = 'n', func = require('navigator.codeAction').code_action, desc = 'code_action' }, - { key = 'ca', mode = 'v', func = require('navigator.codeAction').range_code_action, desc = 'range_code_action' }, + { key = 'gr', func = require('navigator.reference').async_ref, desc = 'async_ref' }, + { key = 'gr', func = require('navigator.reference').reference, desc = 'reference' }, -- reference deprecated + { mode = 'i', key = '', func = vim.lsp.buf.signature_help, desc = 'signature_help' }, + { key = '', func = vim.lsp.buf.signature_help, desc = 'signature_help' }, + { key = 'g0', func = require('navigator.symbols').document_symbols, desc = 'document_symbols' }, + { key = 'gW', func = require('navigator.workspace').workspace_symbol_live, desc = 'workspace_symbol_live' }, + { key = '', func = require('navigator.definition').definition, desc = 'definition' }, + { key = 'gd', func = remap(require('navigator.definition').definition, 'gd'), desc = 'definition' }, + { key = 'gD', func = vim.lsp.buf.declaration, desc = 'declaration', fallback = fallback_fn('gD') }, -- fallback used + -- for lsp handler + { key = 'gp', func = remap(require('navigator.definition').definition_preview, 'gp'), desc = 'definition_preview' }, -- paste + { key = 'gP', func = remap(require('navigator.definition').type_definition_preview, 'gP'), desc = 'type_definition_preview' }, -- paste + { key = 'gt', func = require('navigator.treesitter').buf_ts, desc = 'buf_ts' }, + { key = 'gT', func = require('navigator.treesitter').bufs_ts, desc = 'bufs_ts' }, + { key = 'ct', func = require('navigator.ctags').ctags, desc = 'ctags' }, + { key = 'ca', mode = 'n', func = require('navigator.codeAction').code_action, desc = 'code_action' }, + { key = 'ca', mode = 'v', func = require('navigator.codeAction').range_code_action, desc = 'range_code_action' }, -- { key = 're', func = 'rename()' }, - { key = 'rn', func = require('navigator.rename').rename, desc = 'rename' }, - { key = 'gi', func = vim.lsp.buf.incoming_calls, desc = 'incoming_calls' }, - { key = 'go', func = vim.lsp.buf.outgoing_calls, desc = 'outgoing_calls' }, - { key = 'gi', func = vim.lsp.buf.implementation, desc = 'implementation' }, - { key = 'D', func = vim.lsp.buf.type_definition, desc = 'type_definition' }, - { key = 'gL', func = require('navigator.diagnostics').show_diagnostics, desc = 'show_diagnostics' }, - { key = 'gG', func = require('navigator.diagnostics').show_buf_diagnostics, desc = 'show_buf_diagnostics' }, - { key = 'dt', func = require('navigator.diagnostics').toggle_diagnostics, desc = 'toggle_diagnostics' }, - { key = ']d', func = vim.diagnostic.goto_next, desc = 'next diagnostics' }, - { key = '[d', func = vim.diagnostic.goto_prev, desc = 'prev diagnostics' }, - { key = ']O', func = vim.diagnostic.set_loclist, desc = 'diagnostics set loclist' }, - { key = ']r', func = require('navigator.treesitter').goto_next_usage, desc = 'goto_next_usage' }, - { key = '[r', func = require('navigator.treesitter').goto_previous_usage, desc = 'goto_previous_usage' }, - { key = '', func = vim.lsp.buf.definition, desc = 'definition' }, - { key = 'g', func = vim.lsp.buf.implementation, desc = 'implementation' }, - { key = 'k', func = require('navigator.dochighlight').hi_symbol, desc = 'hi_symbol' }, - { key = 'wa', func = require('navigator.workspace').add_workspace_folder, desc = 'add_workspace_folder' }, - { key = 'wr', func = require('navigator.workspace').remove_workspace_folder, desc = 'remove_workspace_folder' }, - { key = 'ff', func = vim.lsp.buf.format, mode = 'n', desc = 'format' }, - { key = 'ff', func = vim.lsp.buf.range_formatting, mode = 'v', desc = 'range format' }, - { key = 'gm', func = require('navigator.formatting').range_format, mode = 'n', desc = 'range format operator e.g gmip' }, - { key = 'wl', func = require('navigator.workspace').list_workspace_folders, desc = 'list_workspace_folders' }, + { key = 'rn', func = require('navigator.rename').rename, desc = 'rename' }, + { key = 'gi', func = vim.lsp.buf.incoming_calls, desc = 'incoming_calls' }, + { key = 'go', func = vim.lsp.buf.outgoing_calls, desc = 'outgoing_calls' }, + { key = 'gi', func = vim.lsp.buf.implementation, desc = 'implementation', fallback = fallback_fn('gi') }, -- insert + { key = 'D', func = vim.lsp.buf.type_definition, desc = 'type_definition' }, + { key = 'gL', func = require('navigator.diagnostics').show_diagnostics, desc = 'show_diagnostics' }, + { key = 'gG', func = require('navigator.diagnostics').show_buf_diagnostics, desc = 'show_buf_diagnostics' }, + { key = 'dt', func = require('navigator.diagnostics').toggle_diagnostics, desc = 'toggle_diagnostics' }, + { key = ']d', func = vim.diagnostic.goto_next, desc = 'next diagnostics' }, + { key = '[d', func = vim.diagnostic.goto_prev, desc = 'prev diagnostics' }, + { key = ']O', func = vim.diagnostic.set_loclist, desc = 'diagnostics set loclist' }, + { key = ']r', func = require('navigator.treesitter').goto_next_usage, desc = 'goto_next_usage' }, + { key = '[r', func = require('navigator.treesitter').goto_previous_usage, desc = 'goto_previous_usage' }, + { key = '', func = vim.lsp.buf.definition, desc = 'definition', fallback = fallback_fn('')}, + { key = 'g', func = vim.lsp.buf.implementation, desc = 'implementation' }, + { key = 'k', func = require('navigator.dochighlight').hi_symbol, desc = 'hi_symbol' }, + { key = 'wa', func = require('navigator.workspace').add_workspace_folder, desc = 'add_workspace_folder' }, + { key = 'wr', func = require('navigator.workspace').remove_workspace_folder, desc = 'remove_workspace_folder' }, + { key = 'ff', func = vim.lsp.buf.format, mode = 'n', desc = 'format' }, + { key = 'ff', func = vim.lsp.buf.range_formatting, mode = 'v', desc = 'range format' }, + { key = 'gm', func = require('navigator.formatting').range_format, mode = 'n', desc = 'range format operator e.g gmip' }, + { key = 'wl', func = require('navigator.workspace').list_workspace_folders, desc = 'list_workspace_folders' }, { - key = 'la', mode = 'n', + key = 'la', + mode = 'n', func = require('navigator.codelens').run_action, desc = 'run code lens action', } @@ -158,7 +175,7 @@ local function set_mapping(lsp_attach_info) k = ks[mode] end if vim.tbl_contains(k, key) then - vim.api.nvim_buf_del_keymap(bufnr, mode, key, ...) + vim.api.nvim_buf_del_keymap(bufnr, mode, key) end end @@ -174,10 +191,10 @@ local function set_mapping(lsp_attach_info) local exists = false for _, default in pairs(key_maps) do if - -- override only if func and mode are the same - v.func == default.func - and (v.mode or 'n') == (default.mode or 'n') - and not default.override + -- override only if func and mode are the same + v.func == default.func + and (v.mode or 'n') == (default.mode or 'n') + and not default.override then default.key, default.override, exists = v.key, true, true break @@ -193,12 +210,11 @@ local function set_mapping(lsp_attach_info) log('setting maps to ', key_maps) end local fmtkey, rfmtkey, nrfmtkey - require('navigator.formatting') for _, value in pairs(key_maps) do if value.doc then vim.notify('doc field no longer supported in navigator mapping, use desc instead') end - if type(value.func) == 'function' then -- new from 0.7.x + if type(value.func) == 'function' then -- neovim 0.7.0 opts.buffer = key_maps.buffer or value.buffer if value.desc then @@ -337,36 +353,69 @@ function M.setup(attach_opts) autocmd() local client = attach_opts.client or {} - local cap = client.server_capabilities or vim.lsp.protocol.make_client_capabilities() + local cap = client.server_capabilities + if cap == nil then + log('no cap found for client ', client.name) + return + end log('lsp cap:', cap.codeActionProvider) if - _NgConfigValues.lsp.call_hierarchy.enable and cap.call_hierarchy or cap.callHierarchyProvider + _NgConfigValues.lsp.call_hierarchy.enable and cap.call_hierarchy or cap.callHierarchyProvider then vim.lsp.handlers['callHierarchy/incomingCalls'] = - require('navigator.hierarchy').incoming_calls_handler + require('navigator.hierarchy').incoming_calls_handler vim.lsp.handlers['callHierarchy/outgoingCalls'] = - require('navigator.hierarchy').outgoing_calls_handler + require('navigator.hierarchy').outgoing_calls_handler end if _NgConfigValues.lsp.definition.enable then - vim.lsp.handlers['textDocument/definition'] = require('navigator.definition').definition_handler - vim.lsp.handlers['textDocument/typeDefinition'] = - require('navigator.definition').definition_handler + for _, value in pairs(key_maps) do + if value.func == vim.lsp.buf.definition then + vim.lsp.handlers['textDocument/definition'] = util.mk_handler_remap( + require('navigator.definition').definition_handler, value.fallback) + end + + if value.func == vim.lsp.buf.type_definition then + vim.lsp.handlers['textDocument/typeDefinition'] = util.mk_handler_remap( + require('navigator.definition').definition_handler, value.fallback) + end + end + else + -- delete keymaps + for _, value in pairs(key_maps) do + if value.func == vim.lsp.buf.definition or value.func == vim.lsp.buf.type_definition then + vim.keymap.del(value.mode or 'n', value.key, { buffer = attach_opts.bufnr or vim.api.nvim_get_current_buf() }) + end + end end vim.lsp.handlers['textDocument/references'] = require('navigator.reference').reference_handler -- vim.lsp.handlers["textDocument/codeAction"] = require"navigator.codeAction".code_action_handler if cap.declarationProvider then - vim.lsp.handlers['textDocument/declaration'] = - require('navigator.definition').declaration_handler + local hdlr = require('navigator.definition').declaration_handler + + for _, value in pairs(key_maps) do + if value.func == vim.lsp.buf.declaration then + vim.lsp.handlers['textDocument/declaration'] = util.mk_handler_remap(hdlr, value.fallback) + break + end + end + else + -- remove declaration keymap + for _, value in pairs(key_maps) do + if value.func == vim.lsp.buf.declaration then + vim.keymap.del(value.mode or 'n', value.key, { buffer = attach_opts.bufnr or vim.api.nvim_get_current_buf() }) + break + end + end end if _NgConfigValues.lsp.implementation.enable then vim.lsp.handlers['textDocument/implementation'] = - require('navigator.implementation').implementation_handler + require('navigator.implementation').implementation_handler end -- vim.lsp.handlers['textDocument/documentSymbol'] = require('navigator.symbols').document_symbol_handler @@ -375,11 +424,11 @@ function M.setup(attach_opts) end if _NgConfigValues.lsp.diagnostic.enable then vim.lsp.handlers['textDocument/publishDiagnostics'] = - require('navigator.diagnostics').diagnostic_handler + require('navigator.diagnostics').diagnostic_handler end if - vim.fn.empty(_NgConfigValues.signature_help_cfg) == 0 or _NgConfigValues.lsp_signature_help + vim.fn.empty(_NgConfigValues.signature_help_cfg) == 0 or _NgConfigValues.lsp_signature_help then log('setup signature from navigator') local hassig, sig = pcall(require, 'lsp_signature') diff --git a/lua/navigator/util.lua b/lua/navigator/util.lua index e9921f6..228ec09 100644 --- a/lua/navigator/util.lua +++ b/lua/navigator/util.lua @@ -606,4 +606,25 @@ function M.for_each_buffer_client(bufnr, fn) end end +function M.binding_remap(fn, key) + return function(...) + if fn(...) ~= true and key then -- the function failed fallback to key + M.log(key) + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, false, true), 'n', true) + end + end +end + +function M.mk_handler_remap(fn, fallback) + return function(...) + if fn(...) ~= true then -- the function failed fallback to key + M.log('fallback, ', fallback) + if fallback then + M.log('fallback') + fallback() + end + end + end +end + return M