local util = require "navigator.util" local log = util.log local trace = util.trace local code_action = {} local gui = require "navigator.gui" local config = require("navigator").config_values() local api = vim.api function code_action.code_action_handler(err, _, actions, cid, bufnr, _, customSelectionHandler) log(cid, bufnr) if actions == nil or vim.tbl_isempty(actions) then print("No code actions available") return end local data = {"  Auto Fix Apply Exit"} for i, action in ipairs(actions) do local title = action.title:gsub("\r\n", "\\r\\n") title = title:gsub("\n", "\\n") title = string.format("[%d] %s", i, title) table.insert(data, title) end local width = 0 for _, str in ipairs(data) do if #str > width then width = #str end end local function apply_action(idx) local action_chosen = actions[idx - 1] local switch = string.format("silent b %d", bufnr) if action_chosen.edit or type(action_chosen.command) == "table" then if action_chosen.edit then vim.lsp.util.apply_workspace_edit(action_chosen.edit) end if type(action_chosen.command) == "table" then -- switch buff vim.cmd(switch) vim.lsp.buf.execute_command(action_chosen.command) end else vim.cmd(switch) vim.lsp.buf.execute_command(action_chosen) end trace(action_chosen) end gui.new_list_view { items = data, width = width + 4, loc = "top_center", relative = "cursor", rawdata = true, data = data, on_confirm = function(pos) if pos < 2 then pos = 2 end apply_action(pos) end, on_move = function(pos) if pos < 2 then pos = 2 end local l = data[pos] return l end } end -- https://github.com/glepnir/lspsaga.nvim/blob/main/lua/lspsaga/codeaction.lua -- lspsaga has a clever design to inject code action indicator local sign_group = "nvcodeaction" local get_namespace = function() return api.nvim_create_namespace(sign_group) end local get_current_winid = function() return api.nvim_get_current_win() end local sign_name = "NavigatorLightBulb" if vim.tbl_isempty(vim.fn.sign_getdefined(sign_name)) then vim.fn.sign_define(sign_name, {text = config.code_action_icon, texthl = "LspDiagnosticsSignHint"}) end local function _update_virtual_text(line) local namespace = get_namespace() pcall(api.nvim_buf_clear_namespace, 0, namespace, 0, -1) if line then local icon_with_indent = " " .. config.code_action_icon pcall( api.nvim_buf_set_extmark, 0, namespace, line, -1, { virt_text = {{icon_with_indent, "LspDiagnosticsSignHint"}}, virt_text_pos = "overlay", hl_mode = "combine" } ) end end local function _update_sign(line) local winid = get_current_winid() if code_action[winid] == nil then code_action[winid] = {} end if code_action[winid].lightbulb_line ~= 0 then vim.fn.sign_unplace(sign_group, {id = code_action[winid].lightbulb_line, buffer = "%"}) end if line then --log("updatasign", line, sign_group, sign_name) vim.fn.sign_place( line, sign_group, sign_name, "%", {lnum = line + 1, priority = config.code_action_prompt.sign_priority} ) code_action[winid].lightbulb_line = line end end local need_check_diagnostic = { ["go"] = true, ["python"] = true } function code_action:render_action_virtual_text(line, diagnostics) return function(_, _, actions) if actions == nil or type(actions) ~= "table" or vim.tbl_isempty(actions) then if config.code_action_prompt.virtual_text then _update_virtual_text(nil) end if config.code_action_prompt.sign then _update_sign(nil) end else if config.code_action_prompt.sign then if need_check_diagnostic[vim.bo.filetype] then if next(diagnostics) == nil then _update_sign(nil) else _update_sign(line) end else _update_sign(line) end end if config.code_action_prompt.virtual_text then if need_check_diagnostic[vim.bo.filetype] then if next(diagnostics) == nil then _update_virtual_text(nil) else _update_virtual_text(line) end else _update_virtual_text(line) end end end end end local special_buffers = { ["LspSagaCodecode_action"] = true, ["lspsagafinder"] = true, ["NvimTree"] = true, ["vista"] = true, ["guihua"] = true, ["lspinfo"] = true, ["markdown"] = true, ["text"] = true } -- local action_call_back = function (_,_) -- return Action:action_callback() -- end local action_vritual_call_back = function(line, diagnostics) return code_action:render_action_virtual_text(line, diagnostics) end local code_action_req = function(_call_back_fn, diagnostics) local context = {diagnostics = diagnostics} local params = vim.lsp.util.make_range_params() params.context = context local line = params.range.start.line local callback = _call_back_fn(line, diagnostics) vim.lsp.buf_request(0, "textDocument/codeAction", params, callback) end -- code_action.code_action = function() -- local diagnostics = vim.lsp.diagnostic.get_line_diagnostics() -- code_action_req(action_call_back, diagnostics) -- end code_action.code_action_prompt = function() if special_buffers[vim.bo.filetype] then return end local diagnostics = vim.lsp.diagnostic.get_line_diagnostics() local winid = get_current_winid() code_action[winid] = code_action[winid] or {} code_action[winid].lightbulb_line = code_action[winid].lightbulb_line or 0 code_action_req(action_vritual_call_back, diagnostics) end return code_action