diff options
Diffstat (limited to 'crates/ra_ide/src/syntax_highlighting.rs')
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting.rs | 333 |
1 files changed, 260 insertions, 73 deletions
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 0b53ebe69..6ac44c2c0 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | mod tags; | 1 | mod tags; |
2 | mod html; | 2 | mod html; |
3 | mod injection; | ||
3 | #[cfg(test)] | 4 | #[cfg(test)] |
4 | mod tests; | 5 | mod tests; |
5 | 6 | ||
@@ -10,14 +11,14 @@ use ra_ide_db::{ | |||
10 | }; | 11 | }; |
11 | use ra_prof::profile; | 12 | use ra_prof::profile; |
12 | use ra_syntax::{ | 13 | use ra_syntax::{ |
13 | ast::{self, HasFormatSpecifier, HasQuotes, HasStringValue}, | 14 | ast::{self, HasFormatSpecifier}, |
14 | AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, | 15 | AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, |
15 | SyntaxKind::*, | 16 | SyntaxKind::*, |
16 | SyntaxToken, TextRange, WalkEvent, T, | 17 | TextRange, WalkEvent, T, |
17 | }; | 18 | }; |
18 | use rustc_hash::FxHashMap; | 19 | use rustc_hash::FxHashMap; |
19 | 20 | ||
20 | use crate::{call_info::ActiveParameter, Analysis, FileId}; | 21 | use crate::FileId; |
21 | 22 | ||
22 | use ast::FormatSpecifier; | 23 | use ast::FormatSpecifier; |
23 | pub(crate) use html::highlight_as_html; | 24 | pub(crate) use html::highlight_as_html; |
@@ -43,6 +44,7 @@ pub(crate) fn highlight( | |||
43 | db: &RootDatabase, | 44 | db: &RootDatabase, |
44 | file_id: FileId, | 45 | file_id: FileId, |
45 | range_to_highlight: Option<TextRange>, | 46 | range_to_highlight: Option<TextRange>, |
47 | syntactic_name_ref_highlighting: bool, | ||
46 | ) -> Vec<HighlightedRange> { | 48 | ) -> Vec<HighlightedRange> { |
47 | let _p = profile("highlight"); | 49 | let _p = profile("highlight"); |
48 | let sema = Semantics::new(db); | 50 | let sema = Semantics::new(db); |
@@ -103,6 +105,7 @@ pub(crate) fn highlight( | |||
103 | if let Some((highlight, binding_hash)) = highlight_element( | 105 | if let Some((highlight, binding_hash)) = highlight_element( |
104 | &sema, | 106 | &sema, |
105 | &mut bindings_shadow_count, | 107 | &mut bindings_shadow_count, |
108 | syntactic_name_ref_highlighting, | ||
106 | name.syntax().clone().into(), | 109 | name.syntax().clone().into(), |
107 | ) { | 110 | ) { |
108 | stack.add(HighlightedRange { | 111 | stack.add(HighlightedRange { |
@@ -118,7 +121,23 @@ pub(crate) fn highlight( | |||
118 | assert!(current_macro_call == Some(mc)); | 121 | assert!(current_macro_call == Some(mc)); |
119 | current_macro_call = None; | 122 | current_macro_call = None; |
120 | format_string = None; | 123 | format_string = None; |
121 | continue; | 124 | } |
125 | _ => (), | ||
126 | } | ||
127 | |||
128 | // Check for Rust code in documentation | ||
129 | match &event { | ||
130 | WalkEvent::Leave(NodeOrToken::Node(node)) => { | ||
131 | if let Some((doctest, range_mapping, new_comments)) = | ||
132 | injection::extract_doc_comments(node) | ||
133 | { | ||
134 | injection::highlight_doc_comment( | ||
135 | doctest, | ||
136 | range_mapping, | ||
137 | new_comments, | ||
138 | &mut stack, | ||
139 | ); | ||
140 | } | ||
122 | } | 141 | } |
123 | _ => (), | 142 | _ => (), |
124 | } | 143 | } |
@@ -130,7 +149,7 @@ pub(crate) fn highlight( | |||
130 | 149 | ||
131 | let range = element.text_range(); | 150 | let range = element.text_range(); |
132 | 151 | ||
133 | let element_to_highlight = if current_macro_call.is_some() { | 152 | let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT { |
134 | // Inside a macro -- expand it first | 153 | // Inside a macro -- expand it first |
135 | let token = match element.clone().into_token() { | 154 | let token = match element.clone().into_token() { |
136 | Some(it) if it.parent().kind() == TOKEN_TREE => it, | 155 | Some(it) if it.parent().kind() == TOKEN_TREE => it, |
@@ -142,23 +161,25 @@ pub(crate) fn highlight( | |||
142 | // Check if macro takes a format string and remember it for highlighting later. | 161 | // Check if macro takes a format string and remember it for highlighting later. |
143 | // The macros that accept a format string expand to a compiler builtin macros | 162 | // The macros that accept a format string expand to a compiler builtin macros |
144 | // `format_args` and `format_args_nl`. | 163 | // `format_args` and `format_args_nl`. |
145 | if let Some(fmt_macro_call) = parent.parent().and_then(ast::MacroCall::cast) { | 164 | if let Some(name) = parent |
146 | if let Some(name) = | 165 | .parent() |
147 | fmt_macro_call.path().and_then(|p| p.segment()).and_then(|s| s.name_ref()) | 166 | .and_then(ast::MacroCall::cast) |
148 | { | 167 | .and_then(|mc| mc.path()) |
149 | match name.text().as_str() { | 168 | .and_then(|p| p.segment()) |
150 | "format_args" | "format_args_nl" => { | 169 | .and_then(|s| s.name_ref()) |
151 | format_string = parent | 170 | { |
152 | .children_with_tokens() | 171 | match name.text().as_str() { |
153 | .filter(|t| t.kind() != WHITESPACE) | 172 | "format_args" | "format_args_nl" => { |
154 | .nth(1) | 173 | format_string = parent |
155 | .filter(|e| { | 174 | .children_with_tokens() |
156 | ast::String::can_cast(e.kind()) | 175 | .filter(|t| t.kind() != WHITESPACE) |
157 | || ast::RawString::can_cast(e.kind()) | 176 | .nth(1) |
158 | }) | 177 | .filter(|e| { |
159 | } | 178 | ast::String::can_cast(e.kind()) |
160 | _ => {} | 179 | || ast::RawString::can_cast(e.kind()) |
180 | }) | ||
161 | } | 181 | } |
182 | _ => {} | ||
162 | } | 183 | } |
163 | } | 184 | } |
164 | 185 | ||
@@ -173,22 +194,25 @@ pub(crate) fn highlight( | |||
173 | 194 | ||
174 | if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) { | 195 | if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) { |
175 | let expanded = element_to_highlight.as_token().unwrap().clone(); | 196 | let expanded = element_to_highlight.as_token().unwrap().clone(); |
176 | if highlight_injection(&mut stack, &sema, token, expanded).is_some() { | 197 | if injection::highlight_injection(&mut stack, &sema, token, expanded).is_some() { |
177 | continue; | 198 | continue; |
178 | } | 199 | } |
179 | } | 200 | } |
180 | 201 | ||
181 | let is_format_string = format_string.as_ref() == Some(&element_to_highlight); | 202 | let is_format_string = format_string.as_ref() == Some(&element_to_highlight); |
182 | 203 | ||
183 | if let Some((highlight, binding_hash)) = | 204 | if let Some((highlight, binding_hash)) = highlight_element( |
184 | highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight.clone()) | 205 | &sema, |
185 | { | 206 | &mut bindings_shadow_count, |
207 | syntactic_name_ref_highlighting, | ||
208 | element_to_highlight.clone(), | ||
209 | ) { | ||
186 | stack.add(HighlightedRange { range, highlight, binding_hash }); | 210 | stack.add(HighlightedRange { range, highlight, binding_hash }); |
187 | if let Some(string) = | 211 | if let Some(string) = |
188 | element_to_highlight.as_token().cloned().and_then(ast::String::cast) | 212 | element_to_highlight.as_token().cloned().and_then(ast::String::cast) |
189 | { | 213 | { |
190 | stack.push(); | ||
191 | if is_format_string { | 214 | if is_format_string { |
215 | stack.push(); | ||
192 | string.lex_format_specifier(|piece_range, kind| { | 216 | string.lex_format_specifier(|piece_range, kind| { |
193 | if let Some(highlight) = highlight_format_specifier(kind) { | 217 | if let Some(highlight) = highlight_format_specifier(kind) { |
194 | stack.add(HighlightedRange { | 218 | stack.add(HighlightedRange { |
@@ -198,13 +222,27 @@ pub(crate) fn highlight( | |||
198 | }); | 222 | }); |
199 | } | 223 | } |
200 | }); | 224 | }); |
225 | stack.pop(); | ||
226 | } | ||
227 | // Highlight escape sequences | ||
228 | if let Some(char_ranges) = string.char_ranges() { | ||
229 | stack.push(); | ||
230 | for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) { | ||
231 | if string.text()[piece_range.start().into()..].starts_with('\\') { | ||
232 | stack.add(HighlightedRange { | ||
233 | range: piece_range + range.start(), | ||
234 | highlight: HighlightTag::EscapeSequence.into(), | ||
235 | binding_hash: None, | ||
236 | }); | ||
237 | } | ||
238 | } | ||
239 | stack.pop_and_inject(None); | ||
201 | } | 240 | } |
202 | stack.pop(); | ||
203 | } else if let Some(string) = | 241 | } else if let Some(string) = |
204 | element_to_highlight.as_token().cloned().and_then(ast::RawString::cast) | 242 | element_to_highlight.as_token().cloned().and_then(ast::RawString::cast) |
205 | { | 243 | { |
206 | stack.push(); | ||
207 | if is_format_string { | 244 | if is_format_string { |
245 | stack.push(); | ||
208 | string.lex_format_specifier(|piece_range, kind| { | 246 | string.lex_format_specifier(|piece_range, kind| { |
209 | if let Some(highlight) = highlight_format_specifier(kind) { | 247 | if let Some(highlight) = highlight_format_specifier(kind) { |
210 | stack.add(HighlightedRange { | 248 | stack.add(HighlightedRange { |
@@ -214,8 +252,8 @@ pub(crate) fn highlight( | |||
214 | }); | 252 | }); |
215 | } | 253 | } |
216 | }); | 254 | }); |
255 | stack.pop(); | ||
217 | } | 256 | } |
218 | stack.pop(); | ||
219 | } | 257 | } |
220 | } | 258 | } |
221 | } | 259 | } |
@@ -259,9 +297,8 @@ impl HighlightedRangeStack { | |||
259 | let mut parent = prev.pop().unwrap(); | 297 | let mut parent = prev.pop().unwrap(); |
260 | for ele in children { | 298 | for ele in children { |
261 | assert!(parent.range.contains_range(ele.range)); | 299 | assert!(parent.range.contains_range(ele.range)); |
262 | let mut cloned = parent.clone(); | 300 | |
263 | parent.range = TextRange::new(parent.range.start(), ele.range.start()); | 301 | let cloned = Self::intersect(&mut parent, &ele); |
264 | cloned.range = TextRange::new(ele.range.end(), cloned.range.end()); | ||
265 | if !parent.range.is_empty() { | 302 | if !parent.range.is_empty() { |
266 | prev.push(parent); | 303 | prev.push(parent); |
267 | } | 304 | } |
@@ -274,6 +311,92 @@ impl HighlightedRangeStack { | |||
274 | } | 311 | } |
275 | } | 312 | } |
276 | 313 | ||
314 | /// Intersects the `HighlightedRange` `parent` with `child`. | ||
315 | /// `parent` is mutated in place, becoming the range before `child`. | ||
316 | /// Returns the range (of the same type as `parent`) *after* `child`. | ||
317 | fn intersect(parent: &mut HighlightedRange, child: &HighlightedRange) -> HighlightedRange { | ||
318 | assert!(parent.range.contains_range(child.range)); | ||
319 | |||
320 | let mut cloned = parent.clone(); | ||
321 | parent.range = TextRange::new(parent.range.start(), child.range.start()); | ||
322 | cloned.range = TextRange::new(child.range.end(), cloned.range.end()); | ||
323 | |||
324 | cloned | ||
325 | } | ||
326 | |||
327 | /// Remove the `HighlightRange` of `parent` that's currently covered by `child`. | ||
328 | fn intersect_partial(parent: &mut HighlightedRange, child: &HighlightedRange) { | ||
329 | assert!( | ||
330 | parent.range.start() <= child.range.start() | ||
331 | && parent.range.end() >= child.range.start() | ||
332 | && child.range.end() > parent.range.end() | ||
333 | ); | ||
334 | |||
335 | parent.range = TextRange::new(parent.range.start(), child.range.start()); | ||
336 | } | ||
337 | |||
338 | /// Similar to `pop`, but can modify arbitrary prior ranges (where `pop`) | ||
339 | /// can only modify the last range currently on the stack. | ||
340 | /// Can be used to do injections that span multiple ranges, like the | ||
341 | /// doctest injection below. | ||
342 | /// If `overwrite_parent` is non-optional, the highlighting of the parent range | ||
343 | /// is overwritten with the argument. | ||
344 | /// | ||
345 | /// Note that `pop` can be simulated by `pop_and_inject(false)` but the | ||
346 | /// latter is computationally more expensive. | ||
347 | fn pop_and_inject(&mut self, overwrite_parent: Option<Highlight>) { | ||
348 | let mut children = self.stack.pop().unwrap(); | ||
349 | let prev = self.stack.last_mut().unwrap(); | ||
350 | children.sort_by_key(|range| range.range.start()); | ||
351 | prev.sort_by_key(|range| range.range.start()); | ||
352 | |||
353 | for child in children { | ||
354 | if let Some(idx) = | ||
355 | prev.iter().position(|parent| parent.range.contains_range(child.range)) | ||
356 | { | ||
357 | if let Some(tag) = overwrite_parent { | ||
358 | prev[idx].highlight = tag; | ||
359 | } | ||
360 | |||
361 | let cloned = Self::intersect(&mut prev[idx], &child); | ||
362 | let insert_idx = if prev[idx].range.is_empty() { | ||
363 | prev.remove(idx); | ||
364 | idx | ||
365 | } else { | ||
366 | idx + 1 | ||
367 | }; | ||
368 | prev.insert(insert_idx, child); | ||
369 | if !cloned.range.is_empty() { | ||
370 | prev.insert(insert_idx + 1, cloned); | ||
371 | } | ||
372 | } else { | ||
373 | let maybe_idx = | ||
374 | prev.iter().position(|parent| parent.range.contains(child.range.start())); | ||
375 | match (overwrite_parent, maybe_idx) { | ||
376 | (Some(_), Some(idx)) => { | ||
377 | Self::intersect_partial(&mut prev[idx], &child); | ||
378 | let insert_idx = if prev[idx].range.is_empty() { | ||
379 | prev.remove(idx); | ||
380 | idx | ||
381 | } else { | ||
382 | idx + 1 | ||
383 | }; | ||
384 | prev.insert(insert_idx, child); | ||
385 | } | ||
386 | (_, None) => { | ||
387 | let idx = prev | ||
388 | .binary_search_by_key(&child.range.start(), |range| range.range.start()) | ||
389 | .unwrap_or_else(|x| x); | ||
390 | prev.insert(idx, child); | ||
391 | } | ||
392 | _ => { | ||
393 | unreachable!("child range should be completely contained in parent range"); | ||
394 | } | ||
395 | } | ||
396 | } | ||
397 | } | ||
398 | } | ||
399 | |||
277 | fn add(&mut self, range: HighlightedRange) { | 400 | fn add(&mut self, range: HighlightedRange) { |
278 | self.stack | 401 | self.stack |
279 | .last_mut() | 402 | .last_mut() |
@@ -335,6 +458,7 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> { | |||
335 | fn highlight_element( | 458 | fn highlight_element( |
336 | sema: &Semantics<RootDatabase>, | 459 | sema: &Semantics<RootDatabase>, |
337 | bindings_shadow_count: &mut FxHashMap<Name, u32>, | 460 | bindings_shadow_count: &mut FxHashMap<Name, u32>, |
461 | syntactic_name_ref_highlighting: bool, | ||
338 | element: SyntaxElement, | 462 | element: SyntaxElement, |
339 | ) -> Option<(Highlight, Option<u64>)> { | 463 | ) -> Option<(Highlight, Option<u64>)> { |
340 | let db = sema.db; | 464 | let db = sema.db; |
@@ -363,6 +487,7 @@ fn highlight_element( | |||
363 | highlight_name(db, def) | HighlightModifier::Definition | 487 | highlight_name(db, def) | HighlightModifier::Definition |
364 | } | 488 | } |
365 | Some(NameClass::ConstReference(def)) => highlight_name(db, def), | 489 | Some(NameClass::ConstReference(def)) => highlight_name(db, def), |
490 | Some(NameClass::FieldShorthand { .. }) => HighlightTag::Field.into(), | ||
366 | None => highlight_name_by_syntax(name) | HighlightModifier::Definition, | 491 | None => highlight_name_by_syntax(name) | HighlightModifier::Definition, |
367 | } | 492 | } |
368 | } | 493 | } |
@@ -387,12 +512,20 @@ fn highlight_element( | |||
387 | } | 512 | } |
388 | NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), | 513 | NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), |
389 | }, | 514 | }, |
515 | None if syntactic_name_ref_highlighting => highlight_name_ref_by_syntax(name_ref), | ||
390 | None => HighlightTag::UnresolvedReference.into(), | 516 | None => HighlightTag::UnresolvedReference.into(), |
391 | } | 517 | } |
392 | } | 518 | } |
393 | 519 | ||
394 | // Simple token-based highlighting | 520 | // Simple token-based highlighting |
395 | COMMENT => HighlightTag::Comment.into(), | 521 | COMMENT => { |
522 | let comment = element.into_token().and_then(ast::Comment::cast)?; | ||
523 | let h = HighlightTag::Comment; | ||
524 | match comment.kind().doc { | ||
525 | Some(_) => h | HighlightModifier::Documentation, | ||
526 | None => h.into(), | ||
527 | } | ||
528 | } | ||
396 | STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::StringLiteral.into(), | 529 | STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::StringLiteral.into(), |
397 | ATTR => HighlightTag::Attribute.into(), | 530 | ATTR => HighlightTag::Attribute.into(), |
398 | INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(), | 531 | INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(), |
@@ -406,6 +539,21 @@ fn highlight_element( | |||
406 | _ => h, | 539 | _ => h, |
407 | } | 540 | } |
408 | } | 541 | } |
542 | T![*] => { | ||
543 | let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; | ||
544 | |||
545 | let expr = prefix_expr.expr()?; | ||
546 | let ty = sema.type_of_expr(&expr)?; | ||
547 | if !ty.is_raw_ptr() { | ||
548 | return None; | ||
549 | } else { | ||
550 | HighlightTag::Operator | HighlightModifier::Unsafe | ||
551 | } | ||
552 | } | ||
553 | T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { | ||
554 | Highlight::new(HighlightTag::Macro) | ||
555 | } | ||
556 | p if p.is_punct() => HighlightTag::Punctuation.into(), | ||
409 | 557 | ||
410 | k if k.is_keyword() => { | 558 | k if k.is_keyword() => { |
411 | let h = Highlight::new(HighlightTag::Keyword); | 559 | let h = Highlight::new(HighlightTag::Keyword); |
@@ -419,10 +567,31 @@ fn highlight_element( | |||
419 | | T![return] | 567 | | T![return] |
420 | | T![while] | 568 | | T![while] |
421 | | T![in] => h | HighlightModifier::ControlFlow, | 569 | | T![in] => h | HighlightModifier::ControlFlow, |
422 | T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow, | 570 | T![for] if !is_child_of_impl(&element) => h | HighlightModifier::ControlFlow, |
423 | T![unsafe] => h | HighlightModifier::Unsafe, | 571 | T![unsafe] => h | HighlightModifier::Unsafe, |
424 | T![true] | T![false] => HighlightTag::BoolLiteral.into(), | 572 | T![true] | T![false] => HighlightTag::BoolLiteral.into(), |
425 | T![self] => HighlightTag::SelfKeyword.into(), | 573 | T![self] => { |
574 | let self_param_is_mut = element | ||
575 | .parent() | ||
576 | .and_then(ast::SelfParam::cast) | ||
577 | .and_then(|p| p.mut_token()) | ||
578 | .is_some(); | ||
579 | // closure to enforce lazyness | ||
580 | let self_path = || { | ||
581 | sema.resolve_path(&element.parent()?.parent().and_then(ast::Path::cast)?) | ||
582 | }; | ||
583 | if self_param_is_mut | ||
584 | || matches!(self_path(), | ||
585 | Some(hir::PathResolution::Local(local)) | ||
586 | if local.is_self(db) | ||
587 | && (local.is_mut(db) || local.ty(db).is_mutable_reference()) | ||
588 | ) | ||
589 | { | ||
590 | HighlightTag::SelfKeyword | HighlightModifier::Mutable | ||
591 | } else { | ||
592 | HighlightTag::SelfKeyword.into() | ||
593 | } | ||
594 | } | ||
426 | _ => h, | 595 | _ => h, |
427 | } | 596 | } |
428 | } | 597 | } |
@@ -445,7 +614,7 @@ fn highlight_element( | |||
445 | } | 614 | } |
446 | } | 615 | } |
447 | 616 | ||
448 | fn is_child_of_impl(element: SyntaxElement) -> bool { | 617 | fn is_child_of_impl(element: &SyntaxElement) -> bool { |
449 | match element.parent() { | 618 | match element.parent() { |
450 | Some(e) => e.kind() == IMPL_DEF, | 619 | Some(e) => e.kind() == IMPL_DEF, |
451 | _ => false, | 620 | _ => false, |
@@ -458,7 +627,13 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { | |||
458 | Definition::Field(_) => HighlightTag::Field, | 627 | Definition::Field(_) => HighlightTag::Field, |
459 | Definition::ModuleDef(def) => match def { | 628 | Definition::ModuleDef(def) => match def { |
460 | hir::ModuleDef::Module(_) => HighlightTag::Module, | 629 | hir::ModuleDef::Module(_) => HighlightTag::Module, |
461 | hir::ModuleDef::Function(_) => HighlightTag::Function, | 630 | hir::ModuleDef::Function(func) => { |
631 | let mut h = HighlightTag::Function.into(); | ||
632 | if func.is_unsafe(db) { | ||
633 | h |= HighlightModifier::Unsafe; | ||
634 | } | ||
635 | return h; | ||
636 | } | ||
462 | hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, | 637 | hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, |
463 | hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, | 638 | hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, |
464 | hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, | 639 | hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, |
@@ -477,9 +652,10 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { | |||
477 | }, | 652 | }, |
478 | Definition::SelfType(_) => HighlightTag::SelfType, | 653 | Definition::SelfType(_) => HighlightTag::SelfType, |
479 | Definition::TypeParam(_) => HighlightTag::TypeParam, | 654 | Definition::TypeParam(_) => HighlightTag::TypeParam, |
480 | // FIXME: distinguish between locals and parameters | ||
481 | Definition::Local(local) => { | 655 | Definition::Local(local) => { |
482 | let mut h = Highlight::new(HighlightTag::Local); | 656 | let tag = |
657 | if local.is_param(db) { HighlightTag::ValueParam } else { HighlightTag::Local }; | ||
658 | let mut h = Highlight::new(tag); | ||
483 | if local.is_mut(db) || local.ty(db).is_mutable_reference() { | 659 | if local.is_mut(db) || local.ty(db).is_mutable_reference() { |
484 | h |= HighlightModifier::Mutable; | 660 | h |= HighlightModifier::Mutable; |
485 | } | 661 | } |
@@ -517,41 +693,52 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { | |||
517 | tag.into() | 693 | tag.into() |
518 | } | 694 | } |
519 | 695 | ||
520 | fn highlight_injection( | 696 | fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight { |
521 | acc: &mut HighlightedRangeStack, | 697 | let default = HighlightTag::UnresolvedReference; |
522 | sema: &Semantics<RootDatabase>, | ||
523 | literal: ast::RawString, | ||
524 | expanded: SyntaxToken, | ||
525 | ) -> Option<()> { | ||
526 | let active_parameter = ActiveParameter::at_token(&sema, expanded)?; | ||
527 | if !active_parameter.name.starts_with("ra_fixture") { | ||
528 | return None; | ||
529 | } | ||
530 | let value = literal.value()?; | ||
531 | let (analysis, tmp_file_id) = Analysis::from_single_file(value); | ||
532 | |||
533 | if let Some(range) = literal.open_quote_text_range() { | ||
534 | acc.add(HighlightedRange { | ||
535 | range, | ||
536 | highlight: HighlightTag::StringLiteral.into(), | ||
537 | binding_hash: None, | ||
538 | }) | ||
539 | } | ||
540 | 698 | ||
541 | for mut h in analysis.highlight(tmp_file_id).unwrap() { | 699 | let parent = match name.syntax().parent() { |
542 | if let Some(r) = literal.map_range_up(h.range) { | 700 | Some(it) => it, |
543 | h.range = r; | 701 | _ => return default.into(), |
544 | acc.add(h) | 702 | }; |
545 | } | ||
546 | } | ||
547 | 703 | ||
548 | if let Some(range) = literal.close_quote_text_range() { | 704 | let tag = match parent.kind() { |
549 | acc.add(HighlightedRange { | 705 | METHOD_CALL_EXPR => HighlightTag::Function, |
550 | range, | 706 | FIELD_EXPR => HighlightTag::Field, |
551 | highlight: HighlightTag::StringLiteral.into(), | 707 | PATH_SEGMENT => { |
552 | binding_hash: None, | 708 | let path = match parent.parent().and_then(ast::Path::cast) { |
553 | }) | 709 | Some(it) => it, |
554 | } | 710 | _ => return default.into(), |
711 | }; | ||
712 | let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) { | ||
713 | Some(it) => it, | ||
714 | _ => { | ||
715 | // within path, decide whether it is module or adt by checking for uppercase name | ||
716 | return if name.text().chars().next().unwrap_or_default().is_uppercase() { | ||
717 | HighlightTag::Struct | ||
718 | } else { | ||
719 | HighlightTag::Module | ||
720 | } | ||
721 | .into(); | ||
722 | } | ||
723 | }; | ||
724 | let parent = match expr.syntax().parent() { | ||
725 | Some(it) => it, | ||
726 | None => return default.into(), | ||
727 | }; | ||
555 | 728 | ||
556 | Some(()) | 729 | match parent.kind() { |
730 | CALL_EXPR => HighlightTag::Function, | ||
731 | _ => { | ||
732 | if name.text().chars().next().unwrap_or_default().is_uppercase() { | ||
733 | HighlightTag::Struct | ||
734 | } else { | ||
735 | HighlightTag::Constant | ||
736 | } | ||
737 | } | ||
738 | } | ||
739 | } | ||
740 | _ => default, | ||
741 | }; | ||
742 | |||
743 | tag.into() | ||
557 | } | 744 | } |