diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-02-25 21:11:33 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-02-25 21:11:33 +0000 |
commit | ae0aeb1b23aa4bc96a7113de784799365c2b4358 (patch) | |
tree | 4bbcd2d9eb4ac67cf50ba12918c87a41fb5a9624 /crates/ra_ide/src/syntax_highlighting.rs | |
parent | d3040c0deba8266044029a6479a1c12c28e72750 (diff) | |
parent | fa355d6339d7b5ccfd4b1a96f035a4366e8152fe (diff) |
Merge #3307
3307: Semantic Ranges r=matklad a=kjeremy
Co-authored-by: Jeremy Kolb <[email protected]>
Co-authored-by: kjeremy <[email protected]>
Diffstat (limited to 'crates/ra_ide/src/syntax_highlighting.rs')
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting.rs | 111 |
1 files changed, 85 insertions, 26 deletions
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 812229b4e..9bc3ad448 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -5,8 +5,8 @@ use ra_db::SourceDatabase; | |||
5 | use ra_ide_db::{defs::NameDefinition, RootDatabase}; | 5 | use ra_ide_db::{defs::NameDefinition, RootDatabase}; |
6 | use ra_prof::profile; | 6 | use ra_prof::profile; |
7 | use ra_syntax::{ | 7 | use ra_syntax::{ |
8 | ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, TextRange, | 8 | ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, |
9 | WalkEvent, T, | 9 | TextRange, WalkEvent, T, |
10 | }; | 10 | }; |
11 | use rustc_hash::FxHashMap; | 11 | use rustc_hash::FxHashMap; |
12 | 12 | ||
@@ -67,8 +67,13 @@ fn is_control_keyword(kind: SyntaxKind) -> bool { | |||
67 | } | 67 | } |
68 | } | 68 | } |
69 | 69 | ||
70 | pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> { | 70 | pub(crate) fn highlight( |
71 | db: &RootDatabase, | ||
72 | file_id: FileId, | ||
73 | range: Option<TextRange>, | ||
74 | ) -> Vec<HighlightedRange> { | ||
71 | let _p = profile("highlight"); | 75 | let _p = profile("highlight"); |
76 | |||
72 | let parse = db.parse(file_id); | 77 | let parse = db.parse(file_id); |
73 | let root = parse.tree().syntax().clone(); | 78 | let root = parse.tree().syntax().clone(); |
74 | 79 | ||
@@ -79,22 +84,56 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
79 | 84 | ||
80 | let mut in_macro_call = None; | 85 | let mut in_macro_call = None; |
81 | 86 | ||
87 | // Determine the root based on the given range. | ||
88 | let (root, highlight_range) = if let Some(range) = range { | ||
89 | let root = match root.covering_element(range) { | ||
90 | NodeOrToken::Node(node) => node, | ||
91 | NodeOrToken::Token(token) => token.parent(), | ||
92 | }; | ||
93 | (root, range) | ||
94 | } else { | ||
95 | (root.clone(), root.text_range()) | ||
96 | }; | ||
97 | |||
82 | for event in root.preorder_with_tokens() { | 98 | for event in root.preorder_with_tokens() { |
83 | match event { | 99 | match event { |
84 | WalkEvent::Enter(node) => match node.kind() { | 100 | WalkEvent::Enter(node) => { |
85 | MACRO_CALL => { | 101 | if node.text_range().intersection(&highlight_range).is_none() { |
86 | in_macro_call = Some(node.clone()); | 102 | continue; |
87 | if let Some(range) = highlight_macro(InFile::new(file_id.into(), node)) { | ||
88 | res.push(HighlightedRange { range, tag: tags::MACRO, binding_hash: None }); | ||
89 | } | ||
90 | } | 103 | } |
91 | _ if in_macro_call.is_some() => { | 104 | |
92 | if let Some(token) = node.as_token() { | 105 | match node.kind() { |
93 | if let Some((tag, binding_hash)) = highlight_token_tree( | 106 | MACRO_CALL => { |
107 | in_macro_call = Some(node.clone()); | ||
108 | if let Some(range) = highlight_macro(InFile::new(file_id.into(), node)) { | ||
109 | res.push(HighlightedRange { | ||
110 | range, | ||
111 | tag: tags::MACRO, | ||
112 | binding_hash: None, | ||
113 | }); | ||
114 | } | ||
115 | } | ||
116 | _ if in_macro_call.is_some() => { | ||
117 | if let Some(token) = node.as_token() { | ||
118 | if let Some((tag, binding_hash)) = highlight_token_tree( | ||
119 | &mut sb, | ||
120 | &analyzer, | ||
121 | &mut bindings_shadow_count, | ||
122 | InFile::new(file_id.into(), token.clone()), | ||
123 | ) { | ||
124 | res.push(HighlightedRange { | ||
125 | range: node.text_range(), | ||
126 | tag, | ||
127 | binding_hash, | ||
128 | }); | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | _ => { | ||
133 | if let Some((tag, binding_hash)) = highlight_node( | ||
94 | &mut sb, | 134 | &mut sb, |
95 | &analyzer, | ||
96 | &mut bindings_shadow_count, | 135 | &mut bindings_shadow_count, |
97 | InFile::new(file_id.into(), token.clone()), | 136 | InFile::new(file_id.into(), node.clone()), |
98 | ) { | 137 | ) { |
99 | res.push(HighlightedRange { | 138 | res.push(HighlightedRange { |
100 | range: node.text_range(), | 139 | range: node.text_range(), |
@@ -104,17 +143,12 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
104 | } | 143 | } |
105 | } | 144 | } |
106 | } | 145 | } |
107 | _ => { | 146 | } |
108 | if let Some((tag, binding_hash)) = highlight_node( | ||
109 | &mut sb, | ||
110 | &mut bindings_shadow_count, | ||
111 | InFile::new(file_id.into(), node.clone()), | ||
112 | ) { | ||
113 | res.push(HighlightedRange { range: node.text_range(), tag, binding_hash }); | ||
114 | } | ||
115 | } | ||
116 | }, | ||
117 | WalkEvent::Leave(node) => { | 147 | WalkEvent::Leave(node) => { |
148 | if node.text_range().intersection(&highlight_range).is_none() { | ||
149 | continue; | ||
150 | } | ||
151 | |||
118 | if let Some(m) = in_macro_call.as_ref() { | 152 | if let Some(m) = in_macro_call.as_ref() { |
119 | if *m == node { | 153 | if *m == node { |
120 | in_macro_call = None; | 154 | in_macro_call = None; |
@@ -265,7 +299,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo | |||
265 | ) | 299 | ) |
266 | } | 300 | } |
267 | 301 | ||
268 | let mut ranges = highlight(db, file_id); | 302 | let mut ranges = highlight(db, file_id, None); |
269 | ranges.sort_by_key(|it| it.range.start()); | 303 | ranges.sort_by_key(|it| it.range.start()); |
270 | // quick non-optimal heuristic to intersect token ranges and highlighted ranges | 304 | // quick non-optimal heuristic to intersect token ranges and highlighted ranges |
271 | let mut frontier = 0; | 305 | let mut frontier = 0; |
@@ -374,7 +408,10 @@ mod tests { | |||
374 | 408 | ||
375 | use test_utils::{assert_eq_text, project_dir, read_text}; | 409 | use test_utils::{assert_eq_text, project_dir, read_text}; |
376 | 410 | ||
377 | use crate::mock_analysis::{single_file, MockAnalysis}; | 411 | use crate::{ |
412 | mock_analysis::{single_file, MockAnalysis}, | ||
413 | FileRange, TextRange, | ||
414 | }; | ||
378 | 415 | ||
379 | #[test] | 416 | #[test] |
380 | fn test_highlighting() { | 417 | fn test_highlighting() { |
@@ -475,4 +512,26 @@ fn bar() { | |||
475 | let _ = host.analysis().highlight(file_id).unwrap(); | 512 | let _ = host.analysis().highlight(file_id).unwrap(); |
476 | // eprintln!("elapsed: {:?}", t.elapsed()); | 513 | // eprintln!("elapsed: {:?}", t.elapsed()); |
477 | } | 514 | } |
515 | |||
516 | #[test] | ||
517 | fn test_ranges() { | ||
518 | let (analysis, file_id) = single_file( | ||
519 | r#" | ||
520 | #[derive(Clone, Debug)] | ||
521 | struct Foo { | ||
522 | pub x: i32, | ||
523 | pub y: i32, | ||
524 | }"#, | ||
525 | ); | ||
526 | |||
527 | // The "x" | ||
528 | let highlights = &analysis | ||
529 | .highlight_range(FileRange { | ||
530 | file_id, | ||
531 | range: TextRange::offset_len(82.into(), 1.into()), | ||
532 | }) | ||
533 | .unwrap(); | ||
534 | |||
535 | assert_eq!(highlights[0].tag, "field"); | ||
536 | } | ||
478 | } | 537 | } |