Skip to content

Commit 181d253

Browse files
committed
feat(language_server): able to set off for run configuration
1 parent b8f8ce5 commit 181d253

File tree

5 files changed

+122
-22
lines changed

5 files changed

+122
-22
lines changed

crates/oxc_language_server/README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ This crate provides an [LSP](https://microsoft.github.io/language-server-protoco
1919

2020
These options can be passed with [initialize](#initialize), [workspace/didChangeConfiguration](#workspace/didChangeConfiguration) and [workspace/configuration](#workspace/configuration).
2121

22-
| Option Key | Value(s) | Default | Description |
23-
| ------------------------- | ------------------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
24-
| `run` | `"onSave" \| "onType"` | `"onType"` | Should the server lint the files when the user is typing or saving |
25-
| `configPath` | `<string>` \| `null` | `null` | Path to a oxlint configuration file, passing a string will disable nested configuration |
26-
| `tsConfigPath` | `<string>` \| `null` | `null` | Path to a TypeScript configuration file. If your `tsconfig.json` is not at the root, alias paths will not be resolve correctly for the `import` plugin |
27-
| `unusedDisableDirectives` | `"allow" \| "warn"` \| "deny"` | `"allow"` | Define how directive comments like `// oxlint-disable-line` should be reported, when no errors would have been reported on that line anyway |
28-
| `typeAware` | `true` \| `false` | `false` | Enables type-aware linting |
29-
| `flags` | `Map<string, string>` | `<empty>` | Special oxc language server flags, currently only one flag key is supported: `disable_nested_config` |
30-
| `fmt.experimental` | `true` \| `false` | `false` | Enables experimental formatting with `oxc_formatter` |
31-
| `fmt.configPath` | `<string>` \| `null` | `null` | Path to a oxfmt configuration file, when `null` is passed, the server will use `.oxfmtrc.json` and the workspace root |
22+
| Option Key | Value(s) | Default | Description |
23+
| ------------------------- | ------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
24+
| `run` | `"onSave" \| "onType" \| "off"` | `"onType"` | Should the server lint the files when the user is typing or saving |
25+
| `configPath` | `<string>` \| `null` | `null` | Path to a oxlint configuration file, passing a string will disable nested configuration |
26+
| `tsConfigPath` | `<string>` \| `null` | `null` | Path to a TypeScript configuration file. If your `tsconfig.json` is not at the root, alias paths will not be resolve correctly for the `import` plugin |
27+
| `unusedDisableDirectives` | `"allow" \| "warn"` \| "deny"` | `"allow"` | Define how directive comments like `// oxlint-disable-line` should be reported, when no errors would have been reported on that line anyway |
28+
| `typeAware` | `true` \| `false` | `false` | Enables type-aware linting |
29+
| `flags` | `Map<string, string>` | `<empty>` | Special oxc language server flags, currently only one flag key is supported: `disable_nested_config` |
30+
| `fmt.experimental` | `true` \| `false` | `false` | Enables experimental formatting with `oxc_formatter` |
31+
| `fmt.configPath` | `<string>` \| `null` | `null` | Path to a oxfmt configuration file, when `null` is passed, the server will use `.oxfmtrc.json` and the workspace root |
3232

3333
## Supported LSP Specifications from Server
3434

crates/oxc_language_server/src/linter/options.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub enum Run {
2020
OnSave,
2121
#[default]
2222
OnType,
23+
Off,
2324
}
2425

2526
#[derive(Debug, Default, Serialize, Clone)]

crates/oxc_language_server/src/linter/server_linter.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,8 @@ impl ServerLinter {
332332
// run only oxlint on type
333333
// tsgolint does not support memory source_text
334334
(ServerLinterRun::OnType, Run::OnType) => (true, false),
335-
// it does not match, run nothing
336-
(ServerLinterRun::OnType, Run::OnSave) => (false, false),
335+
// it is disabled, or it does not match, run nothing
336+
(_, Run::Off) | (ServerLinterRun::OnType, Run::OnSave) => (false, false),
337337
// In onType mode, only TypeScript type checking runs on save
338338
// If type_aware is disabled (tsgo_linter is None), skip everything to preserve diagnostics
339339
(ServerLinterRun::OnSave, Run::OnType) => {
@@ -403,6 +403,11 @@ impl ServerLinter {
403403
new_options: &LSPLintOptions,
404404
root_path: &Path,
405405
) -> Option<Vec<Pattern>> {
406+
// from off to on, need to watch all patterns
407+
if old_options.run == Run::Off && new_options.run != Run::Off {
408+
return Some(self.get_watch_patterns(new_options, root_path));
409+
}
410+
406411
if old_options.config_path == new_options.config_path
407412
&& old_options.use_nested_configs() == new_options.use_nested_configs()
408413
{

crates/oxc_language_server/src/tester.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ impl Tester<'_> {
162162
match run_type {
163163
Run::OnSave => ServerLinterRun::OnSave,
164164
Run::OnType => ServerLinterRun::OnType,
165+
Run::Off => panic!("Linter is disabled"),
165166
},
166167
)
167168
.await

crates/oxc_language_server/src/worker.rs

Lines changed: 103 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{
1515
formatter::{options::FormatOptions, server_formatter::ServerFormatter},
1616
linter::{
1717
error_with_position::DiagnosticReport,
18-
options::LintOptions,
18+
options::{LintOptions, Run},
1919
server_linter::{ServerLinter, ServerLinterRun},
2020
},
2121
options::Options,
@@ -68,7 +68,12 @@ impl WorkspaceWorker {
6868
pub async fn start_worker(&self, options: &Options) {
6969
*self.options.lock().await = Some(options.clone());
7070

71-
*self.server_linter.write().await = Some(ServerLinter::new(&self.root_uri, &options.lint));
71+
if options.lint.run != Run::Off {
72+
debug!("linter enabled");
73+
*self.server_linter.write().await =
74+
Some(ServerLinter::new(&self.root_uri, &options.lint));
75+
}
76+
7277
if options.format.experimental {
7378
debug!("experimental formatter enabled");
7479
*self.server_formatter.write().await =
@@ -320,13 +325,15 @@ impl WorkspaceWorker {
320325
let format_options = options.as_ref().map(|o| o.format.clone()).unwrap_or_default();
321326
let lint_options = options.as_ref().map(|o| o.lint.clone()).unwrap_or_default();
322327

323-
if format_options.experimental {
328+
if format_options.experimental && lint_options.run != Run::Off {
324329
tokio::join!(
325330
self.refresh_server_formatter(&format_options),
326331
self.refresh_server_linter(&lint_options)
327332
);
328-
} else {
333+
} else if lint_options.run != Run::Off {
329334
self.refresh_server_linter(&lint_options).await;
335+
} else if format_options.experimental {
336+
self.refresh_server_formatter(&format_options).await;
330337
}
331338

332339
Some(self.revalidate_diagnostics(files).await)
@@ -421,7 +428,12 @@ impl WorkspaceWorker {
421428
}
422429
}
423430

424-
if ServerLinter::needs_restart(&current_option.lint, &changed_options.lint) {
431+
let is_linter_enabled =
432+
current_option.lint.run == Run::Off && changed_options.lint.run != Run::Off;
433+
434+
if is_linter_enabled
435+
|| ServerLinter::needs_restart(&current_option.lint, &changed_options.lint)
436+
{
425437
// get the cached files before refreshing the linter
426438
let linter_files = {
427439
let linter_guard = self.server_linter.read().await;
@@ -450,10 +462,13 @@ impl WorkspaceWorker {
450462
}
451463

452464
if let Some(patterns) = patterns {
453-
unregistrations.push(Unregistration {
454-
id: format!("watcher-linter-{}", self.root_uri.as_str()),
455-
method: "workspace/didChangeWatchedFiles".to_string(),
456-
});
465+
// when linter was disabled before, no need to unregister
466+
if !is_linter_enabled {
467+
unregistrations.push(Unregistration {
468+
id: format!("watcher-linter-{}", self.root_uri.as_str()),
469+
method: "workspace/didChangeWatchedFiles".to_string(),
470+
});
471+
}
457472

458473
registrations.push(Registration {
459474
id: format!("watcher-linter-{}", self.root_uri.as_str()),
@@ -472,6 +487,17 @@ impl WorkspaceWorker {
472487
})),
473488
});
474489
}
490+
} else if current_option.lint.run != Run::Off && changed_options.lint.run == Run::Off {
491+
// the current files needs to be cleared
492+
diagnostics = Some(self.get_clear_diagnostics().await);
493+
494+
// linter is turned off
495+
*self.server_linter.write().await = None;
496+
497+
unregistrations.push(Unregistration {
498+
id: format!("watcher-linter-{}", self.root_uri.as_str()),
499+
method: "workspace/didChangeWatchedFiles".to_string(),
500+
});
475501
}
476502

477503
(diagnostics, registrations, unregistrations, formatting)
@@ -599,7 +625,9 @@ mod test_watchers {
599625

600626
mod init_watchers {
601627
use crate::{
602-
formatter::options::FormatOptions, linter::options::LintOptions, options::Options,
628+
formatter::options::FormatOptions,
629+
linter::options::{LintOptions, Run},
630+
options::Options,
603631
worker::test_watchers::Tester,
604632
};
605633

@@ -612,6 +640,20 @@ mod test_watchers {
612640
tester.assert_eq_registration(&registrations[0], "linter", &["**/.oxlintrc.json"]);
613641
}
614642

643+
#[test]
644+
fn test_disabling_linter() {
645+
let tester = Tester::new(
646+
"fixtures/watcher/default",
647+
&Options {
648+
lint: LintOptions { run: Run::Off, ..Default::default() },
649+
..Default::default()
650+
},
651+
);
652+
let registrations = tester.init_watchers();
653+
654+
assert_eq!(registrations.len(), 0);
655+
}
656+
615657
#[test]
616658
fn test_custom_config_path() {
617659
let tester = Tester::new(
@@ -682,6 +724,21 @@ mod test_watchers {
682724
tester.assert_eq_registration(&watchers[1], "formatter", &[".oxfmtrc.json"]);
683725
}
684726

727+
#[test]
728+
fn test_formatter_with_disabled_linter() {
729+
let tester = Tester::new(
730+
"fixtures/watcher/default",
731+
&Options {
732+
lint: LintOptions { run: Run::Off, ..Default::default() },
733+
format: FormatOptions { experimental: true, ..Default::default() },
734+
},
735+
);
736+
let watchers = tester.init_watchers();
737+
738+
assert_eq!(watchers.len(), 1);
739+
tester.assert_eq_registration(&watchers[0], "formatter", &[".oxfmtrc.json"]);
740+
}
741+
685742
#[test]
686743
fn test_formatter_custom_config_path() {
687744
let tester = Tester::new(
@@ -896,5 +953,41 @@ mod test_watchers {
896953
}
897954
);
898955
}
956+
957+
#[test]
958+
fn test_linter_disabling() {
959+
let tester = Tester::new("fixtures/watcher/default", &Options::default());
960+
let (registration, unregistrations) = tester.did_change_configuration(&Options {
961+
lint: LintOptions { run: Run::Off, ..Default::default() },
962+
..Default::default()
963+
});
964+
965+
assert_eq!(unregistrations.len(), 1);
966+
assert_eq!(registration.len(), 0);
967+
assert_eq!(
968+
unregistrations[0],
969+
Unregistration {
970+
id: format!("watcher-linter-{}", tester.worker.get_root_uri().as_str()),
971+
method: "workspace/didChangeWatchedFiles".to_string(),
972+
}
973+
);
974+
}
975+
976+
#[test]
977+
fn test_linter_enabling() {
978+
let tester = Tester::new(
979+
"fixtures/watcher/default",
980+
&Options {
981+
lint: LintOptions { run: Run::Off, ..Default::default() },
982+
..Default::default()
983+
},
984+
);
985+
let (registration, unregistrations) =
986+
tester.did_change_configuration(&Options::default());
987+
988+
assert_eq!(unregistrations.len(), 0);
989+
assert_eq!(registration.len(), 1);
990+
tester.assert_eq_registration(&registration[0], "linter", &["**/.oxlintrc.json"]);
991+
}
899992
}
900993
}

0 commit comments

Comments
 (0)