From e79f5a614f2cc6fab79111fd5e9921706114eb3a Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Wed, 28 Dec 2022 15:56:58 -0600 Subject: feat(nvim): improve external_docs handler --- nvim/.config/nvim/lua/tobyvin/keymaps.lua | 9 +- nvim/.config/nvim/lua/tobyvin/lsp/handlers.lua | 27 +++-- .../.config/nvim/lua/tobyvin/plugins/dap/hover.lua | 25 ++--- .../nvim/lua/tobyvin/plugins/rust-tools.lua | 6 +- .../nvim/lua/tobyvin/utils/documentation.lua | 119 ++++++++++++++++++--- nvim/.config/nvim/lua/tobyvin/utils/hover.lua | 10 ++ 6 files changed, 152 insertions(+), 44 deletions(-) (limited to 'nvim/.config') diff --git a/nvim/.config/nvim/lua/tobyvin/keymaps.lua b/nvim/.config/nvim/lua/tobyvin/keymaps.lua index 951613b..57a7cd3 100644 --- a/nvim/.config/nvim/lua/tobyvin/keymaps.lua +++ b/nvim/.config/nvim/lua/tobyvin/keymaps.lua @@ -10,7 +10,14 @@ local hover = function() return "K" end -vim.keymap.set("n", "gk", utils.documentation.open, { desc = "documentation" }) +local external_docs = function() + if utils.documentation.open() then + return "" + end + return "gx" +end + +vim.keymap.set("n", "gx", external_docs, { desc = "external_docs", expr = true }) vim.keymap.set("n", "K", hover, { expr = true, desc = "hover" }) vim.keymap.set("n", "", "zz", { desc = "up half page and center" }) vim.keymap.set("n", "", "zz", { desc = "down half page and center" }) diff --git a/nvim/.config/nvim/lua/tobyvin/lsp/handlers.lua b/nvim/.config/nvim/lua/tobyvin/lsp/handlers.lua index 6785715..deb2d53 100644 --- a/nvim/.config/nvim/lua/tobyvin/lsp/handlers.lua +++ b/nvim/.config/nvim/lua/tobyvin/lsp/handlers.lua @@ -1,17 +1,33 @@ local utils = require("tobyvin.utils") -local location_handler = vim.lsp.handlers["textDocument/definition"] -local definition_handler = function(err, result, ctx, config) +function vim.lsp.buf.external_docs() + ---@diagnostic disable-next-line: missing-parameter + local params = vim.lsp.util.make_position_params() + return vim.lsp.buf_request(0, "experimental/externalDocs", params) +end + +local definition_handler = vim.lsp.handlers["textDocument/definition"] +vim.lsp.handlers["textDocument/definition"] = function(err, result, ctx, config) if not result or vim.tbl_isempty(result) then vim.notify("No location found", vim.log.levels.INFO, { title = "[LSP] " .. ctx.method }) elseif vim.tbl_islist(result) then result = result[1] end - location_handler(err, result, ctx, config) + return definition_handler(err, result, ctx, config) +end + +local external_docs_handler = vim.lsp.handlers["experimental/externalDocs"] +vim.lsp.handlers["experimental/externalDocs"] = function(err, result, ctx, config) + if external_docs_handler then + result, err = external_docs_handler(err, result, ctx, config) + elseif result then + vim.fn["netrw#BrowseX"](result, 0) + end end -local show_message = function(_, result, ctx) +---@diagnostic disable-next-line: duplicate-set-field +vim.lsp.handlers["window/showMessage"] = function(_, result, ctx) vim.notify(string.format("%s", result.message), 5 - result.type, { title = string.format("[LSP] %s", vim.lsp.get_client_by_id(ctx.client_id)), }) @@ -20,8 +36,6 @@ end local hover_ops = { border = "single" } vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, hover_ops) vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, hover_ops) -vim.lsp.handlers["textDocument/definition"] = definition_handler -vim.lsp.handlers["window/showMessage"] = show_message vim.api.nvim_create_autocmd("LspAttach", { group = vim.api.nvim_create_augroup("tobyvin_lsp_handlers", { clear = true }), @@ -34,6 +48,7 @@ vim.api.nvim_create_autocmd("LspAttach", { end utils.hover.register(vim.lsp.buf.hover, { desc = "lsp", buffer = bufnr, priority = 1 }) + utils.hover.register(vim.lsp.buf.external_docs, { desc = "lsp", buffer = bufnr, priority = 1 }) vim.keymap.set("n", "", vim.lsp.buf.signature_help, { desc = "signature help", buffer = bufnr }) vim.keymap.set("n", "gd", vim.lsp.buf.definition, { desc = "definition", buffer = bufnr }) diff --git a/nvim/.config/nvim/lua/tobyvin/plugins/dap/hover.lua b/nvim/.config/nvim/lua/tobyvin/plugins/dap/hover.lua index 214bcdd..7eb0c58 100644 --- a/nvim/.config/nvim/lua/tobyvin/plugins/dap/hover.lua +++ b/nvim/.config/nvim/lua/tobyvin/plugins/dap/hover.lua @@ -1,15 +1,6 @@ local utils = require("tobyvin.utils") local M = {} -M.hover = function() - local widgets = require("dap.ui.widgets") - if M.hover_available() then - widgets.hover() - else - utils.hover.open() - end -end - M.hover_available = function() local session = require("dap").session() if not session then @@ -34,24 +25,22 @@ M.hover_available = function() end M.setup = function() + local dap_hover_id vim.api.nvim_create_autocmd("User", { pattern = "DapAttach", callback = function() - -- TODO: figure out why calling dap.ui.widgets.hover from util.hover is causing error - -- vim.g.dap_hover_id = utils.hover.register(M.hover, { - -- enabled = M.hover_available, - -- desc = "dap", - -- priority = 20, - -- }) - vim.keymap.set("n", "K", M.hover, { desc = "hover" }) + dap_hover_id = utils.hover.register(require("dap.ui.widgets").hover, { + desc = "lsp", + enabled = M.hover_available, + priority = 20, + }) end, }) vim.api.nvim_create_autocmd("User", { pattern = "DapDetach", callback = function() - -- utils.hover.unregister(vim.g.dap_hover_id) - vim.keymap.set("n", "K", utils.hover.open, { desc = "hover" }) + utils.hover.unregister(dap_hover_id) end, }) end diff --git a/nvim/.config/nvim/lua/tobyvin/plugins/rust-tools.lua b/nvim/.config/nvim/lua/tobyvin/plugins/rust-tools.lua index b602a31..c108e8e 100644 --- a/nvim/.config/nvim/lua/tobyvin/plugins/rust-tools.lua +++ b/nvim/.config/nvim/lua/tobyvin/plugins/rust-tools.lua @@ -33,9 +33,7 @@ function M.init() local runnables = require("rust-tools").runnables.runnables local debuggables = require("rust-tools").debuggables.debuggables local open_cargo_toml = require("rust-tools").open_cargo_toml.open_cargo_toml - local external_docs = require("rust-tools").external_docs.open_external_docs local expand_macro = require("rust-tools").expand_macro.expand_macro - local hover_actions = require("rust-tools").hover_actions.hover_actions local ssr = require("rust-tools").ssr.ssr vim.keymap.set("n", "dd", debuggables, { desc = "debug", buffer = bufnr }) @@ -44,9 +42,7 @@ function M.init() vim.keymap.set("n", "le", expand_macro, { desc = "expand macro", buffer = bufnr }) vim.keymap.set("n", "rs", ssr, { desc = "ssr", buffer = bufnr }) - local utils = require("tobyvin.utils") - utils.documentation.register("rust", external_docs) - utils.hover.register(hover_actions, { desc = "rust-tools hover actions", buffer = bufnr, priority = 10 }) + vim.lsp.handlers["textDocument/hover"] = require("rust-tools").hover_actions.hover_actions.handler end, }) end diff --git a/nvim/.config/nvim/lua/tobyvin/utils/documentation.lua b/nvim/.config/nvim/lua/tobyvin/utils/documentation.lua index 855b495..683e0b6 100644 --- a/nvim/.config/nvim/lua/tobyvin/utils/documentation.lua +++ b/nvim/.config/nvim/lua/tobyvin/utils/documentation.lua @@ -1,26 +1,117 @@ local M = {} -M.sources = { - vim = function() - vim.cmd("help " .. vim.fn.expand("")) - end, - help = function() - vim.cmd("Man " .. vim.fn.expand("")) +---@type Provider[] +vim.g.doc_providers = {} + +local default_opts = { + enabled = function() + return true end, } -M.register = function(filetypes, callback) - for _, filetype in ipairs(vim.tbl_flatten({ filetypes })) do - M.sources[filetype] = callback +---@param buffer number? +---@return Provider[] +local get_providers = function(buffer) + if buffer then + return vim.F.if_nil(vim.b[buffer].doc_providers, {}) + else + return vim.g.doc_providers + end +end + +---@param buffer number? +---@param providers Provider[] +local set_providers = function(buffer, providers) + if buffer == nil then + vim.g.doc_providers = providers + else + vim.b[buffer].doc_providers = providers end end -M.open = function() - local filetype = vim.bo.filetype - if vim.tbl_contains(vim.tbl_keys(M.sources), filetype) then - M.sources[filetype]() +---@param a Provider +---@param b Provider +---@return boolean +local sort_providers = function(a, b) + if a.opts.priority and b.opts.priority then + return a.opts.priority > b.opts.priority else - vim.notify("[Utils] Documentation not available", vim.log.levels.ERROR) + return not b.opts.priority + end +end + +---@param buffer number +---@return Provider[] +M.buf_providers = function(buffer) + local providers = {} + if vim.api.nvim_buf_is_valid(buffer) and type(vim.b[buffer].doc_providers) == "table" then + vim.list_extend(providers, get_providers(buffer)) + end + vim.list_extend(providers, get_providers()) + table.sort(providers, sort_providers) + return providers +end + +---@param handler ProviderHandler +---@param opts ProviderOpts +---@return ProviderId +M.register = function(handler, opts) + ---@type ProviderOpts + opts = vim.F.if_nil(opts, {}) + opts = vim.tbl_extend("keep", opts, default_opts) + + ---@type Provider + local provider = { handler = handler, opts = opts } + + local providers = get_providers(provider.opts.buffer) + local id + + if #providers > 0 and provider.opts.priority then + for i, p in ipairs(providers) do + if not p.opts.priority or p.opts.priority < provider.opts.priority or i == #providers then + table.insert(providers, i, provider) + id = i + break + end + end + else + table.insert(providers, provider) + id = #providers + end + + set_providers(provider.opts.buffer, providers) + return id +end + +---@param id ProviderId +---@param buffer number? +M.unregister = function(id, buffer) + local providers = get_providers(buffer) + + local provider = table.remove(providers, id) + + set_providers(buffer, providers) + return provider +end + +--- Returns `true` if a provider successfully handled the request, otherwise returns `false`. +-- Example usage: +-- ```lua +-- vim.keymap.set("n", "gx", function() +-- if utils.documentation.open() then +-- return "" +-- end +-- return "gx" +-- end, { desc = "documentation", expr = true }) +-- ``` +---@param buffer number? +M.open = function(buffer) + buffer = buffer or vim.api.nvim_get_current_buf() + local providers = M.buf_providers(buffer) + for _, provider in ipairs(providers) do + if provider.opts.enabled and provider.opts.enabled() and not provider.handler() then + return true + end end end diff --git a/nvim/.config/nvim/lua/tobyvin/utils/hover.lua b/nvim/.config/nvim/lua/tobyvin/utils/hover.lua index 3b967eb..47e2e53 100644 --- a/nvim/.config/nvim/lua/tobyvin/utils/hover.lua +++ b/nvim/.config/nvim/lua/tobyvin/utils/hover.lua @@ -107,6 +107,16 @@ M.unregister = function(id, buffer) return provider end +--- Returns `true` if a provider successfully handled the request, otherwise returns `false`. +-- Example usage: +-- ```lua +-- vim.keymap.set("n", "K", function() +-- if utils.hover.open() then +-- return "" +-- end +-- return "K" +-- end, { desc = "hover", expr = true } +-- ``` ---@param buffer number? M.open = function(buffer) buffer = buffer or vim.api.nvim_get_current_buf() -- cgit v1.2.3-70-g09d2