Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

neovim: use ruff to format the current buffer #119

Closed
choucavalier opened this issue May 22, 2023 · 5 comments
Closed

neovim: use ruff to format the current buffer #119

choucavalier opened this issue May 22, 2023 · 5 comments
Labels
question Further information is requested vim Related to the Neo(Vim) editor

Comments

@choucavalier
Copy link

I've been searching for 1 hour how to use ruff to format the current buffer and haven't found any example of config that works.

This issue has been closed, even though :lua vim.lsp.buf.format() does not work as expected.

Could anyone share a config to make ruff format the current buffer? :) Thanks!

@choucavalier choucavalier changed the title Use ruff to format the code neovim: use ruff to format the code May 22, 2023
@choucavalier choucavalier changed the title neovim: use ruff to format the code neovim: use ruff to format the current buffer May 22, 2023
@pythops
Copy link

pythops commented May 29, 2023

You may need to use null-ls plugin.

Plug 'jose-elias-alvarez/null-ls.nvim'

Then add this configuration

lua << EOF
    local null_ls = require("null-ls")
    local augroup = vim.api.nvim_create_augroup("LspFormatting", {})

    null_ls.setup({
        sources = {
            -- formatting
            null_ls.builtins.formatting.ruff,

            -- diagnostics
            null_ls.builtins.diagnostics.ruff,

        },
         on_attach = function(client, bufnr)
                if client.supports_method("textDocument/formatting") then
                    vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
                    vim.api.nvim_create_autocmd("BufWritePre", {
                        group = augroup,
                        buffer = bufnr,
                        callback = function()
                            vim.lsp.buf.format({ bufnr = bufnr })
                        end,
                    })
                end
            end,
    })
EOF

@sbinnee
Copy link

sbinnee commented Jun 7, 2023

I have the same issue. It seems that currently only was to use ruff's import sorting is to call code action and select sorting import as described #61. It's too much for simple formatting. I've been relying on venerable isort for the job. It would be nice to see the progress so that I can get rid of isort in the end.

@charliermarsh charliermarsh added enhancement New feature or request question Further information is requested and removed enhancement New feature or request labels Jun 16, 2023
@dhruvmanila
Copy link
Member

Thanks for opening this issue! Let me give a bit of background into these different LSP capabilities. textDocument/formatting request (which is what is sent by vim.lsp.buf.formatting() is sent to the server which supports the formatting capabilities and it formats the entire document. On the other hand, textDocument/codeAction is used for code fixes to either fix problems (diagnostics) or to refactor code.

With that difference in mind, let's look at what Ruff provides. Ruff is first and foremost a linter (formatter is in progress) which provides diagnostics and a way to auto-fix most of it. The way we provide the auto-fix capability using the Language Server Protocol (LSP) is to use code actions. Why code actions and not formatting request? Because code actions allows us to provide the capability to fix/refactor only certain part of the document while formatting request acts on the entire document.

In the future, when the formatter is complete, we will provide the formatting capability which will run the formatter on the whole document.

Now, let's look at how to solve the problems which is being mentioned in this issue. First, let's look at the capabilities the ruff-lsp server provides using the command:

:lua vim.print(vim.lsp.get_active_clients({name = 'ruff_lsp'})[1].server_capabilities)

{
  codeActionProvider = {
    codeActionKinds = { "quickfix", "source.fixAll", "source.organizeImports", "source.fixAll.ruff", "source.organizeImports.ruff" },
    resolveProvider = true
  },
  executeCommandProvider = {
    commands = { "ruff.applyAutofix", "ruff.applyOrganizeImports" }
  },
  hoverProvider = false,
  textDocumentSync = {
    change = 2,
    openClose = true,
    save = true,
    willSave = false,
    willSaveWaitUntil = false
  },
  workspace = {
    fileOperations = vim.empty_dict(),
    workspaceFolders = {
      changeNotifications = true,
      supported = true
    }
  }
}

How to apply all of the auto-fixes available in the current buffer?

vim.lsp.buf.code_action {
  context = {
	only = { 'source.fixAll.ruff' }
  },
  apply = true,
}

Note that this will only apply for the rules configured in your configuration file. The isort rule I aren't enabled by default so they won't be applied here. You'll have to enable them if you wish to organize the imports as well.

How to organize the imports only (aka run the import sorter)?

vim.lsp.buf.code_action {
  context = {
	only = { 'source.organizeImports.ruff' }
  },
  apply = true,
}

As mentioned by @pythops, you can use null-ls which provides the exact capabilities using a simpler interface and using the ruff binary:

Tips

If you notice in the server capabilities, it provides some commands which can be executed for the current document. This can be used to define native Ex-commands in Neovim when configuring using nvim-lspconfig:

require('lspconfig').ruff_lsp.setup {
  init_options = {
    settings = {
      -- ...
    },
  },
  commands = {
    RuffAutofix = {
      function()
        vim.lsp.buf.execute_command {
          command = 'ruff.applyAutofix',
          arguments = {
            { uri = vim.uri_from_bufnr(0) },
          },
        }
      end,
      description = 'Ruff: Fix all auto-fixable problems',
    },
    RuffOrganizeImports = {
      function()
        vim.lsp.buf.execute_command {
          command = 'ruff.applyOrganizeImports',
          arguments = {
            { uri = vim.uri_from_bufnr(0) },
          },
        }
      end,
      description = 'Ruff: Format imports',
    },
  },
}

Whichever method you use, defining commands or using code actions, you can bind it to a key and/or run it automatically on save using autocmds (BufWritePost).

References:

@choucavalier
Copy link
Author

@dhruvmanila thanks a lot for taking the time to write this detailed answer! much clearer now

zanieb added a commit that referenced this issue Aug 17, 2023
We have previously explained this in:

-
#214 (comment)
-
#209 (comment)
-
#57 (comment)
-
#119 (comment)

We should cover this in the README to avoid mismatched expectations.
@chrisgrieser
Copy link

chrisgrieser commented Aug 30, 2023

@dhruvmanila Thanks for the detailed solution, I just came here exactly for that.


Just a suggestion:

ruff is first and foremost a linter (formatter is in progress) which provides diagnostics and a way to auto-fix most of it. The way we provide the auto-fix capability using the Language Server Protocol (LSP) is to use code actions.

Considering that using vim.buf.lsp.format() for has been a repeated request (this issue, #61, #95, …), it really might make sense to simply use textDocument/formatting for the FixAll feature. While it may technically be true that ruff is no formatter (yet), in practice, there seems to be at least a sizable share of users who already regard rust as a formatter, and expect ruff to work with vim.buf.lsp.format().

Having to scour closed issues in a repo instead of having something work out of the box unnecessarily diminishes the user experience of this otherwise fantastic tool.

@dhruvmanila dhruvmanila added the vim Related to the Neo(Vim) editor label Mar 25, 2024
azurelotus0926 added a commit to azurelotus0926/ruff-lsp that referenced this issue Jun 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested vim Related to the Neo(Vim) editor
Projects
None yet
Development

No branches or pull requests

6 participants