Add support for using uv as an alternative formatter backend (#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 <dhruvmanila@gmail.com>
This commit is contained in:
Zanie Blue 2025-09-09 10:09:53 -05:00 committed by GitHub
parent 79706a2e26
commit 9cdac2d6fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 696 additions and 22 deletions

View file

@ -401,6 +401,7 @@ impl ConfigurationTransformer for EditorConfigurationTransformer<'_> {
configuration,
format_preview,
lint_preview,
format_backend: _,
select,
extend_select,
ignore,

View file

@ -7,9 +7,12 @@ use serde_json::{Map, Value};
use ruff_linter::{RuleSelector, line_width::LineLength, rule_selector::ParseError};
use crate::session::{
Client,
settings::{ClientSettings, EditorSettings, GlobalClientSettings, ResolvedConfiguration},
use crate::{
format::FormatBackend,
session::{
Client,
settings::{ClientSettings, EditorSettings, GlobalClientSettings, ResolvedConfiguration},
},
};
pub(crate) type WorkspaceOptionsMap = FxHashMap<Url, ClientOptions>;
@ -124,6 +127,7 @@ impl ClientOptions {
configuration,
lint_preview: lint.preview,
format_preview: format.preview,
format_backend: format.backend,
select: lint.select.and_then(|select| {
Self::resolve_rules(
&select,
@ -283,11 +287,13 @@ impl Combine for LintOptions {
#[serde(rename_all = "camelCase")]
struct FormatOptions {
preview: Option<bool>,
backend: Option<FormatBackend>,
}
impl Combine for FormatOptions {
fn combine_with(&mut self, other: Self) {
self.preview.combine_with(other.preview);
self.backend.combine_with(other.backend);
}
}
@ -443,6 +449,12 @@ pub(crate) trait Combine {
fn combine_with(&mut self, other: Self);
}
impl Combine for FormatBackend {
fn combine_with(&mut self, other: Self) {
*self = other;
}
}
impl<T> Combine for Option<T>
where
T: Combine,
@ -584,6 +596,7 @@ mod tests {
format: Some(
FormatOptions {
preview: None,
backend: None,
},
),
code_action: Some(
@ -640,6 +653,7 @@ mod tests {
format: Some(
FormatOptions {
preview: None,
backend: None,
},
),
code_action: Some(
@ -704,6 +718,7 @@ mod tests {
format: Some(
FormatOptions {
preview: None,
backend: None,
},
),
code_action: Some(
@ -782,6 +797,7 @@ mod tests {
configuration: None,
lint_preview: Some(true),
format_preview: None,
format_backend: None,
select: Some(vec![
RuleSelector::Linter(Linter::Pyflakes),
RuleSelector::Linter(Linter::Isort)
@ -819,6 +835,7 @@ mod tests {
configuration: None,
lint_preview: Some(false),
format_preview: None,
format_backend: None,
select: Some(vec![
RuleSelector::Linter(Linter::Pyflakes),
RuleSelector::Linter(Linter::Isort)
@ -919,6 +936,7 @@ mod tests {
configuration: None,
lint_preview: None,
format_preview: None,
format_backend: None,
select: None,
extend_select: None,
ignore: Some(vec![RuleSelector::from_str("RUF001").unwrap()]),

View file

@ -8,6 +8,7 @@ use ruff_workspace::options::Options;
use crate::{
ClientOptions,
format::FormatBackend,
session::{
Client,
options::{ClientConfiguration, ConfigurationPreference},
@ -84,6 +85,7 @@ pub(crate) struct EditorSettings {
pub(super) configuration: Option<ResolvedConfiguration>,
pub(super) lint_preview: Option<bool>,
pub(super) format_preview: Option<bool>,
pub(super) format_backend: Option<FormatBackend>,
pub(super) select: Option<Vec<RuleSelector>>,
pub(super) extend_select: Option<Vec<RuleSelector>>,
pub(super) ignore: Option<Vec<RuleSelector>>,
@ -163,3 +165,9 @@ impl ClientSettings {
&self.editor_settings
}
}
impl EditorSettings {
pub(crate) fn format_backend(&self) -> FormatBackend {
self.format_backend.unwrap_or_default()
}
}