diff --git a/crates/oxc_language_server/fixtures/linter/lint_on_run/.oxlintrc.json b/crates/oxc_language_server/fixtures/linter/lint_on_run/.oxlintrc.json deleted file mode 100644 index 0967ef424bce6..0000000000000 --- a/crates/oxc_language_server/fixtures/linter/lint_on_run/.oxlintrc.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/crates/oxc_language_server/fixtures/linter/lint_on_run/on_save/on-save.ts b/crates/oxc_language_server/fixtures/linter/lint_on_run/on_save/on-save.ts deleted file mode 100644 index 636897b610a26..0000000000000 --- a/crates/oxc_language_server/fixtures/linter/lint_on_run/on_save/on-save.ts +++ /dev/null @@ -1,8 +0,0 @@ - -debugger; - -async function returnsPromise() { - return "value"; -} - -returnsPromise().then(() => {}); diff --git a/crates/oxc_language_server/fixtures/linter/lint_on_run/on_save/on-type.ts b/crates/oxc_language_server/fixtures/linter/lint_on_run/on_save/on-type.ts deleted file mode 100644 index 636897b610a26..0000000000000 --- a/crates/oxc_language_server/fixtures/linter/lint_on_run/on_save/on-type.ts +++ /dev/null @@ -1,8 +0,0 @@ - -debugger; - -async function returnsPromise() { - return "value"; -} - -returnsPromise().then(() => {}); diff --git a/crates/oxc_language_server/fixtures/linter/lint_on_run/on_type/on-save-no-type-aware.ts b/crates/oxc_language_server/fixtures/linter/lint_on_run/on_type/on-save-no-type-aware.ts deleted file mode 100644 index 33daf56106c36..0000000000000 --- a/crates/oxc_language_server/fixtures/linter/lint_on_run/on_type/on-save-no-type-aware.ts +++ /dev/null @@ -1,7 +0,0 @@ -debugger; - -async function returnsPromise() { - return "value"; -} - -returnsPromise().then(() => {}); \ No newline at end of file diff --git a/crates/oxc_language_server/fixtures/linter/lint_on_run/on_type/on-save.ts b/crates/oxc_language_server/fixtures/linter/lint_on_run/on_type/on-save.ts deleted file mode 100644 index 636897b610a26..0000000000000 --- a/crates/oxc_language_server/fixtures/linter/lint_on_run/on_type/on-save.ts +++ /dev/null @@ -1,8 +0,0 @@ - -debugger; - -async function returnsPromise() { - return "value"; -} - -returnsPromise().then(() => {}); diff --git a/crates/oxc_language_server/fixtures/linter/lint_on_run/on_type/on-type.ts b/crates/oxc_language_server/fixtures/linter/lint_on_run/on_type/on-type.ts deleted file mode 100644 index 636897b610a26..0000000000000 --- a/crates/oxc_language_server/fixtures/linter/lint_on_run/on_type/on-type.ts +++ /dev/null @@ -1,8 +0,0 @@ - -debugger; - -async function returnsPromise() { - return "value"; -} - -returnsPromise().then(() => {}); diff --git a/crates/oxc_language_server/src/backend.rs b/crates/oxc_language_server/src/backend.rs index 1a3b7681dafd9..e0f656f211cb1 100644 --- a/crates/oxc_language_server/src/backend.rs +++ b/crates/oxc_language_server/src/backend.rs @@ -23,7 +23,7 @@ use crate::{ code_actions::CODE_ACTION_KIND_SOURCE_FIX_ALL_OXC, commands::{FIX_ALL_COMMAND_ID, FixAllCommandArgs}, file_system::LSPFileSystem, - linter::server_linter::ServerLinterRun, + linter::options::Run, options::{Options, WorkspaceOption}, worker::WorkspaceWorker, }; @@ -488,7 +488,11 @@ impl LanguageServer for Backend { self.file_system.write().await.remove(uri); } - if let Some(diagnostics) = worker.lint_file(uri, None, ServerLinterRun::OnSave).await { + if !worker.should_lint_on_run_type(Run::OnSave).await { + return; + } + + if let Some(diagnostics) = worker.lint_file(uri, None).await { self.client .publish_diagnostics( uri.clone(), @@ -516,7 +520,11 @@ impl LanguageServer for Backend { self.file_system.write().await.set(uri, content.clone()); } - if let Some(diagnostics) = worker.lint_file(uri, content, ServerLinterRun::OnType).await { + if !worker.should_lint_on_run_type(Run::OnType).await { + return; + } + + if let Some(diagnostics) = worker.lint_file(uri, content).await { self.client .publish_diagnostics( uri.clone(), @@ -544,9 +552,7 @@ impl LanguageServer for Backend { self.file_system.write().await.set(uri, content.clone()); } - if let Some(diagnostics) = - worker.lint_file(uri, Some(content), ServerLinterRun::Always).await - { + if let Some(diagnostics) = worker.lint_file(uri, Some(content)).await { self.client .publish_diagnostics( uri.clone(), diff --git a/crates/oxc_language_server/src/linter/server_linter.rs b/crates/oxc_language_server/src/linter/server_linter.rs index ed2aa9fee716f..7c7b806d831a2 100644 --- a/crates/oxc_language_server/src/linter/server_linter.rs +++ b/crates/oxc_language_server/src/linter/server_linter.rs @@ -18,36 +18,17 @@ use crate::linter::options::UnusedDisableDirectives; use crate::linter::{ error_with_position::DiagnosticReport, isolated_lint_handler::{IsolatedLintHandler, IsolatedLintHandlerOptions}, - options::{LintOptions as LSPLintOptions, Run}, + options::LintOptions as LSPLintOptions, }; use crate::utils::normalize_path; use crate::{ConcurrentHashMap, LINT_CONFIG_FILE}; use super::config_walker::ConfigWalker; -#[derive(Debug, PartialEq, Eq)] -pub enum ServerLinterRun { - OnType, - OnSave, - Always, -} - -impl ServerLinterRun { - fn matches(&self, run: Run) -> bool { - matches!( - (self, run), - (ServerLinterRun::OnType, Run::OnType) - | (ServerLinterRun::OnSave, Run::OnSave) - | (ServerLinterRun::Always, _) - ) - } -} - pub struct ServerLinter { isolated_linter: Arc>, ignore_matcher: LintIgnoreMatcher, gitignore_glob: Vec, - lint_on_run: Run, diagnostics: ServerLinterDiagnostics, extended_paths: FxHashSet, } @@ -172,7 +153,6 @@ impl ServerLinter { ), gitignore_glob: Self::create_ignore_glob(&root_path), extended_paths, - lint_on_run: options.run, diagnostics: ServerLinterDiagnostics::default(), } } @@ -280,9 +260,7 @@ impl ServerLinter { pub async fn revalidate_diagnostics(&self, uris: Vec) -> Vec<(String, Vec)> { let mut diagnostics = Vec::with_capacity(uris.len()); for uri in uris { - if let Some(file_diagnostic) = - self.run_single(&uri, None, ServerLinterRun::Always).await - { + if let Some(file_diagnostic) = self.run_single(&uri, None).await { diagnostics.push(( uri.to_string(), file_diagnostic.into_iter().map(|d| d.diagnostic).collect(), @@ -318,15 +296,7 @@ impl ServerLinter { &self, uri: &Uri, content: Option, - run_type: ServerLinterRun, ) -> Option> { - let run = matches!(run_type, ServerLinterRun::Always) || run_type.matches(self.lint_on_run); - - // return `None` when both tools do not want to be used - if !run { - return None; - } - if self.is_ignored(uri) { return None; } @@ -461,71 +431,6 @@ mod test { assert!(result_empty.is_none()); } - #[test] - #[cfg(not(target_endian = "big"))] - fn test_lint_on_run_on_type_on_type() { - Tester::new( - "fixtures/linter/lint_on_run/on_type", - Some(LintOptions { - type_aware: true, - run: Run::OnType, - fix_kind: LintFixKindFlag::All, - ..Default::default() - }), - ) - .test_and_snapshot_single_file_with_run_type("on-type.ts", Run::OnType); - } - - #[test] - #[cfg(not(target_endian = "big"))] - fn test_lint_on_run_on_save_on_save() { - Tester::new( - "fixtures/linter/lint_on_run/on_save", - Some(LintOptions { - type_aware: true, - run: Run::OnType, - fix_kind: LintFixKindFlag::All, - ..Default::default() - }), - ) - .test_and_snapshot_single_file_with_run_type("on-save.ts", Run::OnSave); - } - - #[test] - #[cfg(not(target_endian = "big"))] - fn test_lint_on_run_on_save_on_type() { - Tester::new( - "fixtures/linter/lint_on_run/on_save", - Some(LintOptions { type_aware: true, run: Run::OnSave, ..Default::default() }), - ) - .test_and_snapshot_single_file_with_run_type("on-type.ts", Run::OnType); - } - - #[test] - #[cfg(not(target_endian = "big"))] - fn test_lint_on_run_on_type_on_save() { - Tester::new( - "fixtures/linter/lint_on_run/on_save", - Some(LintOptions { - type_aware: true, - run: Run::OnType, - fix_kind: LintFixKindFlag::All, - ..Default::default() - }), - ) - .test_and_snapshot_single_file_with_run_type("on-save.ts", Run::OnSave); - } - - #[test] - #[cfg(not(target_endian = "big"))] - fn test_lint_on_run_on_type_on_save_without_type_aware() { - Tester::new( - "fixtures/linter/lint_on_run/on_type", - Some(LintOptions { type_aware: false, run: Run::OnType, ..Default::default() }), - ) - .test_and_snapshot_single_file_with_run_type("on-save-no-type-aware.ts", Run::OnSave); - } - #[test] fn test_no_errors() { Tester::new("fixtures/linter/no_errors", None) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_save@on-save.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_save@on-save.ts.snap deleted file mode 100644 index bdd699e1fd9ea..0000000000000 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_save@on-save.ts.snap +++ /dev/null @@ -1,7 +0,0 @@ ---- -source: crates/oxc_language_server/src/tester.rs ---- -########## -file: fixtures/linter/lint_on_run/on_save/on-save.ts ----------- -File is ignored diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_save@on-type.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_save@on-type.ts.snap deleted file mode 100644 index dfced02e27672..0000000000000 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_save@on-type.ts.snap +++ /dev/null @@ -1,7 +0,0 @@ ---- -source: crates/oxc_language_server/src/tester.rs ---- -########## -file: fixtures/linter/lint_on_run/on_save/on-type.ts ----------- -File is ignored diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_type@on-save-no-type-aware.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_type@on-save-no-type-aware.ts.snap deleted file mode 100644 index e2d3269a67f6b..0000000000000 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_type@on-save-no-type-aware.ts.snap +++ /dev/null @@ -1,7 +0,0 @@ ---- -source: crates/oxc_language_server/src/tester.rs ---- -########## -file: fixtures/linter/lint_on_run/on_type/on-save-no-type-aware.ts ----------- -File is ignored diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_type@on-type.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_type@on-type.ts.snap deleted file mode 100644 index cb6630f8df32b..0000000000000 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_type@on-type.ts.snap +++ /dev/null @@ -1,149 +0,0 @@ ---- -source: crates/oxc_language_server/src/tester.rs ---- -########## -file: fixtures/linter/lint_on_run/on_type/on-type.ts ----------- - -code: "eslint(no-debugger)" -code_description.href: "https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html" -message: "`debugger` statement is not allowed\nhelp: Remove the debugger statement" -range: Range { start: Position { line: 1, character: 0 }, end: Position { line: 1, character: 9 } } -related_information[0].message: "" -related_information[0].location.uri: "file:///fixtures/linter/lint_on_run/on_type/on-type.ts" -related_information[0].location.range: Range { start: Position { line: 1, character: 0 }, end: Position { line: 1, character: 9 } } -severity: Some(Warning) -source: Some("oxc") -tags: None -fixed: Multiple( - [ - FixedContent { - message: Some( - "Remove the debugger statement", - ), - code: "", - range: Range { - start: Position { - line: 1, - character: 0, - }, - end: Position { - line: 1, - character: 9, - }, - }, - }, - FixedContent { - message: Some( - "Disable no-debugger for this line", - ), - code: "// oxlint-disable-next-line no-debugger\n", - range: Range { - start: Position { - line: 1, - character: 0, - }, - end: Position { - line: 1, - character: 0, - }, - }, - }, - FixedContent { - message: Some( - "Disable no-debugger for this file", - ), - code: "// oxlint-disable no-debugger\n", - range: Range { - start: Position { - line: 0, - character: 0, - }, - end: Position { - line: 0, - character: 0, - }, - }, - }, - ], -) - - -code: "typescript-eslint(no-floating-promises)" -code_description.href: "None" -message: "Promises must be awaited.\nhelp: The promise must end with a call to .catch, or end with a call to .then with a rejection handler, or be explicitly marked as ignored with the `void` operator." -range: Range { start: Position { line: 7, character: 0 }, end: Position { line: 7, character: 32 } } -related_information[0].message: "" -related_information[0].location.uri: "file:///fixtures/linter/lint_on_run/on_type/on-type.ts" -related_information[0].location.range: Range { start: Position { line: 7, character: 0 }, end: Position { line: 7, character: 32 } } -severity: Some(Warning) -source: Some("oxc") -tags: None -fixed: Multiple( - [ - FixedContent { - message: Some( - "Promises must be awaited.", - ), - code: "void ", - range: Range { - start: Position { - line: 7, - character: 0, - }, - end: Position { - line: 7, - character: 0, - }, - }, - }, - FixedContent { - message: Some( - "Promises must be awaited.", - ), - code: "await ", - range: Range { - start: Position { - line: 7, - character: 0, - }, - end: Position { - line: 7, - character: 0, - }, - }, - }, - FixedContent { - message: Some( - "Disable no-floating-promises for this line", - ), - code: "// oxlint-disable-next-line no-floating-promises\n", - range: Range { - start: Position { - line: 7, - character: 0, - }, - end: Position { - line: 7, - character: 0, - }, - }, - }, - FixedContent { - message: Some( - "Disable no-floating-promises for this file", - ), - code: "// oxlint-disable no-floating-promises\n", - range: Range { - start: Position { - line: 0, - character: 0, - }, - end: Position { - line: 0, - character: 0, - }, - }, - }, - ], -) diff --git a/crates/oxc_language_server/src/tester.rs b/crates/oxc_language_server/src/tester.rs index 891cb85e49eea..70bdf7f3212b4 100644 --- a/crates/oxc_language_server/src/tester.rs +++ b/crates/oxc_language_server/src/tester.rs @@ -5,14 +5,7 @@ use tower_lsp_server::{ lsp_types::{CodeDescription, NumberOrString, Uri}, }; -use crate::{ - linter::{ - options::{LintOptions, Run}, - server_linter::ServerLinterRun, - }, - options::Options, - worker::WorkspaceWorker, -}; +use crate::{linter::options::LintOptions, options::Options, worker::WorkspaceWorker}; use super::linter::error_with_position::DiagnosticReport; @@ -123,48 +116,15 @@ impl Tester<'_> { /// Given a relative file path (relative to `oxc_language_server` crate root), run the linter /// and return the resulting diagnostics in a custom snapshot format. pub fn test_and_snapshot_single_file(&self, relative_file_path: &str) { - self.test_and_snapshot_single_file_with_run_type( - relative_file_path, - self.options.as_ref().map_or(Run::default(), |o| o.run), - ); + self.test_and_snapshot_multiple_file(&[relative_file_path]); } pub fn test_and_snapshot_multiple_file(&self, relative_file_paths: &[&str]) { - self.test_and_snapshot_multiple_file_with_run_type( - relative_file_paths, - self.options.as_ref().map_or(Run::default(), |o| o.run), - ); - } - - pub fn test_and_snapshot_single_file_with_run_type( - &self, - relative_file_path: &str, - run_type: Run, - ) { - self.test_and_snapshot_multiple_file_with_run_type(&[relative_file_path], run_type); - } - - #[expect(clippy::disallowed_methods)] - pub fn test_and_snapshot_multiple_file_with_run_type( - &self, - relative_file_paths: &[&str], - run_type: Run, - ) { let mut snapshot_result = String::new(); for relative_file_path in relative_file_paths { let uri = get_file_uri(&format!("{}/{}", self.relative_root_dir, relative_file_path)); let reports = tokio::runtime::Runtime::new().unwrap().block_on(async { - self.create_workspace_worker() - .await - .lint_file( - &uri, - None, - match run_type { - Run::OnSave => ServerLinterRun::OnSave, - Run::OnType => ServerLinterRun::OnType, - }, - ) - .await + self.create_workspace_worker().await.lint_file(&uri, None).await }); let snapshot = if let Some(reports) = reports { @@ -184,10 +144,12 @@ impl Tester<'_> { ); } + #[expect(clippy::disallowed_methods)] let snapshot_name = self.relative_root_dir.replace('/', "_"); let mut settings = insta::Settings::clone_current(); settings.set_prepend_module_to_snapshot(false); settings.set_omit_expression(true); + #[expect(clippy::disallowed_methods)] settings.set_snapshot_suffix(relative_file_paths.join("_").replace('\\', "/")); settings.bind(|| { insta::assert_snapshot!(snapshot_name, snapshot_result); diff --git a/crates/oxc_language_server/src/worker.rs b/crates/oxc_language_server/src/worker.rs index 13d5536b6937c..dd68d9d930554 100644 --- a/crates/oxc_language_server/src/worker.rs +++ b/crates/oxc_language_server/src/worker.rs @@ -15,8 +15,8 @@ use crate::{ formatter::{options::FormatOptions, server_formatter::ServerFormatter}, linter::{ error_with_position::DiagnosticReport, - options::LintOptions, - server_linter::{ServerLinter, ServerLinterRun}, + options::{LintOptions, Run}, + server_linter::ServerLinter, }, options::Options, }; @@ -185,6 +185,14 @@ impl WorkspaceWorker { *self.server_formatter.write().await = Some(server_formatter); } + /// Check if the linter should run for the given run type + /// This compares the current run level from the options with the given run type + pub async fn should_lint_on_run_type(&self, current_run: Run) -> bool { + let run_level = { self.options.lock().await.as_ref().map(|o| o.lint.run) }; + + run_level == Some(current_run) + } + /// Lint a file with the current linter /// - If the file is not lintable, [`None`] is returned /// - If the file is lintable, but no diagnostics are found, an empty vector is returned @@ -192,13 +200,12 @@ impl WorkspaceWorker { &self, uri: &Uri, content: Option, - run_type: ServerLinterRun, ) -> Option> { let Some(server_linter) = &*self.server_linter.read().await else { return None; }; - server_linter.run_single(uri, content, run_type).await + server_linter.run_single(uri, content).await } /// Format a file with the current formatter @@ -260,7 +267,7 @@ impl WorkspaceWorker { let value = if let Some(cached_diagnostics) = server_linter.get_cached_diagnostics(uri) { cached_diagnostics } else { - let diagnostics = server_linter.run_single(uri, None, ServerLinterRun::Always).await; + let diagnostics = server_linter.run_single(uri, None).await; diagnostics.unwrap_or_default() }; @@ -298,7 +305,7 @@ impl WorkspaceWorker { let value = if let Some(cached_diagnostics) = server_linter.get_cached_diagnostics(uri) { cached_diagnostics } else { - let diagnostics = server_linter.run_single(uri, None, ServerLinterRun::Always).await; + let diagnostics = server_linter.run_single(uri, None).await; diagnostics.unwrap_or_default() };