Skip to content

Commit 9c1d490

Browse files
autofix-ci[bot]Sysix
authored andcommitted
[autofix.ci] apply automated fixes
1 parent 15c2922 commit 9c1d490

File tree

10 files changed

+225
-81
lines changed

10 files changed

+225
-81
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/oxc_language_server/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ oxc_diagnostics = { workspace = true }
2727
oxc_formatter = { workspace = true }
2828
oxc_linter = { workspace = true, features = ["language_server"] }
2929
oxc_parser = { workspace = true }
30-
oxc_span = { workspace = true }
3130

3231
#
3332
env_logger = { workspace = true, features = ["humantime"] }

crates/oxc_language_server/src/capabilities.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,19 @@ impl From<ClientCapabilities> for Capabilities {
4040
watched_files.dynamic_registration.is_some_and(|dynamic| dynamic)
4141
})
4242
});
43-
// TODO: enable it when we support formatting
44-
// let formatting = value.text_document.as_ref().is_some_and(|text_document| {
45-
// text_document.formatting.is_some_and(|formatting| {
46-
// formatting.dynamic_registration.is_some_and(|dynamic| dynamic)
47-
// })
48-
// });
43+
let dynamic_formatting = value.text_document.as_ref().is_some_and(|text_document| {
44+
text_document.formatting.is_some_and(|formatting| {
45+
formatting.dynamic_registration.is_some_and(|dynamic| dynamic)
46+
})
47+
});
4948

5049
Self {
5150
code_action_provider,
5251
workspace_apply_edit,
5352
workspace_execute_command,
5453
workspace_configuration,
5554
dynamic_watchers,
56-
dynamic_formatting: false,
55+
dynamic_formatting,
5756
}
5857
}
5958
}
@@ -100,9 +99,8 @@ impl From<Capabilities> for ServerCapabilities {
10099
} else {
101100
None
102101
},
103-
// ToDo: should by dynamic registered with workspace configuration
104-
// THIS MUST NOT BE COMMITTED TO MAIN!!!
105-
document_formatting_provider: Some(OneOf::Left(true)),
102+
// the server supports formatting, but it will tell the client if he enabled the setting
103+
document_formatting_provider: None,
106104
..ServerCapabilities::default()
107105
}
108106
}

crates/oxc_language_server/src/file_system.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ impl LSPFileSystem {
2121
self.files.pin().insert(uri.clone(), content);
2222
}
2323

24-
#[expect(dead_code)] // used for the oxc_formatter in the future
2524
pub fn get(&self, uri: &Uri) -> Option<String> {
2625
self.files.pin().get(uri).cloned()
2726
}
Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,69 @@
1-
use serde::{Deserialize, Serialize};
1+
use serde::{Deserialize, Deserializer, Serialize, de::Error};
2+
use serde_json::Value;
23

3-
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
4+
#[derive(Debug, Default, Serialize, Clone)]
45
#[serde(rename_all = "camelCase")]
5-
pub struct FormatOptions;
6+
pub struct FormatOptions {
7+
pub experimental: bool,
8+
}
9+
10+
impl<'de> Deserialize<'de> for FormatOptions {
11+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
12+
where
13+
D: Deserializer<'de>,
14+
{
15+
let value = Value::deserialize(deserializer)?;
16+
FormatOptions::try_from(value).map_err(Error::custom)
17+
}
18+
}
19+
20+
impl TryFrom<Value> for FormatOptions {
21+
type Error = String;
22+
23+
fn try_from(value: Value) -> Result<Self, Self::Error> {
24+
let Some(object) = value.as_object() else {
25+
return Err("no object passed".to_string());
26+
};
27+
28+
Ok(Self {
29+
experimental: object
30+
.get("fmt.experimental")
31+
.is_some_and(|run| serde_json::from_value::<bool>(run.clone()).unwrap_or_default()),
32+
})
33+
}
34+
}
35+
36+
#[cfg(test)]
37+
mod test {
38+
use serde_json::json;
39+
40+
use super::FormatOptions;
41+
42+
#[test]
43+
fn test_valid_options_json() {
44+
let json = json!({
45+
"fmt.experimental": true,
46+
});
47+
48+
let options = FormatOptions::try_from(json).unwrap();
49+
assert!(options.experimental);
50+
}
51+
52+
#[test]
53+
fn test_empty_options_json() {
54+
let json = json!({});
55+
56+
let options = FormatOptions::try_from(json).unwrap();
57+
assert!(!options.experimental);
58+
}
59+
60+
#[test]
61+
fn test_invalid_options_json() {
62+
let json = json!({
63+
"fmt.experimental": "what", // should be bool
64+
});
65+
66+
let options = FormatOptions::try_from(json).unwrap();
67+
assert!(!options.experimental);
68+
}
69+
}

crates/oxc_language_server/src/formatter/server_formatter.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@ impl ServerFormatter {
1616
}
1717

1818
#[expect(clippy::unused_self)]
19-
pub fn run_single(&self, uri: &Uri) -> Option<Vec<TextEdit>> {
19+
pub fn run_single(&self, uri: &Uri, content: Option<String>) -> Option<Vec<TextEdit>> {
2020
let path = uri.to_file_path()?;
2121
let source_type = get_supported_source_type(&path)?;
22-
let source_text = std::fs::read_to_string(path).expect("Failed to read file");
22+
let source_text = if let Some(content) = content {
23+
content
24+
} else {
25+
std::fs::read_to_string(&path).ok()?
26+
};
2327

2428
let allocator = Allocator::new();
2529
let ret = Parser::new(&allocator, &source_text, source_type)
@@ -37,11 +41,7 @@ impl ServerFormatter {
3741
return None;
3842
}
3943

40-
let options = FormatOptions {
41-
// semicolons: "always".parse().unwrap(),
42-
semicolons: "as-needed".parse().unwrap(),
43-
..FormatOptions::default()
44-
};
44+
let options = FormatOptions::default();
4545
let code = Formatter::new(&allocator, options).build(&ret.program);
4646

4747
Some(vec![TextEdit::new(

crates/oxc_language_server/src/main.rs

Lines changed: 95 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -114,19 +114,19 @@ impl LanguageServer for Backend {
114114
// we will init the linter after requesting for the workspace configuration.
115115
if !capabilities.workspace_configuration || options.is_some() {
116116
for worker in &workers {
117-
worker
118-
.init_linter(
119-
&options
120-
.clone()
121-
.unwrap_or_default()
122-
.iter()
123-
.find(|workspace_option| {
124-
worker.is_responsible_for_uri(&workspace_option.workspace_uri)
125-
})
126-
.map(|workspace_options| workspace_options.options.clone())
127-
.unwrap_or_default(),
128-
)
129-
.await;
117+
let option = &options
118+
.clone()
119+
.unwrap_or_default()
120+
.iter()
121+
.find(|workspace_option| {
122+
worker.is_responsible_for_uri(&workspace_option.workspace_uri)
123+
})
124+
.map(|workspace_options| workspace_options.options.clone())
125+
.unwrap_or_default();
126+
127+
worker.init_options(option).await;
128+
worker.init_linter(&option.lint).await;
129+
worker.init_formatter(&option.format).await;
130130
}
131131
}
132132

@@ -164,11 +164,9 @@ impl LanguageServer for Backend {
164164
ConcurrentHashMap::with_capacity_and_hasher(workers.len(), FxBuildHasher);
165165
let needed_configurations = needed_configurations.pin_owned();
166166
for worker in workers {
167-
if worker.needs_init_linter().await {
167+
if worker.needs_init_linter().await || worker.needs_init_formatter().await {
168168
needed_configurations.insert(worker.get_root_uri().clone(), worker);
169169
}
170-
// ToDo: check for configuration
171-
worker.init_formatter().await;
172170
}
173171

174172
if !needed_configurations.is_empty() {
@@ -178,23 +176,22 @@ impl LanguageServer for Backend {
178176
// every worker should be initialized already in `initialize` request
179177
vec![Some(Options::default()); needed_configurations.len()]
180178
};
179+
let default_options = Options::default();
181180

182181
for (index, worker) in needed_configurations.values().enumerate() {
183-
worker
184-
.init_linter(
185-
configurations
186-
.get(index)
187-
.unwrap_or(&None)
188-
.as_ref()
189-
.unwrap_or(&Options::default()),
190-
)
191-
.await;
182+
let configuration =
183+
configurations.get(index).unwrap_or(&None).as_ref().unwrap_or(&default_options);
184+
185+
worker.init_options(configuration).await;
186+
worker.init_linter(&configuration.lint).await;
187+
worker.init_formatter(&configuration.format).await;
192188
}
193189
}
194190

191+
let mut registrations = vec![];
192+
195193
// init all file watchers
196194
if capabilities.dynamic_watchers {
197-
let mut registrations = vec![];
198195
for worker in workers {
199196
registrations.push(Registration {
200197
id: format!("watcher-{}", worker.get_root_uri().as_str()),
@@ -204,11 +201,33 @@ impl LanguageServer for Backend {
204201
})),
205202
});
206203
}
204+
}
207205

208-
if let Err(err) = self.client.register_capability(registrations).await {
209-
warn!("sending registerCapability.didChangeWatchedFiles failed: {err}");
206+
if capabilities.dynamic_formatting {
207+
// check if one workspace has formatting enabled
208+
let mut started_worker = false;
209+
for worker in workers {
210+
if !worker.needs_init_formatter().await {
211+
started_worker = true;
212+
break;
213+
}
214+
}
215+
216+
if started_worker {
217+
registrations.push(Registration {
218+
id: "dynamic-formatting".to_string(),
219+
method: "textDocument/formatting".to_string(),
220+
register_options: None,
221+
});
210222
}
211223
}
224+
225+
if registrations.is_empty() {
226+
return;
227+
}
228+
if let Err(err) = self.client.register_capability(registrations).await {
229+
warn!("sending registerCapability.didChangeWatchedFiles failed: {err}");
230+
}
212231
}
213232

214233
async fn shutdown(&self) -> Result<()> {
@@ -279,14 +298,26 @@ impl LanguageServer for Backend {
279298
return;
280299
};
281300

301+
let mut global_formatting_added = false;
302+
let mut global_formatting_removed = false;
303+
282304
for option in resolved_options {
283305
let Some(worker) =
284306
workers.iter().find(|worker| worker.is_responsible_for_uri(&option.workspace_uri))
285307
else {
286308
continue;
287309
};
288310

289-
let (diagnostics, watcher) = worker.did_change_configuration(&option.options).await;
311+
let (diagnostics, watcher, formatting) =
312+
worker.did_change_configuration(&option.options).await;
313+
314+
if self.capabilities.get().is_some_and(|c| c.dynamic_formatting) {
315+
if Some(true) == formatting {
316+
global_formatting_added = true;
317+
} else if Some(false) == formatting {
318+
global_formatting_removed = true;
319+
}
320+
}
290321

291322
if let Some(diagnostics) = diagnostics {
292323
for (uri, reports) in &diagnostics.pin() {
@@ -297,7 +328,9 @@ impl LanguageServer for Backend {
297328
}
298329
}
299330

300-
if let Some(watcher) = watcher {
331+
if let Some(watcher) = watcher
332+
&& self.capabilities.get().is_some_and(|capabilities| capabilities.dynamic_watchers)
333+
{
301334
// remove the old watcher
302335
removing_registrations.push(Unregistration {
303336
id: format!("watcher-{}", worker.get_root_uri().as_str()),
@@ -324,16 +357,27 @@ impl LanguageServer for Backend {
324357
self.publish_all_diagnostics(x).await;
325358
}
326359

327-
if self.capabilities.get().is_some_and(|capabilities| capabilities.dynamic_watchers) {
328-
if !removing_registrations.is_empty() {
329-
if let Err(err) = self.client.unregister_capability(removing_registrations).await {
330-
warn!("sending unregisterCapability.didChangeWatchedFiles failed: {err}");
331-
}
360+
if global_formatting_added {
361+
adding_registrations.push(Registration {
362+
id: "dynamic-formatting".to_string(),
363+
method: "textDocument/formatting".to_string(),
364+
register_options: None,
365+
});
366+
} else if global_formatting_removed {
367+
removing_registrations.push(Unregistration {
368+
id: "dynamic-formatting".to_string(),
369+
method: "textDocument/formatting".to_string(),
370+
});
371+
}
372+
373+
if !removing_registrations.is_empty() {
374+
if let Err(err) = self.client.unregister_capability(removing_registrations).await {
375+
warn!("sending unregisterCapability.didChangeWatchedFiles failed: {err}");
332376
}
333-
if !adding_registrations.is_empty() {
334-
if let Err(err) = self.client.register_capability(adding_registrations).await {
335-
warn!("sending registerCapability.didChangeWatchedFiles failed: {err}");
336-
}
377+
}
378+
if !adding_registrations.is_empty() {
379+
if let Err(err) = self.client.register_capability(adding_registrations).await {
380+
warn!("sending registerCapability.didChangeWatchedFiles failed: {err}");
337381
}
338382
}
339383
}
@@ -401,6 +445,8 @@ impl LanguageServer for Backend {
401445

402446
self.publish_all_diagnostics(&cleared_diagnostics).await;
403447

448+
let default_options = Options::default();
449+
404450
// client support `workspace/configuration` request
405451
if self.capabilities.get().is_some_and(|capabilities| capabilities.workspace_configuration)
406452
{
@@ -414,7 +460,12 @@ impl LanguageServer for Backend {
414460
let worker = WorkspaceWorker::new(folder.uri.clone());
415461
// get the configuration from the response and init the linter
416462
let options = configurations.get(index).unwrap_or(&None);
417-
worker.init_linter(options.as_ref().unwrap_or(&Options::default())).await;
463+
let options = options.as_ref().unwrap_or(&default_options);
464+
465+
worker.init_options(options).await;
466+
worker.init_linter(&options.lint).await;
467+
worker.init_formatter(&options.format).await;
468+
418469
added_registrations.push(Registration {
419470
id: format!("watcher-{}", worker.get_root_uri().as_str()),
420471
method: "workspace/didChangeWatchedFiles".to_string(),
@@ -429,7 +480,9 @@ impl LanguageServer for Backend {
429480
for folder in params.event.added {
430481
let worker = WorkspaceWorker::new(folder.uri);
431482
// use default options
432-
worker.init_linter(&Options::default()).await;
483+
worker.init_options(&default_options).await;
484+
worker.init_linter(&default_options.lint).await;
485+
worker.init_formatter(&default_options.format).await;
433486
workers.push(worker);
434487
}
435488
}
@@ -603,7 +656,7 @@ impl LanguageServer for Backend {
603656
let Some(worker) = workers.iter().find(|worker| worker.is_responsible_for_uri(uri)) else {
604657
return Ok(None);
605658
};
606-
Ok(worker.format_file(uri).await)
659+
Ok(worker.format_file(uri, self.file_system.read().await.get(uri)).await)
607660
}
608661
}
609662

0 commit comments

Comments
 (0)