-- The MIT License (MIT) -- -- Copyright © 2026 Scott E. Graves -- -- Permission is hereby granted, free of charge, to any person obtaining a copy of this software -- and associated documentation files (the “Software”), to deal in the Software without restriction, -- including without limitation the rights to use, copy, modify, merge, publish, distribute, -- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -- furnished to do so, subject to the following conditions: -- -- The above copyright notice and this permission notice shall be included in all copies or -- substantial portions of the Software. -- -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING -- BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -- local M = {} local namespace = vim.api.nvim_create_namespace("DarculaLightbulb") local requests = {} local revisions = {} local config = { priority = 10, sign = { enabled = true, text = "💡", hl = "DiagnosticSignInfo" }, virtual_text = { enabled = false, text = "💡", hl = "DiagnosticVirtualTextInfo" } } local function cancel_request(bufnr) local cancel = requests[bufnr] requests[bufnr] = nil if cancel then pcall(cancel) end end local function clear_indicator(bufnr) if vim.api.nvim_buf_is_valid(bufnr) then vim.api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) end end local function render(bufnr, line) clear_indicator(bufnr) if not vim.api.nvim_buf_is_valid(bufnr) then return end line = math.max(0, math.min(line, vim.api.nvim_buf_line_count(bufnr) - 1)) vim.api.nvim_buf_set_extmark( bufnr, namespace, line, 0, { strict = false, priority = config.priority, sign_text = config.sign.enabled and config.sign.text or nil, sign_hl_group = config.sign.enabled and config.sign.hl or nil, virt_text = config.virtual_text.enabled and {{config.virtual_text.text, config.virtual_text.hl}} or nil, virt_text_pos = config.virtual_text.enabled and "eol" or nil, hl_mode = config.virtual_text.enabled and "combine" or nil } ) end function M.clear(bufnr) bufnr = bufnr or vim.api.nvim_get_current_buf() revisions[bufnr] = (revisions[bufnr] or 0) + 1 cancel_request(bufnr) clear_indicator(bufnr) end function M.update() local bufnr = vim.api.nvim_get_current_buf() local win = vim.api.nvim_get_current_win() local cursor = vim.api.nvim_win_get_cursor(win) local line = cursor[1] - 1 local revision = (revisions[bufnr] or 0) + 1 revisions[bufnr] = revision cancel_request(bufnr) local clients = vim.lsp.get_clients( {bufnr = bufnr, method = "textDocument/codeAction"} ) if #clients == 0 then clear_indicator(bufnr) return end local diagnostics = vim.lsp.diagnostic.from( vim.diagnostic.get(bufnr, {lnum = line}) ) requests[bufnr] = vim.lsp.buf_request_all( bufnr, "textDocument/codeAction", function(client) local params = vim.lsp.util.make_range_params(win, client.offset_encoding) params.context = {diagnostics = diagnostics} return params end, function(responses) if revisions[bufnr] ~= revision or not vim.api.nvim_buf_is_valid(bufnr) then return end requests[bufnr] = nil local has_actions = false for _, response in pairs(responses) do if response.result ~= nil and not vim.tbl_isempty(response.result) then has_actions = true break end end if has_actions then render(bufnr, line) else clear_indicator(bufnr) end end ) end function M.setup(opts) config = vim.tbl_deep_extend("force", config, opts or {}) local group = vim.api.nvim_create_augroup("DarculaLightbulb", {clear = true}) vim.api.nvim_create_autocmd( {"CursorHold", "CursorHoldI"}, { callback = M.update, group = group } ) vim.api.nvim_create_autocmd( "WinLeave", { callback = function(args) M.clear(args.buf) end, group = group } ) vim.api.nvim_create_autocmd( "BufWipeout", { callback = function(args) cancel_request(args.buf) requests[args.buf] = nil revisions[args.buf] = nil end, group = group } ) end return M