From 79650be92e3f89cc5a46032978bb77d783526ed4 Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Thu, 4 Apr 2024 20:36:16 -0500 Subject: feat(nvim,wip): add initial utils for lsp timeout --- nvim/.config/nvim/lua/plugins/ferris.lua | 4 +- nvim/.config/nvim/lua/plugins/lspconfig.lua | 13 ++-- nvim/.config/nvim/lua/tobyvin/utils.lua | 25 +------ nvim/.config/nvim/lua/tobyvin/utils/lsp.lua | 108 ++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 31 deletions(-) create mode 100644 nvim/.config/nvim/lua/tobyvin/utils/lsp.lua diff --git a/nvim/.config/nvim/lua/plugins/ferris.lua b/nvim/.config/nvim/lua/plugins/ferris.lua index 5d3b685..8a800c1 100644 --- a/nvim/.config/nvim/lua/plugins/ferris.lua +++ b/nvim/.config/nvim/lua/plugins/ferris.lua @@ -8,9 +8,9 @@ local M = { } function M:init() - U.on_attach(function() + U.lsp.on_attach("rust_analyzer", function() vim.keymap.set("n", "gx", require("ferris.methods.open_documentation"), { desc = "open external docs" }) - end, { name = "rust_analyzer" }) + end, { desc = "setup ferris keymaps" }) end return M diff --git a/nvim/.config/nvim/lua/plugins/lspconfig.lua b/nvim/.config/nvim/lua/plugins/lspconfig.lua index df7450e..70652ef 100644 --- a/nvim/.config/nvim/lua/plugins/lspconfig.lua +++ b/nvim/.config/nvim/lua/plugins/lspconfig.lua @@ -11,16 +11,19 @@ local M = { function M:config() require("neoconf") - require("lspconfig.util").default_config.capabilities = + require("lspconfig").util.default_config.capabilities = require("cmp_nvim_lsp").default_capabilities(vim.lsp.protocol.make_client_capabilities()) require("lspconfig.ui.windows").default_options.border = "single" - local available = require("lspconfig.util").available_servers() - for name, config in pairs(require("tobyvin.lsp.configs")) do - if not vim.tbl_contains(available, name) then + local avail = require("lspconfig").util.available_servers() + + vim.iter(require("tobyvin.lsp.configs")):each(function(name, config) + if not vim.tbl_contains(avail, name) then require("lspconfig")[name].setup(config) end - end + + require("tobyvin.lsp.configs")[name] = require("lspconfig")[name].manager.config + end) end return M diff --git a/nvim/.config/nvim/lua/tobyvin/utils.lua b/nvim/.config/nvim/lua/tobyvin/utils.lua index c107d17..1cb6329 100644 --- a/nvim/.config/nvim/lua/tobyvin/utils.lua +++ b/nvim/.config/nvim/lua/tobyvin/utils.lua @@ -4,6 +4,7 @@ local M = { dashboard = require("tobyvin.utils.dashboard"), session = require("tobyvin.utils.session"), dap = require("tobyvin.utils.dap"), + lsp = require("tobyvin.utils.lsp"), } function M.inspect(v) @@ -74,30 +75,6 @@ function M.system(...) return s end ----Register callback to run when a lsp server matching a filter attaches to a buffer ----@param on_attach fun(client: lsp.Client, buffer: integer): boolean|nil ----@param filter vim.lsp.get_clients.filter|nil -function M.on_attach(on_attach, filter) - vim.api.nvim_create_autocmd("LspAttach", { - desc = "on client attach", - callback = function(args) - local bufnr = args.buf ---@type number - local client = vim.lsp.get_client_by_id(args.data.client_id) - filter = filter or {} - - if - client - and (filter.id == nil or client.id == filter.id) - and (filter.name == nil or client.name == filter.name) - and (filter.bufnr == nil or bufnr == filter.bufnr) - and (filter.method == nil or client.supports_method(filter.method, { bufnr = bufnr })) - then - on_attach(client, bufnr) - end - end, - }) -end - ---Merges two or more highlights groups into a new highlight group. --- ---**Note**: This will overwrite any existing group named . If you would like to both merge diff --git a/nvim/.config/nvim/lua/tobyvin/utils/lsp.lua b/nvim/.config/nvim/lua/tobyvin/utils/lsp.lua new file mode 100644 index 0000000..e0fc17b --- /dev/null +++ b/nvim/.config/nvim/lua/tobyvin/utils/lsp.lua @@ -0,0 +1,108 @@ +local M = { + lsp_timeout_ms = 600000, + lsp_restart_delay_ms = 1000, +} + +---Register callback to run when a lsp server matching a filter attaches to a buffer +---@param filter string|string[]|vim.lsp.get_clients.Filter|vim.lsp.get_clients.Filter[]|nil +---@param on_attach fun(client: vim.lsp.Client, bufnr: integer): boolean|nil +---@param opts vim.api.keyset.create_autocmd? +function M.on_attach(filter, on_attach, opts) + opts = opts or {} + opts.callback = function(args) + local bufnr = args.buf ---@type number + local client = vim.lsp.get_client_by_id(args.data.client_id) + + if + client + and vim.iter({ filter }):all(function(f) + return (type(f) == "string" and f == client.name) + or (f.id == nil or client.id == f.id) + and (f.name == nil or client.name == f.name) + and (f.bufnr == nil or bufnr == f.bufnr) + and (f.method == nil or client.supports_method(f.method, { bufnr = bufnr })) + end) + then + on_attach(client, bufnr) + end + end + + vim.api.nvim_create_autocmd("LspAttach", opts) +end + +---@param client vim.lsp.Client +---@param bufnr integer +function M.initiate_timeout(client, bufnr) + if vim.b[bufnr].timed_out then + return + end + + if vim.b[bufnr].restarting then + vim.b[bufnr].restarting:stop() + end + + vim.b[bufnr].lsp_timeout = vim.defer_fn(function() + vim.b[bufnr].timed_out = true + + local timed_out = vim.iter(client.attached_buffers):all(function(buf) + return vim.b[buf].timed_out + end) + + if timed_out then + client.stop() + client.rpc.terminate() + end + end, M.lsp_timeout) +end + +---@param client vim.lsp.Client +---@param bufnr integer +function M.initiate_restart(client, bufnr) + if not vim.b[bufnr].timed_out then + return + end + + vim.b[bufnr].restarting = vim.defer_fn(function() + vim.b[bufnr].lsp_timeout:stop() + require("lspconfig.configs")[client.name].launch(bufnr) + + vim.b[bufnr].restarting:close() + vim.b[bufnr].restarting = nil + vim.b[bufnr].timed_out = nil + end, M.lsp_restart_delay_ms) +end + +---@param client vim.lsp.Client +---@param bufnr integer +function M.setup_timeout(client, bufnr) + vim.api.nvim_create_autocmd("BufLeave", { + buffer = bufnr, + callback = function() + M.initiate_timeout(client, bufnr) + end, + }) + + vim.api.nvim_create_autocmd("BufEnter", { + buffer = bufnr, + callback = function() + M.initiate_restart(client, bufnr) + end, + }) +end + +---Sends a notification. +---@param kind string Accepted values are: +---{ "lsp_has_started", "lsp_has_stopped" } +function M.notify(kind) + if kind == "lsp_has_started" then + vim.notify("Focus recovered. Starting LSP clients.", vim.log.levels.INFO, { title = "garbage-day.nvim" }) + elseif kind == "lsp_has_stopped" then + vim.notify( + "Inactive LSP clients have been stopped to save resources.", + vim.log.levels.INFO, + { title = "garbage-day.nvim" } + ) + end +end + +return M -- cgit v1.2.3-70-g09d2