Skip to content

Commit df87be8

Browse files
committed
Factor format string highlighting out
1 parent 0fb069c commit df87be8

File tree

2 files changed

+90
-75
lines changed

2 files changed

+90
-75
lines changed

crates/ide/src/syntax_highlighting.rs

Lines changed: 8 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
mod tags;
1+
mod format;
22
mod html;
33
mod injection;
4+
mod tags;
45
#[cfg(test)]
56
mod tests;
67

@@ -17,9 +18,8 @@ use syntax::{
1718
SyntaxNode, SyntaxToken, TextRange, WalkEvent, T,
1819
};
1920

20-
use crate::FileId;
21+
use crate::{syntax_highlighting::format::FormatStringHighlighter, FileId};
2122

22-
use ast::FormatSpecifier;
2323
pub(crate) use html::highlight_as_html;
2424
pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag};
2525

@@ -69,7 +69,7 @@ pub(crate) fn highlight(
6969
let mut stack = HighlightedRangeStack::new();
7070

7171
let mut current_macro_call: Option<(ast::MacroCall, Option<MacroMatcherParseState>)> = None;
72-
let mut format_string: Option<SyntaxElement> = None;
72+
let mut format_string_highlighter = FormatStringHighlighter::default();
7373

7474
// Walk all nodes, keeping track of whether we are inside a macro or not.
7575
// If in macro, expand it first and highlight the expanded code.
@@ -121,7 +121,7 @@ pub(crate) fn highlight(
121121
WalkEvent::Leave(Some(mc)) => {
122122
assert!(current_macro_call.map(|it| it.0) == Some(mc));
123123
current_macro_call = None;
124-
format_string = None;
124+
format_string_highlighter.reset();
125125
}
126126
_ => (),
127127
}
@@ -173,30 +173,7 @@ pub(crate) fn highlight(
173173
let token = sema.descend_into_macros(token.clone());
174174
let parent = token.parent();
175175

176-
// Check if macro takes a format string and remember it for highlighting later.
177-
// The macros that accept a format string expand to a compiler builtin macros
178-
// `format_args` and `format_args_nl`.
179-
if let Some(name) = parent
180-
.parent()
181-
.and_then(ast::MacroCall::cast)
182-
.and_then(|mc| mc.path())
183-
.and_then(|p| p.segment())
184-
.and_then(|s| s.name_ref())
185-
{
186-
match name.text().as_str() {
187-
"format_args" | "format_args_nl" => {
188-
format_string = parent
189-
.children_with_tokens()
190-
.filter(|t| t.kind() != WHITESPACE)
191-
.nth(1)
192-
.filter(|e| {
193-
ast::String::can_cast(e.kind())
194-
|| ast::RawString::can_cast(e.kind())
195-
})
196-
}
197-
_ => {}
198-
}
199-
}
176+
format_string_highlighter.check_for_format_string(&parent);
200177

201178
// We only care Name and Name_ref
202179
match (token.kind(), parent.kind()) {
@@ -214,8 +191,6 @@ pub(crate) fn highlight(
214191
}
215192
}
216193

217-
let is_format_string = format_string.as_ref() == Some(&element_to_highlight);
218-
219194
if let Some((highlight, binding_hash)) = highlight_element(
220195
&sema,
221196
&mut bindings_shadow_count,
@@ -226,19 +201,7 @@ pub(crate) fn highlight(
226201
if let Some(string) =
227202
element_to_highlight.as_token().cloned().and_then(ast::String::cast)
228203
{
229-
if is_format_string {
230-
stack.push();
231-
string.lex_format_specifier(|piece_range, kind| {
232-
if let Some(highlight) = highlight_format_specifier(kind) {
233-
stack.add(HighlightedRange {
234-
range: piece_range + range.start(),
235-
highlight: highlight.into(),
236-
binding_hash: None,
237-
});
238-
}
239-
});
240-
stack.pop();
241-
}
204+
format_string_highlighter.highlight_format_string(&mut stack, &string, range);
242205
// Highlight escape sequences
243206
if let Some(char_ranges) = string.char_ranges() {
244207
stack.push();
@@ -256,19 +219,7 @@ pub(crate) fn highlight(
256219
} else if let Some(string) =
257220
element_to_highlight.as_token().cloned().and_then(ast::RawString::cast)
258221
{
259-
if is_format_string {
260-
stack.push();
261-
string.lex_format_specifier(|piece_range, kind| {
262-
if let Some(highlight) = highlight_format_specifier(kind) {
263-
stack.add(HighlightedRange {
264-
range: piece_range + range.start(),
265-
highlight: highlight.into(),
266-
binding_hash: None,
267-
});
268-
}
269-
});
270-
stack.pop();
271-
}
222+
format_string_highlighter.highlight_format_string(&mut stack, &string, range);
272223
}
273224
}
274225
}
@@ -436,24 +387,6 @@ impl HighlightedRangeStack {
436387
}
437388
}
438389

439-
fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> {
440-
Some(match kind {
441-
FormatSpecifier::Open
442-
| FormatSpecifier::Close
443-
| FormatSpecifier::Colon
444-
| FormatSpecifier::Fill
445-
| FormatSpecifier::Align
446-
| FormatSpecifier::Sign
447-
| FormatSpecifier::NumberSign
448-
| FormatSpecifier::DollarSign
449-
| FormatSpecifier::Dot
450-
| FormatSpecifier::Asterisk
451-
| FormatSpecifier::QuestionMark => HighlightTag::FormatSpecifier,
452-
FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral,
453-
FormatSpecifier::Identifier => HighlightTag::Local,
454-
})
455-
}
456-
457390
fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
458391
let path = macro_call.path()?;
459392
let name_ref = path.segment()?.name_ref()?;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

0 commit comments

Comments
 (0)