diff --git a/Cargo.lock b/Cargo.lock index a2417cf7c5..c008395ec8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2425,6 +2425,7 @@ dependencies = [ "serde", "serde_json", "tracing", + "walkdir", ] [[package]] diff --git a/crates/ruff_server/Cargo.toml b/crates/ruff_server/Cargo.toml index 0b6056dcbf..24297c9707 100644 --- a/crates/ruff_server/Cargo.toml +++ b/crates/ruff_server/Cargo.toml @@ -35,6 +35,7 @@ rustc-hash = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } tracing = { workspace = true } +walkdir = { workspace = true } [dev-dependencies] insta = { workspace = true } diff --git a/crates/ruff_server/src/server/api/notifications/did_change_watched_files.rs b/crates/ruff_server/src/server/api/notifications/did_change_watched_files.rs index 75547ff73a..3c2f2e09ef 100644 --- a/crates/ruff_server/src/server/api/notifications/did_change_watched_files.rs +++ b/crates/ruff_server/src/server/api/notifications/did_change_watched_files.rs @@ -19,7 +19,7 @@ impl super::SyncNotificationHandler for DidChangeWatchedFiles { ) -> Result<()> { for change in params.changes { session - .reload_configuration(&change.uri) + .reload_settings(&change.uri) .with_failure_code(lsp_server::ErrorCode::InternalError)?; } Ok(()) diff --git a/crates/ruff_server/src/server/api/requests/code_action.rs b/crates/ruff_server/src/server/api/requests/code_action.rs index 0d697b0e59..59a651d764 100644 --- a/crates/ruff_server/src/server/api/requests/code_action.rs +++ b/crates/ruff_server/src/server/api/requests/code_action.rs @@ -105,7 +105,7 @@ fn fix_all(snapshot: &DocumentSnapshot) -> crate::Result { document, snapshot.resolved_client_capabilities(), snapshot.url(), - &snapshot.configuration().linter, + &snapshot.settings().linter, snapshot.encoding(), document.version(), )?), @@ -140,7 +140,7 @@ fn organize_imports(snapshot: &DocumentSnapshot) -> crate::Result { let edits = super::code_action_resolve::fix_all_edit( snapshot.document(), - &snapshot.configuration().linter, + &snapshot.settings().linter, snapshot.encoding(), ) .with_failure_code(ErrorCode::InternalError)?; @@ -83,7 +83,7 @@ impl super::SyncRequestHandler for ExecuteCommand { Command::OrganizeImports => { let edits = super::code_action_resolve::organize_imports_edit( snapshot.document(), - &snapshot.configuration().linter, + &snapshot.settings().linter, snapshot.encoding(), ) .with_failure_code(ErrorCode::InternalError)?; diff --git a/crates/ruff_server/src/server/api/requests/format.rs b/crates/ruff_server/src/server/api/requests/format.rs index 05a4485f59..c8a9260324 100644 --- a/crates/ruff_server/src/server/api/requests/format.rs +++ b/crates/ruff_server/src/server/api/requests/format.rs @@ -26,7 +26,7 @@ impl super::BackgroundDocumentRequestHandler for Format { pub(super) fn format_document(snapshot: &DocumentSnapshot) -> Result { let doc = snapshot.document(); let source = doc.contents(); - let formatted = crate::format::format(doc, &snapshot.configuration().formatter) + let formatted = crate::format::format(doc, &snapshot.settings().formatter) .with_failure_code(lsp_server::ErrorCode::InternalError)?; // fast path - if the code is the same, return early if formatted == source { diff --git a/crates/ruff_server/src/server/api/requests/format_range.rs b/crates/ruff_server/src/server/api/requests/format_range.rs index aef39d971b..904be4914e 100644 --- a/crates/ruff_server/src/server/api/requests/format_range.rs +++ b/crates/ruff_server/src/server/api/requests/format_range.rs @@ -22,7 +22,7 @@ impl super::BackgroundDocumentRequestHandler for FormatRange { let index = document.index(); let range = params.range.to_text_range(text, index, snapshot.encoding()); let formatted_range = - crate::format::format_range(document, &snapshot.configuration().formatter, range) + crate::format::format_range(document, &snapshot.settings().formatter, range) .with_failure_code(lsp_server::ErrorCode::InternalError)?; Ok(Some(vec![types::TextEdit { range: formatted_range diff --git a/crates/ruff_server/src/session.rs b/crates/ruff_server/src/session.rs index 72580c3a30..d1d7b80f2e 100644 --- a/crates/ruff_server/src/session.rs +++ b/crates/ruff_server/src/session.rs @@ -2,27 +2,23 @@ mod capabilities; mod settings; +mod workspace; -use std::collections::BTreeMap; -use std::path::{Path, PathBuf}; -use std::{ops::Deref, sync::Arc}; +use std::sync::Arc; use anyhow::anyhow; use lsp_types::{ClientCapabilities, Url}; -use ruff_workspace::resolver::{ConfigurationTransformer, Relativity}; -use rustc_hash::FxHashMap; -use crate::edit::{Document, DocumentVersion}; +use crate::edit::DocumentVersion; use crate::PositionEncoding; pub(crate) use self::capabilities::ResolvedClientCapabilities; -use self::settings::ResolvedClientSettings; pub(crate) use self::settings::{AllSettings, ClientSettings}; /// The global state for the LSP pub(crate) struct Session { /// Workspace folders in the current session, which contain the state of all open files. - workspaces: Workspaces, + workspaces: workspace::Workspaces, /// The global position encoding, negotiated during LSP initialization. position_encoding: PositionEncoding, /// Global settings provided by the client. @@ -34,49 +30,13 @@ pub(crate) struct Session { /// An immutable snapshot of `Session` that references /// a specific document. pub(crate) struct DocumentSnapshot { - configuration: Arc, resolved_client_capabilities: Arc, client_settings: settings::ResolvedClientSettings, - document_ref: DocumentRef, + document_ref: workspace::DocumentRef, position_encoding: PositionEncoding, url: Url, } -#[derive(Default)] -pub(crate) struct RuffConfiguration { - // settings to pass into the ruff linter - pub(crate) linter: ruff_linter::settings::LinterSettings, - // settings to pass into the ruff formatter - pub(crate) formatter: ruff_workspace::FormatterSettings, -} - -#[derive(Default)] -pub(crate) struct Workspaces(BTreeMap); - -pub(crate) struct Workspace { - open_documents: OpenDocuments, - configuration: Arc, - settings: ClientSettings, -} - -#[derive(Default)] -pub(crate) struct OpenDocuments { - documents: FxHashMap, -} - -/// A mutable handler to an underlying document. -/// Handles copy-on-write mutation automatically when -/// calling `deref_mut`. -pub(crate) struct DocumentController { - document: Arc, -} - -/// A read-only reference to a document. -#[derive(Clone)] -pub(crate) struct DocumentRef { - document: Arc, -} - impl Session { pub(crate) fn new( client_capabilities: &ClientCapabilities, @@ -90,13 +50,12 @@ impl Session { resolved_client_capabilities: Arc::new(ResolvedClientCapabilities::new( client_capabilities, )), - workspaces: Workspaces::new(workspaces)?, + workspaces: workspace::Workspaces::new(workspaces)?, }) } pub(crate) fn take_snapshot(&self, url: &Url) -> Option { Some(DocumentSnapshot { - configuration: self.workspaces.configuration(url)?.clone(), resolved_client_capabilities: self.resolved_client_capabilities.clone(), client_settings: self.workspaces.client_settings(url, &self.global_settings), document_ref: self.workspaces.snapshot(url)?, @@ -117,14 +76,14 @@ impl Session { pub(crate) fn document_controller( &mut self, url: &Url, - ) -> crate::Result<&mut DocumentController> { + ) -> crate::Result<&mut workspace::DocumentController> { self.workspaces .controller(url) .ok_or_else(|| anyhow!("Tried to open unavailable document `{url}`")) } - pub(crate) fn reload_configuration(&mut self, url: &Url) -> crate::Result<()> { - self.workspaces.reload_configuration(url) + pub(crate) fn reload_settings(&mut self, url: &Url) -> crate::Result<()> { + self.workspaces.reload_settings(url) } pub(crate) fn open_workspace_folder(&mut self, url: &Url) -> crate::Result<()> { @@ -146,81 +105,20 @@ impl Session { } } -impl OpenDocuments { - fn snapshot(&self, url: &Url) -> Option { - Some(self.documents.get(url)?.make_ref()) - } - - fn controller(&mut self, url: &Url) -> Option<&mut DocumentController> { - self.documents.get_mut(url) - } - - fn open(&mut self, url: &Url, contents: String, version: DocumentVersion) { - if self - .documents - .insert(url.clone(), DocumentController::new(contents, version)) - .is_some() - { - tracing::warn!("Opening document `{url}` that is already open!"); - } - } - - fn close(&mut self, url: &Url) -> crate::Result<()> { - let Some(_) = self.documents.remove(url) else { - return Err(anyhow!( - "Tried to close document `{url}`, which was not open" - )); - }; - Ok(()) - } -} - -impl DocumentController { - fn new(contents: String, version: DocumentVersion) -> Self { - Self { - document: Arc::new(Document::new(contents, version)), - } - } - - pub(crate) fn make_ref(&self) -> DocumentRef { - DocumentRef { - document: self.document.clone(), - } - } - - pub(crate) fn make_mut(&mut self) -> &mut Document { - Arc::make_mut(&mut self.document) - } -} - -impl Deref for DocumentController { - type Target = Document; - fn deref(&self) -> &Self::Target { - &self.document - } -} - -impl Deref for DocumentRef { - type Target = Document; - fn deref(&self) -> &Self::Target { - &self.document - } -} - impl DocumentSnapshot { - pub(crate) fn configuration(&self) -> &RuffConfiguration { - &self.configuration + pub(crate) fn settings(&self) -> &workspace::RuffSettings { + self.document().settings() } pub(crate) fn resolved_client_capabilities(&self) -> &ResolvedClientCapabilities { &self.resolved_client_capabilities } - pub(crate) fn client_settings(&self) -> &ResolvedClientSettings { + pub(crate) fn client_settings(&self) -> &settings::ResolvedClientSettings { &self.client_settings } - pub(crate) fn document(&self) -> &DocumentRef { + pub(crate) fn document(&self) -> &workspace::DocumentRef { &self.document_ref } @@ -232,166 +130,3 @@ impl DocumentSnapshot { &self.url } } - -impl Workspaces { - fn new(workspaces: Vec<(Url, ClientSettings)>) -> crate::Result { - Ok(Self( - workspaces - .into_iter() - .map(|(url, settings)| Workspace::new(&url, settings)) - .collect::>()?, - )) - } - - fn open_workspace_folder(&mut self, folder_url: &Url) -> crate::Result<()> { - // TODO(jane): find a way to allow for workspace settings to be updated dynamically - let (path, workspace) = Workspace::new(folder_url, ClientSettings::default())?; - self.0.insert(path, workspace); - Ok(()) - } - - fn close_workspace_folder(&mut self, folder_url: &Url) -> crate::Result<()> { - let path = folder_url - .to_file_path() - .map_err(|()| anyhow!("Folder URI was not a proper file path"))?; - self.0 - .remove(&path) - .ok_or_else(|| anyhow!("Tried to remove non-existent folder {}", path.display()))?; - Ok(()) - } - - fn snapshot(&self, document_url: &Url) -> Option { - self.workspace_for_url(document_url)? - .open_documents - .snapshot(document_url) - } - - fn controller(&mut self, document_url: &Url) -> Option<&mut DocumentController> { - self.workspace_for_url_mut(document_url)? - .open_documents - .controller(document_url) - } - - fn configuration(&self, document_url: &Url) -> Option<&Arc> { - Some(&self.workspace_for_url(document_url)?.configuration) - } - - fn reload_configuration(&mut self, changed_url: &Url) -> crate::Result<()> { - let (path, workspace) = self - .entry_for_url_mut(changed_url) - .ok_or_else(|| anyhow!("Workspace not found for {changed_url}"))?; - workspace.reload_configuration(path); - Ok(()) - } - - fn open(&mut self, url: &Url, contents: String, version: DocumentVersion) { - if let Some(workspace) = self.workspace_for_url_mut(url) { - workspace.open_documents.open(url, contents, version); - } - } - - fn close(&mut self, url: &Url) -> crate::Result<()> { - self.workspace_for_url_mut(url) - .ok_or_else(|| anyhow!("Workspace not found for {url}"))? - .open_documents - .close(url) - } - - fn client_settings( - &self, - url: &Url, - global_settings: &ClientSettings, - ) -> ResolvedClientSettings { - self.workspace_for_url(url).map_or_else( - || { - tracing::warn!( - "Workspace not found for {url}. Global settings will be used for this document" - ); - ResolvedClientSettings::global(global_settings) - }, - |workspace| { - ResolvedClientSettings::with_workspace(&workspace.settings, global_settings) - }, - ) - } - - fn workspace_for_url(&self, url: &Url) -> Option<&Workspace> { - Some(self.entry_for_url(url)?.1) - } - - fn workspace_for_url_mut(&mut self, url: &Url) -> Option<&mut Workspace> { - Some(self.entry_for_url_mut(url)?.1) - } - - fn entry_for_url(&self, url: &Url) -> Option<(&Path, &Workspace)> { - let path = url.to_file_path().ok()?; - self.0 - .range(..path) - .next_back() - .map(|(path, workspace)| (path.as_path(), workspace)) - } - - fn entry_for_url_mut(&mut self, url: &Url) -> Option<(&Path, &mut Workspace)> { - let path = url.to_file_path().ok()?; - self.0 - .range_mut(..path) - .next_back() - .map(|(path, workspace)| (path.as_path(), workspace)) - } -} - -impl Workspace { - pub(crate) fn new(root: &Url, settings: ClientSettings) -> crate::Result<(PathBuf, Self)> { - let path = root - .to_file_path() - .map_err(|()| anyhow!("workspace URL was not a file path!"))?; - // Fall-back to default configuration - let configuration = Self::find_configuration_or_fallback(&path); - - Ok(( - path, - Self { - open_documents: OpenDocuments::default(), - configuration: Arc::new(configuration), - settings, - }, - )) - } - - fn reload_configuration(&mut self, path: &Path) { - self.configuration = Arc::new(Self::find_configuration_or_fallback(path)); - } - - fn find_configuration_or_fallback(root: &Path) -> RuffConfiguration { - find_configuration_from_root(root).unwrap_or_else(|err| { - tracing::error!("The following error occurred when trying to find a configuration file at `{}`:\n{err}", root.display()); - tracing::error!("Falling back to default configuration for `{}`", root.display()); - RuffConfiguration::default() - }) - } -} - -pub(crate) fn find_configuration_from_root(root: &Path) -> crate::Result { - let pyproject = ruff_workspace::pyproject::find_settings_toml(root)? - .ok_or_else(|| anyhow!("No pyproject.toml/ruff.toml/.ruff.toml file was found"))?; - let settings = ruff_workspace::resolver::resolve_root_settings( - &pyproject, - Relativity::Parent, - &LSPConfigTransformer, - )?; - Ok(RuffConfiguration { - linter: settings.linter, - formatter: settings.formatter, - }) -} - -struct LSPConfigTransformer; - -impl ConfigurationTransformer for LSPConfigTransformer { - fn transform( - &self, - config: ruff_workspace::configuration::Configuration, - ) -> ruff_workspace::configuration::Configuration { - config - } -} diff --git a/crates/ruff_server/src/session/workspace.rs b/crates/ruff_server/src/session/workspace.rs new file mode 100644 index 0000000000..a508a360c8 --- /dev/null +++ b/crates/ruff_server/src/session/workspace.rs @@ -0,0 +1,254 @@ +use anyhow::anyhow; +use lsp_types::Url; +use rustc_hash::FxHashMap; +use std::{ + collections::BTreeMap, + ops::Deref, + path::{Path, PathBuf}, + sync::Arc, +}; + +use crate::{edit::DocumentVersion, Document}; + +use self::ruff_settings::RuffSettingsIndex; + +use super::{settings, ClientSettings}; + +mod ruff_settings; + +pub(crate) use ruff_settings::RuffSettings; + +#[derive(Default)] +pub(crate) struct Workspaces(BTreeMap); + +pub(crate) struct Workspace { + open_documents: OpenDocuments, + settings: ClientSettings, +} + +pub(crate) struct OpenDocuments { + documents: FxHashMap, + settings_index: ruff_settings::RuffSettingsIndex, +} + +/// A mutable handler to an underlying document. +/// Handles copy-on-write mutation automatically when +/// calling `deref_mut`. +pub(crate) struct DocumentController { + document: Arc, +} + +/// A read-only reference to a document. +#[derive(Clone)] +pub(crate) struct DocumentRef { + document: Arc, + settings: Arc, +} + +impl Workspaces { + pub(super) fn new(workspaces: Vec<(Url, ClientSettings)>) -> crate::Result { + Ok(Self( + workspaces + .into_iter() + .map(|(url, settings)| Workspace::new(&url, settings)) + .collect::>()?, + )) + } + + pub(super) fn open_workspace_folder(&mut self, folder_url: &Url) -> crate::Result<()> { + // TODO(jane): find a way to allow for workspace settings to be updated dynamically + let (path, workspace) = Workspace::new(folder_url, ClientSettings::default())?; + self.0.insert(path, workspace); + Ok(()) + } + + pub(super) fn close_workspace_folder(&mut self, folder_url: &Url) -> crate::Result<()> { + let path = folder_url + .to_file_path() + .map_err(|()| anyhow!("Folder URI was not a proper file path"))?; + self.0 + .remove(&path) + .ok_or_else(|| anyhow!("Tried to remove non-existent folder {}", path.display()))?; + Ok(()) + } + + pub(super) fn snapshot(&self, document_url: &Url) -> Option { + self.workspace_for_url(document_url)? + .open_documents + .snapshot(document_url) + } + + pub(super) fn controller(&mut self, document_url: &Url) -> Option<&mut DocumentController> { + self.workspace_for_url_mut(document_url)? + .open_documents + .controller(document_url) + } + + pub(super) fn reload_settings(&mut self, changed_url: &Url) -> crate::Result<()> { + let (root, workspace) = self + .entry_for_url_mut(changed_url) + .ok_or_else(|| anyhow!("Workspace not found for {changed_url}"))?; + workspace.reload_settings(root); + Ok(()) + } + + pub(super) fn open(&mut self, url: &Url, contents: String, version: DocumentVersion) { + if let Some(workspace) = self.workspace_for_url_mut(url) { + workspace.open_documents.open(url, contents, version); + } + } + + pub(super) fn close(&mut self, url: &Url) -> crate::Result<()> { + self.workspace_for_url_mut(url) + .ok_or_else(|| anyhow!("Workspace not found for {url}"))? + .open_documents + .close(url) + } + + pub(super) fn client_settings( + &self, + url: &Url, + global_settings: &ClientSettings, + ) -> settings::ResolvedClientSettings { + self.workspace_for_url(url).map_or_else( + || { + tracing::warn!( + "Workspace not found for {url}. Global settings will be used for this document" + ); + settings::ResolvedClientSettings::global(global_settings) + }, + |workspace| { + settings::ResolvedClientSettings::with_workspace( + &workspace.settings, + global_settings, + ) + }, + ) + } + + fn workspace_for_url(&self, url: &Url) -> Option<&Workspace> { + Some(self.entry_for_url(url)?.1) + } + + fn workspace_for_url_mut(&mut self, url: &Url) -> Option<&mut Workspace> { + Some(self.entry_for_url_mut(url)?.1) + } + + fn entry_for_url(&self, url: &Url) -> Option<(&Path, &Workspace)> { + let path = url.to_file_path().ok()?; + self.0 + .range(..path) + .next_back() + .map(|(path, workspace)| (path.as_path(), workspace)) + } + + fn entry_for_url_mut(&mut self, url: &Url) -> Option<(&Path, &mut Workspace)> { + let path = url.to_file_path().ok()?; + self.0 + .range_mut(..path) + .next_back() + .map(|(path, workspace)| (path.as_path(), workspace)) + } +} + +impl Workspace { + pub(crate) fn new(root: &Url, settings: ClientSettings) -> crate::Result<(PathBuf, Self)> { + let path = root + .to_file_path() + .map_err(|()| anyhow!("workspace URL was not a file path!"))?; + + let workspace = Self { + open_documents: OpenDocuments::new(&path), + settings, + }; + + Ok((path, workspace)) + } + + fn reload_settings(&mut self, root: &Path) { + self.open_documents.reload_settings(root); + } +} + +impl OpenDocuments { + fn new(path: &Path) -> Self { + Self { + documents: FxHashMap::default(), + settings_index: RuffSettingsIndex::new(path), + } + } + + fn snapshot(&self, url: &Url) -> Option { + let path = url + .to_file_path() + .expect("document URL should convert to file path: {url}"); + let document_settings = self.settings_index.get(&path); + Some(self.documents.get(url)?.make_ref(document_settings)) + } + + fn controller(&mut self, url: &Url) -> Option<&mut DocumentController> { + self.documents.get_mut(url) + } + + fn open(&mut self, url: &Url, contents: String, version: DocumentVersion) { + if self + .documents + .insert(url.clone(), DocumentController::new(contents, version)) + .is_some() + { + tracing::warn!("Opening document `{url}` that is already open!"); + } + } + + fn close(&mut self, url: &Url) -> crate::Result<()> { + let Some(_) = self.documents.remove(url) else { + return Err(anyhow!( + "Tried to close document `{url}`, which was not open" + )); + }; + Ok(()) + } + + fn reload_settings(&mut self, root: &Path) { + self.settings_index = RuffSettingsIndex::new(root); + } +} + +impl DocumentController { + fn new(contents: String, version: DocumentVersion) -> Self { + Self { + document: Arc::new(Document::new(contents, version)), + } + } + + pub(crate) fn make_ref(&self, document_settings: Arc) -> DocumentRef { + DocumentRef { + document: self.document.clone(), + settings: document_settings, + } + } + + pub(crate) fn make_mut(&mut self) -> &mut Document { + Arc::make_mut(&mut self.document) + } +} + +impl Deref for DocumentController { + type Target = Document; + fn deref(&self) -> &Self::Target { + &self.document + } +} + +impl Deref for DocumentRef { + type Target = Document; + fn deref(&self) -> &Self::Target { + &self.document + } +} + +impl DocumentRef { + pub(crate) fn settings(&self) -> &RuffSettings { + &self.settings + } +} diff --git a/crates/ruff_server/src/session/workspace/ruff_settings.rs b/crates/ruff_server/src/session/workspace/ruff_settings.rs new file mode 100644 index 0000000000..d1e13d6370 --- /dev/null +++ b/crates/ruff_server/src/session/workspace/ruff_settings.rs @@ -0,0 +1,98 @@ +use ruff_linter::display_settings; +use ruff_workspace::{ + pyproject::settings_toml, + resolver::{ConfigurationTransformer, Relativity}, +}; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, + sync::Arc, +}; +use walkdir::{DirEntry, WalkDir}; + +#[derive(Default)] +pub(crate) struct RuffSettings { + // settings to pass into the ruff linter + pub(crate) linter: ruff_linter::settings::LinterSettings, + // settings to pass into the ruff formatter + pub(crate) formatter: ruff_workspace::FormatterSettings, +} + +pub(super) struct RuffSettingsIndex { + index: BTreeMap>, + fallback: Arc, +} + +impl std::fmt::Display for RuffSettings { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + display_settings! { + formatter = f, + fields = [ + self.linter, + self.formatter + ] + } + Ok(()) + } +} + +impl RuffSettingsIndex { + pub(super) fn new(root: &Path) -> Self { + let mut index = BTreeMap::default(); + + for directory in WalkDir::new(root) + .into_iter() + .filter_map(Result::ok) + .filter(|entry| entry.file_type().is_dir()) + .map(DirEntry::into_path) + { + if let Some(pyproject) = settings_toml(&directory).ok().flatten() { + let Ok(settings) = ruff_workspace::resolver::resolve_root_settings( + &pyproject, + Relativity::Parent, + &LSPConfigTransformer, + ) else { + continue; + }; + index.insert( + directory, + Arc::new(RuffSettings { + linter: settings.linter, + formatter: settings.formatter, + }), + ); + } + } + + Self { + index, + fallback: Arc::default(), + } + } + + pub(super) fn get(&self, document_path: &Path) -> Arc { + if let Some((_, settings)) = self + .index + .range(..document_path.to_path_buf()) + .rev() + .find(|(path, _)| document_path.starts_with(path)) + { + return settings.clone(); + } + + tracing::info!("No ruff settings file (pyproject.toml/ruff.toml/.ruff.toml) found for {} - falling back to default configuration", document_path.display()); + + self.fallback.clone() + } +} + +struct LSPConfigTransformer; + +impl ConfigurationTransformer for LSPConfigTransformer { + fn transform( + &self, + config: ruff_workspace::configuration::Configuration, + ) -> ruff_workspace::configuration::Configuration { + config + } +}