Enable import autosort on save
steakhutzeee opened this issue · 14 comments
Hi,
i'm trying to configure ruff in Neovim. I'm using this config:
return {
-- https://github.com/VonHeikemen/lsp-zero.nvim
'VonHeikemen/lsp-zero.nvim',
branch = 'v3.x',
dependencies = {
{'williamboman/mason.nvim'},
{'williamboman/mason-lspconfig.nvim'},
{'neovim/nvim-lspconfig'},
{'hrsh7th/cmp-nvim-lsp'},
{'hrsh7th/nvim-cmp'},
{'L3MON4D3/LuaSnip'},
},
config = function()
local lsp_zero = require('lsp-zero')
local navic = require('nvim-navic')
lsp_zero.on_attach(function(client, bufnr)
-- see :help lsp-zero-keybindings
-- to learn the available actions
lsp_zero.default_keymaps({
buffer = bufnr,
preserve_mappings = false
})
-- For nvim-ufo
lsp_zero.set_server_config({
capabilities = {
textDocument = {
foldingRange = {
dynamicRegistration = false,
lineFoldingOnly = true
}
}
}
})
-- Enable navic
if client.server_capabilities.documentSymbolProvider then
navic.attach(client, bufnr)
end
-- Disable hover in favor of Pyright
if client.name == 'ruff_lsp' then
-- Disable hover in favor of Pyright
client.server_capabilities.hoverProvider = false
end
end)
-- Ruff command to organize imports on save
---@param name string
---@return lsp.Client
local function lsp_client(name)
return assert(
vim.lsp.get_active_clients({ bufnr = vim.api.nvim_get_current_buf(), name = name })[1],
('No %s client found for the current buffer'):format(name)
)
end
local servers = {
ruff_lsp = {
init_options = {
settings = {
-- ...
},
},
commands = {
RuffAutofix = {
function()
lsp_client('ruff_lsp').request("workspace/executeCommand", {
command = 'ruff.applyAutofix',
arguments = {
{ uri = vim.uri_from_bufnr(0) },
},
})
end,
description = 'Ruff: Fix all auto-fixable problems',
},
RuffOrganizeImports = {
function()
lsp_client('ruff_lsp').request("workspace/executeCommand", {
command = 'ruff.applyOrganizeImports',
arguments = {
{ uri = vim.uri_from_bufnr(0) },
},
})
end,
description = 'Ruff: Format imports',
},
},
},
}
-- don't add this function in the `on_attach` callback.
-- `format_on_save` should run only once, before the language servers are active.
lsp_zero.format_on_save({
format_opts = {
async = false,
timeout_ms = 10000,
},
servers = {
['ruff_lsp'] = {'python'},
}
})
lsp_zero.set_sign_icons({
error = '✘',
warn = '▲',
hint = '⚑',
info = '»'
})
-- cmp configuration
local cmp = require('cmp')
cmp.setup({
-- Preselect first item
preselect = 'item',
completion = {
completeopt = 'menu,menuone,noinsert'
},
window = {
completion = cmp.config.window.bordered(),
documentation = cmp.config.window.bordered(),
}
})
-- to learn how to use mason.nvim with lsp-zero
-- read this: https://github.com/VonHeikemen/lsp-zero.nvim/blob/v3.x/doc/md/guide/integrate-with-mason-nvim.md
require('mason').setup({})
require('mason-lspconfig').setup({
ensure_installed = {'ruff_lsp', 'pyright'},
handlers = {
lsp_zero.default_setup,
ruff_lsp = function()
require('lspconfig').ruff_lsp.setup({
on_attach = on_attach,
init_options = {
settings = {
-- CLI arguments
args = {
"pyproject.toml"
},
},
},
})
end,
pyright = function()
require('lspconfig').pyright.setup {
on_attach = on_attach,
settings = {
pyright = {
-- Using Ruff's import organizer
disableOrganizeImports = true,
},
python = {
analysis = {
-- Ignore all files for analysis to exclusively use Ruff for linting
ignore = { '*' },
},
},
},
}
end,
},
})
end,
}
so i'm formatting the code on save, but how to also sort the imports on save? I looked at other issues on Github but it's not clear how to do that.
Thanks!
I've described a solution in #295 to create a command which will organize the imports. This command can then be used through autocmd
to run on file save (BufWritePost
). Does this help?
I've described a solution in #295 to create a command which will organize the imports. This command can then be used through
autocmd
to run on file save (BufWritePost
). Does this help?
Thank you. I added the snippet to my configuration in the first post under -- Ruff command to organize imports on save. Is it placed correctly? I'm quite new to Neovim, do not know where/how to set the autocmd if the above is correct.
Instead of formatting on save I started using the following:
["<leader>lf"] = { "<cmd>lua vim.lsp.buf.format({async = true, filter = function(client) return client.name ~= 'typescript-tools' end})<cr>", "Format", },
to format the code. It works but is there a way to also organize the imports with the same key map?
There are two possible solutions to this:
- You can either configure an autocmd to run the
source.organizeImports.ruff
on save like #409 (comment) - Or, use a plugin like
conform.nvim
which allows you to define your own formatter command. So, you can define aruff_organize_imports
like (you might want to refer to the plugin docs for more info):
require('conform').setup {
formatters_by_ft = {
python = { 'ruff_organize_imports' },
},
formatters = {
ruff_organize_imports = {
command = 'ruff',
args = {
'check',
'--force-exclude',
'--select=I001',
'--fix',
'--exit-zero',
'--stdin-filename',
'$FILENAME',
'-',
},
stdin = true,
cwd = require('conform.util').root_file {
'pyproject.toml',
'ruff.toml',
'.ruff.toml',
},
},
},
}
I'll close this issue as it's not actionable from within ruff-lsp
.
I have a keybinding to format:
['<leader>lf'] = {"<cmd>lua vim.lsp.buf.format({async = true, filter = function(client) return client.name ~= 'typescript-tools' end})<cr>", 'Format'
and one for the Code Actions:
['<leader>la'] = { '<cmd>lua vim.lsp.buf.code_action()<cr>', 'Code Action' }
But I have to select manually to sort the imports or fix all.
Is there a direct command I can call to only sort the imports instead?
@steakhutzeee Can you try updating the function invoked by the <leader>la
keybinding with the appropriate option to signal Neovim to apply the code actions directly? This is suggested in #409 (comment), I'll put it here for reference:
vim.lsp.buf.code_action {
context = {
-- It's important to only ask for a single code action otherwise Neovim will open
-- up a UI prompt if there are more.
only = { 'source.fixAll.ruff' },
},
-- Inform Neovim to apply the code action edits
apply = true,
}
@steakhutzeee Can you try updating the function invoked by the
<leader>la
keybinding with the appropriate option to signal Neovim to apply the code actions directly? This is suggested in #409 (comment), I'll put it here for reference:vim.lsp.buf.code_action { context = { -- It's important to only ask for a single code action otherwise Neovim will open -- up a UI prompt if there are more. only = { 'source.fixAll.ruff' }, }, -- Inform Neovim to apply the code action edits apply = true, }
Thanks, I actually managed to apply the following to format and sort imports at the same time on save:
vim.api.nvim_create_autocmd({ "BufWritePost" }, {
pattern = { "*.py" },
callback = function()
vim.lsp.buf.code_action {
context = {
only = { 'source.organizeImports.ruff' },
},
apply = true,
}
vim.lsp.buf.format({async = true, filter = function(client) return client.name ~= 'typescript-tools' end})
vim.wait(100)
end,
})
Looks it's working. Do you see any issues with it?
I think it could create a race condition because both vim.lsp.buf.code_action
and vim.lsp.buf.format
(with async = true
) are asynchronous. So, they could, sometimes, write the content to the same file which could lead to undefined behavior. I'm not exactly sure how to solve this as I've been using conform.nvim
for my formatting needs.
I think it could create a race condition because both
vim.lsp.buf.code_action
andvim.lsp.buf.format
(withasync = true
) are asynchronous. So, they could, sometimes, write the content to the same file which could lead to undefined behavior. I'm not exactly sure how to solve this as I've been usingconform.nvim
for my formatting needs.
I tried your conform configuration but with a little edit because I was also using 'ruff_format'
.
formatters_by_ft = {
python = {'ruff_organize_imports', 'ruff_format'},
},
Well it was formatting fine but the imports were not sorted.
Looking at the Conform
logs i could see it could not find any ruff
command. So i wonder how it was formatting?
Anyway I had to also install ruff
with Mason
, so I actually have both ruff
and ruff-lsp
.
Is this correct? Should have both?
In case someone comes here from a search engine:
vim.api.nvim_create_autocmd("FileType", {
desc = "Comprehensively reformat Python with Ruff",
pattern = "python",
callback = function()
vim.keymap.set('n', 'gF', function()
vim.lsp.buf.code_action {
context = { only = { 'source.fixAll' }, diagnostics = {} },
apply = true,
}
vim.lsp.buf.format { async = true }
end, { desc = 'Format buffer' })
end
})
Lazy: SichangHe/.config@f60646e.
Could somebody tell me please then how can i format also imports on save?
return {
"nvimtools/none-ls.nvim",
dependencies = {
"nvimtools/none-ls-extras.nvim",
"nvim-lua/plenary.nvim",
},
config = function()
local null_ls = require("null-ls")
null_ls.setup({
debug = true,
sources = {
null_ls.builtins.diagnostics.mypy,
null_ls.builtins.formatting.stylua,
require("none-ls.diagnostics.ruff"),
require("none-ls.formatting.ruff"),
},
})
vim.keymap.set("n", "<leader>gf", vim.lsp.buf.format, {})
vim.cmd([[autocmd BufWritePre * lua vim.lsp.buf.format()]])
end,
}
what do add in here?
Could somebody tell me please then how can i format also imports on save?
return { "nvimtools/none-ls.nvim", dependencies = { "nvimtools/none-ls-extras.nvim", "nvim-lua/plenary.nvim", }, config = function() local null_ls = require("null-ls") null_ls.setup({ debug = true, sources = { null_ls.builtins.diagnostics.mypy, null_ls.builtins.formatting.stylua, require("none-ls.diagnostics.ruff"), require("none-ls.formatting.ruff"), }, }) vim.keymap.set("n", "<leader>gf", vim.lsp.buf.format, {}) vim.cmd([[autocmd BufWritePre * lua vim.lsp.buf.format()]]) end, }
what do add in here?
https://github.com/nvimtools/none-ls.nvim/wiki/Formatting-on-save
Lazy: SichangHe/.config@f60646e.
Would be possible to use this in Conform?