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

@ -33,6 +33,10 @@ impl super::BackgroundDocumentRequestHandler for Format {
pub(super) fn format_full_document(snapshot: &DocumentSnapshot) -> Result<Fixes> {
let mut fixes = Fixes::default();
let query = snapshot.query();
let backend = snapshot
.client_settings()
.editor_settings()
.format_backend();
match snapshot.query() {
DocumentQuery::Notebook { notebook, .. } => {
@ -41,7 +45,7 @@ pub(super) fn format_full_document(snapshot: &DocumentSnapshot) -> Result<Fixes>
.map(|url| (url.clone(), notebook.cell_document_by_uri(url).unwrap()))
{
if let Some(changes) =
format_text_document(text_document, query, snapshot.encoding(), true)?
format_text_document(text_document, query, snapshot.encoding(), true, backend)?
{
fixes.insert(url, changes);
}
@ -49,7 +53,7 @@ pub(super) fn format_full_document(snapshot: &DocumentSnapshot) -> Result<Fixes>
}
DocumentQuery::Text { document, .. } => {
if let Some(changes) =
format_text_document(document, query, snapshot.encoding(), false)?
format_text_document(document, query, snapshot.encoding(), false, backend)?
{
fixes.insert(snapshot.query().make_key().into_url(), changes);
}
@ -68,11 +72,16 @@ pub(super) fn format_document(snapshot: &DocumentSnapshot) -> Result<super::Form
.context("Failed to get text document for the format request")
.unwrap();
let query = snapshot.query();
let backend = snapshot
.client_settings()
.editor_settings()
.format_backend();
format_text_document(
text_document,
query,
snapshot.encoding(),
query.as_notebook().is_some(),
backend,
)
}
@ -81,6 +90,7 @@ fn format_text_document(
query: &DocumentQuery,
encoding: PositionEncoding,
is_notebook: bool,
backend: crate::format::FormatBackend,
) -> Result<super::FormatResponse> {
let settings = query.settings();
let file_path = query.virtual_file_path();
@ -101,6 +111,7 @@ fn format_text_document(
query.source_type(),
&settings.formatter,
&file_path,
backend,
)
.with_failure_code(lsp_server::ErrorCode::InternalError)?;
let Some(mut formatted) = formatted else {

View file

@ -36,7 +36,11 @@ fn format_document_range(
.context("Failed to get text document for the format range request")
.unwrap();
let query = snapshot.query();
format_text_document_range(text_document, range, query, snapshot.encoding())
let backend = snapshot
.client_settings()
.editor_settings()
.format_backend();
format_text_document_range(text_document, range, query, snapshot.encoding(), backend)
}
/// Formats the specified [`Range`] in the [`TextDocument`].
@ -45,6 +49,7 @@ fn format_text_document_range(
range: Range,
query: &DocumentQuery,
encoding: PositionEncoding,
backend: crate::format::FormatBackend,
) -> Result<super::FormatResponse> {
let settings = query.settings();
let file_path = query.virtual_file_path();
@ -68,6 +73,7 @@ fn format_text_document_range(
&settings.formatter,
range,
&file_path,
backend,
)
.with_failure_code(lsp_server::ErrorCode::InternalError)?;