diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index b82e3775e1d..f2d4da78d00 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -24,7 +24,7 @@ use syntax::{ use crate::{ syntax_highlighting::{ - format::FormatStringHighlighter, highlights::Highlights, + format::highlight_format_string, highlights::Highlights, macro_rules::MacroRulesHighlighter, tags::Highlight, }, FileId, HlMod, HlTag, SymbolKind, @@ -88,7 +88,6 @@ fn traverse( let mut current_macro_call: Option = None; let mut current_macro_rules: Option = None; - let mut format_string_highlighter = FormatStringHighlighter::default(); let mut macro_rules_highlighter = MacroRulesHighlighter::default(); let mut inside_attribute = false; @@ -120,7 +119,6 @@ fn traverse( WalkEvent::Leave(Some(mc)) => { assert_eq!(current_macro_call, Some(mc)); current_macro_call = None; - format_string_highlighter = FormatStringHighlighter::default(); } _ => (), } @@ -175,8 +173,6 @@ fn traverse( let token = sema.descend_into_macros(token.clone()); let parent = token.parent(); - format_string_highlighter.check_for_format_string(&parent); - // We only care Name and Name_ref match (token.kind(), parent.kind()) { (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), @@ -195,6 +191,10 @@ fn traverse( } } + if let Some(_) = macro_rules_highlighter.highlight(element_to_highlight.clone()) { + continue; + } + if let Some((mut highlight, binding_hash)) = highlight::element( &sema, &mut bindings_shadow_count, @@ -205,24 +205,20 @@ fn traverse( highlight = highlight | HlMod::Attribute; } - if macro_rules_highlighter.highlight(element_to_highlight.clone()).is_none() { - hl.add(HlRange { range, highlight, binding_hash }); - } + hl.add(HlRange { range, highlight, binding_hash }); + } - if let Some(string) = - element_to_highlight.as_token().cloned().and_then(ast::String::cast) - { - format_string_highlighter.highlight_format_string(hl, &string, range); - // Highlight escape sequences - if let Some(char_ranges) = string.char_ranges() { - for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) { - if string.text()[piece_range.start().into()..].starts_with('\\') { - hl.add(HlRange { - range: piece_range + range.start(), - highlight: HlTag::EscapeSequence.into(), - binding_hash: None, - }); - } + if let Some(string) = element_to_highlight.as_token().cloned().and_then(ast::String::cast) { + highlight_format_string(hl, &string, range); + // Highlight escape sequences + if let Some(char_ranges) = string.char_ranges() { + for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) { + if string.text()[piece_range.start().into()..].starts_with('\\') { + hl.add(HlRange { + range: piece_range + range.start(), + highlight: HlTag::EscapeSequence.into(), + binding_hash: None, + }); } } } diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs index d807ad0ad5c..a74ca844b33 100644 --- a/crates/ide/src/syntax_highlighting/format.rs +++ b/crates/ide/src/syntax_highlighting/format.rs @@ -1,60 +1,48 @@ //! Syntax highlighting for format macro strings. use syntax::{ ast::{self, FormatSpecifier, HasFormatSpecifier}, - AstNode, AstToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, + AstNode, AstToken, TextRange, }; -use crate::{HlRange, HlTag, SymbolKind}; +use crate::{syntax_highlighting::highlights::Highlights, HlRange, HlTag, SymbolKind}; -use super::highlights::Highlights; - -#[derive(Default)] -pub(super) struct FormatStringHighlighter { - format_string: Option, -} - -impl FormatStringHighlighter { - pub(super) fn check_for_format_string(&mut self, parent: &SyntaxNode) { - // Check if macro takes a format string and remember it for highlighting later. - // The macros that accept a format string expand to a compiler builtin macros - // `format_args` and `format_args_nl`. - if let Some(name) = parent - .parent() - .and_then(ast::MacroCall::cast) - .and_then(|mc| mc.path()) - .and_then(|p| p.segment()) - .and_then(|s| s.name_ref()) - { - match name.text().as_str() { - "format_args" | "format_args_nl" => { - self.format_string = parent - .children_with_tokens() - .filter(|t| t.kind() != SyntaxKind::WHITESPACE) - .nth(1) - .filter(|e| ast::String::can_cast(e.kind())) - } - _ => {} - } - } +pub(super) fn highlight_format_string( + stack: &mut Highlights, + string: &ast::String, + range: TextRange, +) { + if is_format_string(string).is_none() { + return; } - pub(super) fn highlight_format_string( - &self, - stack: &mut Highlights, - string: &impl HasFormatSpecifier, - range: TextRange, - ) { - if self.format_string.as_ref() == Some(&SyntaxElement::from(string.syntax().clone())) { - string.lex_format_specifier(|piece_range, kind| { - if let Some(highlight) = highlight_format_specifier(kind) { - stack.add(HlRange { - range: piece_range + range.start(), - highlight: highlight.into(), - binding_hash: None, - }); - } + + string.lex_format_specifier(|piece_range, kind| { + if let Some(highlight) = highlight_format_specifier(kind) { + stack.add(HlRange { + range: piece_range + range.start(), + highlight: highlight.into(), + binding_hash: None, }); } + }); +} + +fn is_format_string(string: &ast::String) -> Option<()> { + let parent = string.syntax().parent(); + + let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?; + if !matches!(name.text().as_str(), "format_args" | "format_args_nl") { + return None; } + + let first_literal = parent + .children_with_tokens() + .filter_map(|it| it.as_token().cloned().and_then(ast::String::cast)) + .next()?; + if &first_literal != string { + return None; + } + + Some(()) } fn highlight_format_specifier(kind: FormatSpecifier) -> Option { @@ -70,7 +58,9 @@ fn highlight_format_specifier(kind: FormatSpecifier) -> Option { | FormatSpecifier::Dot | FormatSpecifier::Asterisk | FormatSpecifier::QuestionMark => HlTag::FormatSpecifier, + FormatSpecifier::Integer | FormatSpecifier::Zero => HlTag::NumericLiteral, + FormatSpecifier::Identifier => HlTag::Symbol(SymbolKind::Local), }) }