diff options
Diffstat (limited to 'crates/ra_ide/src/syntax_highlighting.rs')
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting.rs | 122 |
1 files changed, 99 insertions, 23 deletions
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index ab45c364a..854b6cc6d 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -44,6 +44,7 @@ pub(crate) fn highlight( | |||
44 | db: &RootDatabase, | 44 | db: &RootDatabase, |
45 | file_id: FileId, | 45 | file_id: FileId, |
46 | range_to_highlight: Option<TextRange>, | 46 | range_to_highlight: Option<TextRange>, |
47 | syntactic_name_ref_highlighting: bool, | ||
47 | ) -> Vec<HighlightedRange> { | 48 | ) -> Vec<HighlightedRange> { |
48 | let _p = profile("highlight"); | 49 | let _p = profile("highlight"); |
49 | let sema = Semantics::new(db); | 50 | let sema = Semantics::new(db); |
@@ -104,6 +105,7 @@ pub(crate) fn highlight( | |||
104 | if let Some((highlight, binding_hash)) = highlight_element( | 105 | if let Some((highlight, binding_hash)) = highlight_element( |
105 | &sema, | 106 | &sema, |
106 | &mut bindings_shadow_count, | 107 | &mut bindings_shadow_count, |
108 | syntactic_name_ref_highlighting, | ||
107 | name.syntax().clone().into(), | 109 | name.syntax().clone().into(), |
108 | ) { | 110 | ) { |
109 | stack.add(HighlightedRange { | 111 | stack.add(HighlightedRange { |
@@ -160,23 +162,25 @@ pub(crate) fn highlight( | |||
160 | // Check if macro takes a format string and remember it for highlighting later. | 162 | // Check if macro takes a format string and remember it for highlighting later. |
161 | // The macros that accept a format string expand to a compiler builtin macros | 163 | // The macros that accept a format string expand to a compiler builtin macros |
162 | // `format_args` and `format_args_nl`. | 164 | // `format_args` and `format_args_nl`. |
163 | if let Some(fmt_macro_call) = parent.parent().and_then(ast::MacroCall::cast) { | 165 | if let Some(name) = parent |
164 | if let Some(name) = | 166 | .parent() |
165 | fmt_macro_call.path().and_then(|p| p.segment()).and_then(|s| s.name_ref()) | 167 | .and_then(ast::MacroCall::cast) |
166 | { | 168 | .and_then(|mc| mc.path()) |
167 | match name.text().as_str() { | 169 | .and_then(|p| p.segment()) |
168 | "format_args" | "format_args_nl" => { | 170 | .and_then(|s| s.name_ref()) |
169 | format_string = parent | 171 | { |
170 | .children_with_tokens() | 172 | match name.text().as_str() { |
171 | .filter(|t| t.kind() != WHITESPACE) | 173 | "format_args" | "format_args_nl" => { |
172 | .nth(1) | 174 | format_string = parent |
173 | .filter(|e| { | 175 | .children_with_tokens() |
174 | ast::String::can_cast(e.kind()) | 176 | .filter(|t| t.kind() != WHITESPACE) |
175 | || ast::RawString::can_cast(e.kind()) | 177 | .nth(1) |
176 | }) | 178 | .filter(|e| { |
177 | } | 179 | ast::String::can_cast(e.kind()) |
178 | _ => {} | 180 | || ast::RawString::can_cast(e.kind()) |
181 | }) | ||
179 | } | 182 | } |
183 | _ => {} | ||
180 | } | 184 | } |
181 | } | 185 | } |
182 | 186 | ||
@@ -198,15 +202,18 @@ pub(crate) fn highlight( | |||
198 | 202 | ||
199 | let is_format_string = format_string.as_ref() == Some(&element_to_highlight); | 203 | let is_format_string = format_string.as_ref() == Some(&element_to_highlight); |
200 | 204 | ||
201 | if let Some((highlight, binding_hash)) = | 205 | if let Some((highlight, binding_hash)) = highlight_element( |
202 | highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight.clone()) | 206 | &sema, |
203 | { | 207 | &mut bindings_shadow_count, |
208 | syntactic_name_ref_highlighting, | ||
209 | element_to_highlight.clone(), | ||
210 | ) { | ||
204 | stack.add(HighlightedRange { range, highlight, binding_hash }); | 211 | stack.add(HighlightedRange { range, highlight, binding_hash }); |
205 | if let Some(string) = | 212 | if let Some(string) = |
206 | element_to_highlight.as_token().cloned().and_then(ast::String::cast) | 213 | element_to_highlight.as_token().cloned().and_then(ast::String::cast) |
207 | { | 214 | { |
208 | stack.push(); | ||
209 | if is_format_string { | 215 | if is_format_string { |
216 | stack.push(); | ||
210 | string.lex_format_specifier(|piece_range, kind| { | 217 | string.lex_format_specifier(|piece_range, kind| { |
211 | if let Some(highlight) = highlight_format_specifier(kind) { | 218 | if let Some(highlight) = highlight_format_specifier(kind) { |
212 | stack.add(HighlightedRange { | 219 | stack.add(HighlightedRange { |
@@ -216,13 +223,27 @@ pub(crate) fn highlight( | |||
216 | }); | 223 | }); |
217 | } | 224 | } |
218 | }); | 225 | }); |
226 | stack.pop(); | ||
227 | } | ||
228 | // Highlight escape sequences | ||
229 | if let Some(char_ranges) = string.char_ranges() { | ||
230 | stack.push(); | ||
231 | for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) { | ||
232 | if string.text()[piece_range.start().into()..].starts_with('\\') { | ||
233 | stack.add(HighlightedRange { | ||
234 | range: piece_range + range.start(), | ||
235 | highlight: HighlightTag::EscapeSequence.into(), | ||
236 | binding_hash: None, | ||
237 | }); | ||
238 | } | ||
239 | } | ||
240 | stack.pop_and_inject(false); | ||
219 | } | 241 | } |
220 | stack.pop(); | ||
221 | } else if let Some(string) = | 242 | } else if let Some(string) = |
222 | element_to_highlight.as_token().cloned().and_then(ast::RawString::cast) | 243 | element_to_highlight.as_token().cloned().and_then(ast::RawString::cast) |
223 | { | 244 | { |
224 | stack.push(); | ||
225 | if is_format_string { | 245 | if is_format_string { |
246 | stack.push(); | ||
226 | string.lex_format_specifier(|piece_range, kind| { | 247 | string.lex_format_specifier(|piece_range, kind| { |
227 | if let Some(highlight) = highlight_format_specifier(kind) { | 248 | if let Some(highlight) = highlight_format_specifier(kind) { |
228 | stack.add(HighlightedRange { | 249 | stack.add(HighlightedRange { |
@@ -232,8 +253,8 @@ pub(crate) fn highlight( | |||
232 | }); | 253 | }); |
233 | } | 254 | } |
234 | }); | 255 | }); |
256 | stack.pop(); | ||
235 | } | 257 | } |
236 | stack.pop(); | ||
237 | } | 258 | } |
238 | } | 259 | } |
239 | } | 260 | } |
@@ -408,6 +429,7 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> { | |||
408 | fn highlight_element( | 429 | fn highlight_element( |
409 | sema: &Semantics<RootDatabase>, | 430 | sema: &Semantics<RootDatabase>, |
410 | bindings_shadow_count: &mut FxHashMap<Name, u32>, | 431 | bindings_shadow_count: &mut FxHashMap<Name, u32>, |
432 | syntactic_name_ref_highlighting: bool, | ||
411 | element: SyntaxElement, | 433 | element: SyntaxElement, |
412 | ) -> Option<(Highlight, Option<u64>)> { | 434 | ) -> Option<(Highlight, Option<u64>)> { |
413 | let db = sema.db; | 435 | let db = sema.db; |
@@ -461,6 +483,7 @@ fn highlight_element( | |||
461 | } | 483 | } |
462 | NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), | 484 | NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), |
463 | }, | 485 | }, |
486 | None if syntactic_name_ref_highlighting => highlight_name_ref_by_syntax(name_ref), | ||
464 | None => HighlightTag::UnresolvedReference.into(), | 487 | None => HighlightTag::UnresolvedReference.into(), |
465 | } | 488 | } |
466 | } | 489 | } |
@@ -493,6 +516,9 @@ fn highlight_element( | |||
493 | h |= HighlightModifier::Unsafe; | 516 | h |= HighlightModifier::Unsafe; |
494 | h | 517 | h |
495 | } | 518 | } |
519 | T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { | ||
520 | Highlight::new(HighlightTag::Macro) | ||
521 | } | ||
496 | 522 | ||
497 | k if k.is_keyword() => { | 523 | k if k.is_keyword() => { |
498 | let h = Highlight::new(HighlightTag::Keyword); | 524 | let h = Highlight::new(HighlightTag::Keyword); |
@@ -609,3 +635,53 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { | |||
609 | 635 | ||
610 | tag.into() | 636 | tag.into() |
611 | } | 637 | } |
638 | |||
639 | fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight { | ||
640 | let default = HighlightTag::UnresolvedReference; | ||
641 | |||
642 | let parent = match name.syntax().parent() { | ||
643 | Some(it) => it, | ||
644 | _ => return default.into(), | ||
645 | }; | ||
646 | |||
647 | let tag = match parent.kind() { | ||
648 | METHOD_CALL_EXPR => HighlightTag::Function, | ||
649 | FIELD_EXPR => HighlightTag::Field, | ||
650 | PATH_SEGMENT => { | ||
651 | let path = match parent.parent().and_then(ast::Path::cast) { | ||
652 | Some(it) => it, | ||
653 | _ => return default.into(), | ||
654 | }; | ||
655 | let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) { | ||
656 | Some(it) => it, | ||
657 | _ => { | ||
658 | // within path, decide whether it is module or adt by checking for uppercase name | ||
659 | return if name.text().chars().next().unwrap_or_default().is_uppercase() { | ||
660 | HighlightTag::Struct | ||
661 | } else { | ||
662 | HighlightTag::Module | ||
663 | } | ||
664 | .into(); | ||
665 | } | ||
666 | }; | ||
667 | let parent = match expr.syntax().parent() { | ||
668 | Some(it) => it, | ||
669 | None => return default.into(), | ||
670 | }; | ||
671 | |||
672 | match parent.kind() { | ||
673 | CALL_EXPR => HighlightTag::Function, | ||
674 | _ => { | ||
675 | if name.text().chars().next().unwrap_or_default().is_uppercase() { | ||
676 | HighlightTag::Struct | ||
677 | } else { | ||
678 | HighlightTag::Constant | ||
679 | } | ||
680 | } | ||
681 | } | ||
682 | } | ||
683 | _ => default, | ||
684 | }; | ||
685 | |||
686 | tag.into() | ||
687 | } | ||