Skip to content

Commit 3c6c017

Browse files
authored
Centralize client options validation (astral-sh#18623)
1 parent ef56409 commit 3c6c017

File tree

9 files changed

+1164
-1003
lines changed

9 files changed

+1164
-1003
lines changed

crates/ruff_server/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
pub use edit::{DocumentKey, NotebookDocument, PositionEncoding, TextDocument};
44
use lsp_types::CodeActionKind;
55
pub use server::Server;
6-
pub use session::{ClientSettings, DocumentQuery, DocumentSnapshot, Session};
6+
pub use session::{ClientOptions, DocumentQuery, DocumentSnapshot, GlobalOptions, Session};
77
pub use workspace::{Workspace, Workspaces};
88

99
#[macro_use]

crates/ruff_server/src/server.rs

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use self::schedule::Scheduler;
3030
use self::schedule::Task;
3131
use self::schedule::event_loop_thread;
3232
use crate::PositionEncoding;
33-
use crate::session::AllSettings;
33+
use crate::session::AllOptions;
3434
use crate::session::Session;
3535
use crate::workspace::Workspaces;
3636

@@ -77,39 +77,36 @@ impl Server {
7777
..
7878
} = init_params;
7979

80-
let mut all_settings = AllSettings::from_value(
80+
let mut all_options = AllOptions::from_value(
8181
initialization_options
8282
.unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::default())),
8383
);
8484
if let Some(preview) = preview {
85-
all_settings.set_preview(preview);
85+
all_options.set_preview(preview);
8686
}
87-
let AllSettings {
88-
global_settings,
89-
workspace_settings,
90-
} = all_settings;
87+
let AllOptions {
88+
global: global_options,
89+
workspace: workspace_options,
90+
} = all_options;
9191

9292
crate::logging::init_logging(
93-
global_settings.tracing.log_level.unwrap_or_default(),
94-
global_settings.tracing.log_file.as_deref(),
93+
global_options.tracing.log_level.unwrap_or_default(),
94+
global_options.tracing.log_file.as_deref(),
9595
);
9696

9797
let workspaces = Workspaces::from_workspace_folders(
9898
workspace_folders,
99-
workspace_settings.unwrap_or_default(),
99+
workspace_options.unwrap_or_default(),
100100
)?;
101101

102102
tracing::debug!("Negotiated position encoding: {position_encoding:?}");
103103

104+
let global = global_options.into_settings();
105+
104106
Ok(Self {
105107
connection,
106108
worker_threads,
107-
session: Session::new(
108-
&client_capabilities,
109-
position_encoding,
110-
global_settings,
111-
&workspaces,
112-
)?,
109+
session: Session::new(&client_capabilities, position_encoding, global, &workspaces)?,
113110
client_capabilities,
114111
})
115112
}

crates/ruff_server/src/session.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,21 @@ use std::path::Path;
44
use std::sync::Arc;
55

66
use lsp_types::{ClientCapabilities, FileEvent, NotebookDocumentCellChange, Url};
7-
use settings::ResolvedClientSettings;
7+
use settings::ClientSettings;
88

99
use crate::edit::{DocumentKey, DocumentVersion, NotebookDocument};
10+
use crate::session::settings::GlobalClientSettings;
1011
use crate::workspace::Workspaces;
1112
use crate::{PositionEncoding, TextDocument};
1213

1314
pub(crate) use self::capabilities::ResolvedClientCapabilities;
1415
pub use self::index::DocumentQuery;
15-
pub use self::settings::ClientSettings;
16-
pub(crate) use self::settings::{AllSettings, WorkspaceSettingsMap};
16+
pub(crate) use self::options::{AllOptions, WorkspaceOptionsMap};
17+
pub use self::options::{ClientOptions, GlobalOptions};
1718

1819
mod capabilities;
1920
mod index;
21+
mod options;
2022
mod settings;
2123

2224
/// The global state for the LSP
@@ -26,7 +28,8 @@ pub struct Session {
2628
/// The global position encoding, negotiated during LSP initialization.
2729
position_encoding: PositionEncoding,
2830
/// Global settings provided by the client.
29-
global_settings: ClientSettings,
31+
global_settings: GlobalClientSettings,
32+
3033
/// Tracks what LSP features the client supports and doesn't support.
3134
resolved_client_capabilities: Arc<ResolvedClientCapabilities>,
3235
}
@@ -35,7 +38,7 @@ pub struct Session {
3538
/// a specific document.
3639
pub struct DocumentSnapshot {
3740
resolved_client_capabilities: Arc<ResolvedClientCapabilities>,
38-
client_settings: settings::ResolvedClientSettings,
41+
client_settings: Arc<settings::ClientSettings>,
3942
document_ref: index::DocumentQuery,
4043
position_encoding: PositionEncoding,
4144
}
@@ -44,13 +47,13 @@ impl Session {
4447
pub fn new(
4548
client_capabilities: &ClientCapabilities,
4649
position_encoding: PositionEncoding,
47-
global_settings: ClientSettings,
50+
global: GlobalClientSettings,
4851
workspaces: &Workspaces,
4952
) -> crate::Result<Self> {
5053
Ok(Self {
5154
position_encoding,
52-
index: index::Index::new(workspaces, &global_settings)?,
53-
global_settings,
55+
index: index::Index::new(workspaces, &global)?,
56+
global_settings: global,
5457
resolved_client_capabilities: Arc::new(ResolvedClientCapabilities::new(
5558
client_capabilities,
5659
)),
@@ -66,7 +69,10 @@ impl Session {
6669
let key = self.key_from_url(url);
6770
Some(DocumentSnapshot {
6871
resolved_client_capabilities: self.resolved_client_capabilities.clone(),
69-
client_settings: self.index.client_settings(&key, &self.global_settings),
72+
client_settings: self
73+
.index
74+
.client_settings(&key)
75+
.unwrap_or_else(|| self.global_settings.to_settings_arc()),
7076
document_ref: self.index.make_document_ref(key, &self.global_settings)?,
7177
position_encoding: self.position_encoding,
7278
})
@@ -163,8 +169,8 @@ impl Session {
163169
}
164170

165171
/// Returns the resolved global client settings.
166-
pub(crate) fn global_client_settings(&self) -> ResolvedClientSettings {
167-
ResolvedClientSettings::global(&self.global_settings)
172+
pub(crate) fn global_client_settings(&self) -> &ClientSettings {
173+
self.global_settings.to_settings()
168174
}
169175

170176
/// Returns the number of open documents in the session.
@@ -183,7 +189,7 @@ impl DocumentSnapshot {
183189
&self.resolved_client_capabilities
184190
}
185191

186-
pub(crate) fn client_settings(&self) -> &settings::ResolvedClientSettings {
192+
pub(crate) fn client_settings(&self) -> &settings::ClientSettings {
187193
&self.client_settings
188194
}
189195

crates/ruff_server/src/session/index.rs

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ use thiserror::Error;
1111
pub(crate) use ruff_settings::RuffSettings;
1212

1313
use crate::edit::LanguageId;
14+
use crate::session::options::Combine;
15+
use crate::session::settings::GlobalClientSettings;
1416
use crate::workspace::{Workspace, Workspaces};
1517
use crate::{
1618
PositionEncoding, TextDocument,
1719
edit::{DocumentKey, DocumentVersion, NotebookDocument},
1820
};
1921

20-
use super::{ClientSettings, settings::ResolvedClientSettings};
22+
use super::settings::ClientSettings;
2123

2224
mod ruff_settings;
2325

@@ -36,7 +38,7 @@ pub(crate) struct Index {
3638

3739
/// Settings associated with a workspace.
3840
struct WorkspaceSettings {
39-
client_settings: ResolvedClientSettings,
41+
client_settings: Arc<ClientSettings>,
4042
ruff_settings: ruff_settings::RuffSettingsIndex,
4143
}
4244

@@ -70,11 +72,11 @@ pub enum DocumentQuery {
7072
impl Index {
7173
pub(super) fn new(
7274
workspaces: &Workspaces,
73-
global_settings: &ClientSettings,
75+
global: &GlobalClientSettings,
7476
) -> crate::Result<Self> {
7577
let mut settings = WorkspaceSettingsIndex::default();
7678
for workspace in &**workspaces {
77-
settings.register_workspace(workspace, global_settings)?;
79+
settings.register_workspace(workspace, global)?;
7880
}
7981

8082
Ok(Self {
@@ -170,11 +172,11 @@ impl Index {
170172
pub(super) fn open_workspace_folder(
171173
&mut self,
172174
url: Url,
173-
global_settings: &ClientSettings,
175+
global: &GlobalClientSettings,
174176
) -> crate::Result<()> {
175177
// TODO(jane): Find a way for workspace client settings to be added or changed dynamically.
176178
self.settings
177-
.register_workspace(&Workspace::new(url), global_settings)
179+
.register_workspace(&Workspace::new(url), global)
178180
}
179181

180182
pub(super) fn close_workspace_folder(&mut self, workspace_url: &Url) -> crate::Result<()> {
@@ -201,7 +203,7 @@ impl Index {
201203
pub(super) fn make_document_ref(
202204
&self,
203205
key: DocumentKey,
204-
global_settings: &ClientSettings,
206+
global: &GlobalClientSettings,
205207
) -> Option<DocumentQuery> {
206208
let url = self.url_for_key(&key)?.clone();
207209

@@ -230,13 +232,12 @@ impl Index {
230232
"No settings available for {} - falling back to default settings",
231233
url
232234
);
233-
let resolved_global = ResolvedClientSettings::global(global_settings);
234235
// The path here is only for completeness, it's okay to use a non-existing path
235236
// in case this is an unsaved (untitled) document.
236237
let path = Path::new(url.path());
237238
let root = path.parent().unwrap_or(path);
238239
Arc::new(RuffSettings::fallback(
239-
resolved_global.editor_settings(),
240+
global.to_settings().editor_settings(),
240241
root,
241242
))
242243
});
@@ -330,21 +331,12 @@ impl Index {
330331
Ok(())
331332
}
332333

333-
pub(super) fn client_settings(
334-
&self,
335-
key: &DocumentKey,
336-
global_settings: &ClientSettings,
337-
) -> ResolvedClientSettings {
338-
let Some(url) = self.url_for_key(key) else {
339-
return ResolvedClientSettings::global(global_settings);
340-
};
341-
let Some(WorkspaceSettings {
334+
pub(super) fn client_settings(&self, key: &DocumentKey) -> Option<Arc<ClientSettings>> {
335+
let url = self.url_for_key(key)?;
336+
let WorkspaceSettings {
342337
client_settings, ..
343-
}) = self.settings_for_url(url)
344-
else {
345-
return ResolvedClientSettings::global(global_settings);
346-
};
347-
client_settings.clone()
338+
} = self.settings_for_url(url)?;
339+
Some(client_settings.clone())
348340
}
349341

350342
fn document_controller_for_key(
@@ -422,7 +414,7 @@ impl WorkspaceSettingsIndex {
422414
fn register_workspace(
423415
&mut self,
424416
workspace: &Workspace,
425-
global_settings: &ClientSettings,
417+
global: &GlobalClientSettings,
426418
) -> crate::Result<()> {
427419
let workspace_url = workspace.url();
428420
if workspace_url.scheme() != "file" {
@@ -434,10 +426,21 @@ impl WorkspaceSettingsIndex {
434426
anyhow!("Failed to convert workspace URL to file path: {workspace_url}")
435427
})?;
436428

437-
let client_settings = if let Some(workspace_settings) = workspace.settings() {
438-
ResolvedClientSettings::with_workspace(workspace_settings, global_settings)
429+
let client_settings = if let Some(workspace_options) = workspace.options() {
430+
let options = workspace_options.clone().combine(global.options().clone());
431+
let settings = match options.into_settings() {
432+
Ok(settings) => settings,
433+
Err(settings) => {
434+
show_err_msg!(
435+
"The settings for the workspace {workspace_path} are invalid. Refer to the logs for more information.",
436+
workspace_path = workspace_path.display()
437+
);
438+
settings
439+
}
440+
};
441+
Arc::new(settings)
439442
} else {
440-
ResolvedClientSettings::global(global_settings)
443+
global.to_settings_arc()
441444
};
442445

443446
let workspace_settings_index = ruff_settings::RuffSettingsIndex::new(

crates/ruff_server/src/session/index/ruff_settings.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@ use ruff_workspace::{
1818
resolver::ConfigurationTransformer,
1919
};
2020

21-
use crate::session::settings::{
22-
ConfigurationPreference, ResolvedConfiguration, ResolvedEditorSettings,
23-
};
21+
use crate::session::options::ConfigurationPreference;
22+
use crate::session::settings::{EditorSettings, ResolvedConfiguration};
2423

2524
#[derive(Debug)]
2625
pub struct RuffSettings {
@@ -64,7 +63,7 @@ impl RuffSettings {
6463
///
6564
/// In the absence of a valid configuration file, it gracefully falls back to
6665
/// editor-only settings.
67-
pub(crate) fn fallback(editor_settings: &ResolvedEditorSettings, root: &Path) -> RuffSettings {
66+
pub(crate) fn fallback(editor_settings: &EditorSettings, root: &Path) -> RuffSettings {
6867
struct FallbackTransformer<'a> {
6968
inner: EditorConfigurationTransformer<'a>,
7069
}
@@ -122,14 +121,14 @@ impl RuffSettings {
122121

123122
/// Constructs [`RuffSettings`] by merging the editor-defined settings with the
124123
/// default configuration.
125-
fn editor_only(editor_settings: &ResolvedEditorSettings, root: &Path) -> RuffSettings {
124+
fn editor_only(editor_settings: &EditorSettings, root: &Path) -> RuffSettings {
126125
Self::with_editor_settings(editor_settings, root, Configuration::default())
127126
.expect("editor configuration should merge successfully with default configuration")
128127
}
129128

130129
/// Merges the `configuration` with the editor defined settings.
131130
fn with_editor_settings(
132-
editor_settings: &ResolvedEditorSettings,
131+
editor_settings: &EditorSettings,
133132
root: &Path,
134133
configuration: Configuration,
135134
) -> anyhow::Result<RuffSettings> {
@@ -157,7 +156,7 @@ impl RuffSettingsIndex {
157156
/// skipping (3).
158157
pub(super) fn new(
159158
root: &Path,
160-
editor_settings: &ResolvedEditorSettings,
159+
editor_settings: &EditorSettings,
161160
is_default_workspace: bool,
162161
) -> Self {
163162
if editor_settings.configuration_preference == ConfigurationPreference::EditorOnly {
@@ -392,11 +391,11 @@ impl RuffSettingsIndex {
392391
}
393392
}
394393

395-
struct EditorConfigurationTransformer<'a>(&'a ResolvedEditorSettings, &'a Path);
394+
struct EditorConfigurationTransformer<'a>(&'a EditorSettings, &'a Path);
396395

397396
impl ConfigurationTransformer for EditorConfigurationTransformer<'_> {
398397
fn transform(&self, filesystem_configuration: Configuration) -> Configuration {
399-
let ResolvedEditorSettings {
398+
let EditorSettings {
400399
configuration,
401400
format_preview,
402401
lint_preview,
@@ -515,7 +514,7 @@ mod tests {
515514
/// This test ensures that the inline configuration is correctly applied to the configuration.
516515
#[test]
517516
fn inline_settings() {
518-
let editor_settings = ResolvedEditorSettings {
517+
let editor_settings = EditorSettings {
519518
configuration: Some(ResolvedConfiguration::Inline(Box::new(Options {
520519
line_length: Some(LineLength::try_from(120).unwrap()),
521520
..Default::default()
@@ -533,7 +532,7 @@ mod tests {
533532
/// settings is prioritized.
534533
#[test]
535534
fn inline_and_specific_settings_resolution_order() {
536-
let editor_settings = ResolvedEditorSettings {
535+
let editor_settings = EditorSettings {
537536
configuration: Some(ResolvedConfiguration::Inline(Box::new(Options {
538537
line_length: Some(LineLength::try_from(120).unwrap()),
539538
..Default::default()

0 commit comments

Comments
 (0)