Skip to content

Commit 94abea4

Browse files
authored
ruff server: Fix multiple issues with Neovim and Helix (#11497)
## Summary Fixes #11236. This PR fixes several issues, most of which relate to non-VS Code editors (Helix and Neovim). 1. Global-only initialization options are now correctly deserialized from Neovim and Helix 2. Empty diagnostics are now published correctly for Neovim and Helix. 3. A workspace folder is created at the current working directory if the initialization parameters send an empty list of workspace folders. 4. The server now gracefully handles opening files outside of any known workspace, and will use global fallback settings taken from client editor settings and a user settings TOML, if it exists. ## Test Plan I've tested to confirm that each issue has been fixed. * Global-only initialization options are now correctly deserialized from Neovim and Helix + the server gracefully handles opening files outside of any known workspace https://github.com/astral-sh/ruff/assets/19577865/4f33477f-20c8-4e50-8214-6608b1a1ea6b * Empty diagnostics are now published correctly for Neovim and Helix https://github.com/astral-sh/ruff/assets/19577865/c93f56a0-f75d-466f-9f40-d77f99cf0637 * A workspace folder is created at the current working directory if the initialization parameters send an empty list of workspace folders. https://github.com/astral-sh/ruff/assets/19577865/b4b2e818-4b0d-40ce-961d-5831478cc726
1 parent 519a650 commit 94abea4

File tree

7 files changed

+120
-101
lines changed

7 files changed

+120
-101
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
{
2-
"settings": {
3-
"codeAction": {
4-
"disableRuleComment": {
5-
"enable": false
6-
}
7-
},
8-
"lint": {
9-
"ignore": ["RUF001"],
10-
"run": "onSave"
11-
},
12-
"fixAll": false,
13-
"logLevel": "warn",
14-
"lineLength": 80,
15-
"exclude": ["third_party"]
16-
}
2+
"codeAction": {
3+
"disableRuleComment": {
4+
"enable": false
5+
}
6+
},
7+
"lint": {
8+
"ignore": ["RUF001"],
9+
"run": "onSave"
10+
},
11+
"fixAll": false,
12+
"logLevel": "warn",
13+
"lineLength": 80,
14+
"exclude": ["third_party"]
1715
}

crates/ruff_server/src/lint.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,14 @@ pub(crate) fn check(
119119

120120
let mut diagnostics = Diagnostics::default();
121121

122-
// Populate all cell URLs with an empty diagnostic list.
123-
// This ensures that cells without diagnostics still get updated.
122+
// Populates all relevant URLs with an empty diagnostic list.
123+
// This ensures that documents without diagnostics still get updated.
124124
if let Some(notebook) = query.as_notebook() {
125125
for url in notebook.urls() {
126126
diagnostics.entry(url.clone()).or_default();
127127
}
128+
} else {
129+
diagnostics.entry(query.make_key().into_url()).or_default();
128130
}
129131

130132
let lsp_diagnostics = data

crates/ruff_server/src/server.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ impl Server {
6969
let AllSettings {
7070
global_settings,
7171
mut workspace_settings,
72-
} = AllSettings::from_value(init_params.initialization_options.unwrap_or_default());
72+
} = AllSettings::from_value(
73+
init_params
74+
.initialization_options
75+
.unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::default())),
76+
);
7377

7478
let mut workspace_for_path = |path: PathBuf| {
7579
let Some(workspace_settings) = workspace_settings.as_mut() else {
@@ -84,11 +88,12 @@ impl Server {
8488

8589
let workspaces = init_params
8690
.workspace_folders
91+
.filter(|folders| !folders.is_empty())
8792
.map(|folders| folders.into_iter().map(|folder| {
8893
workspace_for_path(folder.uri.to_file_path().unwrap())
8994
}).collect())
9095
.or_else(|| {
91-
tracing::debug!("No workspace(s) were provided during initialization. Using the current working directory as a default workspace...");
96+
tracing::warn!("No workspace(s) were provided during initialization. Using the current working directory as a default workspace...");
9297
let uri = types::Url::from_file_path(std::env::current_dir().ok()?).ok()?;
9398
Some(vec![workspace_for_path(uri.to_file_path().unwrap())])
9499
})

crates/ruff_server/src/session.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ impl Session {
6767
Some(DocumentSnapshot {
6868
resolved_client_capabilities: self.resolved_client_capabilities.clone(),
6969
client_settings: self.index.client_settings(&key, &self.global_settings),
70-
document_ref: self.index.make_document_ref(key)?,
70+
document_ref: self.index.make_document_ref(key, &self.global_settings)?,
7171
position_encoding: self.position_encoding,
7272
})
7373
}

crates/ruff_server/src/session/index.rs

+19-4
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,27 @@ impl Index {
237237
Ok(())
238238
}
239239

240-
pub(super) fn make_document_ref(&self, key: DocumentKey) -> Option<DocumentQuery> {
240+
pub(super) fn make_document_ref(
241+
&self,
242+
key: DocumentKey,
243+
global_settings: &ClientSettings,
244+
) -> Option<DocumentQuery> {
241245
let path = self.path_for_key(&key)?.clone();
242246
let document_settings = self
243-
.settings_for_path(&path)?
244-
.workspace_settings_index
245-
.get(&path);
247+
.settings_for_path(&path)
248+
.map(|settings| settings.workspace_settings_index.get(&path))
249+
.unwrap_or_else(|| {
250+
tracing::warn!(
251+
"No settings available for {} - falling back to default settings",
252+
path.display()
253+
);
254+
let resolved_global = ResolvedClientSettings::global(global_settings);
255+
let root = path.parent().unwrap_or(&path);
256+
Arc::new(RuffSettings::fallback(
257+
resolved_global.editor_settings(),
258+
root,
259+
))
260+
});
246261

247262
let controller = self.documents.get(&path)?;
248263
let cell_uri = match key {

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

+28-30
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,32 @@ impl std::fmt::Display for RuffSettings {
4343
}
4444

4545
impl RuffSettings {
46+
pub(crate) fn fallback(editor_settings: &ResolvedEditorSettings, root: &Path) -> RuffSettings {
47+
let fallback = find_user_settings_toml()
48+
.and_then(|user_settings| {
49+
ruff_workspace::resolver::resolve_root_settings(
50+
&user_settings,
51+
Relativity::Cwd,
52+
&EditorConfigurationTransformer(editor_settings, root),
53+
)
54+
.ok()
55+
})
56+
.unwrap_or_else(|| {
57+
let default_configuration = ruff_workspace::configuration::Configuration::default();
58+
EditorConfigurationTransformer(editor_settings, root)
59+
.transform(default_configuration)
60+
.into_settings(root)
61+
.expect(
62+
"editor configuration should merge successfully with default configuration",
63+
)
64+
});
65+
66+
RuffSettings {
67+
formatter: fallback.formatter,
68+
linter: fallback.linter,
69+
}
70+
}
71+
4672
pub(crate) fn linter(&self) -> &ruff_linter::settings::LinterSettings {
4773
&self.linter
4874
}
@@ -80,32 +106,9 @@ impl RuffSettingsIndex {
80106
}
81107
}
82108

83-
let fallback = find_user_settings_toml()
84-
.and_then(|user_settings| {
85-
ruff_workspace::resolver::resolve_root_settings(
86-
&user_settings,
87-
Relativity::Cwd,
88-
&EditorConfigurationTransformer(editor_settings, root),
89-
)
90-
.ok()
91-
})
92-
.unwrap_or_else(|| {
93-
let default_configuration = ruff_workspace::configuration::Configuration::default();
94-
EditorConfigurationTransformer(editor_settings, root)
95-
.transform(default_configuration)
96-
.into_settings(root)
97-
.expect(
98-
"editor configuration should merge successfully with default configuration",
99-
)
100-
});
109+
let fallback = Arc::new(RuffSettings::fallback(editor_settings, root));
101110

102-
Self {
103-
index,
104-
fallback: Arc::new(RuffSettings {
105-
formatter: fallback.formatter,
106-
linter: fallback.linter,
107-
}),
108-
}
111+
Self { index, fallback }
109112
}
110113

111114
pub(super) fn get(&self, document_path: &Path) -> Arc<RuffSettings> {
@@ -118,11 +121,6 @@ impl RuffSettingsIndex {
118121
return settings.clone();
119122
}
120123

121-
tracing::info!(
122-
"No Ruff settings file found for {}; falling back to default configuration",
123-
document_path.display()
124-
);
125-
126124
self.fallback.clone()
127125
}
128126
}

crates/ruff_server/src/session/settings.rs

+48-47
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ enum InitializationOptions {
130130
workspace_settings: Vec<WorkspaceSettings>,
131131
},
132132
GlobalOnly {
133-
settings: Option<ClientSettings>,
133+
#[serde(flatten)]
134+
settings: ClientSettings,
134135
},
135136
}
136137

@@ -157,7 +158,7 @@ impl AllSettings {
157158

158159
fn from_init_options(options: InitializationOptions) -> Self {
159160
let (global_settings, workspace_settings) = match options {
160-
InitializationOptions::GlobalOnly { settings } => (settings.unwrap_or_default(), None),
161+
InitializationOptions::GlobalOnly { settings } => (settings, None),
161162
InitializationOptions::HasWorkspaces {
162163
global_settings,
163164
workspace_settings,
@@ -341,7 +342,9 @@ impl ResolvedClientSettings {
341342

342343
impl Default for InitializationOptions {
343344
fn default() -> Self {
344-
Self::GlobalOnly { settings: None }
345+
Self::GlobalOnly {
346+
settings: ClientSettings::default(),
347+
}
345348
}
346349
}
347350

@@ -626,52 +629,50 @@ mod tests {
626629

627630
assert_debug_snapshot!(options, @r###"
628631
GlobalOnly {
629-
settings: Some(
630-
ClientSettings {
631-
configuration: None,
632-
fix_all: Some(
633-
false,
634-
),
635-
organize_imports: None,
636-
lint: Some(
637-
LintOptions {
638-
enable: None,
639-
preview: None,
640-
select: None,
641-
extend_select: None,
642-
ignore: Some(
643-
[
644-
"RUF001",
645-
],
646-
),
647-
},
648-
),
649-
format: None,
650-
code_action: Some(
651-
CodeActionOptions {
652-
disable_rule_comment: Some(
653-
CodeActionParameters {
654-
enable: Some(
655-
false,
656-
),
657-
},
658-
),
659-
fix_violation: None,
660-
},
661-
),
662-
exclude: Some(
663-
[
664-
"third_party",
665-
],
666-
),
667-
line_length: Some(
668-
LineLength(
669-
80,
632+
settings: ClientSettings {
633+
configuration: None,
634+
fix_all: Some(
635+
false,
636+
),
637+
organize_imports: None,
638+
lint: Some(
639+
LintOptions {
640+
enable: None,
641+
preview: None,
642+
select: None,
643+
extend_select: None,
644+
ignore: Some(
645+
[
646+
"RUF001",
647+
],
670648
),
649+
},
650+
),
651+
format: None,
652+
code_action: Some(
653+
CodeActionOptions {
654+
disable_rule_comment: Some(
655+
CodeActionParameters {
656+
enable: Some(
657+
false,
658+
),
659+
},
660+
),
661+
fix_violation: None,
662+
},
663+
),
664+
exclude: Some(
665+
[
666+
"third_party",
667+
],
668+
),
669+
line_length: Some(
670+
LineLength(
671+
80,
671672
),
672-
configuration_preference: None,
673-
},
674-
),
673+
),
674+
configuration_preference: None,
675+
},
675676
}
676677
"###);
677678
}

0 commit comments

Comments
 (0)