Skip to content

Commit e3d65c3

Browse files
committed
feat(language_server): Search both .json and .jsonc config file (#14868)
The same as the CLI is now doing it: #14848
1 parent 2bc1978 commit e3d65c3

File tree

3 files changed

+64
-28
lines changed

3 files changed

+64
-28
lines changed

crates/oxc_language_server/src/formatter/server_formatter.rs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::path::Path;
1+
use std::path::{Path, PathBuf};
22

33
use log::warn;
44
use oxc_allocator::Allocator;
@@ -13,7 +13,7 @@ use tower_lsp_server::{
1313
};
1414

1515
use crate::formatter::options::FormatOptions as LSPFormatOptions;
16-
use crate::{FORMAT_CONFIG_FILE, utils::normalize_path};
16+
use crate::{FORMAT_CONFIG_FILES, utils::normalize_path};
1717
pub struct ServerFormatter {
1818
options: FormatOptions,
1919
}
@@ -77,10 +77,28 @@ impl ServerFormatter {
7777
)])
7878
}
7979

80+
fn search_config_file(root_path: &Path, config_path: Option<&String>) -> Option<PathBuf> {
81+
if let Some(config_path) = config_path {
82+
let config = normalize_path(root_path.join(config_path));
83+
if config.try_exists().is_ok_and(|exists| exists) {
84+
return Some(config);
85+
}
86+
87+
warn!(
88+
"Config file not found: {}, searching for `{}` in the root path",
89+
config.to_string_lossy(),
90+
FORMAT_CONFIG_FILES.join(", ")
91+
);
92+
}
93+
94+
FORMAT_CONFIG_FILES.iter().find_map(|&file| {
95+
let config = normalize_path(root_path.join(file));
96+
config.try_exists().is_ok_and(|exists| exists).then_some(config)
97+
})
98+
}
99+
80100
fn get_format_options(root_path: &Path, config_path: Option<&String>) -> FormatOptions {
81-
let config_path = config_path.map_or(FORMAT_CONFIG_FILE, |v| v);
82-
let config = normalize_path(root_path.join(config_path));
83-
let oxfmtrc = if config.try_exists().is_ok_and(|exists| exists) {
101+
let oxfmtrc = if let Some(config) = Self::search_config_file(root_path, config_path) {
84102
if let Ok(oxfmtrc) = Oxfmtrc::from_file(&config) {
85103
oxfmtrc
86104
} else {
@@ -90,7 +108,7 @@ impl ServerFormatter {
90108
} else {
91109
warn!(
92110
"Config file not found: {}, fallback to default config",
93-
config.to_string_lossy()
111+
config_path.unwrap_or(&FORMAT_CONFIG_FILES.join(", "))
94112
);
95113
Oxfmtrc::default()
96114
};
@@ -105,15 +123,19 @@ impl ServerFormatter {
105123
}
106124

107125
#[expect(clippy::unused_self)]
108-
pub fn get_watcher_patterns(&self, options: &LSPFormatOptions) -> Pattern {
109-
options.config_path.as_ref().map_or(FORMAT_CONFIG_FILE, |v| v).to_owned()
126+
pub fn get_watcher_patterns(&self, options: &LSPFormatOptions) -> Vec<Pattern> {
127+
if let Some(config_path) = options.config_path.as_ref() {
128+
return vec![config_path.to_string()];
129+
}
130+
131+
FORMAT_CONFIG_FILES.iter().map(|file| (*file).to_string()).collect()
110132
}
111133

112134
pub fn get_changed_watch_patterns(
113135
&self,
114136
old_options: &LSPFormatOptions,
115137
new_options: &LSPFormatOptions,
116-
) -> Option<Pattern> {
138+
) -> Option<Vec<Pattern>> {
117139
if old_options != new_options && new_options.experimental {
118140
return Some(self.get_watcher_patterns(new_options));
119141
}

crates/oxc_language_server/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::backend::Backend;
1919
type ConcurrentHashMap<K, V> = papaya::HashMap<K, V, FxBuildHasher>;
2020

2121
const LINT_CONFIG_FILE: &str = ".oxlintrc.json";
22-
const FORMAT_CONFIG_FILE: &str = ".oxfmtrc.json";
22+
const FORMAT_CONFIG_FILES: &[&str; 2] = &[".oxfmtrc.json", ".oxfmtrc.jsonc"];
2323

2424
#[tokio::main]
2525
async fn main() {

crates/oxc_language_server/src/worker.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,16 @@ impl WorkspaceWorker {
129129
id: format!("watcher-formatter-{}", self.root_uri.as_str()),
130130
method: "workspace/didChangeWatchedFiles".to_string(),
131131
register_options: Some(json!(DidChangeWatchedFilesRegistrationOptions {
132-
watchers: vec![FileSystemWatcher {
133-
glob_pattern: GlobPattern::Relative(RelativePattern {
134-
base_uri: OneOf::Right(self.root_uri.clone()),
135-
pattern: format_patterns
136-
}),
137-
kind: Some(WatchKind::all()), // created, deleted, changed
138-
}]
132+
watchers: format_patterns
133+
.into_iter()
134+
.map(|pattern| FileSystemWatcher {
135+
glob_pattern: GlobPattern::Relative(RelativePattern {
136+
base_uri: OneOf::Right(self.root_uri.clone()),
137+
pattern,
138+
}),
139+
kind: Some(WatchKind::all()), // created, deleted, changed
140+
})
141+
.collect::<Vec<_>>(),
139142
})),
140143
});
141144
}
@@ -378,7 +381,7 @@ impl WorkspaceWorker {
378381
formatting = true;
379382

380383
// Extract pattern data without holding the lock
381-
let pattern = {
384+
let patterns = {
382385
let formatter_guard = self.server_formatter.read().await;
383386
formatter_guard.as_ref().and_then(|formatter| {
384387
formatter.get_changed_watch_patterns(
@@ -388,7 +391,7 @@ impl WorkspaceWorker {
388391
})
389392
};
390393

391-
if let Some(pattern) = pattern {
394+
if let Some(patterns) = patterns {
392395
if current_option.format.experimental {
393396
// unregister the old watcher
394397
unregistrations.push(Unregistration {
@@ -401,13 +404,16 @@ impl WorkspaceWorker {
401404
id: format!("watcher-formatter-{}", self.root_uri.as_str()),
402405
method: "workspace/didChangeWatchedFiles".to_string(),
403406
register_options: Some(json!(DidChangeWatchedFilesRegistrationOptions {
404-
watchers: vec![FileSystemWatcher {
405-
glob_pattern: GlobPattern::Relative(RelativePattern {
406-
base_uri: OneOf::Right(self.root_uri.clone()),
407-
pattern
408-
}),
409-
kind: Some(WatchKind::all()), // created, deleted, changed
410-
}]
407+
watchers: patterns
408+
.into_iter()
409+
.map(|pattern| FileSystemWatcher {
410+
glob_pattern: GlobPattern::Relative(RelativePattern {
411+
base_uri: OneOf::Right(self.root_uri.clone()),
412+
pattern,
413+
}),
414+
kind: Some(WatchKind::all()), // created, deleted, changed
415+
})
416+
.collect::<Vec<_>>(),
411417
})),
412418
});
413419
}
@@ -679,7 +685,11 @@ mod test_watchers {
679685

680686
assert_eq!(watchers.len(), 2);
681687
tester.assert_eq_registration(&watchers[0], "linter", &["**/.oxlintrc.json"]);
682-
tester.assert_eq_registration(&watchers[1], "formatter", &[".oxfmtrc.json"]);
688+
tester.assert_eq_registration(
689+
&watchers[1],
690+
"formatter",
691+
&[".oxfmtrc.json", ".oxfmtrc.jsonc"],
692+
);
683693
}
684694

685695
#[test]
@@ -835,7 +845,11 @@ mod test_watchers {
835845

836846
assert_eq!(unregistrations.len(), 0);
837847
assert_eq!(registration.len(), 1);
838-
tester.assert_eq_registration(&registration[0], "formatter", &[".oxfmtrc.json"]);
848+
tester.assert_eq_registration(
849+
&registration[0],
850+
"formatter",
851+
&[".oxfmtrc.json", ".oxfmtrc.jsonc"],
852+
);
839853
}
840854

841855
#[test]

0 commit comments

Comments
 (0)