diff options
Diffstat (limited to 'crates/ra_ide/src/syntax_highlighting.rs')
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting.rs | 69 |
1 files changed, 66 insertions, 3 deletions
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index e7d9bf696..e342ca9df 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -12,7 +12,7 @@ use ra_ide_db::{ | |||
12 | }; | 12 | }; |
13 | use ra_prof::profile; | 13 | use ra_prof::profile; |
14 | use ra_syntax::{ | 14 | use ra_syntax::{ |
15 | ast::{self, HasQuotes, HasStringValue}, | 15 | ast::{self, HasFormatSpecifier, HasQuotes, HasStringValue}, |
16 | AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, | 16 | AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, |
17 | SyntaxKind::*, | 17 | SyntaxKind::*, |
18 | SyntaxToken, TextRange, WalkEvent, T, | 18 | SyntaxToken, TextRange, WalkEvent, T, |
@@ -21,6 +21,7 @@ use rustc_hash::FxHashMap; | |||
21 | 21 | ||
22 | use crate::{call_info::call_info_for_token, Analysis, FileId}; | 22 | use crate::{call_info::call_info_for_token, Analysis, FileId}; |
23 | 23 | ||
24 | use ast::FormatSpecifier; | ||
24 | pub(crate) use html::highlight_as_html; | 25 | pub(crate) use html::highlight_as_html; |
25 | pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; | 26 | pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; |
26 | 27 | ||
@@ -95,7 +96,8 @@ impl HighlightedRangeStack { | |||
95 | 1, | 96 | 1, |
96 | "after DFS traversal, the stack should only contain a single element" | 97 | "after DFS traversal, the stack should only contain a single element" |
97 | ); | 98 | ); |
98 | let res = self.stack.pop().unwrap(); | 99 | let mut res = self.stack.pop().unwrap(); |
100 | res.sort_by_key(|range| range.range.start()); | ||
99 | // Check that ranges are sorted and disjoint | 101 | // Check that ranges are sorted and disjoint |
100 | assert!(res | 102 | assert!(res |
101 | .iter() | 103 | .iter() |
@@ -134,6 +136,7 @@ pub(crate) fn highlight( | |||
134 | let mut stack = HighlightedRangeStack::new(); | 136 | let mut stack = HighlightedRangeStack::new(); |
135 | 137 | ||
136 | let mut current_macro_call: Option<ast::MacroCall> = None; | 138 | let mut current_macro_call: Option<ast::MacroCall> = None; |
139 | let mut format_string: Option<SyntaxElement> = None; | ||
137 | 140 | ||
138 | // Walk all nodes, keeping track of whether we are inside a macro or not. | 141 | // Walk all nodes, keeping track of whether we are inside a macro or not. |
139 | // If in macro, expand it first and highlight the expanded code. | 142 | // If in macro, expand it first and highlight the expanded code. |
@@ -169,6 +172,7 @@ pub(crate) fn highlight( | |||
169 | WalkEvent::Leave(Some(mc)) => { | 172 | WalkEvent::Leave(Some(mc)) => { |
170 | assert!(current_macro_call == Some(mc)); | 173 | assert!(current_macro_call == Some(mc)); |
171 | current_macro_call = None; | 174 | current_macro_call = None; |
175 | format_string = None; | ||
172 | continue; | 176 | continue; |
173 | } | 177 | } |
174 | _ => (), | 178 | _ => (), |
@@ -189,6 +193,30 @@ pub(crate) fn highlight( | |||
189 | }; | 193 | }; |
190 | let token = sema.descend_into_macros(token.clone()); | 194 | let token = sema.descend_into_macros(token.clone()); |
191 | let parent = token.parent(); | 195 | let parent = token.parent(); |
196 | |||
197 | // Check if macro takes a format string and remeber it for highlighting later. | ||
198 | // The macros that accept a format string expand to a compiler builtin macros | ||
199 | // `format_args` and `format_args_nl`. | ||
200 | if let Some(fmt_macro_call) = parent.parent().and_then(ast::MacroCall::cast) { | ||
201 | if let Some(name) = | ||
202 | fmt_macro_call.path().and_then(|p| p.segment()).and_then(|s| s.name_ref()) | ||
203 | { | ||
204 | match name.text().as_str() { | ||
205 | "format_args" | "format_args_nl" => { | ||
206 | format_string = parent | ||
207 | .children_with_tokens() | ||
208 | .filter(|t| t.kind() != WHITESPACE) | ||
209 | .nth(1) | ||
210 | .filter(|e| { | ||
211 | ast::String::can_cast(e.kind()) | ||
212 | || ast::RawString::can_cast(e.kind()) | ||
213 | }) | ||
214 | } | ||
215 | _ => {} | ||
216 | } | ||
217 | } | ||
218 | } | ||
219 | |||
192 | // We only care Name and Name_ref | 220 | // We only care Name and Name_ref |
193 | match (token.kind(), parent.kind()) { | 221 | match (token.kind(), parent.kind()) { |
194 | (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), | 222 | (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), |
@@ -205,10 +233,45 @@ pub(crate) fn highlight( | |||
205 | } | 233 | } |
206 | } | 234 | } |
207 | 235 | ||
236 | let is_format_string = | ||
237 | format_string.as_ref().map(|fs| fs == &element_to_highlight).unwrap_or_default(); | ||
238 | |||
208 | if let Some((highlight, binding_hash)) = | 239 | if let Some((highlight, binding_hash)) = |
209 | highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight) | 240 | highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight.clone()) |
210 | { | 241 | { |
211 | stack.add(HighlightedRange { range, highlight, binding_hash }); | 242 | stack.add(HighlightedRange { range, highlight, binding_hash }); |
243 | if let Some(string) = | ||
244 | element_to_highlight.as_token().cloned().and_then(ast::String::cast) | ||
245 | { | ||
246 | stack.push(); | ||
247 | if is_format_string { | ||
248 | string.lex_format_specifier(&mut |piece_range, kind| { | ||
249 | let highlight = match kind { | ||
250 | FormatSpecifier::Open | ||
251 | | FormatSpecifier::Close | ||
252 | | FormatSpecifier::Colon | ||
253 | | FormatSpecifier::Fill | ||
254 | | FormatSpecifier::Align | ||
255 | | FormatSpecifier::Sign | ||
256 | | FormatSpecifier::NumberSign | ||
257 | | FormatSpecifier::DollarSign | ||
258 | | FormatSpecifier::Dot | ||
259 | | FormatSpecifier::Asterisk | ||
260 | | FormatSpecifier::QuestionMark => HighlightTag::Attribute, | ||
261 | FormatSpecifier::Integer | FormatSpecifier::Zero => { | ||
262 | HighlightTag::NumericLiteral | ||
263 | } | ||
264 | FormatSpecifier::Identifier => HighlightTag::Local, | ||
265 | }; | ||
266 | stack.add(HighlightedRange { | ||
267 | range: piece_range + range.start(), | ||
268 | highlight: highlight.into(), | ||
269 | binding_hash: None, | ||
270 | }); | ||
271 | }); | ||
272 | } | ||
273 | stack.pop(); | ||
274 | } | ||
212 | } | 275 | } |
213 | } | 276 | } |
214 | 277 | ||