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 + Mason + ruff_lsp, trying to pass in a pyproject.toml #177

Open
Xifong opened this issue Jul 4, 2023 · 20 comments
Open

Neovim + Mason + ruff_lsp, trying to pass in a pyproject.toml #177

Xifong opened this issue Jul 4, 2023 · 20 comments
Labels
question Further information is requested vim Related to the Neo(Vim) editor

Comments

@Xifong
Copy link

Xifong commented Jul 4, 2023

Hi, I'm using neovim and getting ruff-lsp from the Mason registry.

I'm trying to pass a pyproject.toml to ruff as suggested in the readme, but find that the line length setting is not applied (still seeing warning about 88 characters):

Lsp config:

          require("lspconfig").ruff_lsp.setup({
            init_options = {
              settings = {
                -- Any extra CLI arguments for `ruff` go here.
                args = {
                  config = { "~/temp/pyproject.toml" },
                },
              },
            },
          })

pyproject.toml:

[tool.ruff]
line-length = 100

ruff version: ruff 0.0.269 (I installed ruff using snap install for testing, but neovim will acutally be using whatever ruff-lsp/ruff is coming from the Mason registry)
ruff-lsp version: 0.0.35

I've tested:
ruff check . --config=/home/xifong/temp/pyproject.toml, which works as expected, with only lines above 100 being flagged.
Not sure if it's relevant but:
ruff check . --config=~/temp/pyproject.toml is looking in the wrong path, maybe just due to the snap install.
error: Failed to parse /home/xifong/snap/ruff/190/temp/pyproject.toml: No such file or directory (os error 2)

Does this setup seem like it should be working?

@Xifong
Copy link
Author

Xifong commented Jul 4, 2023

Update
I've tried

require(lspconfig).ruff_lsp.setup{
  init_options = {
    settings = {
      -- Any extra CLI arguments for `ruff` go here.
      args = { '--line-length=100' },
    }
  }
}

but I'm still seeing "Line too long (96 > 88 characters)"

@charliermarsh
Copy link
Member

@dhruvmanila -- Any chance you could take a look?

@dhruvmanila
Copy link
Member

Hey, sure. So, I'm assuming you're trying to set a default setting across all projects.

As per the PR description, you would want to pass the --config=~/temp/pyproject.toml CLI flag as follows:

          require("lspconfig").ruff_lsp.setup({
            init_options = {
              settings = {
                -- Any extra CLI arguments for `ruff` go here.
                args = {
                  "--config=~/temp/pyproject.toml",
                },
              },
            },
          })

Update I've tried

require(lspconfig).ruff_lsp.setup{
  init_options = {
    settings = {
      -- Any extra CLI arguments for `ruff` go here.
      args = { '--line-length=100' },
    }
  }
}

but I'm still seeing "Line too long (96 > 88 characters)"

Currently, we don't really have a way to update every setting from the command-line so this won't work as there's no --line-length CLI flag for the check command.

@Xifong
Copy link
Author

Xifong commented Jul 4, 2023

Hi, thanks for your replies.

You're right that I'm trying to set a default across all projects @dhruvmanila, but I've just tried with "--config=~/temp/pyproject.toml" and it hasn't worked. Is there any logging that I can look at to see what actually got passed to ruff?

@dhruvmanila
Copy link
Member

Can you try by expanding the tilde (~)? I think that might be creating some problem:

          require("lspconfig").ruff_lsp.setup({
            init_options = {
              settings = {
                -- Any extra CLI arguments for `ruff` go here.
                args = {
                  "--config=" .. vim.loop.os_homedir() .. "/temp/pyproject.toml",
                },
              },
            },
          })

@Xifong
Copy link
Author

Xifong commented Jul 4, 2023

Hmmmm, also no luck with that. Still seeing 93 > 88 lines

@charliermarsh
Copy link
Member

Is it possible that your Neovim configuration isn’t being picked up at all? What happens if you add a bogus argument to the args list?

@Xifong
Copy link
Author

Xifong commented Jul 4, 2023

It's possible, adding a bogus argument seems to have no effect. Other config is being picked up though (for example, pylsp). I'm using LazyVim, but I've bypassed the configuration framework code (to rule that out as the issue) by just placing the ruff_lsp setup into it:

        local function setup(server)
          local server_opts = vim.tbl_deep_extend("force", {
            capabilities = vim.deepcopy(capabilities),
          }, servers[server] or {})

          if opts.setup[server] then
            if opts.setup[server](server, server_opts) then
              return
            end
          elseif opts.setup["*"] then
            if opts.setup["*"](server, server_opts) then
              return
            end
          end
          require("lspconfig")[server].setup(server_opts)
          require("lspconfig").ruff_lsp.setup({
            init_options = {
              settings = {
                -- Any extra CLI arguments for `ruff` go here.
                args = {
                  "--config=" .. vim.loop.os_homedir() .. "/temp/pyproject.toml",
                },
              },
            },
          })
        end

@dhruvmanila
Copy link
Member

Is your config available on GitHub? I could possible take a quick look at it. If not then can you provide some additional context about your setup like the LSP setup, directory structure (to determine if the config is being picked up or not).

@dhruvmanila dhruvmanila added the question Further information is requested label Jul 8, 2023
@ShellCode33
Copy link

In order to have a default configuration for all your projects, you can use ~/.config/ruff/pyproject.toml. No need for additional configuration of the LSP.

In the Ruff documentation we can read:

If no pyproject.toml file is found in the filesystem hierarchy, Ruff will fall back to using a default configuration. If a user-specific configuration file exists at ${config_dir}/ruff/pyproject.toml, that file will be used instead of the default configuration, with ${config_dir} being determined via the dirs crate, and all relative paths being again resolved relative to the current working directory.

Here's dirs documentation.

And here's my ~/.config/ruff/pyproject.toml:

[tool.ruff]
select = ["E", "F", "B", "W", "Q"]
fix = true
line-length = 88

@mschreil
Copy link

Just a little add on since it took me a while to figure it out.
If you want to specify the line length or other CLI arguments for the linter and the formatter, you can set it by adding args separately for lint and format

lspconfig.ruff_lsp.setup {
  on_attach = on_attach,
  capabilities = capabilities,
  filetypes = { "python" },
  init_options = {
    settings = {
      lint = { args = { "--line-length=99" } },
      format = { args = { "--line-length=99" } },
    },
  },
}

@dhruvmanila
Copy link
Member

Just a little add on since it took me a while to figure it out.

Do you believe there's room for improvement in helping users understand this more quickly? Perhaps through updating the documentation or other means?

@mschreil
Copy link

Do you believe there's room for improvement in helping users understand this more quickly? Perhaps through updating the documentation or other means?

An example config, where the linter and formatter settings are configured, would have helped.

-- Configure `ruff-lsp`.
-- See: https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#ruff_lsp
-- For the default config, along with instructions on how to customize the settings
require('lspconfig').ruff_lsp.setup {
  on_attach = on_attach,
  init_options = {
    settings = {
      -- Any extra CLI arguments for `ruff` go here.
      args = {},
      -- Any Linter args go here .
      lint = { args = { ... }}
      -- ANY  Format args go here.
      fromat = {args = { ... }}
    }
  }
}

Still not sure what values go into settings.args = {}. Can you bring some light into that?

@eddiebergman
Copy link

eddiebergman commented Feb 3, 2024

Hi,

Just chiming in since I also tended to have issues using Mason and it may help others who stumble across here.

In my case, I needed to use different version of ruff and linters depending on the project and so I needed the ruff_lsp installed in my virtual env to take precedence over Mason's one.

The issue was that Mason by default will "prepend" to your sys path, meaning the version it installs will take precedence. If you'd like the ruff_lsp you've installed into your local environment to take precedence, you will have to change Mason to "append" to PATH

Sorry maybe this information was best put in issue #71

@naim
Copy link

naim commented Mar 19, 2024

I found my way to this issue after similarly struggling to get settings passed into ruff, and I'm at the point where I've hit a wall that seems quite weird to me.

I'm using neovim, mason, mason-lspconfig, and ruff-lsp, and I'm trying to set the format.quote-style setting to "single" instead of the default "double". My ruff-lsp setup looks like this:

lspconfig.ruff_lsp.setup {
  on_attach = on_attach,
  init_options = {
    settings = {
      args = {'--config=' .. vim.loop.os_homedir() .. '/.config/ruff/ruff.toml'},
    },
  },
}

The config file is extremely simple and just has this in it:

[format]
quote-style = "single"

It is definitely reading my ruff.toml, because if I try changing that option to something that's an invalid setting it get this error:

LSP[ruff_lsp] Ruff: Lint failed (ruff failed
  Cause: Failed to parse /Users/naim/.config/ruff/ruff.toml
  Cause: TOML parse error at line 4, column 1
  |
4 | quote-style-xxx = "single"
  | ^^^^^^^^^^^^^^^
unknown field `quote-style-xxx`, expected one of `exclude`, `preview`, `indent-style`, `quote-style`, `skip-magic-trai
ling-comma`, `line-ending`, `docstring-code-format`, `docstring-code-line-length`

Also, I figured out how to get ruff-lsp to spit out some debug logs — here's a relevant line from it showing the path to the config is being provided correctly:

INFO:pygls.protocol.json_rpc:Sending data: {"params": {"type": 4, "message": "Running Ruff with: /Users/naim/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff ['check', '--force-exclude', '--no-cache', '--no-fix', '--quiet', '--output-format', 'json', '-', '--config=/Users/naim/.config/ruff/ruff.toml', '--stdin-filename', '/Users/naim/src/test.py']"}, "method": "window/logMessage", "jsonrpc": "2.0"}

So it's being passed in, but none of the settings are being respected.

I also tried the approach of passing it in directly in the ruff-lsp config, which I had a bit more success with, but still didn't fix my issue. Here was the config I tried for that:

lspconfig.ruff_lsp.setup {
  on_attach = on_attach,
  init_options = {
    settings = {
      args = { '--config=format.quote-style="single"' },
      format = { args = { '--line-length=40' } },    -- length set absurdly low so I can see if the formatter is working
    },
  },
}

With that config it does set the line lengths to 40, but quote-style isn't respected, even though it's definitely being passed correctly:

DEBUG:pygls.protocol.json_rpc:Sending notification: 'window/logMessage' LogMessageParams(type=<MessageType.Log: 4>, message='Running Ruff with: /Users/naim/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff [\'check\', \'--force-exclude\', \'--no-cache\', \'--no-fix\', \'--quiet\', \'--output-format\', \'json\', \'-\', \'--config=format.quote-style="single"\', \'--stdin-filename\', \'/Users/naim/src/test.py\']')

If there's any guidance on how I can get ruff to do what I'm expecting, I'd appreciate it.

@MichaReiser
Copy link
Member

@naim I'm not familiar with neovim, but it seems that the editor uses the check command.

DEBUG:pygls.protocol.json_rpc:Sending notification: 'window/logMessage' LogMessageParams(type=<MessageType.Log: 4>, message='Running Ruff with: /Users/naim/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff [\'check\', \'--force-exclude\', \'--no-cache\', \'--no-fix\', \'--quiet\', \'--output-format\', \'json\', \'-\', \'--config=format.quote-style="single"\', \'--stdin-filename\', \'/Users/naim/src/test.py\']')

However, formatting only runs if you run ruff format (and not ruff check)). Any chance the quote-style is respected when running ruff format?

If not, have you tried running the commands from the CLI? E.g. I created a ./.config.ruff.toml with

[format]
quote-style="single"

and ran ruff format --config ./.config/ruff.toml and the quote style is correctly applied.

@dhruvmanila
Copy link
Member

If there's any guidance on how I can get ruff to do what I'm expecting, I'd appreciate it.

@naim Can you inform us on what you're expecting from ruff-lsp? The config option you've chosen is only for the formatter and not the linter. Can you tell us how you've configured Neovim on running the Ruff formatter? Are you using any external plugins like conform.nvim? Have you configured any autocmd to run the vim.lsp.buf.format() on save?

@naim
Copy link

naim commented Mar 25, 2024

Ah, I think I see the issue. I wasn't paying close enough attention and grabbed a log message that was for the check command, not format. My specified args in the config are not being passed in when the format command is being checked.

Edit: And yes, I have configured a command to run format:

    vim.keymap.set('n', '<space>x', function()
      vim.lsp.buf.format { async = true }
    end, opts)

Here's the log message when I run that command:

DEBUG:pygls.protocol.json_rpc:Sending notification: 'window/logMessage' LogMessageParams(type=<MessageType.Log: 4>, message="Running Ruff with: /Users/naim/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff ['format', '--force-exclude', '--quiet', '--stdin-filename', '/Users/naim/src/test.py']")

Note that --config isn't being passed in.

Given that my ruff-lsp config looks like this:

lspconfig.ruff_lsp.setup {
  on_attach = on_attach,
  init_options = {
    settings = {
      args = {'--config=' .. vim.loop.os_homedir() .. '/.config/ruff/ruff.toml'},
    },
  },
}

and the documentation in this repo says

require('lspconfig').ruff_lsp.setup {
  init_options = {
    settings = {
      -- Any extra CLI arguments for `ruff` go here.
      args = {},
    }
  }
}

I assumed that my CLI arguments specified there would be passed to any ruff command, not just the linter. What should the settings look like to pass in the --config flag to the format command as well?

@dhruvmanila
Copy link
Member

dhruvmanila commented Mar 25, 2024

That would be format.args. For example,

settings = {
	format = {
		args = { '--line-length', '120' }
	}
}

@naim can you check if that works for you? I can see it being passed in:

[INFO][2024-03-26 00:19:09] ...lsp/handlers.lua:584	"Running Ruff with: /Users/dhruv/.local/bin/ruff ['format', '--force-exclude', '--quiet', '--stdin-filename', '/Users/dhruv/playground/ruff/parser/_.py', '--line-length', '120']"

@dhruvmanila dhruvmanila added the vim Related to the Neo(Vim) editor label Mar 25, 2024
@pygaiwan
Copy link

pygaiwan commented Jun 9, 2024

Seen this thread open while configuring my neovim.

I solved it like in the code below. It searches for ruff.toml in the the current working directory, if it doesn't find it, it will rely on the one in my home directory under .config/ruff/ruff.toml. My ruff.toml follows the latest best practices from ruff, and implements both lint and formatting.

Lines 6 to 12 do the checks for the toml and set the config path based on the presence of a project local ruff.toml, else points back to the default path.
Lines 13 till the end configures the LSP itself.

Seems to be working fine so far.

The full config is here: https://github.com/pygaiwan/neovim

require('mason-lspconfig').setup({
    ensure_installed = { 'lua_ls', 'ruff', 'pylsp' },
    handlers = {
        function(server_name)
            if server_name == 'ruff' then
                local ruff_config_path = vim.loop.os_homedir() .. '/.config/ruff/ruff.toml'
                local project_ruff_config = vim.loop.cwd() .. '/ruff.toml'
                local f = io.open(project_ruff_config, 'r')
                if f ~= nil then 
                    io.close(f)
                    ruff_config_path = project_ruff_config
                end
                require('lspconfig').ruff_lsp.setup({
                    init_options = {
                        settings = {
                            format = {
                                args = { "--config=" .. ruff_config_path }
                            },
                            lint = {
                                args = { "--config=" .. ruff_config_path }
                            }
                        }
                    }
                })

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

9 participants