aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/syntax_highlighting.rs
diff options
context:
space:
mode:
authorMikhail Rakhmanov <[email protected]>2020-06-13 07:42:15 +0100
committerMikhail Rakhmanov <[email protected]>2020-06-13 07:42:15 +0100
commit16bbf4ab7f132e6e5e5318dccdef9a5d71afdd7f (patch)
tree4b79fa8c046be56b02427ba843e70cdf3ac05767 /crates/ra_ide/src/syntax_highlighting.rs
parenteeb8b9e236796da8734ba81a49164864497f7226 (diff)
parentb56ad148db0c69eb279c225f45d324b4e80e7367 (diff)
Merge branch 'master' into keyword_completion
# Conflicts: # docs/user/generated_features.adoc
Diffstat (limited to 'crates/ra_ide/src/syntax_highlighting.rs')
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs148
1 files changed, 101 insertions, 47 deletions
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 0b53ebe69..ab45c364a 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -1,5 +1,6 @@
1mod tags; 1mod tags;
2mod html; 2mod html;
3mod injection;
3#[cfg(test)] 4#[cfg(test)]
4mod tests; 5mod tests;
5 6
@@ -10,14 +11,14 @@ use ra_ide_db::{
10}; 11};
11use ra_prof::profile; 12use ra_prof::profile;
12use ra_syntax::{ 13use 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};
18use rustc_hash::FxHashMap; 19use rustc_hash::FxHashMap;
19 20
20use crate::{call_info::ActiveParameter, Analysis, FileId}; 21use crate::FileId;
21 22
22use ast::FormatSpecifier; 23use ast::FormatSpecifier;
23pub(crate) use html::highlight_as_html; 24pub(crate) use html::highlight_as_html;
@@ -123,6 +124,23 @@ pub(crate) fn highlight(
123 _ => (), 124 _ => (),
124 } 125 }
125 126
127 // Check for Rust code in documentation
128 match &event {
129 WalkEvent::Leave(NodeOrToken::Node(node)) => {
130 if let Some((doctest, range_mapping, new_comments)) =
131 injection::extract_doc_comments(node)
132 {
133 injection::highlight_doc_comment(
134 doctest,
135 range_mapping,
136 new_comments,
137 &mut stack,
138 );
139 }
140 }
141 _ => (),
142 }
143
126 let element = match event { 144 let element = match event {
127 WalkEvent::Enter(it) => it, 145 WalkEvent::Enter(it) => it,
128 WalkEvent::Leave(_) => continue, 146 WalkEvent::Leave(_) => continue,
@@ -173,7 +191,7 @@ pub(crate) fn highlight(
173 191
174 if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) { 192 if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) {
175 let expanded = element_to_highlight.as_token().unwrap().clone(); 193 let expanded = element_to_highlight.as_token().unwrap().clone();
176 if highlight_injection(&mut stack, &sema, token, expanded).is_some() { 194 if injection::highlight_injection(&mut stack, &sema, token, expanded).is_some() {
177 continue; 195 continue;
178 } 196 }
179 } 197 }
@@ -259,9 +277,8 @@ impl HighlightedRangeStack {
259 let mut parent = prev.pop().unwrap(); 277 let mut parent = prev.pop().unwrap();
260 for ele in children { 278 for ele in children {
261 assert!(parent.range.contains_range(ele.range)); 279 assert!(parent.range.contains_range(ele.range));
262 let mut cloned = parent.clone(); 280
263 parent.range = TextRange::new(parent.range.start(), ele.range.start()); 281 let cloned = Self::intersect(&mut parent, &ele);
264 cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
265 if !parent.range.is_empty() { 282 if !parent.range.is_empty() {
266 prev.push(parent); 283 prev.push(parent);
267 } 284 }
@@ -274,6 +291,62 @@ impl HighlightedRangeStack {
274 } 291 }
275 } 292 }
276 293
294 /// Intersects the `HighlightedRange` `parent` with `child`.
295 /// `parent` is mutated in place, becoming the range before `child`.
296 /// Returns the range (of the same type as `parent`) *after* `child`.
297 fn intersect(parent: &mut HighlightedRange, child: &HighlightedRange) -> HighlightedRange {
298 assert!(parent.range.contains_range(child.range));
299
300 let mut cloned = parent.clone();
301 parent.range = TextRange::new(parent.range.start(), child.range.start());
302 cloned.range = TextRange::new(child.range.end(), cloned.range.end());
303
304 cloned
305 }
306
307 /// Similar to `pop`, but can modify arbitrary prior ranges (where `pop`)
308 /// can only modify the last range currently on the stack.
309 /// Can be used to do injections that span multiple ranges, like the
310 /// doctest injection below.
311 /// If `delete` is set to true, the parent range is deleted instead of
312 /// intersected.
313 ///
314 /// Note that `pop` can be simulated by `pop_and_inject(false)` but the
315 /// latter is computationally more expensive.
316 fn pop_and_inject(&mut self, delete: bool) {
317 let mut children = self.stack.pop().unwrap();
318 let prev = self.stack.last_mut().unwrap();
319 children.sort_by_key(|range| range.range.start());
320 prev.sort_by_key(|range| range.range.start());
321
322 for child in children {
323 if let Some(idx) =
324 prev.iter().position(|parent| parent.range.contains_range(child.range))
325 {
326 let cloned = Self::intersect(&mut prev[idx], &child);
327 let insert_idx = if delete || prev[idx].range.is_empty() {
328 prev.remove(idx);
329 idx
330 } else {
331 idx + 1
332 };
333 prev.insert(insert_idx, child);
334 if !delete && !cloned.range.is_empty() {
335 prev.insert(insert_idx + 1, cloned);
336 }
337 } else if let Some(_idx) =
338 prev.iter().position(|parent| parent.range.contains(child.range.start()))
339 {
340 unreachable!("child range should be completely contained in parent range");
341 } else {
342 let idx = prev
343 .binary_search_by_key(&child.range.start(), |range| range.range.start())
344 .unwrap_or_else(|x| x);
345 prev.insert(idx, child);
346 }
347 }
348 }
349
277 fn add(&mut self, range: HighlightedRange) { 350 fn add(&mut self, range: HighlightedRange) {
278 self.stack 351 self.stack
279 .last_mut() 352 .last_mut()
@@ -363,6 +436,7 @@ fn highlight_element(
363 highlight_name(db, def) | HighlightModifier::Definition 436 highlight_name(db, def) | HighlightModifier::Definition
364 } 437 }
365 Some(NameClass::ConstReference(def)) => highlight_name(db, def), 438 Some(NameClass::ConstReference(def)) => highlight_name(db, def),
439 Some(NameClass::FieldShorthand { .. }) => HighlightTag::Field.into(),
366 None => highlight_name_by_syntax(name) | HighlightModifier::Definition, 440 None => highlight_name_by_syntax(name) | HighlightModifier::Definition,
367 } 441 }
368 } 442 }
@@ -406,6 +480,19 @@ fn highlight_element(
406 _ => h, 480 _ => h,
407 } 481 }
408 } 482 }
483 T![*] => {
484 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
485
486 let expr = prefix_expr.expr()?;
487 let ty = sema.type_of_expr(&expr)?;
488 if !ty.is_raw_ptr() {
489 return None;
490 }
491
492 let mut h = Highlight::new(HighlightTag::Operator);
493 h |= HighlightModifier::Unsafe;
494 h
495 }
409 496
410 k if k.is_keyword() => { 497 k if k.is_keyword() => {
411 let h = Highlight::new(HighlightTag::Keyword); 498 let h = Highlight::new(HighlightTag::Keyword);
@@ -458,7 +545,13 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
458 Definition::Field(_) => HighlightTag::Field, 545 Definition::Field(_) => HighlightTag::Field,
459 Definition::ModuleDef(def) => match def { 546 Definition::ModuleDef(def) => match def {
460 hir::ModuleDef::Module(_) => HighlightTag::Module, 547 hir::ModuleDef::Module(_) => HighlightTag::Module,
461 hir::ModuleDef::Function(_) => HighlightTag::Function, 548 hir::ModuleDef::Function(func) => {
549 let mut h = HighlightTag::Function.into();
550 if func.is_unsafe(db) {
551 h |= HighlightModifier::Unsafe;
552 }
553 return h;
554 }
462 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, 555 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
463 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, 556 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum,
464 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, 557 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union,
@@ -516,42 +609,3 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
516 609
517 tag.into() 610 tag.into()
518} 611}
519
520fn highlight_injection(
521 acc: &mut HighlightedRangeStack,
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
541 for mut h in analysis.highlight(tmp_file_id).unwrap() {
542 if let Some(r) = literal.map_range_up(h.range) {
543 h.range = r;
544 acc.add(h)
545 }
546 }
547
548 if let Some(range) = literal.close_quote_text_range() {
549 acc.add(HighlightedRange {
550 range,
551 highlight: HighlightTag::StringLiteral.into(),
552 binding_hash: None,
553 })
554 }
555
556 Some(())
557}