diff --git a/crates/oxc_language_server/src/code_actions.rs b/crates/oxc_language_server/src/code_actions.rs index ddd009588078f..363e070f67833 100644 --- a/crates/oxc_language_server/src/code_actions.rs +++ b/crates/oxc_language_server/src/code_actions.rs @@ -1,6 +1,4 @@ -use tower_lsp_server::lsp_types::{ - CodeAction, CodeActionKind, Position, Range, TextEdit, Uri, WorkspaceEdit, -}; +use tower_lsp_server::lsp_types::{CodeAction, CodeActionKind, TextEdit, Uri, WorkspaceEdit}; use crate::linter::error_with_position::{DiagnosticReport, FixedContent, PossibleFixContent}; @@ -11,6 +9,7 @@ fn fix_content_to_code_action( fixed_content: &FixedContent, uri: &Uri, alternative_message: &str, + is_preferred: bool, ) -> CodeAction { // 1) Use `fixed_content.message` if it exists // 2) Try to parse the report diagnostic message @@ -29,7 +28,7 @@ fn fix_content_to_code_action( CodeAction { title, kind: Some(CodeActionKind::QUICKFIX), - is_preferred: Some(true), + is_preferred: Some(is_preferred), edit: Some(WorkspaceEdit { #[expect(clippy::disallowed_types)] changes: Some(std::collections::HashMap::from([( @@ -48,17 +47,31 @@ fn fix_content_to_code_action( pub fn apply_fix_code_actions(report: &DiagnosticReport, uri: &Uri) -> Option> { match &report.fixed_content { PossibleFixContent::None => None, - PossibleFixContent::Single(fixed_content) => { - Some(vec![fix_content_to_code_action(fixed_content, uri, &report.diagnostic.message)]) + PossibleFixContent::Single(fixed_content) => Some(vec![fix_content_to_code_action( + fixed_content, + uri, + &report.diagnostic.message, + true, + )]), + PossibleFixContent::Multiple(fixed_contents) => { + // only the first code action is preferred + let mut preferred = true; + Some( + fixed_contents + .iter() + .map(|fixed_content| { + let action = fix_content_to_code_action( + fixed_content, + uri, + &report.diagnostic.message, + preferred, + ); + preferred = false; + action + }) + .collect(), + ) } - PossibleFixContent::Multiple(fixed_contents) => Some( - fixed_contents - .iter() - .map(|fixed_content| { - fix_content_to_code_action(fixed_content, uri, &report.diagnostic.message) - }) - .collect(), - ), } } @@ -108,82 +121,3 @@ pub fn apply_all_fix_code_action<'a>( command: None, }) } - -pub fn ignore_this_line_code_action(report: &DiagnosticReport, uri: &Uri) -> CodeAction { - let rule_name = report.rule_name.as_ref(); - - // TODO: This CodeAction doesn't support disabling multiple rules by name for a given line. - // To do that, we need to read `report.diagnostic.range.start.line` and check if a disable comment already exists. - // If it does, it needs to be appended to instead of a completely new line inserted. - CodeAction { - title: rule_name.as_ref().map_or_else( - || "Disable oxlint for this line".into(), - |s| format!("Disable {s} for this line"), - ), - kind: Some(CodeActionKind::QUICKFIX), - is_preferred: Some(false), - edit: Some(WorkspaceEdit { - #[expect(clippy::disallowed_types)] - changes: Some(std::collections::HashMap::from([( - uri.clone(), - vec![TextEdit { - range: Range { - start: Position { - line: report.diagnostic.range.start.line, - // TODO: character should be set to match the first non-whitespace character in the source text to match the existing indentation. - character: 0, - }, - end: Position { - line: report.diagnostic.range.start.line, - // TODO: character should be set to match the first non-whitespace character in the source text to match the existing indentation. - character: 0, - }, - }, - new_text: rule_name.as_ref().map_or_else( - || "// oxlint-disable-next-line\n".into(), - |s| format!("// oxlint-disable-next-line {s}\n"), - ), - }], - )])), - ..WorkspaceEdit::default() - }), - disabled: None, - data: None, - diagnostics: None, - command: None, - } -} - -pub fn ignore_this_rule_code_action(report: &DiagnosticReport, uri: &Uri) -> CodeAction { - let rule_name = report.rule_name.as_ref(); - - CodeAction { - title: rule_name.as_ref().map_or_else( - || "Disable oxlint for this file".into(), - |s| format!("Disable {s} for this file"), - ), - kind: Some(CodeActionKind::QUICKFIX), - is_preferred: Some(false), - edit: Some(WorkspaceEdit { - #[expect(clippy::disallowed_types)] - changes: Some(std::collections::HashMap::from([( - uri.clone(), - vec![TextEdit { - range: Range { - start: Position { line: 0, character: 0 }, - end: Position { line: 0, character: 0 }, - }, - new_text: rule_name.as_ref().map_or_else( - || "// oxlint-disable\n".into(), - |s| format!("// oxlint-disable {s}\n"), - ), - }], - )])), - ..WorkspaceEdit::default() - }), - disabled: None, - data: None, - diagnostics: None, - command: None, - } -} diff --git a/crates/oxc_language_server/src/linter/error_with_position.rs b/crates/oxc_language_server/src/linter/error_with_position.rs index 589419068cbb4..a55cae382a55c 100644 --- a/crates/oxc_language_server/src/linter/error_with_position.rs +++ b/crates/oxc_language_server/src/linter/error_with_position.rs @@ -13,7 +13,6 @@ use crate::LSP_MAX_INT; pub struct DiagnosticReport { pub diagnostic: lsp_types::Diagnostic, pub fixed_content: PossibleFixContent, - pub rule_name: Option, } #[derive(Debug, Clone)] @@ -145,6 +144,5 @@ pub fn message_with_position_to_lsp_diagnostic_report( fixes.iter().map(fix_with_position_to_fix_content).collect(), ), }, - rule_name: message.code.number.as_ref().map(std::string::ToString::to_string), } } diff --git a/crates/oxc_language_server/src/linter/isolated_lint_handler.rs b/crates/oxc_language_server/src/linter/isolated_lint_handler.rs index 7e720ea9ad0cd..06881f8e20dd0 100644 --- a/crates/oxc_language_server/src/linter/isolated_lint_handler.rs +++ b/crates/oxc_language_server/src/linter/isolated_lint_handler.rs @@ -134,7 +134,6 @@ impl IsolatedLintHandler { data: None, }, fixed_content: PossibleFixContent::None, - rule_name: None, }); } } diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_astro@debugger.astro.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_astro@debugger.astro.snap index 2543ae92e5a33..64d9e454e0d2e 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_astro@debugger.astro.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_astro@debugger.astro.snap @@ -15,7 +15,58 @@ related_information[0].location.range: Range { start: Position { line: 1, charac severity: Some(Warning) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 1, character: 0 }, end: Position { line: 1, character: 8 } } }) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Remove the debugger statement", + ), + code: "", + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 8, + }, + }, + }, + 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: "eslint(no-debugger)" @@ -28,7 +79,58 @@ related_information[0].location.range: Range { start: Position { line: 10, chara severity: Some(Warning) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 10, character: 2 }, end: Position { line: 10, character: 10 } } }) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Remove the debugger statement", + ), + code: "", + range: Range { + start: Position { + line: 10, + character: 2, + }, + end: Position { + line: 10, + character: 10, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-debugger for this line", + ), + code: "// oxlint-disable-next-line no-debugger\n", + range: Range { + start: Position { + line: 10, + character: 0, + }, + end: Position { + line: 10, + 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: "eslint(no-debugger)" @@ -41,7 +143,58 @@ related_information[0].location.range: Range { start: Position { line: 14, chara severity: Some(Warning) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 14, character: 2 }, end: Position { line: 14, character: 10 } } }) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Remove the debugger statement", + ), + code: "", + range: Range { + start: Position { + line: 14, + character: 2, + }, + end: Position { + line: 14, + character: 10, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-debugger for this line", + ), + code: "// oxlint-disable-next-line no-debugger\n", + range: Range { + start: Position { + line: 14, + character: 0, + }, + end: Position { + line: 14, + 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: "eslint(no-debugger)" @@ -54,4 +207,55 @@ related_information[0].location.range: Range { start: Position { line: 18, chara severity: Some(Warning) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 18, character: 2 }, end: Position { line: 18, character: 10 } } }) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Remove the debugger statement", + ), + code: "", + range: Range { + start: Position { + line: 18, + character: 2, + }, + end: Position { + line: 18, + character: 10, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-debugger for this line", + ), + code: "// oxlint-disable-next-line no-debugger\n", + range: Range { + start: Position { + line: 18, + character: 0, + }, + end: Position { + line: 18, + 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, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module@debugger.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module@debugger.ts.snap index 64f9851996faa..2e5498f5984d5 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module@debugger.ts.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module@debugger.ts.snap @@ -15,4 +15,55 @@ related_information[0].location.range: Range { start: Position { line: 1, charac severity: Some(Warning) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 1, character: 0 }, end: Position { line: 1, character: 9 } } }) +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, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module@dep-a.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module@dep-a.ts.snap index 4e87629705d0a..26ef2bb685b7f 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module@dep-a.ts.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module@dep-a.ts.snap @@ -15,4 +15,39 @@ related_information[0].location.range: Range { start: Position { line: 1, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-cycle for this line", + ), + code: "// oxlint-disable-next-line no-cycle\n", + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-cycle for this file", + ), + code: "// oxlint-disable no-cycle\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module_extended_config@dep-a.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module_extended_config@dep-a.ts.snap index f3bfbaa540109..c7a05a26ce22b 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module_extended_config@dep-a.ts.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module_extended_config@dep-a.ts.snap @@ -15,4 +15,39 @@ related_information[0].location.range: Range { start: Position { line: 1, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-cycle for this line", + ), + code: "// oxlint-disable-next-line no-cycle\n", + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-cycle for this file", + ), + code: "// oxlint-disable no-cycle\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module_nested_config@dep-a.ts_folder__folder-dep-a.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module_nested_config@dep-a.ts_folder__folder-dep-a.ts.snap index b5970ee66f7ea..43535a2560420 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module_nested_config@dep-a.ts_folder__folder-dep-a.ts.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_cross_module_nested_config@dep-a.ts_folder__folder-dep-a.ts.snap @@ -19,4 +19,39 @@ related_information[0].location.range: Range { start: Position { line: 1, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-cycle for this line", + ), + code: "// oxlint-disable-next-line no-cycle\n", + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-cycle for this file", + ), + code: "// oxlint-disable no-cycle\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_deny_no_console@hello_world.js.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_deny_no_console@hello_world.js.snap index cc6783346767a..5f77d936ec5eb 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_deny_no_console@hello_world.js.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_deny_no_console@hello_world.js.snap @@ -15,4 +15,39 @@ related_information[0].location.range: Range { start: Position { line: 0, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-console for this line", + ), + code: "// oxlint-disable-next-line no-console\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-console for this file", + ), + code: "// oxlint-disable no-console\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_ignore_patterns@ignored-file.ts_another_config__not-ignored-file.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_ignore_patterns@ignored-file.ts_another_config__not-ignored-file.ts.snap index 78922b319beb1..1e8aeaac2d149 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_ignore_patterns@ignored-file.ts_another_config__not-ignored-file.ts.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_ignore_patterns@ignored-file.ts_another_config__not-ignored-file.ts.snap @@ -19,4 +19,55 @@ related_information[0].location.range: Range { start: Position { line: 0, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 0, character: 0 }, end: Position { line: 0, character: 9 } } }) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Remove the debugger statement", + ), + code: "", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 9, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-debugger for this line", + ), + code: "// oxlint-disable-next-line no-debugger\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + 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, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_issue_9958@issue.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_issue_9958@issue.ts.snap index 94207d06b1512..4f52f67a1b7b9 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_issue_9958@issue.ts.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_issue_9958@issue.ts.snap @@ -15,7 +15,42 @@ related_information[0].location.range: Range { start: Position { line: 3, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-extra-boolean-cast for this line", + ), + code: "// oxlint-disable-next-line no-extra-boolean-cast\n", + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-extra-boolean-cast for this file", + ), + code: "// oxlint-disable no-extra-boolean-cast\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) code: "typescript-eslint(no-non-null-asserted-optional-chain)" @@ -31,7 +66,42 @@ related_information[1].location.range: Range { start: Position { line: 11, chara severity: Some(Error) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-non-null-asserted-optional-chain for this line", + ), + code: "// oxlint-disable-next-line no-non-null-asserted-optional-chain\n", + range: Range { + start: Position { + line: 11, + character: 0, + }, + end: Position { + line: 11, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-non-null-asserted-optional-chain for this file", + ), + code: "// oxlint-disable no-non-null-asserted-optional-chain\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) code: "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 index b79fb5d1b473c..99694c68a7051 100644 --- 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 @@ -15,4 +15,71 @@ related_information[0].location.range: Range { start: Position { line: 7, charac 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 } } }]) +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/snapshots/fixtures_linter_lint_on_run_on_type@on-save.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_type@on-save.ts.snap index 9e82447af607e..aa164ae89a6be 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_type@on-save.ts.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_lint_on_run_on_type@on-save.ts.snap @@ -15,7 +15,58 @@ related_information[0].location.range: Range { start: Position { line: 1, charac severity: Some(Warning) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 1, character: 0 }, end: Position { line: 1, character: 9 } } }) +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)" @@ -28,4 +79,71 @@ related_information[0].location.range: Range { start: Position { line: 7, charac 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 } } }]) +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/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 index fbe7b694f7881..44c02019338f2 100644 --- 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 @@ -15,4 +15,55 @@ related_information[0].location.range: Range { start: Position { line: 1, charac severity: Some(Warning) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 1, character: 0 }, end: Position { line: 1, character: 9 } } }) +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, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_multiple_suggestions@forward_ref.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_multiple_suggestions@forward_ref.ts.snap index bf711ae766ccc..e53d3f034fea4 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_multiple_suggestions@forward_ref.ts.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_multiple_suggestions@forward_ref.ts.snap @@ -15,4 +15,71 @@ related_information[0].location.range: Range { start: Position { line: 0, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: Multiple([FixedContent { message: Some("remove `forwardRef` wrapper"), code: "(props) => {}", range: Range { start: Position { line: 0, character: 0 }, end: Position { line: 0, character: 25 } } }, FixedContent { message: Some("add `ref` parameter"), code: "(props, ref)", range: Range { start: Position { line: 0, character: 11 }, end: Position { line: 0, character: 18 } } }]) +fixed: Multiple( + [ + FixedContent { + message: Some( + "remove `forwardRef` wrapper", + ), + code: "(props) => {}", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 25, + }, + }, + }, + FixedContent { + message: Some( + "add `ref` parameter", + ), + code: "(props, ref)", + range: Range { + start: Position { + line: 0, + character: 11, + }, + end: Position { + line: 0, + character: 18, + }, + }, + }, + FixedContent { + message: Some( + "Disable forward-ref-uses-ref for this line", + ), + code: "// oxlint-disable-next-line forward-ref-uses-ref\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable forward-ref-uses-ref for this file", + ), + code: "// oxlint-disable forward-ref-uses-ref\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_regexp_feature@index.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_regexp_feature@index.ts.snap index 6e5c3f548fd85..91a6a93b43a77 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_regexp_feature@index.ts.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_regexp_feature@index.ts.snap @@ -15,7 +15,42 @@ related_information[0].location.range: Range { start: Position { line: 1, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-control-regex for this line", + ), + code: "// oxlint-disable-next-line no-control-regex\n", + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-control-regex for this file", + ), + code: "// oxlint-disable no-control-regex\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) code: "eslint(no-useless-escape)" @@ -28,4 +63,55 @@ related_information[0].location.range: Range { start: Position { line: 0, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Replace `\\/` with `/`."), code: "/", range: Range { start: Position { line: 0, character: 16 }, end: Position { line: 0, character: 18 } } }) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Replace `\\/` with `/`.", + ), + code: "/", + range: Range { + start: Position { + line: 0, + character: 16, + }, + end: Position { + line: 0, + character: 18, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-useless-escape for this line", + ), + code: "// oxlint-disable-next-line no-useless-escape\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-useless-escape for this file", + ), + code: "// oxlint-disable no-useless-escape\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_svelte@debugger.svelte.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_svelte@debugger.svelte.snap index 7bab73a3bc470..800c8a1063eb4 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_svelte@debugger.svelte.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_svelte@debugger.svelte.snap @@ -15,7 +15,42 @@ related_information[0].location.range: Range { start: Position { line: 3, charac severity: Some(Warning) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-unassigned-vars for this line", + ), + code: "// oxlint-disable-next-line no-unassigned-vars\n", + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-unassigned-vars for this file", + ), + code: "// oxlint-disable no-unassigned-vars\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) code: "eslint(no-unassigned-vars)" @@ -28,7 +63,42 @@ related_information[0].location.range: Range { start: Position { line: 4, charac severity: Some(Warning) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-unassigned-vars for this line", + ), + code: "// oxlint-disable-next-line no-unassigned-vars\n", + range: Range { + start: Position { + line: 4, + character: 0, + }, + end: Position { + line: 4, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-unassigned-vars for this file", + ), + code: "// oxlint-disable no-unassigned-vars\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) code: "eslint(no-debugger)" @@ -41,4 +111,55 @@ related_information[0].location.range: Range { start: Position { line: 1, charac severity: Some(Warning) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 1, character: 1 }, end: Position { line: 1, character: 10 } } }) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Remove the debugger statement", + ), + code: "", + range: Range { + start: Position { + line: 1, + character: 1, + }, + end: Position { + line: 1, + character: 10, + }, + }, + }, + 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, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_ts_path_alias@deep__src__dep-a.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_ts_path_alias@deep__src__dep-a.ts.snap index 66e26da914d61..93498cf1b1a73 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_ts_path_alias@deep__src__dep-a.ts.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_ts_path_alias@deep__src__dep-a.ts.snap @@ -15,4 +15,39 @@ related_information[0].location.range: Range { start: Position { line: 1, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-cycle for this line", + ), + code: "// oxlint-disable-next-line no-cycle\n", + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-cycle for this file", + ), + code: "// oxlint-disable no-cycle\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_tsgolint@no-floating-promises__index.ts.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_tsgolint@no-floating-promises__index.ts.snap index cb9bfd2c6b5fa..09592468923dc 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_tsgolint@no-floating-promises__index.ts.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_tsgolint@no-floating-promises__index.ts.snap @@ -15,7 +15,42 @@ related_information[0].location.range: Range { start: Position { line: 1, charac severity: Some(Warning) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-unused-expressions for this line", + ), + code: "// oxlint-disable-next-line no-unused-expressions\n", + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-unused-expressions for this file", + ), + code: "// oxlint-disable no-unused-expressions\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) code: "typescript-eslint(no-floating-promises)" @@ -28,7 +63,74 @@ related_information[0].location.range: Range { start: Position { line: 1, charac severity: Some(Warning) source: Some("oxc") tags: None -fixed: Multiple([FixedContent { message: Some("Promises must be awaited."), code: "void ", range: Range { start: Position { line: 1, character: 0 }, end: Position { line: 1, character: 0 } } }, FixedContent { message: Some("Promises must be awaited."), code: "await ", range: Range { start: Position { line: 1, character: 0 }, end: Position { line: 1, character: 0 } } }]) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Promises must be awaited.", + ), + code: "void ", + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Promises must be awaited.", + ), + code: "await ", + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + 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: 1, + character: 0, + }, + end: Position { + line: 1, + 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, + }, + }, + }, + ], +) code: "typescript-eslint(no-floating-promises)" @@ -41,7 +143,74 @@ related_information[0].location.range: Range { start: Position { line: 7, charac 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 } } }]) +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, + }, + }, + }, + ], +) code: "typescript-eslint(no-floating-promises)" @@ -54,7 +223,74 @@ related_information[0].location.range: Range { start: Position { line: 9, charac severity: Some(Warning) source: Some("oxc") tags: None -fixed: Multiple([FixedContent { message: Some("Promises must be awaited."), code: "void ", range: Range { start: Position { line: 9, character: 0 }, end: Position { line: 9, character: 0 } } }, FixedContent { message: Some("Promises must be awaited."), code: "await ", range: Range { start: Position { line: 9, character: 0 }, end: Position { line: 9, character: 0 } } }]) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Promises must be awaited.", + ), + code: "void ", + range: Range { + start: Position { + line: 9, + character: 0, + }, + end: Position { + line: 9, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Promises must be awaited.", + ), + code: "await ", + range: Range { + start: Position { + line: 9, + character: 0, + }, + end: Position { + line: 9, + 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: 9, + character: 0, + }, + end: Position { + line: 9, + 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, + }, + }, + }, + ], +) code: "typescript-eslint(no-floating-promises)" @@ -67,7 +303,74 @@ related_information[0].location.range: Range { start: Position { line: 11, chara severity: Some(Warning) source: Some("oxc") tags: None -fixed: Multiple([FixedContent { message: Some("Promises must be awaited."), code: "void ", range: Range { start: Position { line: 11, character: 0 }, end: Position { line: 11, character: 0 } } }, FixedContent { message: Some("Promises must be awaited."), code: "await ", range: Range { start: Position { line: 11, character: 0 }, end: Position { line: 11, character: 0 } } }]) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Promises must be awaited.", + ), + code: "void ", + range: Range { + start: Position { + line: 11, + character: 0, + }, + end: Position { + line: 11, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Promises must be awaited.", + ), + code: "await ", + range: Range { + start: Position { + line: 11, + character: 0, + }, + end: Position { + line: 11, + 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: 11, + character: 0, + }, + end: Position { + line: 11, + 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, + }, + }, + }, + ], +) code: "typescript-eslint(no-floating-promises)" @@ -80,4 +383,39 @@ related_information[0].location.range: Range { start: Position { line: 13, chara severity: Some(Warning) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-floating-promises for this line", + ), + code: "// oxlint-disable-next-line no-floating-promises\n", + range: Range { + start: Position { + line: 13, + character: 0, + }, + end: Position { + line: 13, + 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/snapshots/fixtures_linter_unused_disabled_directives@test.js.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_unused_disabled_directives@test.js.snap index 404ce24a3c44a..b2aaa07b55332 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_unused_disabled_directives@test.js.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_unused_disabled_directives@test.js.snap @@ -15,7 +15,42 @@ related_information[0].location.range: Range { start: Position { line: 9, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: None +fixed: Multiple( + [ + FixedContent { + message: Some( + "Disable no-console for this line", + ), + code: "// oxlint-disable-next-line no-console\n", + range: Range { + start: Position { + line: 9, + character: 0, + }, + end: Position { + line: 9, + character: 0, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-console for this file", + ), + code: "// oxlint-disable no-console\n", + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + }, + ], +) code: "eslint(no-debugger)" @@ -28,7 +63,58 @@ related_information[0].location.range: Range { start: Position { line: 2, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 2, character: 2 }, end: Position { line: 2, character: 11 } } }) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Remove the debugger statement", + ), + code: "", + range: Range { + start: Position { + line: 2, + character: 2, + }, + end: Position { + line: 2, + character: 11, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-debugger for this line", + ), + code: "// oxlint-disable-next-line no-debugger\n", + range: Range { + start: Position { + line: 2, + character: 0, + }, + end: Position { + line: 2, + 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: "" @@ -41,7 +127,24 @@ related_information[0].location.range: Range { start: Position { line: 0, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("remove unused disable directive"), code: "", range: Range { start: Position { line: 0, character: 2 }, end: Position { line: 0, character: 56 } } }) +fixed: Single( + FixedContent { + message: Some( + "remove unused disable directive", + ), + code: "", + range: Range { + start: Position { + line: 0, + character: 2, + }, + end: Position { + line: 0, + character: 56, + }, + }, + }, +) code: "" @@ -54,7 +157,24 @@ related_information[0].location.range: Range { start: Position { line: 5, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("remove unused disable directive"), code: "", range: Range { start: Position { line: 5, character: 39 }, end: Position { line: 5, character: 52 } } }) +fixed: Single( + FixedContent { + message: Some( + "remove unused disable directive", + ), + code: "", + range: Range { + start: Position { + line: 5, + character: 39, + }, + end: Position { + line: 5, + character: 52, + }, + }, + }, +) code: "" @@ -67,4 +187,21 @@ related_information[0].location.range: Range { start: Position { line: 8, charac severity: Some(Error) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("remove unused disable directive"), code: "", range: Range { start: Position { line: 8, character: 2 }, end: Position { line: 8, character: 52 } } }) +fixed: Single( + FixedContent { + message: Some( + "remove unused disable directive", + ), + code: "", + range: Range { + start: Position { + line: 8, + character: 2, + }, + end: Position { + line: 8, + character: 52, + }, + }, + }, +) diff --git a/crates/oxc_language_server/src/snapshots/fixtures_linter_vue@debugger.vue.snap b/crates/oxc_language_server/src/snapshots/fixtures_linter_vue@debugger.vue.snap index 23456c2bc83f1..aa194f7fe7777 100644 --- a/crates/oxc_language_server/src/snapshots/fixtures_linter_vue@debugger.vue.snap +++ b/crates/oxc_language_server/src/snapshots/fixtures_linter_vue@debugger.vue.snap @@ -15,7 +15,58 @@ related_information[0].location.range: Range { start: Position { line: 5, charac severity: Some(Warning) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 5, character: 4 }, end: Position { line: 5, character: 12 } } }) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Remove the debugger statement", + ), + code: "", + range: Range { + start: Position { + line: 5, + character: 4, + }, + end: Position { + line: 5, + character: 12, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-debugger for this line", + ), + code: "// oxlint-disable-next-line no-debugger\n", + range: Range { + start: Position { + line: 5, + character: 0, + }, + end: Position { + line: 5, + 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: "eslint(no-debugger)" @@ -28,4 +79,55 @@ related_information[0].location.range: Range { start: Position { line: 10, chara severity: Some(Warning) source: Some("oxc") tags: None -fixed: Single(FixedContent { message: Some("Remove the debugger statement"), code: "", range: Range { start: Position { line: 10, character: 4 }, end: Position { line: 10, character: 13 } } }) +fixed: Multiple( + [ + FixedContent { + message: Some( + "Remove the debugger statement", + ), + code: "", + range: Range { + start: Position { + line: 10, + character: 4, + }, + end: Position { + line: 10, + character: 13, + }, + }, + }, + FixedContent { + message: Some( + "Disable no-debugger for this line", + ), + code: "// oxlint-disable-next-line no-debugger\n", + range: Range { + start: Position { + line: 10, + character: 0, + }, + end: Position { + line: 10, + 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, + }, + }, + }, + ], +) diff --git a/crates/oxc_language_server/src/tester.rs b/crates/oxc_language_server/src/tester.rs index 2f52c30e5691b..459207c65f3bd 100644 --- a/crates/oxc_language_server/src/tester.rs +++ b/crates/oxc_language_server/src/tester.rs @@ -91,7 +91,7 @@ range: {range:?} severity: {severity:?} source: {source:?} tags: {tags:?} -fixed: {fixed:?} +fixed: {fixed:#?} " ) } diff --git a/crates/oxc_language_server/src/worker.rs b/crates/oxc_language_server/src/worker.rs index adcc8d5f51325..56c28b34f4ca3 100644 --- a/crates/oxc_language_server/src/worker.rs +++ b/crates/oxc_language_server/src/worker.rs @@ -12,10 +12,7 @@ use tower_lsp_server::{ use crate::{ ConcurrentHashMap, - code_actions::{ - apply_all_fix_code_action, apply_fix_code_actions, ignore_this_line_code_action, - ignore_this_rule_code_action, - }, + code_actions::{apply_all_fix_code_action, apply_fix_code_actions}, formatter::server_formatter::ServerFormatter, linter::{ error_with_position::{DiagnosticReport, PossibleFixContent}, @@ -264,30 +261,10 @@ impl WorkspaceWorker { let mut code_actions_vec: Vec = vec![]; for report in reports { - let mut append_ignore_code_actions = true; - if let Some(fix_actions) = apply_fix_code_actions(report, uri) { - // do not append ignore code actions when the error is the ignore action - if fix_actions - .first() - .as_ref() - .is_some_and(|fix| fix.title == "remove unused disable directive") - { - append_ignore_code_actions = false; - } code_actions_vec .extend(fix_actions.into_iter().map(CodeActionOrCommand::CodeAction)); } - - if append_ignore_code_actions { - code_actions_vec.push(CodeActionOrCommand::CodeAction( - ignore_this_line_code_action(report, uri), - )); - - code_actions_vec.push(CodeActionOrCommand::CodeAction( - ignore_this_rule_code_action(report, uri), - )); - } } code_actions_vec diff --git a/crates/oxc_linter/src/lsp.rs b/crates/oxc_linter/src/lsp.rs index 81e0fc0703245..bb75cccf27347 100644 --- a/crates/oxc_linter/src/lsp.rs +++ b/crates/oxc_linter/src/lsp.rs @@ -1,4 +1,5 @@ use oxc_data_structures::rope::{Rope, get_line_column}; +use oxc_span::GetSpan; use std::borrow::Cow; use crate::fixer::{Fix, Message, PossibleFixes}; @@ -106,8 +107,13 @@ pub fn message_to_message_with_position<'a>( source_text: &str, rope: &Rope, ) -> MessageWithPosition<'a> { + let code = message.error.code.clone(); + let error_offset = message.span().start; + // TODO: feature flag for knowing the source text section start offset of this message + let section_offset = 0; + let mut result = oxc_diagnostic_to_message_with_position(message.error, source_text, rope); - result.fixes = match &message.fixes { + let fixes = match &message.fixes { PossibleFixes::None => PossibleFixesWithPosition::None, PossibleFixes::Single(fix) => { PossibleFixesWithPosition::Single(fix_to_fix_with_position(fix, rope, source_text)) @@ -117,13 +123,28 @@ pub fn message_to_message_with_position<'a>( ), }; + result.fixes = add_ignore_fixes(fixes, &code, error_offset, section_offset, rope, source_text); + result } +/// Possible fixes with position information. +/// +/// This is similar to `PossibleFixes` but with position information. +/// It also includes "ignore this line" and "ignore this rule" fixes for the Language Server. +/// +/// The struct should be build with `message_to_message_with_position` +/// or `oxc_diagnostic_to_message_with_position` function to ensure the ignore fixes are added correctly. #[derive(Debug)] pub enum PossibleFixesWithPosition<'a> { + // No possible fixes. + // This happens on parser/semantic errors. None, + // A single possible fix. + // This happens when a unused disable directive is reported. Single(FixWithPosition<'a>), + // Multiple possible fixes. + // This happens when a lint reports a violation, then ignore fixes are added. Multiple(Vec>), } @@ -147,6 +168,73 @@ fn fix_to_fix_with_position<'a>( } } +/// Add "ignore this line" and "ignore this rule" fixes to the existing fixes. +/// These fixes will be added to the end of the existing fixes. +/// If the existing fixes already contain an "remove unused disable directive" fix, +/// then no ignore fixes will be added. +fn add_ignore_fixes<'a>( + fixes: PossibleFixesWithPosition<'a>, + code: &OxcCode, + error_offset: u32, + section_offset: u32, + rope: &Rope, + source_text: &str, +) -> PossibleFixesWithPosition<'a> { + // do not append ignore code actions when the error is the ignore action + if matches!(fixes, PossibleFixesWithPosition::Single(ref fix) if fix.span.message.as_ref().is_some_and(|message| message.starts_with("remove unused disable directive"))) + { + return fixes; + } + + let mut new_fixes: Vec> = vec![]; + if let PossibleFixesWithPosition::Single(fix) = fixes { + new_fixes.push(fix); + } else if let PossibleFixesWithPosition::Multiple(existing_fixes) = fixes { + new_fixes.extend(existing_fixes); + } + + if let Some(rule_name) = code.number.as_ref() { + // TODO: doesn't support disabling multiple rules by name for a given line. + new_fixes.push(disable_for_this_line(rule_name, error_offset, rope, source_text)); + new_fixes.push(disable_for_this_section(rule_name, section_offset, rope, source_text)); + } + + PossibleFixesWithPosition::Multiple(new_fixes) +} + +fn disable_for_this_line<'a>( + rule_name: &str, + error_offset: u32, + rope: &Rope, + source_text: &str, +) -> FixWithPosition<'a> { + let mut start_position = offset_to_position(rope, error_offset, source_text); + start_position.character = 0; // TODO: character should be set to match the first non-whitespace character in the source text to match the existing indentation. + let end_position = start_position.clone(); + FixWithPosition { + content: Cow::Owned(format!("// oxlint-disable-next-line {rule_name}\n")), + span: SpanPositionMessage::new(start_position, end_position) + .with_message(Some(Cow::Owned(format!("Disable {rule_name} for this line")))), + } +} + +fn disable_for_this_section<'a>( + rule_name: &str, + section_offset: u32, + rope: &Rope, + source_text: &str, +) -> FixWithPosition<'a> { + let mut start_position = offset_to_position(rope, section_offset, source_text); + start_position.character = 0; // TODO: character should be set to match the first non-whitespace character in the source text to match the existing indentation. + let end_position = start_position.clone(); + + FixWithPosition { + content: Cow::Owned(format!("// oxlint-disable {rule_name}\n")), + span: SpanPositionMessage::new(start_position, end_position) + .with_message(Some(Cow::Owned(format!("Disable {rule_name} for this file")))), + } +} + #[cfg(test)] mod test { use oxc_data_structures::rope::Rope; diff --git a/crates/oxc_linter/src/service/runtime.rs b/crates/oxc_linter/src/service/runtime.rs index aa65fdab3237b..5528220617ff5 100644 --- a/crates/oxc_linter/src/service/runtime.rs +++ b/crates/oxc_linter/src/service/runtime.rs @@ -753,7 +753,6 @@ impl Runtime { messages.lock().unwrap().extend(section_messages.iter().map(|message| { let message = message_cloner.clone_message(message); - message_to_message_with_position(message, source_text, rope) })); },