Skip to content

Instantly share code, notes, and snippets.

@alextes
Last active September 20, 2023 13:09
Show Gist options
  • Save alextes/e6704e5376709d194d21f615f8542ccb to your computer and use it in GitHub Desktop.
Save alextes/e6704e5376709d194d21f615f8542ccb to your computer and use it in GitHub Desktop.
FixCurrent script for code actions in neovim in Lua
return function()
local params = vim.lsp.util.make_range_params() -- get params for current position
params.context = {
diagnostics = vim.lsp.diagnostic.get_line_diagnostics(),
only = { "quickfix" },
}
local actions_per_client, err = vim.lsp.buf_request_sync(
0,
"textDocument/codeAction",
params,
900
)
if err then
return
end
if actions_per_client == nil or vim.tbl_isempty(actions_per_client) or #actions_per_client == 0 then
vim.notify("no quickfixes available")
return
end
-- Collect the available actions
local actions = {}
for cid, resp in pairs(actions_per_client) do
if resp.result ~= nil then
for _, result in pairs(resp.result) do
-- add the actions with a cid to the table
local action = {}
action["cid"] = cid
for k, v in pairs(result) do
action[k] = v
end
table.insert(actions, action)
end
end
end
-- Try to find a preferred action.
local preferred_action = nil
for _, action in ipairs(actions) do
if action.isPreferred then
preferred_action = action
break
end
end
-- If we failed to find a preferred action, try to find a non-null-ls action.
local non_null_ls_action = nil
for _, action in ipairs(actions) do
if action.command ~= "NULL_LS_CODE_ACTION" then
non_null_ls_action = action
break
end
end
-- If we failed to find a non-null-ls action, use the first one.
local first_action = nil
if #actions > 0 then
first_action = actions[1]
end
-- Using null-ls a lot of quickfixes are returned but all tend to be
-- worse than what the real LSP is offering, we try to use other
-- actions first, then only fall back to whatever null-ls is offering.
local top_action = preferred_action or non_null_ls_action or first_action
-- print(vim.inspect(top_action))
local picked_one = false
vim.lsp.buf.code_action({
context = {
only = { "quickfix" },
},
filter = function(action, ctx)
if picked_one then
return true
elseif top_action ~= nil and action.title == top_action.title then
picked_one = true
return false
else
return true
end
end,
apply = true,
})
end
local on_attach = function(client, bufnr)
local nmap = function(keys, func, desc)
if desc then
desc = "LSP: " .. desc
end
vim.keymap.set("n", keys, func, { buffer = bufnr, desc = desc })
end
nmap("gq", require("fixcurrent"), "[G]o [Q]uickfix")
end
local servers = {
gopls = {},
-- pyright = {},
rust_analyzer = {},
tsserver = {},
lua_ls = {
Lua = {
workspace = { checkThirdParty = false },
telemetry = { enable = false },
},
},
}
local capabilities = vim.lsp.protocol.make_client_capabilities()
-- Setup mason so it can manage external tooling
require("mason").setup()
-- Ensure the servers above are installed
local mason_lspconfig = require("mason-lspconfig")
mason_lspconfig.setup({
ensure_installed = vim.tbl_keys(servers),
})
mason_lspconfig.setup_handlers({
function(server_name)
require("lspconfig")[server_name].setup({
capabilities = capabilities,
on_attach = on_attach,
settings = servers[server_name],
})
end,
})
@duongdominhchau
Copy link

Thank you, this works for Python. Sadly, it doesn't work with jdtls :(

@alextes
Copy link
Author

alextes commented Sep 19, 2023

@duongdominhchau yea, Rust isn't handling every action either, only most. It is however quite easy to sprinkle some print(vim.inspect(...)) in there and see why it isn't picking the action you like. Feel free to check why it doesn't and suggest improvements if you like. Does jdtls report any actions?

@duongdominhchau
Copy link

Yes, it does. I removed the part about null-ls as I use jdtls with neovim-lsp directly, so it's just local top_action = actions[1]. The error I got is

E5108: Error executing lua: /usr/share/nvim/runtime/lua/vim/lsp/buf.lua:796: command: expected string, got nil

and a stack trace pointing to https://gist.github.com/alextes/e6704e5376709d194d21f615f8542ccb#file-init-lua-L10. Seems like a validate() call inside vim.lsp.buf.execute_command expects the argument passed in to be a table containing command key, but the action object here does not have that key.

{
  cid = 1,
  data = {
    pid = "0",
    rid = "0"
  },
  diagnostics = { {
      code = "570425394",
      data = { "Micronaut" },
      message = "Micronaut cannot be resolved",
      range = {
        ["end"] = {
          character = 17,
          line = 4
        },
        start = {
          character = 8,
          line = 4
        }
      },
      severity = 1,
      source = "Java"
    } },
  kind = "quickfix",
  title = "Import 'Micronaut' (io.micronaut.runtime)"
}

I don't know much about LSP or Neovim API, so I can't fix it for now. Just commented to show appreciation, as all the language servers I used advertised isPreferred support, but none have that key in the action :|

@alextes
Copy link
Author

alextes commented Sep 19, 2023

@duongdominhchau my bad! I didn't notice what you're looking at is very old!! I'll update the gist right now.

Thanks for the appreciation 💛

@duongdominhchau
Copy link

duongdominhchau commented Sep 20, 2023

Thank you for the update. I tried this new config but cannot autofix with Ruff, to make it works, I flipped the boolean value returned from filter and remove the only = "quickfix". It sounds reasonable to me, not sure why it needs to be removed 🤷

Writing it here in case someone else gets into the same issue as me.

Edit: Wonderful, the new solution works with both jdtls and ruff_lsp. Thanks again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment