-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add support for using uv as an alternative formatter backend #19665
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
Conversation
|
crates/ruff_server/src/format.rs
Outdated
| // TODO(zanieb): We should consider returning this case to the caller somehow | ||
| if !output.status.success() { | ||
| let stderr = String::from_utf8_lossy(&output.stderr); | ||
| tracing::warn!("uvx ruff format failed: {}", stderr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probalby show a message to the user if that happens see Client
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we differentiate between failures due to syntax errors in an easy way?
Will look at the Client for sending the message. Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See d23af97
My understanding is this will propagate upstream as the format_internal errors do. We could show a message directly to the user with more context, but I think we can defer that for now?
As a frontend to Ruff's formatter. There are some interesting choices here, some of which may just be temporary: 1. We pin a default version of Ruff, so `uv format` is stable for a given uv version 2. We install Ruff from GitHub instead of PyPI, which means we don't need a Python interpreter or environment 3. We do not read the Ruff version from the dependency tree See astral-sh/ruff#19665 for a prototype of the LSP integration.
|
@dhruvmanila would you mind helping me ship this? I'm not sure what all needs to happen :) |
Sure! I'll look into this in a bit |
|
I'll look into the test failures. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
I read the corresponding Notion document for this in which you've mentioned:
Micha and I talked quite a bit offline about the IDE experience, and there are a couple possible approaches that we can take to alleviate those problems.
Is there any mention of other approaches apart from the one that's implemented in this PR? Nevermind, I see it in the PR description.
There are couple of follow-ups that would need to be done:
- Updating the documentation to include the new option, this needs to be done manually for editor settings
- Updating the VS Code extension to expose this option in VS Code settings and pass it to the server
Can you confirm whether this was tested in the editor? If so, which editor did you test this with, I can try it out in some other editor.
crates/ruff_server/src/format.rs
Outdated
| let line_width = FormatOptions::line_width(&self.options).value().to_string(); | ||
| let indent_width = FormatOptions::indent_width(&self.options) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this use self.options.line_width() and self.options.indent_width() instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, thanks!
| command.arg("--config"); | ||
| command.arg(format!( | ||
| "format.quote-style = '{}'", | ||
| self.options.quote_style().as_str() | ||
| )); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit surprised that this works, is this equivalent to --config format.quote-style = 'single' (notice there are no quotes to the --config value):
$ ruff format --config format.quote-style = 'single' --diff isolated.py
error: invalid value 'format.quote-style' for '--config <CONFIG_OPTION>'
tip: A `--config` flag must either be a path to a `.toml` configuration file
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
option
For more information, try '--help'.I think the entire value would need to be quoted, so the following should work:
$ ruff format --config "format.quote-style = 'single'" --diff isolated.pySame goes for other --config flags.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When using Command::arg the outer quotes (") are implicit. Those outer quotes are being parsed by your shell into a single argument. I think providing them here would actually break things, as they'd be included in the value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, thanks for clarifying
| ) -> Command { | ||
| // TODO(zanieb): We should probably check the uv version to make sure it supports | ||
| // `uv format` or add a special error message when the subcommand doesn't exist | ||
| let mut command = Command::new("uv"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will always use the uv command that comes the earliest on PATH as seen from within the editor. Do you see any issues that could occur when doing so?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It could be the wrong version.
I don't think I expect multiple uv versions to be available on people's machines — and when it happens it's usually an accident. We can provide a way to set the uv path in the future though. We'll want it for other things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that makes sense
crates/ruff_server/src/format.rs
Outdated
| // TODO(zanieb): We should probably check the uv version to make sure it supports | ||
| // `uv format` or add a special error message when the subcommand doesn't exist |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we'd also need to provide a better error message when the uv command itself isn't available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What were you thinking for that case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like, what the error message would be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but I just made a small improvement here for now.
| fn format_range_external( | ||
| document: &TextDocument, | ||
| source_type: PySourceType, | ||
| formatter_settings: &FormatterSettings, | ||
| range: TextRange, | ||
| path: &Path, | ||
| ) -> crate::Result<Option<PrintedRange>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning Result type here would mean that any errors resulted would be counted as internal server error and will be shown to the user. I think this is fine for now, but we might want to categorize a subset of them with better error message for example the TODO comment to check whether the subcommand is available or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, thanks for clarifying!
| struct FormatOptions { | ||
| preview: Option<bool>, | ||
| backend: Option<FormatBackend>, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Including it directly in the format table might indicate that this is a stable option, I think there are two ways to communicate the experimental nature of this feature:
- Make it explicit in the documentation
- Include it under
experimental.format.backendsimilar to ty'sexperimentalnamespace (https://docs.astral.sh/ty/reference/editor-settings/#experimental)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have strong feelings. (2) seems nice but is weird to transition off of?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this more, I think the current structure is fine (including it in the existing format namespace).
Does this mean something like Yeah, I think I'd be a bit hesitant to make the change as described here mainly because we only control the client side implementation for VS Code while for other editors we'd need to explicitly specify on how to start the server and what does it mean to start it from |
Yeah, basically.
Yeah, this was the same concern Micha and I had. |
|
The test failures on Windows are due to concurrent-installs being unsafe in uv. I'll fix that upstream. |
This PR adds the `ruff.format.backend` setting corresponding to astral-sh/ruff#19665 in Ruff.
…sh#19665) This adds a new `backend: internal | uv` option to the LSP `FormatOptions` allowing users to perform document and range formatting operations though uv. The idea here is to prototype a solution for users to transition to a `uv format` command without encountering version mismatches (and consequently, formatting differences) between the LSP's version of `ruff` and uv's version of `ruff`. The primarily alternative to this would be to use uv to discover the `ruff` version used to start the LSP in the first place. However, this would increase the scope of a minimal `uv format` command beyond "run a formatter", and raise larger questions about how uv should be used to coordinate toolchain discovery. I think those are good things to explore, but I'm hesitant to let them block a `uv format` implementation. Another downside of using uv to discover `ruff`, is that it needs to be implemented _outside_ the LSP; e.g., we'd need to change the instructions on how to run the LSP and implement it in each editor integration, like the VS Code plugin. --------- Co-authored-by: Dhruv Manilawala <[email protected]>
This adds a new
backend: internal | uvoption to the LSPFormatOptionsallowing users to perform document and range formatting operations though uv. The idea here is to prototype a solution for users to transition to auv formatcommand without encountering version mismatches (and consequently, formatting differences) between the LSP's version ofruffand uv's version ofruff.The primarily alternative to this would be to use uv to discover the
ruffversion used to start the LSP in the first place. However, this would increase the scope of a minimaluv formatcommand beyond "run a formatter", and raise larger questions about how uv should be used to coordinate toolchain discovery. I think those are good things to explore, but I'm hesitant to let them block auv formatimplementation. Another downside of using uv to discoverruff, is that it needs to be implemented outside the LSP; e.g., we'd need to change the instructions on how to run the LSP and implement it in each editor integration, like the VS Code plugin.