diff options
Diffstat (limited to 'crates/ra_ide')
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting.rs | 129 |
1 files changed, 64 insertions, 65 deletions
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 5f11b091c..e8ca7d652 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -46,93 +46,92 @@ fn is_control_keyword(kind: SyntaxKind) -> bool { | |||
46 | pub(crate) fn highlight( | 46 | pub(crate) fn highlight( |
47 | db: &RootDatabase, | 47 | db: &RootDatabase, |
48 | file_id: FileId, | 48 | file_id: FileId, |
49 | range: Option<TextRange>, | 49 | range_to_highlight: Option<TextRange>, |
50 | ) -> Vec<HighlightedRange> { | 50 | ) -> Vec<HighlightedRange> { |
51 | let _p = profile("highlight"); | 51 | let _p = profile("highlight"); |
52 | let sema = Semantics::new(db); | 52 | let sema = Semantics::new(db); |
53 | let root = sema.parse(file_id).syntax().clone(); | 53 | |
54 | // Determine the root based on the given range. | ||
55 | let (root, range_to_highlight) = { | ||
56 | let source_file = sema.parse(file_id); | ||
57 | match range_to_highlight { | ||
58 | Some(range) => { | ||
59 | let node = match source_file.syntax().covering_element(range) { | ||
60 | NodeOrToken::Node(it) => it, | ||
61 | NodeOrToken::Token(it) => it.parent(), | ||
62 | }; | ||
63 | (node, range) | ||
64 | } | ||
65 | None => (source_file.syntax().clone(), source_file.syntax().text_range()), | ||
66 | } | ||
67 | }; | ||
54 | 68 | ||
55 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); | 69 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); |
56 | let mut res = Vec::new(); | 70 | let mut res = Vec::new(); |
57 | 71 | ||
58 | let mut in_macro_call = None; | 72 | let mut current_macro_call: Option<ast::MacroCall> = None; |
59 | 73 | ||
60 | // Determine the root based on the given range. | 74 | for event in root.preorder_with_tokens() { |
61 | let (root, highlight_range) = if let Some(range) = range { | 75 | let event_range = match &event { |
62 | let root = match root.covering_element(range) { | 76 | WalkEvent::Enter(it) => it.text_range(), |
63 | NodeOrToken::Node(node) => node, | 77 | WalkEvent::Leave(it) => it.text_range(), |
64 | NodeOrToken::Token(token) => token.parent(), | ||
65 | }; | 78 | }; |
66 | (root, range) | ||
67 | } else { | ||
68 | (root.clone(), root.text_range()) | ||
69 | }; | ||
70 | 79 | ||
71 | for event in root.preorder_with_tokens() { | 80 | if event_range.intersection(&range_to_highlight).is_none() { |
72 | match event { | 81 | continue; |
73 | WalkEvent::Enter(node) => { | 82 | } |
74 | if node.text_range().intersection(&highlight_range).is_none() { | ||
75 | continue; | ||
76 | } | ||
77 | 83 | ||
78 | match node.kind() { | 84 | match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) { |
79 | MACRO_CALL => { | 85 | WalkEvent::Enter(Some(mc)) => { |
80 | in_macro_call = Some(node.clone()); | 86 | current_macro_call = Some(mc.clone()); |
81 | if let Some(range) = highlight_macro(node) { | 87 | if let Some(range) = highlight_macro(&mc) { |
82 | res.push(HighlightedRange { | 88 | res.push(HighlightedRange { |
83 | range, | 89 | range, |
84 | highlight: HighlightTag::Macro.into(), | 90 | highlight: HighlightTag::Macro.into(), |
85 | binding_hash: None, | 91 | binding_hash: None, |
86 | }); | 92 | }); |
87 | } | ||
88 | } | ||
89 | _ if in_macro_call.is_some() => { | ||
90 | if let Some(token) = node.as_token() { | ||
91 | if let Some((highlight, binding_hash)) = highlight_token_tree( | ||
92 | &sema, | ||
93 | &mut bindings_shadow_count, | ||
94 | token.clone(), | ||
95 | ) { | ||
96 | res.push(HighlightedRange { | ||
97 | range: node.text_range(), | ||
98 | highlight, | ||
99 | binding_hash, | ||
100 | }); | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | _ => { | ||
105 | if let Some((highlight, binding_hash)) = | ||
106 | highlight_node(&sema, &mut bindings_shadow_count, node.clone()) | ||
107 | { | ||
108 | res.push(HighlightedRange { | ||
109 | range: node.text_range(), | ||
110 | highlight, | ||
111 | binding_hash, | ||
112 | }); | ||
113 | } | ||
114 | } | ||
115 | } | 93 | } |
94 | continue; | ||
116 | } | 95 | } |
117 | WalkEvent::Leave(node) => { | 96 | WalkEvent::Leave(Some(mc)) => { |
118 | if node.text_range().intersection(&highlight_range).is_none() { | 97 | assert!(current_macro_call == Some(mc)); |
119 | continue; | 98 | current_macro_call = None; |
120 | } | 99 | continue; |
100 | } | ||
101 | _ => (), | ||
102 | } | ||
121 | 103 | ||
122 | if let Some(m) = in_macro_call.as_ref() { | 104 | let node = match event { |
123 | if *m == node { | 105 | WalkEvent::Enter(it) => it, |
124 | in_macro_call = None; | 106 | WalkEvent::Leave(_) => continue, |
125 | } | 107 | }; |
108 | |||
109 | if current_macro_call.is_some() { | ||
110 | if let Some(token) = node.into_token() { | ||
111 | if let Some((highlight, binding_hash)) = | ||
112 | highlight_token_tree(&sema, &mut bindings_shadow_count, token.clone()) | ||
113 | { | ||
114 | res.push(HighlightedRange { | ||
115 | range: token.text_range(), | ||
116 | highlight, | ||
117 | binding_hash, | ||
118 | }); | ||
126 | } | 119 | } |
127 | } | 120 | } |
121 | continue; | ||
122 | } | ||
123 | |||
124 | if let Some((highlight, binding_hash)) = | ||
125 | highlight_node(&sema, &mut bindings_shadow_count, node.clone()) | ||
126 | { | ||
127 | res.push(HighlightedRange { range: node.text_range(), highlight, binding_hash }); | ||
128 | } | 128 | } |
129 | } | 129 | } |
130 | 130 | ||
131 | res | 131 | res |
132 | } | 132 | } |
133 | 133 | ||
134 | fn highlight_macro(node: SyntaxElement) -> Option<TextRange> { | 134 | fn highlight_macro(macro_call: &ast::MacroCall) -> Option<TextRange> { |
135 | let macro_call = ast::MacroCall::cast(node.as_node()?.clone())?; | ||
136 | let path = macro_call.path()?; | 135 | let path = macro_call.path()?; |
137 | let name_ref = path.segment()?.name_ref()?; | 136 | let name_ref = path.segment()?.name_ref()?; |
138 | 137 | ||