From c7dd0830837437b5f85ef0145fd5e64964ebb1f5 Mon Sep 17 00:00:00 2001 From: Oscar Wallberg Date: Mon, 20 Apr 2026 22:11:18 +0200 Subject: [PATCH] refactor: address emmylua diagnostics --- .luafmt.toml | 5 +- after/lsp/bashls.lua | 2 +- after/lsp/clangd.lua | 2 +- after/lsp/emmylua_ls.lua | 54 +++++-- after/lsp/gopls.lua | 2 +- after/lsp/intelephense.lua | 2 +- after/lsp/lua_ls.lua | 73 --------- after/lsp/ruff.lua | 2 +- init.lua | 1 + lua/core/commands.lua | 2 +- lua/core/keymap.lua | 6 +- lua/core/options.lua | 1 + lua/dap/hover/content.lua | 1 + lua/git.lua | 19 ++- lua/linter.lua | 39 +++-- lua/lsp/codelens/init.lua | 33 ++-- lua/lsp/codelens/refresh_task.lua | 45 +++--- lua/lsp/codelens/row.lua | 25 ++- lua/lsp/codelens/session.lua | 54 +++++-- lua/lsp/completion/init.lua | 107 +++++++------ lua/lsp/completion/popup.lua | 130 ++++++++------- lua/lsp/completion/session.lua | 102 ++++++------ lua/lsp/init.lua | 9 ++ lua/pack.lua | 77 +++++---- lua/ts.lua | 2 + lua/util.lua | 253 +++++++++++++----------------- plugins/blink.cmp.lua | 19 +-- plugins/comment.lua | 2 +- plugins/mason-auto-install.lua | 5 +- 29 files changed, 542 insertions(+), 532 deletions(-) delete mode 100644 after/lsp/lua_ls.lua diff --git a/.luafmt.toml b/.luafmt.toml index 032a7ed..f75f7b7 100644 --- a/.luafmt.toml +++ b/.luafmt.toml @@ -3,9 +3,10 @@ kind = "Space" width = 4 [layout] -max_line_width = 80 +max_line_width = 79 max_blank_lines = 1 +call_args_expand = "Always" [output] -trailing_comma = "Multiline" +trailing_table_separator = "Multiline" quote_style = "Double" diff --git a/after/lsp/bashls.lua b/after/lsp/bashls.lua index 9c5abca..5ac16f7 100644 --- a/after/lsp/bashls.lua +++ b/after/lsp/bashls.lua @@ -7,7 +7,7 @@ return { "bash", "zsh", }, - on_attach = function(client, bufnr) + on_attach = function(_, bufnr) vim.keymap.set("n", "lf", function() util.format({ buf = bufnr, diff --git a/after/lsp/clangd.lua b/after/lsp/clangd.lua index f703962..cc0afd5 100644 --- a/after/lsp/clangd.lua +++ b/after/lsp/clangd.lua @@ -12,7 +12,7 @@ return { "--enable-config", }, single_file_support = true, - on_attach = function(client, bufnr) + on_attach = function(_, bufnr) Linter.add(bufnr, { cmd = { "clang-tidy", diff --git a/after/lsp/emmylua_ls.lua b/after/lsp/emmylua_ls.lua index 7dd473f..0849896 100644 --- a/after/lsp/emmylua_ls.lua +++ b/after/lsp/emmylua_ls.lua @@ -1,5 +1,3 @@ -local lsp = require("lsp") - local lua_library_paths = { vim.env.VIMRUNTIME } vim.list_extend(lua_library_paths, require("pack").get_paths()) @@ -7,17 +5,17 @@ vim.list_extend(lua_library_paths, require("pack").get_paths()) return { settings = { emmylua = { - format = { - useDiff = true, - externalTool = { - program = "luafmt", - args = { - "--stdin", - "--level=lua-jit", - "--config=.luafmt.toml", - } + diagnostics = { + disable = { + "unnecessary-if", + "preferred-local-alias", + "redefined-local", }, }, + format = { + -- Re-enable once luafmt is integrated in server + -- useDiff = true, + }, runtime = { version = "LuaJIT", requirePattern = { @@ -33,5 +31,37 @@ return { }, } }, - on_attach = lsp.on_attach, + on_attach = function(_, bufnr) + local util = require("util") + + vim.keymap.set("n", "lf", function() + util.format({ + buf = bufnr, + cmd = { + "stylua", + "--stdin-filepath", + "%file%", + "-", + }, + output = "stdout", + }) + end, { buffer = bufnr }) + + vim.keymap.set("x", "lf", function() + util.format({ + buf = bufnr, + cmd = { + "stylua", + "--range-start", + "%byte_start%", + "--range-end", + "%byte_end%", + "--stdin-filepath", + "%file%", + "-", + }, + output = "stdout", + }) + end, { buffer = bufnr }) + end, } diff --git a/after/lsp/gopls.lua b/after/lsp/gopls.lua index be7d2d3..dcc15dc 100644 --- a/after/lsp/gopls.lua +++ b/after/lsp/gopls.lua @@ -11,7 +11,7 @@ return { }, }, }, - on_attach = function(client, bufnr) + on_attach = function(_, bufnr) vim.keymap.set("n", "lf", function() util.format({ buf = bufnr, diff --git a/after/lsp/intelephense.lua b/after/lsp/intelephense.lua index dd48328..823d636 100644 --- a/after/lsp/intelephense.lua +++ b/after/lsp/intelephense.lua @@ -11,7 +11,7 @@ return { }, }, }, - on_attach = function(client, bufnr) + on_attach = function(_, bufnr) Linter.add(bufnr, { cmd = { "phpcs", diff --git a/after/lsp/lua_ls.lua b/after/lsp/lua_ls.lua deleted file mode 100644 index 70c99d9..0000000 --- a/after/lsp/lua_ls.lua +++ /dev/null @@ -1,73 +0,0 @@ -local lua_library_paths = { vim.env.VIMRUNTIME } -vim.list_extend(lua_library_paths, require("pack").get_paths()) - ----@type vim.lsp.Config -return { - settings = { - Lua = { - codeLens = { - enable = true, - }, - completion = { - callSnippet = "Replace", - keywordSnippet = "Replace", - showWord = "Disable", - }, - runtime = { - version = "LuaJIT", - path = { - "lua/?.lua", - "lua/?/init.lua", - }, - pathStrict = true, - }, - workspace = { - library = lua_library_paths, - checkThirdParty = false, - }, - hint = { - enable = false, - arrayIndex = "Disable", - await = true, - paramName = "All", - paramType = true, - semicolon = "Disable", - setType = true, - }, - telemetry = { enable = false }, - }, - }, - on_attach = function(_, bufnr) - local util = require("util") - - vim.keymap.set("n", "lf", function() - util.format({ - buf = bufnr, - cmd = { - "stylua", - "--stdin-filepath", - "%file%", - "-", - }, - output = "stdout", - }) - end, { buffer = bufnr }) - - vim.keymap.set("x", "lf", function() - util.format({ - buf = bufnr, - cmd = { - "stylua", - "--range-start", - "%byte_start%", - "--range-end", - "%byte_end%", - "--stdin-filepath", - "%file%", - "-", - }, - output = "stdout", - }) - end, { buffer = bufnr }) - end, -} diff --git a/after/lsp/ruff.lua b/after/lsp/ruff.lua index d996894..f9f7b85 100644 --- a/after/lsp/ruff.lua +++ b/after/lsp/ruff.lua @@ -2,7 +2,7 @@ local util = require("util") ---@type vim.lsp.Config return { - on_attach = function(client, bufnr) + on_attach = function(_, bufnr) vim.keymap.set("n", "lf", function() vim.lsp.buf.format() util.format({ diff --git a/init.lua b/init.lua index e24a596..8dede5b 100644 --- a/init.lua +++ b/init.lua @@ -15,6 +15,7 @@ for _, file in ipairs(files) do local pkg = "core." .. file local ok, err = pcall(require, pkg) if not ok then + ---@cast err string log.error("Error while loading package " .. pkg) log.error(err) return diff --git a/lua/core/commands.lua b/lua/core/commands.lua index 5514746..9195389 100644 --- a/lua/core/commands.lua +++ b/lua/core/commands.lua @@ -11,7 +11,7 @@ vim.api.nvim_create_user_command("PluginUnwatch", function() end, { desc = "Stop watching plugin configs" }) vim.api.nvim_create_user_command("PluginReload", function(opts) - require("pack").reload(opts.args) + require("pack").reload_plugin(opts.args) end, { nargs = 1, complete = function(lead) diff --git a/lua/core/keymap.lua b/lua/core/keymap.lua index 0ae67e7..b9fd776 100644 --- a/lua/core/keymap.lua +++ b/lua/core/keymap.lua @@ -58,9 +58,11 @@ local function delete_buffer(force) if #buffers < 2 then return end + local b1 = assert(buffers[1]) + local b2 = assert(buffers[2]) - local current = tonumber(buffers[1]:match("^%s*(%d+)")) - local previous = tonumber(buffers[2]:match("^%s*(%d+)")) + local current = tonumber(b1:match("^%s*(%d+)")) --[[@as integer?]] + local previous = tonumber(b2:match("^%s*(%d+)")) --[[@as integer?]] if not current or not previous then return diff --git a/lua/core/options.lua b/lua/core/options.lua index e4d7a6d..6cdaf9b 100644 --- a/lua/core/options.lua +++ b/lua/core/options.lua @@ -26,6 +26,7 @@ vim.opt.foldlevel = 99 vim.opt.foldlevelstart = 99 vim.opt.foldmethod = "indent" vim.opt.foldignore = "" +---@diagnostic disable-next-line: assign-type-mismatch vim.opt.completeopt = { "menu", "menuone", diff --git a/lua/dap/hover/content.lua b/lua/dap/hover/content.lua index 4214c19..57f7187 100644 --- a/lua/dap/hover/content.lua +++ b/lua/dap/hover/content.lua @@ -69,6 +69,7 @@ function Content:add_with_treesitter(text, lang) if not ok or not parser then return end + ---@cast parser vim.treesitter.LanguageTree local trees = parser:parse() if not trees then diff --git a/lua/git.lua b/lua/git.lua index 5a0b99a..94c0cdd 100644 --- a/lua/git.lua +++ b/lua/git.lua @@ -91,7 +91,8 @@ end ---@field worktree string ---@field buffers integer[] ---@field watcher? uv.uv_fs_event_t ----@field refresh fun() | ow.Util.Debouncer +---@field refresh fun(self: ow.Git.Repo) +---@field refresh_handle ow.Util.DebounceHandle local Repo = {} Repo.__index = Repo @@ -101,13 +102,13 @@ function Repo:start_watcher() if err or (filename ~= "index" and filename ~= "HEAD") then return end - self.refresh() + self:refresh() end)) self.watcher = watcher end function Repo:stop_watcher() - self.refresh:cancel() + self.refresh_handle.close() if self.watcher then self.watcher:stop() self.watcher:close() @@ -179,9 +180,7 @@ function Repo.new(gitdir, worktree) worktree = worktree, buffers = {}, }, Repo) - self.refresh = util.debounce(function() - do_refresh(self) - end, 50) + self.refresh, self.refresh_handle = util.debounce(do_refresh, 50) self:start_watcher() return self end @@ -232,7 +231,7 @@ local function unregister(buf) end ---@param buf integer -local function refresh(buf) +local function refresh_buf(buf) if not vim.api.nvim_buf_is_valid(buf) or vim.bo[buf].buftype ~= "" then return end @@ -241,7 +240,7 @@ local function refresh(buf) vim.b[buf].git_status = nil return end - repo.refresh() + repo:refresh() end local M = {} @@ -260,7 +259,7 @@ function M.setup() { group = group, callback = function(args) - refresh(args.buf) + refresh_buf(args.buf) end, } ) @@ -273,7 +272,7 @@ function M.setup() vim.api.nvim_create_autocmd("FocusGained", { group = group, callback = function() - refresh(vim.api.nvim_get_current_buf()) + refresh_buf(vim.api.nvim_get_current_buf()) end, }) vim.api.nvim_create_autocmd("VimLeavePre", { diff --git a/lua/linter.lua b/lua/linter.lua index 695db17..633820f 100644 --- a/lua/linter.lua +++ b/lua/linter.lua @@ -46,7 +46,7 @@ local util = require("util") ---@field pattern? string --- Named capture groups for pattern matching (required if not using json) ---@field groups? ow.lsp.linter.Group[] ---- Map severity strings to vim diagnostic levels +--- Map severity strings to vim diagnostic levels (required if not using json) ---@field severity_map? table --- Source name for diagnostics (default: command name) ---@field source? string @@ -131,17 +131,13 @@ function Linter:add_tags(diag) return end - local have_unnecessary = vim.islist(self.config.tags.unnecessary) - local have_deprecated = vim.islist(self.config.tags.deprecated) - - if not have_unnecessary and not have_deprecated then - return - end - - diag._tags = {} + diag._tags = { + unnecessary = false, + deprecated = false, + } if - have_unnecessary + self.config.tags.unnecessary and vim.list_contains(self.config.tags.unnecessary, diag.code) then diag._tags.unnecessary = true @@ -149,7 +145,7 @@ function Linter:add_tags(diag) end if - have_deprecated + self.config.tags.deprecated and vim.list_contains(self.config.tags.deprecated, diag.code) then diag._tags.deprecated = true @@ -181,13 +177,15 @@ function Linter:fix_indexing(diag) end end +---@param json any function Linter:process_json_output(json) ---@type vim.Diagnostic[] local diagnostics = {} + local cfg = assert(self.config.json) local items = json - if self.config.json.diagnostics_root then - items = Linter.get_json_value(json, self.config.json.diagnostics_root) + if cfg.diagnostics_root then + items = Linter.get_json_value(json, cfg.diagnostics_root) end if type(items) ~= "table" then @@ -202,8 +200,9 @@ function Linter:process_json_output(json) for _, item in ipairs(items) do local diag = {} - for field, path in pairs(self.config.json) do + for field, path in pairs(cfg) do if field ~= "diagnostics_root" and field ~= "callback" then + ---@cast path string diag[field] = Linter.get_json_value(item, path) end end @@ -218,12 +217,14 @@ function Linter:process_json_output(json) diag.severity = self.config.severity_map[diag.severity] end + ---@cast diag vim.Diagnostic + self:fix_indexing(diag) self:clamp_col(diag) self:add_tags(diag) - if type(self.config.json.callback) == "function" then - self.config.json.callback(diag) + if type(cfg.callback) == "function" then + cfg.callback(diag) end table.insert(diagnostics, diag) @@ -371,6 +372,9 @@ function Linter:run() return end + ---@cast self.config.pattern -nil + ---@cast self.config.groups -nil + ---@cast self.config.severity_map -nil local diagnostics = {} for _, line in ipairs(lines) do @@ -387,6 +391,7 @@ function Linter:run() success = false return elseif resp then + ---@cast resp vim.Diagnostic resp.source = resp.source or self.config.source self:clamp_col(resp) self:add_tags(resp) @@ -439,7 +444,7 @@ function Linter.add(bufnr, config) buffer = linter.bufnr, callback = util.debounce(function() linter:run() - end, config.debounce) --[[@as fun()]], + end, config.debounce), group = linter.augroup, }) diff --git a/lua/lsp/codelens/init.lua b/lua/lsp/codelens/init.lua index e3a5009..a526f96 100644 --- a/lua/lsp/codelens/init.lua +++ b/lua/lsp/codelens/init.lua @@ -7,23 +7,13 @@ local log = require("log") local GROUP = vim.api.nvim_create_augroup("ow.lsp.codelens", { clear = true }) ----@type table -local session_by_buf = {} - local M = {} ----@param buf integer ----@return ow.lsp.codelens.Session -local function get_session(buf) - session_by_buf[buf] = session_by_buf[buf] or Session.new(buf) - return session_by_buf[buf] -end - ---@param buf? integer ---@return boolean function M.is_enabled(buf) buf = buf or vim.api.nvim_get_current_buf() - local session = session_by_buf[buf] + local session = Session.find(buf) return session ~= nil and session.enabled end @@ -34,13 +24,13 @@ function M.enable(value, buf) value = true end buf = buf or vim.api.nvim_get_current_buf() - get_session(buf):enable(value) + Session.get(buf):enable(value) end ---@param buf? integer function M.toggle(buf) buf = buf or vim.api.nvim_get_current_buf() - get_session(buf):toggle() + Session.get(buf):toggle() end ---@class ow.lsp.codelens.SetupOpts : ow.lsp.codelens.row.ConfigureOpts @@ -49,17 +39,18 @@ end function M.setup(opts) Row.configure(opts or {}) - local method = vim.lsp.protocol.Methods.workspace_codeLens_refresh - vim.lsp.handlers[method] = function(err, _, ctx) + ---@param err lsp.ResponseError? + ---@param ctx lsp.HandlerContext + vim.lsp.handlers["workspace/codeLens/refresh"] = function(err, _, ctx) if err then log.warning( "client %d: error on %s: %s", ctx.client_id, - method, + ctx.method, err.message ) end - for buf, session in pairs(session_by_buf) do + for buf, session in pairs(Session.all()) do if session.enabled and #vim.lsp.get_clients({ bufnr = buf, id = ctx.client_id }) @@ -74,7 +65,7 @@ function M.setup(opts) vim.api.nvim_create_autocmd({ "BufEnter", "LspAttach" }, { group = GROUP, callback = function(ev) - local session = session_by_buf[ev.buf] + local session = Session.find(ev.buf) if session and session.enabled then session:refresh() end @@ -84,11 +75,7 @@ function M.setup(opts) vim.api.nvim_create_autocmd({ "BufDelete", "BufWipeout" }, { group = GROUP, callback = function(ev) - local session = session_by_buf[ev.buf] - if session then - session:abort() - end - session_by_buf[ev.buf] = nil + Session.remove(ev.buf) end, }) end diff --git a/lua/lsp/codelens/refresh_task.lua b/lua/lsp/codelens/refresh_task.lua index 004a4bf..9a93344 100644 --- a/lua/lsp/codelens/refresh_task.lua +++ b/lua/lsp/codelens/refresh_task.lua @@ -15,7 +15,8 @@ function RefreshTask.new(session) session = session, aborted = false, cancels = {}, - }, RefreshTask) + }, + RefreshTask) end ---@param cancel fun() @@ -35,20 +36,19 @@ end ---@param lens lsp.CodeLens ---@param row ow.lsp.codelens.Row function RefreshTask:resolve(client, lens, row) - local method = vim.lsp.protocol.Methods.codeLens_resolve local _, req_id = client:request( - method, + "codeLens/resolve", lens, + ---@param err lsp.ResponseError? ---@param resolved lsp.CodeLens? - function(err, resolved) + ---@param ctx lsp.HandlerContext + function(err, resolved, ctx) if self.aborted then return end if err then log.warning( - "client %d: %s failed: %s", - client.id, - method, + "client %d: %s failed: %s", client.id, ctx.method, err.message ) end @@ -77,33 +77,27 @@ function RefreshTask:process_lens(rows, client, lens) row:add(lens) return end - if - not client - or not client:supports_method(vim.lsp.protocol.Methods.codeLens_resolve) - then + if not client or not client:supports_method("codeLens/resolve") then return end row:expect() self:resolve(client, lens, row) end ----@param responses table -function RefreshTask:process_responses(responses) - local method = vim.lsp.protocol.Methods.textDocument_codeLens +---@param results table +function RefreshTask:process_results(results) local session = self.session local new_rows = {} - for client_id, response in pairs(responses) do - if response.err then + for client_id, result in pairs(results) do + if result.err then log.warning( - "client %d: %s failed: %s", - client_id, - method, - response.err.message + "client %d: %s failed: %s", client_id, result.context.method, + result.err.message ) end - if not response.err and type(response.result) == "table" then + if not result.err and type(result.result) == "table" then local client = vim.lsp.get_client_by_id(client_id) - for _, lens in ipairs(response.result) do + for _, lens in ipairs(result.result) do self:process_lens(new_rows, client, lens) end end @@ -113,19 +107,18 @@ function RefreshTask:process_responses(responses) end function RefreshTask:run() - local method = vim.lsp.protocol.Methods.textDocument_codeLens local params = { textDocument = vim.lsp.util.make_text_document_params(self.session.buf), } local cancel = vim.lsp.buf_request_all( self.session.buf, - method, + "textDocument/codeLens", params, - function(responses) + function(results) if self.aborted then return end - self:process_responses(responses) + self:process_results(results) end ) self:track(cancel) diff --git a/lua/lsp/codelens/row.lua b/lua/lsp/codelens/row.lua index 1431738..2e3f01b 100644 --- a/lua/lsp/codelens/row.lua +++ b/lua/lsp/codelens/row.lua @@ -6,9 +6,12 @@ local NS = vim.api.nvim_create_namespace("ow.lsp.codelens") local position = "above" local separator = " | " +---@class ow.lsp.CodeLens : lsp.CodeLens +---@field command lsp.Command + ---@class ow.lsp.codelens.Row ---@field row integer ----@field lenses lsp.CodeLens[] +---@field lenses ow.lsp.CodeLens[] ---@field pending integer local Row = {} Row.__index = Row @@ -19,7 +22,7 @@ function Row.new(row) return setmetatable({ row = row, lenses = {}, pending = 0 }, Row) end ----@param lens lsp.CodeLens +---@param lens ow.lsp.CodeLens function Row:add(lens) table.insert(self.lenses, lens) end @@ -31,8 +34,8 @@ end ---@param lens? lsp.CodeLens function Row:resolve(lens) self.pending = self.pending - 1 - if lens then - table.insert(self.lenses, lens) + if lens and lens.command then + self:add(lens) end end @@ -58,7 +61,9 @@ function Row:render(buf) end end - local col, opts + ---@type vim.api.keyset.set_extmark + local opts + local col if position == "above" then local line = vim.api.nvim_buf_get_lines( buf, @@ -79,7 +84,8 @@ function Row:render(buf) hl_mode = "combine", } else - col = position == "inline" and self.lenses[1].range.start.character or 0 + local first = assert(self.lenses[1]) + col = position == "inline" and first.range.start.character or 0 opts = { virt_text = parts, virt_text_pos = position, @@ -97,6 +103,11 @@ function Row:render(buf) { self.row, -1 }, { details = true } ) + ---@type { + --- id: integer, + --- col: integer, + --- details: vim.api.keyset.extmark_details? + ---} local primary for i, mark in ipairs(marks) do if i == 1 then @@ -107,7 +118,7 @@ function Row:render(buf) end if primary and primary.col == col and primary.details then - local d = primary.details --[[@as vim.api.keyset.extmark_details]] + local d = primary.details local same = opts.virt_lines and vim.deep_equal(d.virt_lines, opts.virt_lines) and d.virt_lines_above == opts.virt_lines_above diff --git a/lua/lsp/codelens/session.lua b/lua/lsp/codelens/session.lua index f03ec79..b178571 100644 --- a/lua/lsp/codelens/session.lua +++ b/lua/lsp/codelens/session.lua @@ -13,9 +13,13 @@ local REFRESH_DEBOUNCE_MS = 200 local Session = {} Session.__index = Session ----@param session ow.lsp.codelens.Session -local function do_refresh(session) - if not session.enabled then +---@type table +local by_buf = {} + +---@param buf integer +local function do_refresh(buf) + local session = by_buf[buf] + if not session or not session.enabled then return end session:abort() @@ -24,21 +28,45 @@ local function do_refresh(session) task:run() end -local refresher = util.keyed_debounce(do_refresh, REFRESH_DEBOUNCE_MS) +local refresh, refresh_handle = + util.keyed_debounce(do_refresh, REFRESH_DEBOUNCE_MS) ---@param buf integer ---@return ow.lsp.codelens.Session -function Session.new(buf) - return setmetatable({ - buf = buf, - enabled = false, - attached = false, - rows = {}, - }, Session) +function Session.get(buf) + if not by_buf[buf] then + by_buf[buf] = setmetatable({ + buf = buf, + enabled = false, + attached = false, + rows = {}, + }, Session) + end + return by_buf[buf] +end + +---@param buf integer +---@return ow.lsp.codelens.Session? +function Session.find(buf) + return by_buf[buf] +end + +---@return table +function Session.all() + return by_buf +end + +---@param buf integer +function Session.remove(buf) + local session = by_buf[buf] + if session then + session:abort() + end + by_buf[buf] = nil end function Session:abort() - refresher:cancel(self) + refresh_handle.cancel(self.buf) if self.current then self.current:abort() self.current = nil @@ -46,7 +74,7 @@ function Session:abort() end function Session:refresh() - refresher(self) + refresh(self.buf) end function Session:render() diff --git a/lua/lsp/completion/init.lua b/lua/lsp/completion/init.lua index b1d06fb..0e15446 100644 --- a/lua/lsp/completion/init.lua +++ b/lua/lsp/completion/init.lua @@ -10,11 +10,7 @@ local popup = Popup.new() ---@param client vim.lsp.Client ---@param buf integer local function on_attach(client, buf) - if - not client:supports_method( - vim.lsp.protocol.Methods.textDocument_completion - ) - then + if not client:supports_method("textDocument/completion") then return end @@ -64,26 +60,23 @@ function M.setup() end local ft = vim.bo[ev.buf].filetype + ---@type ow.lsp.completion.Pum local pum = { - row = vim.v.event.row, - col = vim.v.event.col, - width = vim.v.event.width, - height = vim.v.event.height, - scrollbar = vim.v.event.scrollbar, + row = vim.v.event.row --[[@as integer]], + col = vim.v.event.col --[[@as integer]], + width = vim.v.event.width --[[@as integer]], + height = vim.v.event.height --[[@as integer]], + scrollbar = vim.v.event.scrollbar --[[@as boolean]], } - if - client:supports_method( - vim.lsp.protocol.Methods.completionItem_resolve - ) - then + if client:supports_method("completionItem/resolve") then popup:dispatch_resolve( client, item, ft, pum, - completed.word, + completed.word or "", ev.buf ) else @@ -125,22 +118,27 @@ function M.setup() ) end if raw.command then - local method = - vim.lsp.protocol.Methods.workspace_executeCommand - client:request(method, { - command = raw.command.command, - arguments = raw.command.arguments, - }, function(err) - if err then - log.warning( - "client %d: %s failed for %s: %s", - client.id, - method, - raw.command.title, - err.message - ) - end - end, ev.buf) + client:request( + "workspace/executeCommand", + { + command = raw.command.command, + arguments = raw.command.arguments, + }, + ---@param err lsp.ResponseError? + ---@param ctx lsp.HandlerContext + function(err, _, ctx) + if err then + log.warning( + "client %d: %s failed for %s: %s", + client.id, + ctx.method, + raw.command.title, + err.message + ) + end + end, + ev.buf + ) end if target.snippet then local word = completed.word or "" @@ -161,32 +159,37 @@ function M.setup() -- Prefer the resolved item when we have it. If we haven't cached -- one yet and the original is missing edits, resolve on the fly. - local cached = popup:resolved_for(completed.word) + local cached = popup:resolved_for(completed.word or "") if cached then apply(cached) elseif - client:supports_method( - vim.lsp.protocol.Methods.completionItem_resolve - ) + client:supports_method("completionItem/resolve") and not item.raw.additionalTextEdits and not item.raw.command then - local method = vim.lsp.protocol.Methods.completionItem_resolve - client:request(method, item.raw, function(err, resolved) - if err then - log.warning( - "client %d: %s failed: %s", - client.id, - method, - err.message - ) - end - if err or not resolved then - return - end - item:apply_resolved(resolved) - apply(item) - end, ev.buf) + client:request( + "completionItem/resolve", + item.raw, + ---@param err lsp.ResponseError? + ---@param resolved lsp.CompletionItem? + ---@param ctx lsp.HandlerContext + function(err, resolved, ctx) + if err then + log.warning( + "client %d: %s failed: %s", + client.id, + ctx.method, + err.message + ) + end + if err or not resolved then + return + end + item:apply_resolved(resolved) + apply(item) + end, + ev.buf + ) else apply(item) end diff --git a/lua/lsp/completion/popup.lua b/lua/lsp/completion/popup.lua index 888698a..43f853a 100644 --- a/lua/lsp/completion/popup.lua +++ b/lua/lsp/completion/popup.lua @@ -81,15 +81,24 @@ function Popup:resolved_for(word) return nil end +---@return integer? +function Popup:active_win() + if self.winid and vim.api.nvim_win_is_valid(self.winid) then + return self.winid + end + return nil +end + ---@return boolean function Popup:is_visible() - return self.winid ~= nil and vim.api.nvim_win_is_valid(self.winid) + return self:active_win() ~= nil end function Popup:close() self:cancel_pending() - if self:is_visible() then - vim.api.nvim_win_close(self.winid, true) + local winid = self:active_win() + if winid then + vim.api.nvim_win_close(winid, true) end self.winid = nil end @@ -115,28 +124,35 @@ end function Popup:dispatch_resolve(client, item, ft, pum, word, buf) self:cancel_pending() self.resolved = nil - local method = vim.lsp.protocol.Methods.completionItem_resolve - local _, request_id = client:request(method, item.raw, function(err, result) - if err then - log.warning( - "client %d: %s failed: %s", - client.id, - method, - err.message - ) - end - self.pending = nil - if err or not result then - return - end - local cur = vim.fn.complete_info({ "completed" }) - if (vim.tbl_get(cur, "completed", "word") or "") ~= word then - return - end - item:apply_resolved(result) - self.resolved = { word = word, item = item } - self:show(item, ft, pum) - end, buf) + local _, request_id = client:request( + "completionItem/resolve", + item.raw, + ---@param err lsp.ResponseError? + ---@param result lsp.CompletionItem? + ---@param ctx lsp.HandlerContext + function(err, result, ctx) + if err then + log.warning( + "client %d: %s failed: %s", + client.id, + ctx.method, + err.message + ) + end + self.pending = nil + if err or not result then + return + end + local cur = vim.fn.complete_info({ "completed" }) + if (vim.tbl_get(cur, "completed", "word") or "") ~= word then + return + end + item:apply_resolved(result) + self.resolved = { word = word, item = item } + self:show(item, ft, pum) + end, + buf + ) if request_id then self.pending = { client = client, id = request_id } end @@ -148,14 +164,14 @@ Popup.HALF_PAGE = HALF_HEIGHT ---@param pum ow.lsp.completion.Pum ---@param width integer function Popup:render(content, pum, width) - self:ensure_buffer() + local bufnr = self:ensure_buffer() local lines = vim.split(content, "\n", { plain = true }) - vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, false, lines) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) - vim.api.nvim_buf_clear_namespace(self.bufnr, NS, 0, -1) + vim.api.nvim_buf_clear_namespace(bufnr, NS, 0, -1) for i, line in ipairs(lines) do if line ~= "" and line:gsub("─", "") == "" then - vim.api.nvim_buf_set_extmark(self.bufnr, NS, i - 1, 0, { + vim.api.nvim_buf_set_extmark(bufnr, NS, i - 1, 0, { end_row = i - 1, end_col = #line, hl_group = "FloatBorder", @@ -187,22 +203,24 @@ function Popup:render(content, pum, width) style = "minimal", } - if self:is_visible() then - vim.api.nvim_win_set_config(self.winid, cfg) + local winid = self:active_win() + if winid then + vim.api.nvim_win_set_config(winid, cfg) else cfg.noautocmd = true - self.winid = vim.api.nvim_open_win(self.bufnr, false, cfg) - vim.wo[self.winid].wrap = true - vim.wo[self.winid].linebreak = true - vim.wo[self.winid].conceallevel = 2 + winid = vim.api.nvim_open_win(bufnr, false, cfg) + self.winid = winid + vim.wo[winid].wrap = true + vim.wo[winid].linebreak = true + vim.wo[winid].conceallevel = 2 end - vim.api.nvim_win_set_cursor(self.winid, { 1, 0 }) + vim.api.nvim_win_set_cursor(winid, { 1, 0 }) - local actual = vim.api.nvim_win_text_height(self.winid, { + local actual = vim.api.nvim_win_text_height(winid, { max_height = MAX_HEIGHT, }).all if actual ~= height then - vim.api.nvim_win_set_height(self.winid, actual) + vim.api.nvim_win_set_height(winid, actual) end self:update_indicators() @@ -211,12 +229,13 @@ end ---@param direction "up" | "down" ---@param count integer function Popup:scroll(direction, count) - if not self:is_visible() then + local winid = self:active_win() + if not winid then return end local key = direction == "down" and vim.keycode("") or vim.keycode("") - vim.api.nvim_win_call(self.winid, function() + vim.api.nvim_win_call(winid, function() vim.cmd.normal({ args = { count .. key }, bang = true }) end) self:update_indicators() @@ -229,46 +248,51 @@ function Popup:cancel_pending() end end +---@return integer function Popup:ensure_buffer() if self.bufnr and vim.api.nvim_buf_is_valid(self.bufnr) then - return + return self.bufnr end - self.bufnr = vim.api.nvim_create_buf(false, true) - vim.bo[self.bufnr].buftype = "nofile" - vim.bo[self.bufnr].bufhidden = "hide" - vim.bo[self.bufnr].swapfile = false + local bufnr = vim.api.nvim_create_buf(false, true) + vim.bo[bufnr].buftype = "nofile" + vim.bo[bufnr].bufhidden = "hide" + vim.bo[bufnr].swapfile = false -- Markdown parser may not be installed; fall back to no highlighting. - pcall(vim.treesitter.start, self.bufnr, "markdown") + pcall(vim.treesitter.start, bufnr, "markdown") + self.bufnr = bufnr + return bufnr end function Popup:update_indicators() - if not self:is_visible() then + local winid = self:active_win() + if not winid then return end - local visible = vim.api.nvim_win_get_height(self.winid) - local topline, botline = unpack(vim.api.nvim_win_call(self.winid, function() + local visible = vim.api.nvim_win_get_height(winid) + ---@type integer, integer + local topline, botline = unpack(vim.api.nvim_win_call(winid, function() return { vim.fn.line("w0"), vim.fn.line("w$") } end)) local display_topline = 1 if topline > 1 then - display_topline = vim.api.nvim_win_text_height(self.winid, { + display_topline = vim.api.nvim_win_text_height(winid, { end_row = topline - 2, }).all + 1 end local display_bot = display_topline + visible - 1 - local total = vim.api.nvim_win_text_height(self.winid, { + local total = vim.api.nvim_win_text_height(winid, { max_height = display_bot + 1, }).all local has_above = display_topline > 1 local has_below = display_bot < total and botline - < vim.api.nvim_buf_line_count(vim.api.nvim_win_get_buf(self.winid)) + < vim.api.nvim_buf_line_count(vim.api.nvim_win_get_buf(winid)) - vim.api.nvim_win_set_config(self.winid, { + vim.api.nvim_win_set_config(winid, { title = has_above and { { "▲ ", "FloatBorder" } } or "", title_pos = has_above and "right" or nil, footer = has_below and { { "▼ ", "FloatBorder" } } or "", diff --git a/lua/lsp/completion/session.lua b/lua/lsp/completion/session.lua index 159c379..8056358 100644 --- a/lua/lsp/completion/session.lua +++ b/lua/lsp/completion/session.lua @@ -113,6 +113,7 @@ local function build_items(responses, base) items = vim.fn.matchfuzzy(items, base, { key = "filter" }) else items = vim.tbl_filter(function(item) + ---@diagnostic disable-next-line: undefined-field return vim.startswith(item.filter, base) end, items) end @@ -123,7 +124,7 @@ end local function buffer_has_completion_client() return #vim.lsp.get_clients({ bufnr = 0, - method = vim.lsp.protocol.Methods.textDocument_completion, + method = "textDocument/completion", }) > 0 end @@ -140,7 +141,7 @@ end local function is_trigger_char(char) local clients = vim.lsp.get_clients({ bufnr = 0, - method = vim.lsp.protocol.Methods.textDocument_completion, + method = "textDocument/completion", }) for _, client in ipairs(clients) do local chars = vim.tbl_get( @@ -199,61 +200,66 @@ function Session:dispatch(trigger_kind, trigger_char, manual) triggerKind = trigger_kind, triggerCharacter = trigger_char, } - local method = vim.lsp.protocol.Methods.textDocument_completion - self.cancel = vim.lsp.buf_request_all(buf, method, params, function(result) - if self.generation ~= gen then - return - end - vim.schedule(function() - -- Re-check: another dispatch may have fired between response - -- and the scheduled callback running. + self.cancel = vim.lsp.buf_request_all( + buf, + "textDocument/completion", + params, + function(result, ctx) if self.generation ~= gen then return end - if vim.fn.mode() ~= "i" then - return - end - local word_start, cursor = word_bounds() - if - not self.manual - and not self.trigger_char - and word_start == cursor - then - return - end - self.is_incomplete = false - for client_id, response in pairs(result) do - if response.err then - log.warning( - "client %d: %s failed: %s", - client_id, - method, - response.err.message - ) + vim.schedule(function() + -- Re-check: another dispatch may have fired between response + -- and the scheduled callback running. + if self.generation ~= gen then + return end - local r = response.result - if type(r) == "table" and r.isIncomplete then - self.is_incomplete = true - break + if vim.fn.mode() ~= "i" then + return end - end - local start = word_start - for _, response in pairs(result) do - local pos = edit_start(response) - if pos then - start = pos.character - break + local word_start, cursor = word_bounds() + if + not self.manual + and not self.trigger_char + and word_start == cursor + then + return end - end - local base = vim.api.nvim_get_current_line():sub(start + 1, cursor) - vim.fn.complete(start + 1, build_items(result, base)) - end) - end) + self.is_incomplete = false + for client_id, response in pairs(result) do + if response.err then + log.warning( + "client %d: %s failed: %s", + client_id, + ctx.method, + response.err.message + ) + end + local r = response.result + if type(r) == "table" and r.isIncomplete then + self.is_incomplete = true + break + end + end + local start = word_start + for _, response in pairs(result) do + local pos = edit_start(response) + if pos then + start = pos.character + break + end + end + local base = + vim.api.nvim_get_current_line():sub(start + 1, cursor) + vim.fn.complete(start + 1, build_items(result, base)) + end) + end + ) end local session = Session.new() -local dispatcher = util.debounce(function(trigger_kind, char) +local dispatch = util.debounce(function(trigger_kind, char) if vim.fn.mode() ~= "i" then return end @@ -288,7 +294,7 @@ function M.on_insert_char_pre() local kind_num = is_trigger and vim.lsp.protocol.CompletionTriggerKind.TriggerCharacter or vim.lsp.protocol.CompletionTriggerKind.Invoked - dispatcher(kind_num, is_trigger and char or nil) + dispatch(kind_num, is_trigger and char or nil) end return M diff --git a/lua/lsp/init.lua b/lua/lsp/init.lua index a018731..430e6e1 100644 --- a/lua/lsp/init.lua +++ b/lua/lsp/init.lua @@ -33,6 +33,15 @@ local function with_file(path, settings) return end + if type(resp) ~= "table" then + log.warning( + "Invalid json in file %s: expected table, got: %s", + path, + type(resp) + ) + return + end + return vim.tbl_deep_extend("force", settings or {}, resp) end diff --git a/lua/pack.lua b/lua/pack.lua index 69781ae..86e67c8 100644 --- a/lua/pack.lua +++ b/lua/pack.lua @@ -1,7 +1,11 @@ local log = require("log") local util = require("util") -local plugins_dir = vim.fs.joinpath(vim.fn.stdpath("config"), "plugins") +local config_dir = vim.fn.stdpath("config") +if type(config_dir) == "table" then + config_dir = assert(config_dir[1]) +end +local plugins_dir = vim.fs.joinpath(config_dir, "plugins") ---@param path string ---@return boolean success @@ -111,7 +115,7 @@ end ---@param plugin ow.Pack.Plugin local function run_ts_build(plugin) local ts = require("ts") - for _, p in ipairs(ts.normalize(plugin.ts_parser)) do + for _, p in ipairs(ts.normalize(assert(plugin.ts_parser))) do ts.build(plugin, p) end end @@ -149,9 +153,8 @@ end ---@type uv.uv_fs_event_t? local watcher = nil - ----@type (fun(filename: string) | ow.Util.KeyedDebouncer)? -local reload = nil +---@type ow.Util.KeyedDebounceHandle? +local on_change_handle = nil ---@class ow.Pack ---@field plugins ow.Pack.Plugin[] @@ -174,7 +177,7 @@ function M.get_paths() end ---@param name string -function M.reload(name) +function M.reload_plugin(name) load(name, true) end @@ -184,29 +187,40 @@ function M.watch() end watcher = assert(vim.uv.new_fs_event()) - reload = util.keyed_debounce(function(filename) - local path = vim.fs.joinpath(plugins_dir, filename) - if not vim.uv.fs_stat(path) then - return - end - local ok, load_err = exec(path) - if ok then - log.info("Reloaded %s", filename) - else - log.error("Failed to reload %s: %s", filename, load_err) - end - end, 100) + local on_change, handle = util.keyed_debounce( + ---@param filename string + function(filename) + local path = vim.fs.joinpath(plugins_dir, filename) + if not vim.uv.fs_stat(path) then + return + end + local ok, load_err = exec(path) + if ok then + log.info("Reloaded %s", filename) + else + log.error("Failed to reload %s: %s", filename, load_err) + end + end, + 200 + ) + on_change_handle = handle - assert(watcher:start(plugins_dir, {}, function(err, filename) - if err then - log.error("Watch error: %s", err) - return + assert(watcher:start( + plugins_dir, + {}, + ---@param err string? + ---@param filename string + function(err, filename) + if err then + log.error("Watch error: %s", err) + return + end + if not filename or not filename:match("%.lua$") then + return + end + on_change(filename) end - if not filename or not filename:match("%.lua$") then - return - end - reload(filename) - end)) + )) end function M.unwatch() @@ -214,14 +228,13 @@ function M.unwatch() return end - if reload then - reload:close() - reload = nil - end - watcher:stop() watcher:close() watcher = nil + if on_change_handle then + on_change_handle.close() + on_change_handle = nil + end end ---@param specs (string | ow.Pack.PluginSpec)[] diff --git a/lua/ts.lua b/lua/ts.lua index be9adf7..fa37b7d 100644 --- a/lua/ts.lua +++ b/lua/ts.lua @@ -114,9 +114,11 @@ function M.normalize(field) if type(field) == "table" then if type(field.lang) == "string" then + ---@cast field ow.TS.ParserSpec return { field } end if type(field[1]) == "table" then + ---@cast field ow.TS.ParserSpec[] return field end end diff --git a/lua/util.lua b/lua/util.lua index d65157e..e786a69 100644 --- a/lua/util.lua +++ b/lua/util.lua @@ -293,167 +293,130 @@ function M.is_list_or_nil(val, t) end end ----@class ow.Util.Debouncer ----@field package _fn fun(...) ----@field package _delay integer ----@field package _timer uv.uv_timer_t ----@field package _gen integer ----@field package _fired_gen integer ----@field package _args? table ----@field package _cb_main fun() ----@field package _cb_uv fun() -local Debouncer = {} -Debouncer.__index = Debouncer - ----@param fn fun(...) ----@param delay integer ----@return ow.Util.Debouncer -function Debouncer.new(fn, delay) - local self = setmetatable({ - _fn = fn, - _delay = delay, - _timer = assert(vim.uv.new_timer()), - _gen = 0, - _fired_gen = 0, - _args = nil, - }, Debouncer) - self._cb_main = vim.schedule_wrap(function() - -- Identity check: the libuv fire may have been superseded by a - -- re-arm or a cancel between the timer firing and this scheduled - -- callback running. - if self._fired_gen ~= self._gen or self._args == nil then - return - end - local args = self._args - self._args = nil - self._fn(vim.F.unpack_len(args)) - end) - self._cb_uv = function() - self._fired_gen = self._gen - self._cb_main() - end - return self -end - -function Debouncer:__call(...) - self._args = vim.F.pack_len(...) - self._gen = self._gen + 1 - self._timer:start(self._delay, 0, self._cb_uv) -end - -function Debouncer:cancel() - self._timer:stop() - self._args = nil -end - -function Debouncer:flush() - if self._args == nil then - return - end - self._timer:stop() - local args = self._args - self._args = nil - self._fn(vim.F.unpack_len(args)) -end - ----@return boolean -function Debouncer:pending() - return self._args ~= nil -end - -function Debouncer:close() - self._timer:stop() - if not self._timer:is_closing() then - self._timer:close() - end - self._args = nil -end +---@class ow.Util.DebounceHandle +---@field cancel fun() +---@field flush fun() +---@field pending fun(): boolean +---@field close fun() ---@generic F: fun(...) ---@param fn F ---@param delay integer ----@return F | ow.Util.Debouncer +---@return F, ow.Util.DebounceHandle function M.debounce(fn, delay) - return Debouncer.new(fn, delay) -end + local timer = assert(vim.uv.new_timer()) + local args ---@type table? + local gen = 0 + local fired_gen = 0 ----@class ow.Util.KeyedDebouncer ----@field package _fn fun(key: T, ...) ----@field package _delay integer ----@field package _slots table -local KeyedDebouncer = {} -KeyedDebouncer.__index = KeyedDebouncer + local cb_main = vim.schedule_wrap(function() + -- Identity check: the libuv fire may have been superseded by a + -- re-arm or a cancel between the timer firing and this scheduled + -- callback running. + if fired_gen ~= gen or args == nil then + return + end + local a = args + args = nil + fn(vim.F.unpack_len(a)) + end) ----@generic T ----@param fn fun(key: T, ...) ----@param delay integer ----@return ow.Util.KeyedDebouncer -function KeyedDebouncer.new(fn, delay) - return setmetatable({ - _fn = fn, - _delay = delay, - _slots = {}, - }, KeyedDebouncer) -end - ----@generic T ----@param self ow.Util.KeyedDebouncer ----@param key T -function KeyedDebouncer:__call(key, ...) - local slot = self._slots[key] - if not slot then - slot = Debouncer.new(function(...) - self._fn(key, ...) - end, self._delay) - self._slots[key] = slot + local cb_uv = function() + fired_gen = gen + cb_main() end - slot(...) -end ----@generic T ----@param self ow.Util.KeyedDebouncer ----@param key T -function KeyedDebouncer:cancel(key) - local slot = self._slots[key] - if slot then - slot:close() - self._slots[key] = nil + local function call(...) + args = vim.F.pack_len(...) + gen = gen + 1 + timer:start(delay, 0, cb_uv) end + + return call, + { + cancel = function() + timer:stop() + args = nil + end, + flush = function() + if args == nil then + return + end + timer:stop() + local a = args + args = nil + fn(vim.F.unpack_len(a)) + end, + pending = function() + return args ~= nil + end, + close = function() + timer:stop() + if not timer:is_closing() then + timer:close() + end + args = nil + end, + } end ----@generic T ----@param self ow.Util.KeyedDebouncer ----@param key T -function KeyedDebouncer:flush(key) - local slot = self._slots[key] - if slot then - slot:flush() - end -end +---@class ow.Util.KeyedDebounceHandle +---@field cancel fun(key: K) +---@field flush fun(key: K) +---@field pending fun(key: K): boolean +---@field close fun() ----@generic T ----@param self ow.Util.KeyedDebouncer ----@param key T ----@return boolean -function KeyedDebouncer:pending(key) - local slot = self._slots[key] - return slot ~= nil and slot:pending() -end - -function KeyedDebouncer:close() - for _, slot in pairs(self._slots) do - slot:close() - end - self._slots = {} -end - ----@diagnostic disable-next-line: undefined-doc-name ----@generic T, F: fun(key: T, ...) +---@generic K, F: fun(key: K, ...) ---@param fn F ---@param delay integer ----@return F | ow.Util.KeyedDebouncer +---@return F, ow.Util.KeyedDebounceHandle function M.keyed_debounce(fn, delay) - return KeyedDebouncer.new(fn, delay) + ---@type table + local slots = {} + + local function call(key, ...) + local t = type(key) + assert( + t == "string" or t == "number" or t == "boolean", + "key must be a primitive (string, number, boolean)" + ) + local slot = slots[key] + if not slot then + local c, h = M.debounce(function(...) + fn(key, ...) + end, delay) + slot = { call = c, handle = h } + slots[key] = slot + end + slot.call(...) + end + + return call, + { + cancel = function(key) + local slot = slots[key] + if slot then + slot.handle.close() + slots[key] = nil + end + end, + flush = function(key) + local slot = slots[key] + if slot then + slot.handle.flush() + end + end, + pending = function(key) + local slot = slots[key] + return slot ~= nil and slot.handle.pending() + end, + close = function() + for _, slot in pairs(slots) do + slot.handle.close() + end + slots = {} + end, + } end function M.get_hl_source(name) diff --git a/plugins/blink.cmp.lua b/plugins/blink.cmp.lua index 2ed9c18..e8abe5f 100644 --- a/plugins/blink.cmp.lua +++ b/plugins/blink.cmp.lua @@ -1,3 +1,4 @@ +---@diagnostic disable: param-type-mismatch, missing-fields local blink = require("blink.cmp") blink.setup({ completion = { @@ -32,15 +33,15 @@ blink.setup({ return ctx.label end, highlight = function(ctx) - local highlights = { - { - 0, - #ctx.label, - group = ctx.deprecated - and "BlinkCmpLabelDeprecated" - or "BlinkCmpLabel", - }, - } + ---@type blink.cmp.DrawHighlight[] + local highlights = {} + table.insert(highlights, { + 0, + #ctx.label, + group = ctx.deprecated + and "BlinkCmpLabelDeprecated" + or "BlinkCmpLabel", + }) for _, idx in ipairs(ctx.label_matched_indices) do table.insert(highlights, { idx, diff --git a/plugins/comment.lua b/plugins/comment.lua index d90b2a9..3709f39 100644 --- a/plugins/comment.lua +++ b/plugins/comment.lua @@ -1,4 +1,4 @@ ----@diagnostic disable-next-line: missing-fields +---@diagnostic disable-next-line: missing-fields, param-type-mismatch require("Comment").setup({ --ignore empty lines ignore = "^$", diff --git a/plugins/mason-auto-install.lua b/plugins/mason-auto-install.lua index c3b49ec..e42dfa5 100644 --- a/plugins/mason-auto-install.lua +++ b/plugins/mason-auto-install.lua @@ -9,7 +9,10 @@ require("mason-auto-install").setup({ "cmake-language-server", dependencies = { "golines" }, }, - "emmylua_ls", + { + "emmylua_ls", + dependencies = { "stylua" }, + }, "gopls", "hyprls", {