diff options
Diffstat (limited to 'crates/ide/src/syntax_highlighting')
-rw-r--r-- | crates/ide/src/syntax_highlighting/format.rs | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs new file mode 100644 index 000000000..3ab01295a --- /dev/null +++ b/crates/ide/src/syntax_highlighting/format.rs | |||
@@ -0,0 +1,82 @@ | |||
1 | //! Syntax highlighting for format macro strings. | ||
2 | use syntax::{ | ||
3 | ast::{self, FormatSpecifier, HasFormatSpecifier}, | ||
4 | AstNode, AstToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, | ||
5 | }; | ||
6 | |||
7 | use crate::{syntax_highlighting::HighlightedRangeStack, HighlightTag, HighlightedRange}; | ||
8 | |||
9 | #[derive(Default)] | ||
10 | pub(super) struct FormatStringHighlighter { | ||
11 | format_string: Option<SyntaxElement>, | ||
12 | } | ||
13 | |||
14 | impl FormatStringHighlighter { | ||
15 | pub(super) fn reset(&mut self) { | ||
16 | self.format_string = None; | ||
17 | } | ||
18 | |||
19 | pub(super) fn check_for_format_string(&mut self, parent: &SyntaxNode) { | ||
20 | // Check if macro takes a format string and remember it for highlighting later. | ||
21 | // The macros that accept a format string expand to a compiler builtin macros | ||
22 | // `format_args` and `format_args_nl`. | ||
23 | if let Some(name) = parent | ||
24 | .parent() | ||
25 | .and_then(ast::MacroCall::cast) | ||
26 | .and_then(|mc| mc.path()) | ||
27 | .and_then(|p| p.segment()) | ||
28 | .and_then(|s| s.name_ref()) | ||
29 | { | ||
30 | match name.text().as_str() { | ||
31 | "format_args" | "format_args_nl" => { | ||
32 | self.format_string = parent | ||
33 | .children_with_tokens() | ||
34 | .filter(|t| t.kind() != SyntaxKind::WHITESPACE) | ||
35 | .nth(1) | ||
36 | .filter(|e| { | ||
37 | ast::String::can_cast(e.kind()) || ast::RawString::can_cast(e.kind()) | ||
38 | }) | ||
39 | } | ||
40 | _ => {} | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | pub(super) fn highlight_format_string( | ||
45 | &self, | ||
46 | range_stack: &mut HighlightedRangeStack, | ||
47 | string: &impl HasFormatSpecifier, | ||
48 | range: TextRange, | ||
49 | ) { | ||
50 | if self.format_string.as_ref() == Some(&SyntaxElement::from(string.syntax().clone())) { | ||
51 | range_stack.push(); | ||
52 | string.lex_format_specifier(|piece_range, kind| { | ||
53 | if let Some(highlight) = highlight_format_specifier(kind) { | ||
54 | range_stack.add(HighlightedRange { | ||
55 | range: piece_range + range.start(), | ||
56 | highlight: highlight.into(), | ||
57 | binding_hash: None, | ||
58 | }); | ||
59 | } | ||
60 | }); | ||
61 | range_stack.pop(); | ||
62 | } | ||
63 | } | ||
64 | } | ||
65 | |||
66 | fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { | ||
67 | Some(match kind { | ||
68 | FormatSpecifier::Open | ||
69 | | FormatSpecifier::Close | ||
70 | | FormatSpecifier::Colon | ||
71 | | FormatSpecifier::Fill | ||
72 | | FormatSpecifier::Align | ||
73 | | FormatSpecifier::Sign | ||
74 | | FormatSpecifier::NumberSign | ||
75 | | FormatSpecifier::DollarSign | ||
76 | | FormatSpecifier::Dot | ||
77 | | FormatSpecifier::Asterisk | ||
78 | | FormatSpecifier::QuestionMark => HighlightTag::FormatSpecifier, | ||
79 | FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral, | ||
80 | FormatSpecifier::Identifier => HighlightTag::Local, | ||
81 | }) | ||
82 | } | ||