diff --git a/Cargo.lock b/Cargo.lock index 537138de087..705622ba4f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1489,6 +1489,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "tracing", ] [[package]] @@ -1814,6 +1815,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "tracing", ] [[package]] diff --git a/crates/rome_analyze/Cargo.toml b/crates/rome_analyze/Cargo.toml index 9ba2f0d1403..e9d73e07b47 100644 --- a/crates/rome_analyze/Cargo.toml +++ b/crates/rome_analyze/Cargo.toml @@ -18,6 +18,7 @@ rustc-hash = { workspace = true } serde = { version = "1.0.136", features = ["derive"] } serde_json = { version = "1.0.85", features = ["raw_value"]} schemars = { version = "0.8.10", optional = true } +tracing = { workspace = true } [dev-dependencies] rome_js_syntax = { path = "../rome_js_syntax" } diff --git a/crates/rome_analyze/src/categories.rs b/crates/rome_analyze/src/categories.rs index a1f7fc997d7..91f2b17a77b 100644 --- a/crates/rome_analyze/src/categories.rs +++ b/crates/rome_analyze/src/categories.rs @@ -20,6 +20,9 @@ pub enum RuleCategory { Action, } +/// Actions that suppress rules should start with this string +pub const SUPPRESSION_ACTION_CATEGORY: &str = "quickfix.rome.suppressRule"; + /// The category of a code action, this type maps directly to the /// [CodeActionKind] type in the Language Server Protocol specification /// diff --git a/crates/rome_analyze/src/lib.rs b/crates/rome_analyze/src/lib.rs index 981caa3d480..2086575e61e 100644 --- a/crates/rome_analyze/src/lib.rs +++ b/crates/rome_analyze/src/lib.rs @@ -33,7 +33,7 @@ pub use crate::registry::{ }; pub use crate::rule::{ CategoryLanguage, GroupCategory, GroupLanguage, Rule, RuleAction, RuleDiagnostic, RuleGroup, - RuleMeta, RuleMetadata, + RuleMeta, RuleMetadata, SuppressAction, }; pub use crate::services::{FromServices, MissingServicesDiagnostic, ServiceBag}; use crate::signals::DiagnosticSignal; diff --git a/crates/rome_analyze/src/rule.rs b/crates/rome_analyze/src/rule.rs index 83b0ca63d1a..f8948bc2352 100644 --- a/crates/rome_analyze/src/rule.rs +++ b/crates/rome_analyze/src/rule.rs @@ -12,7 +12,7 @@ use rome_diagnostics::v2::{ Visit, }; use rome_diagnostics::Applicability; -use rome_rowan::{BatchMutation, Language, TextRange}; +use rome_rowan::{BatchMutation, Language, SyntaxNode, TextRange}; use serde::de::DeserializeOwned; /// Static metadata containing information about a rule @@ -328,6 +328,16 @@ pub trait Rule: RuleMeta { let (..) = (ctx, state); None } + + /// Create a code action that allows to suppress the rule. The function + /// has to return the node to which the suppression comment needs to be applied. + fn can_suppress( + ctx: &RuleContext, + state: &Self::State, + ) -> Option>> { + let _ = (ctx, state); + None + } } /// Diagnostic object returned by a single analysis rule @@ -482,3 +492,17 @@ pub struct RuleAction { pub message: MarkupBuf, pub mutation: BatchMutation, } + +pub struct SuppressAction(SyntaxNode); + +impl SuppressAction { + pub fn node(&self) -> &SyntaxNode { + &self.0 + } +} + +impl From> for SuppressAction { + fn from(node: SyntaxNode) -> Self { + Self(node) + } +} diff --git a/crates/rome_analyze/src/signals.rs b/crates/rome_analyze/src/signals.rs index 3c118029daf..5a6ba0fb237 100644 --- a/crates/rome_analyze/src/signals.rs +++ b/crates/rome_analyze/src/signals.rs @@ -1,3 +1,4 @@ +use crate::categories::SUPPRESSION_ACTION_CATEGORY; use crate::{ categories::ActionCategory, context::RuleContext, @@ -5,17 +6,20 @@ use crate::{ rule::Rule, AnalyzerDiagnostic, AnalyzerOptions, Queryable, RuleGroup, ServiceBag, }; -use rome_console::MarkupBuf; +use rome_console::{markup, MarkupBuf}; use rome_diagnostics::file::FileSpan; use rome_diagnostics::v2::advice::CodeSuggestionAdvice; use rome_diagnostics::{file::FileId, Applicability, CodeSuggestion}; -use rome_rowan::{BatchMutation, Language}; +use rome_rowan::{AstNode, BatchMutation, BatchMutationExt, Language, TriviaPieceKind}; +use std::borrow::Cow; +use std::iter::FusedIterator; +use std::vec::IntoIter; /// Event raised by the analyzer when a [Rule](crate::Rule) /// emits a diagnostic, a code action, or both pub trait AnalyzerSignal { fn diagnostic(&self) -> Option; - fn action(&self) -> Option>; + fn actions(&self) -> Option>; } /// Simple implementation of [AnalyzerSignal] generating a [AnalyzerDiagnostic] from a @@ -41,7 +45,7 @@ where Some((self.factory)()) } - fn action(&self) -> Option> { + fn actions(&self) -> Option> { None } } @@ -51,7 +55,7 @@ where /// /// This struct can be converted into a [CodeSuggestion] and injected into /// a diagnostic emitted by the same signal -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct AnalyzerAction { pub group_name: &'static str, pub rule_name: &'static str, @@ -62,13 +66,19 @@ pub struct AnalyzerAction { pub mutation: BatchMutation, } -impl From> for CodeSuggestionAdvice -where - L: Language, -{ +impl AnalyzerAction { + pub fn is_suppression(&self) -> bool { + self.category.matches("quickfix.rome.suppressRule") + } +} + +pub struct AnalyzerActionIter { + analyzer_actions: IntoIter>, +} + +impl From> for CodeSuggestionAdvice { fn from(action: AnalyzerAction) -> Self { let (_, suggestion) = action.mutation.as_text_edits().unwrap_or_default(); - CodeSuggestionAdvice { applicability: action.applicability, msg: action.message, @@ -77,22 +87,121 @@ where } } -impl From> for CodeSuggestion -where - L: Language, -{ +impl<'a, L: Language> From> for CodeSuggestionItem<'a> { fn from(action: AnalyzerAction) -> Self { let (range, suggestion) = action.mutation.as_text_edits().unwrap_or_default(); - CodeSuggestion { - span: FileSpan { - file: action.file_id, - range, + CodeSuggestionItem { + rule_name: action.rule_name, + category: action.category, + group_name: action.group_name, + suggestion: CodeSuggestion { + span: FileSpan { + file: action.file_id, + range, + }, + applicability: action.applicability, + msg: action.message, + suggestion, + labels: vec![], }, - applicability: action.applicability, - msg: action.message, - suggestion, - labels: vec![], + } + } +} + +impl AnalyzerActionIter { + pub fn new(actions: Vec>) -> Self { + Self { + analyzer_actions: actions.into_iter(), + } + } +} + +impl Iterator for AnalyzerActionIter { + type Item = AnalyzerAction; + + fn next(&mut self) -> Option { + self.analyzer_actions.next() + } +} + +impl FusedIterator for AnalyzerActionIter {} + +impl ExactSizeIterator for AnalyzerActionIter { + fn len(&self) -> usize { + self.analyzer_actions.len() + } +} + +#[derive(Debug)] +pub struct AnalyzerMutation { + pub message: MarkupBuf, + pub mutation: BatchMutation, + pub category: ActionCategory, + pub rule_name: String, +} + +pub struct CodeSuggestionAdviceIter { + iter: IntoIter>, +} + +impl Iterator for CodeSuggestionAdviceIter { + type Item = CodeSuggestionAdvice; + + fn next(&mut self) -> Option { + let action = self.iter.next()?; + Some(action.into()) + } +} + +impl FusedIterator for CodeSuggestionAdviceIter {} + +impl ExactSizeIterator for CodeSuggestionAdviceIter { + fn len(&self) -> usize { + self.iter.len() + } +} + +pub struct CodeActionIter { + iter: IntoIter>, +} + +pub struct CodeSuggestionItem<'a> { + pub category: ActionCategory, + pub suggestion: CodeSuggestion, + pub rule_name: &'a str, + pub group_name: &'a str, +} + +impl Iterator for CodeActionIter { + type Item = CodeSuggestionItem<'static>; + + fn next(&mut self) -> Option { + let action = self.iter.next()?; + Some(action.into()) + } +} + +impl FusedIterator for CodeActionIter {} + +impl ExactSizeIterator for CodeActionIter { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl AnalyzerActionIter { + /// Returns an iterator that yields [CodeSuggestionAdvice] + pub fn into_code_suggestion_advices(self) -> CodeSuggestionAdviceIter { + CodeSuggestionAdviceIter { + iter: self.analyzer_actions, + } + } + + /// Returns an iterator that yields [CodeAction] + pub fn into_code_action_iter(self) -> CodeActionIter { + CodeActionIter { + iter: self.analyzer_actions, } } } @@ -141,18 +250,78 @@ where R::diagnostic(&ctx, &self.state).map(|diag| diag.into_analyzer_diagnostic(self.file_id)) } - fn action(&self) -> Option>> { + fn actions(&self) -> Option>> { let ctx = RuleContext::new(&self.query_result, self.root, self.services, &self.options).ok()?; + let mut actions = Vec::new(); + if let Some(action) = R::action(&ctx, &self.state) { + actions.push(AnalyzerAction { + group_name: ::NAME, + rule_name: R::METADATA.name, + file_id: self.file_id, + category: action.category, + applicability: action.applicability, + mutation: action.mutation, + message: action.message, + }); + }; + let node_to_suppress = R::can_suppress(&ctx, &self.state); + let suppression_node = node_to_suppress.and_then(|suppression_node| { + let ancestor = suppression_node.node().ancestors().find_map(|node| { + if node + .first_token() + .map(|token| { + token + .leading_trivia() + .pieces() + .any(|trivia| trivia.is_newline()) + }) + .unwrap_or(false) + { + Some(node) + } else { + None + } + }); + if ancestor.is_some() { + ancestor + } else { + Some(ctx.root().syntax().clone()) + } + }); + let suppression_action = suppression_node.and_then(|suppression_node| { + let first_token = suppression_node.first_token(); + let rule = format!( + "lint({}/{})", + ::NAME, + R::METADATA.name + ); + let mes = format!("// rome-ignore {}: suppressed", rule); - R::action(&ctx, &self.state).map(|action| AnalyzerAction { - group_name: ::NAME, - rule_name: R::METADATA.name, - file_id: self.file_id, - category: action.category, - applicability: action.applicability, - message: action.message, - mutation: action.mutation, - }) + first_token.map(|first_token| { + let trivia = vec![ + (TriviaPieceKind::Newline, "\n"), + (TriviaPieceKind::SingleLineComment, mes.as_str()), + (TriviaPieceKind::Newline, "\n"), + ]; + let mut mutation = ctx.root().begin(); + let new_token = first_token.with_leading_trivia(trivia.clone()); + + mutation.replace_token_discard_trivia(first_token, new_token); + AnalyzerAction { + group_name: ::NAME, + rule_name: R::METADATA.name, + file_id: self.file_id, + category: ActionCategory::Other(Cow::Borrowed(SUPPRESSION_ACTION_CATEGORY)), + applicability: Applicability::Always, + mutation, + message: markup! { "Suppress rule " {rule} }.to_owned(), + } + }) + }); + if let Some(suppression_action) = suppression_action { + actions.push(suppression_action); + } + Some(AnalyzerActionIter::new(actions)) } } diff --git a/crates/rome_diagnostics/src/suggestion.rs b/crates/rome_diagnostics/src/suggestion.rs index 401ef23d628..42b2062cf24 100644 --- a/crates/rome_diagnostics/src/suggestion.rs +++ b/crates/rome_diagnostics/src/suggestion.rs @@ -4,7 +4,7 @@ use rome_rowan::TextRange; use rome_text_edit::TextEdit; use serde::{Deserialize, Serialize}; -/// A Suggestion that is provided by rslint, and +/// A Suggestion that is provided by Rome's linter, and /// can be reported to the user, and can be automatically /// applied if it has the right [`Applicability`]. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] diff --git a/crates/rome_js_analyze/src/analyzers/a11y/use_alt_text.rs b/crates/rome_js_analyze/src/analyzers/a11y/use_alt_text.rs index 0a817f8ff2f..cd82e8d84f9 100644 --- a/crates/rome_js_analyze/src/analyzers/a11y/use_alt_text.rs +++ b/crates/rome_js_analyze/src/analyzers/a11y/use_alt_text.rs @@ -1,3 +1,4 @@ +use crate::JsSuppressAction; use rome_analyze::{context::RuleContext, declare_rule, Ast, Rule, RuleDiagnostic}; use rome_console::markup; use rome_js_syntax::{ @@ -142,6 +143,10 @@ impl Rule for UseAltText { }), ) } + + fn can_suppress(ctx: &RuleContext, _state: &Self::State) -> Option { + Some(ctx.query().syntax().clone().into()) + } } /// This function checks for the attribute `type` for input element where we checking for the input type which is image. diff --git a/crates/rome_js_analyze/src/analyzers/complexity/use_simplified_logic_expression.rs b/crates/rome_js_analyze/src/analyzers/complexity/use_simplified_logic_expression.rs index 8213b631034..5d17efbf8d2 100644 --- a/crates/rome_js_analyze/src/analyzers/complexity/use_simplified_logic_expression.rs +++ b/crates/rome_js_analyze/src/analyzers/complexity/use_simplified_logic_expression.rs @@ -8,7 +8,7 @@ use rome_js_syntax::{ }; use rome_rowan::{AstNode, AstNodeExt, BatchMutationExt}; -use crate::JsRuleAction; +use crate::{JsRuleAction, JsSuppressAction}; declare_rule! { /// Discard redundant terms from logical expressions. @@ -158,6 +158,10 @@ impl Rule for UseSimplifiedLogicExpression { mutation, }) } + + fn can_suppress(ctx: &RuleContext, _: &Self::State) -> Option { + Some(ctx.query().syntax().clone().into()) + } } /// https://en.wikipedia.org/wiki/De_Morgan%27s_laws diff --git a/crates/rome_js_analyze/src/lib.rs b/crates/rome_js_analyze/src/lib.rs index df082d690d7..8b31d9df456 100644 --- a/crates/rome_js_analyze/src/lib.rs +++ b/crates/rome_js_analyze/src/lib.rs @@ -2,7 +2,7 @@ use control_flow::make_visitor; use rome_analyze::{ AnalysisFilter, Analyzer, AnalyzerContext, AnalyzerOptions, AnalyzerSignal, ControlFlow, InspectMatcher, LanguageRoot, MatchQueryParams, MetadataRegistry, Phases, RuleAction, - RuleRegistry, ServiceBag, SyntaxVisitor, + RuleRegistry, ServiceBag, SuppressAction, SyntaxVisitor, }; use rome_diagnostics::file::FileId; use rome_js_syntax::{ @@ -28,6 +28,7 @@ pub use crate::registry::visit_registry; use crate::semantic_services::{SemanticModelBuilderVisitor, SemanticModelVisitor}; pub(crate) type JsRuleAction = RuleAction; +pub(crate) type JsSuppressAction = SuppressAction; /// Return the static [MetadataRegistry] for the JS analyzer rules pub fn metadata() -> &'static MetadataRegistry { @@ -158,9 +159,11 @@ mod tests { if let Some(mut diag) = signal.diagnostic() { diag.set_severity(Severity::Warning); error_ranges.push(diag.location().unwrap().span.unwrap()); - if let Some(action) = signal.action() { - let new_code = action.mutation.commit(); - eprintln!("{new_code}"); + if let Some(actions) = signal.actions() { + for action in actions { + let new_code = action.mutation.commit(); + eprintln!("{new_code}"); + } } let error = diag.with_file_path("ahahah").with_file_source_code(SOURCE); let text = markup_to_string(markup! { diff --git a/crates/rome_js_analyze/tests/spec_tests.rs b/crates/rome_js_analyze/tests/spec_tests.rs index 38b24857fc2..6d7f2b2bce2 100644 --- a/crates/rome_js_analyze/tests/spec_tests.rs +++ b/crates/rome_js_analyze/tests/spec_tests.rs @@ -8,6 +8,7 @@ use rome_console::{ }; use rome_diagnostics::file::FileId; use rome_diagnostics::termcolor::NoColor; +use rome_diagnostics::v2::advice::CodeSuggestionAdvice; use rome_diagnostics::v2::{DiagnosticExt, PrintDiagnostic, Severity}; use rome_js_parser::{ parse, @@ -47,18 +48,24 @@ fn run_test(input: &'static str, _: &str, _: &str, _: &str) { rome_js_analyze::analyze(FileId::zero(), &root, filter, &options, |event| { if let Some(mut diag) = event.diagnostic() { diag.set_severity(Severity::Warning); - if let Some(action) = event.action() { - check_code_action(input_file, &input_code, source_type, &action); - diag.add_code_suggestion(action.into()); + if let Some(actions) = event.actions() { + for action in actions { + if !action.is_suppression() { + check_code_action(input_file, &input_code, source_type, &action); + diag.add_code_suggestion(CodeSuggestionAdvice::from(action)); + } + } } diagnostics.push(diagnostic_to_string(file_name, &input_code, diag)); return ControlFlow::Continue(()); } - if let Some(action) = event.action() { - check_code_action(input_file, &input_code, source_type, &action); - code_fixes.push(code_fix_to_string(&input_code, action)); + if let Some(actions) = event.actions() { + for action in actions { + check_code_action(input_file, &input_code, source_type, &action); + code_fixes.push(code_fix_to_string(&input_code, action)); + } } ControlFlow::::Continue(()) diff --git a/crates/rome_lsp/src/handlers/analysis.rs b/crates/rome_lsp/src/handlers/analysis.rs index 0c8ea56b764..2972c42fc52 100644 --- a/crates/rome_lsp/src/handlers/analysis.rs +++ b/crates/rome_lsp/src/handlers/analysis.rs @@ -1,6 +1,6 @@ -use std::borrow::Cow; -use std::collections::HashMap; - +use crate::line_index::LineIndex; +use crate::session::Session; +use crate::utils; use anyhow::{Context, Result}; use rome_analyze::{ActionCategory, SourceActionKind}; use rome_fs::RomePath; @@ -8,13 +8,12 @@ use rome_service::workspace::{ FeatureName, FixFileMode, FixFileParams, PullActionsParams, SupportsFeatureParams, }; use rome_service::RomeError; +use std::borrow::Cow; +use std::collections::HashMap; use tower_lsp::lsp_types::{ self as lsp, CodeActionKind, CodeActionOrCommand, CodeActionParams, CodeActionResponse, }; - -use crate::line_index::LineIndex; -use crate::session::Session; -use crate::utils; +use tracing::debug; const FIX_ALL_CATEGORY: ActionCategory = ActionCategory::Source(SourceActionKind::FixAll); @@ -25,10 +24,17 @@ fn fix_all_kind() -> CodeActionKind { } } +/// Checks +fn is_suppression_action_kind(action_kind: &CodeActionKind) -> bool { + action_kind + .as_str() + .starts_with("quickfix.rome.suppressRule") +} + /// Queries the [`AnalysisServer`] for code actions of the file matching [FileId] /// /// If the AnalysisServer has no matching file, results in error. -#[tracing::instrument(level = "trace", skip(session), err)] +#[tracing::instrument(level = "debug", skip(session), err)] pub(crate) fn code_actions( session: &Session, params: CodeActionParams, @@ -109,18 +115,26 @@ pub(crate) fn code_actions( if has_fixes { actions.retain(|action| { if let CodeActionOrCommand::CodeAction(action) = action { - action.kind.as_ref() == Some(&fix_all_kind()) || action.diagnostics.is_some() + action.kind.as_ref() == Some(&fix_all_kind()) + || action + .kind + .as_ref() + .map(is_suppression_action_kind) + .unwrap_or(false) + || action.diagnostics.is_some() } else { true } }); } + debug!("Suggested actions: \n{:?}", &actions); + Ok(Some(actions)) } /// Generate a "fix all" code action for the given document -#[tracing::instrument(level = "trace", skip(session), err)] +#[tracing::instrument(level = "debug", skip(session), err)] fn fix_all( session: &Session, url: &lsp::Url, diff --git a/crates/rome_lsp/src/handlers/formatting.rs b/crates/rome_lsp/src/handlers/formatting.rs index 369075fde8c..80ac79162c5 100644 --- a/crates/rome_lsp/src/handlers/formatting.rs +++ b/crates/rome_lsp/src/handlers/formatting.rs @@ -50,7 +50,7 @@ pub(crate) fn format( Ok(Some(edits)) } -#[tracing::instrument(level = "trace", skip(session), err)] +#[tracing::instrument(level = "debug", skip(session), err)] pub(crate) fn format_range( session: &Session, params: DocumentRangeFormattingParams, @@ -117,7 +117,7 @@ pub(crate) fn format_range( }])) } -#[tracing::instrument(level = "trace", skip(session), err)] +#[tracing::instrument(level = "debug", skip(session), err)] pub(crate) fn format_on_type( session: &Session, params: DocumentOnTypeFormattingParams, diff --git a/crates/rome_lsp/src/handlers/rename.rs b/crates/rome_lsp/src/handlers/rename.rs index 12cf9b6a47b..9192aa37eee 100644 --- a/crates/rome_lsp/src/handlers/rename.rs +++ b/crates/rome_lsp/src/handlers/rename.rs @@ -5,7 +5,7 @@ use anyhow::{Context, Result}; use tower_lsp::lsp_types::{RenameParams, WorkspaceEdit}; use tracing::trace; -#[tracing::instrument(level = "trace", skip(session), err)] +#[tracing::instrument(level = "debug", skip(session), err)] pub(crate) fn rename(session: &Session, params: RenameParams) -> Result> { let url = params.text_document_position.text_document.uri; let rome_path = session.file_path(&url); diff --git a/crates/rome_lsp/src/handlers/text_document.rs b/crates/rome_lsp/src/handlers/text_document.rs index 4242d2b1283..2b06911af93 100644 --- a/crates/rome_lsp/src/handlers/text_document.rs +++ b/crates/rome_lsp/src/handlers/text_document.rs @@ -8,7 +8,7 @@ use tracing::error; use crate::{documents::Document, session::Session, utils}; /// Handler for `textDocument/didOpen` LSP notification -#[tracing::instrument(level = "trace", skip(session), err)] +#[tracing::instrument(level = "debug", skip(session), err)] pub(crate) async fn did_open( session: &Session, params: lsp_types::DidOpenTextDocumentParams, @@ -38,7 +38,7 @@ pub(crate) async fn did_open( } /// Handler for `textDocument/didChange` LSP notification -#[tracing::instrument(level = "trace", skip(session), err)] +#[tracing::instrument(level = "debug", skip(session), err)] pub(crate) async fn did_change( session: &Session, params: lsp_types::DidChangeTextDocumentParams, @@ -80,7 +80,7 @@ pub(crate) async fn did_change( } /// Handler for `textDocument/didClose` LSP notification -#[tracing::instrument(level = "trace", skip(session), err)] +#[tracing::instrument(level = "debug", skip(session), err)] pub(crate) async fn did_close( session: &Session, params: lsp_types::DidCloseTextDocumentParams, diff --git a/crates/rome_lsp/src/server.rs b/crates/rome_lsp/src/server.rs index 72b6597382c..18fb7937af0 100644 --- a/crates/rome_lsp/src/server.rs +++ b/crates/rome_lsp/src/server.rs @@ -58,7 +58,7 @@ impl LSPServer { requests::syntax_tree::syntax_tree(&self.session, &url).map_err(into_lsp_error) } - #[tracing::instrument(skip(self), name = "rome/rage", level = "trace")] + #[tracing::instrument(skip(self), name = "rome/rage", level = "debug")] async fn rage(&self, params: RageParams) -> LspResult { let mut entries = vec![ RageEntry::section("Server"), @@ -185,7 +185,7 @@ impl LSPServer { #[tower_lsp::async_trait] impl LanguageServer for LSPServer { - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] async fn initialize(&self, params: InitializeParams) -> LspResult { info!("Starting Rome Language Server..."); self.is_initialized.store(true, Ordering::Relaxed); @@ -219,7 +219,7 @@ impl LanguageServer for LSPServer { Ok(init) } - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] async fn initialized(&self, params: InitializedParams) { let _ = params; @@ -244,14 +244,14 @@ impl LanguageServer for LSPServer { Ok(()) } - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] async fn did_change_configuration(&self, params: DidChangeConfigurationParams) { let _ = params; self.session.fetch_client_configuration().await; self.setup_capabilities().await; } - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] async fn did_change_watched_files(&self, params: DidChangeWatchedFilesParams) { let file_paths = params .changes diff --git a/crates/rome_lsp/src/session.rs b/crates/rome_lsp/src/session.rs index ad4106c8e2e..9fa8f040749 100644 --- a/crates/rome_lsp/src/session.rs +++ b/crates/rome_lsp/src/session.rs @@ -135,7 +135,7 @@ impl Session { /// Computes diagnostics for the file matching the provided url and publishes /// them to the client. Called from [`handlers::text_document`] when a file's /// contents changes. - #[tracing::instrument(level = "trace", skip(self), err)] + #[tracing::instrument(level = "debug", skip(self), err)] pub(crate) async fn update_diagnostics(&self, url: lsp_types::Url) -> anyhow::Result<()> { let rome_path = self.file_path(&url); let doc = self.document(&url)?; diff --git a/crates/rome_rowan/Cargo.toml b/crates/rome_rowan/Cargo.toml index 5fc4f4571a3..6c170e5455f 100644 --- a/crates/rome_rowan/Cargo.toml +++ b/crates/rome_rowan/Cargo.toml @@ -18,6 +18,7 @@ countme = { workspace = true } serde = { version = "1.0.133", optional = true, default-features = false } rome_text_edit = { path = "../rome_text_edit" } schemars = { version = "0.8.10", optional = true } +tracing = { workspace = true } [dev-dependencies] quickcheck = "1.0.3" diff --git a/crates/rome_rowan/src/ast/batch.rs b/crates/rome_rowan/src/ast/batch.rs index fd2e8041469..ad3b1ac965b 100644 --- a/crates/rome_rowan/src/ast/batch.rs +++ b/crates/rome_rowan/src/ast/batch.rs @@ -1,5 +1,6 @@ use rome_text_edit::TextEdit; use rome_text_size::TextRange; +use tracing::debug; use crate::{AstNode, Language, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxSlot, SyntaxToken}; use std::{ @@ -261,6 +262,7 @@ where }); let parent_depth = parent.as_ref().map(|p| p.ancestors().count()).unwrap_or(0); + debug!("pushing change..."); self.changes.push(CommitChange { parent_depth, parent, @@ -276,6 +278,8 @@ where pub fn as_text_edits(&self) -> Option<(TextRange, TextEdit)> { let mut range = None; + debug!(" changes {:?}", &self.changes); + for change in &self.changes { let parent = change.parent.as_ref().unwrap_or(&self.root); let delete = match parent.slots().nth(change.new_node_slot) { @@ -389,6 +393,10 @@ where root } + + pub fn root(&self) -> &SyntaxNode { + &self.root + } } #[cfg(test)] diff --git a/crates/rome_service/src/file_handlers/javascript.rs b/crates/rome_service/src/file_handlers/javascript.rs index 06781aac3f4..de4ab609eae 100644 --- a/crates/rome_service/src/file_handlers/javascript.rs +++ b/crates/rome_service/src/file_handlers/javascript.rs @@ -10,7 +10,7 @@ use rome_analyze::{ AnalysisFilter, AnalyzerOptions, ControlFlow, GroupCategory, Never, QueryMatch, RegistryVisitor, RuleCategories, RuleCategory, RuleFilter, RuleGroup, }; -use rome_diagnostics::{v2::category, Applicability, CodeSuggestion}; +use rome_diagnostics::{v2::category, Applicability}; use rome_formatter::{FormatError, Printed}; use rome_fs::RomePath; use rome_js_analyze::{analyze, analyze_with_inspect_matcher, visit_registry, RuleError}; @@ -248,8 +248,10 @@ fn lint(params: LintParams) -> LintResults { if diagnostic_count <= params.max_diagnostics { diagnostic.set_severity(severity); - if let Some(action) = signal.action() { - diagnostic.add_code_suggestion(action.into()); + if let Some(action) = signal.actions() { + for code_suggestion in action.into_code_suggestion_advices() { + diagnostic.add_code_suggestion(code_suggestion); + } } diagnostics.push(v2::serde::Diagnostic::new(diagnostic)); @@ -333,13 +335,13 @@ fn code_actions( let analyzer_options = compute_analyzer_options(&settings); analyze(file_id, &tree, filter, &analyzer_options, |signal| { - if let Some(action) = signal.action() { - actions.push(CodeAction { - category: action.category.clone(), - group_name: Cow::Borrowed(action.group_name), - rule_name: Cow::Borrowed(action.rule_name), - suggestion: CodeSuggestion::from(action), - }); + if let Some(action) = signal.actions() { + actions.extend(action.into_code_action_iter().map(|item| CodeAction { + category: item.category.clone(), + group_name: Cow::Borrowed(item.group_name), + rule_name: Cow::Borrowed(item.rule_name), + suggestion: item.suggestion, + })); } ControlFlow::::Continue(()) @@ -381,22 +383,24 @@ fn fix_all(params: FixAllParams) -> Result { let analyzer_options = compute_analyzer_options(&settings); loop { let action = analyze(file_id, &tree, filter, &analyzer_options, |signal| { - if let Some(action) = signal.action() { - match fix_file_mode { - FixFileMode::SafeFixes => { - if action.applicability == Applicability::MaybeIncorrect { - skipped_suggested_fixes += 1; - } - if action.applicability == Applicability::Always { - return ControlFlow::Break(action); + if let Some(actions) = signal.actions() { + for action in actions { + match fix_file_mode { + FixFileMode::SafeFixes => { + if action.applicability == Applicability::MaybeIncorrect { + skipped_suggested_fixes += 1; + } + if action.applicability == Applicability::Always { + return ControlFlow::Break(action); + } } - } - FixFileMode::SafeAndSuggestedFixes => { - if matches!( - action.applicability, - Applicability::Always | Applicability::MaybeIncorrect - ) { - return ControlFlow::Break(action); + FixFileMode::SafeAndSuggestedFixes => { + if matches!( + action.applicability, + Applicability::Always | Applicability::MaybeIncorrect + ) { + return ControlFlow::Break(action); + } } } } diff --git a/xtask/bench/src/features/analyzer.rs b/xtask/bench/src/features/analyzer.rs index 61803b84119..9c2135cd230 100644 --- a/xtask/bench/src/features/analyzer.rs +++ b/xtask/bench/src/features/analyzer.rs @@ -31,7 +31,7 @@ pub fn run_analyzer(root: &JsAnyRoot) { let options = AnalyzerOptions::default(); analyze(FileId::zero(), root, filter, &options, |event| { black_box(event.diagnostic()); - black_box(event.action()); + black_box(event.actions()); ControlFlow::::Continue(()) }); } diff --git a/xtask/lintdoc/src/main.rs b/xtask/lintdoc/src/main.rs index 48d3fe92fdb..2b2d2252bc5 100644 --- a/xtask/lintdoc/src/main.rs +++ b/xtask/lintdoc/src/main.rs @@ -541,8 +541,12 @@ fn assert_lint( ); diag.set_severity(severity); - if let Some(action) = signal.action() { - diag.add_code_suggestion(action.into()); + if let Some(actions) = signal.actions() { + for action in actions { + if !action.is_suppression() { + diag.add_code_suggestion(action.into()); + } + } } let error = diag