diff options
author | Aleksey Kladov <[email protected]> | 2020-02-18 17:35:10 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-02-26 11:55:50 +0000 |
commit | c3a4c4429de83450654795534e64e878a774a088 (patch) | |
tree | 12d89798f61b276f8bd640db07276a7d4e92b1c2 /crates/ra_ide/src/syntax_highlighting.rs | |
parent | 04deae3dba7c9b7054f7a1d64e4b93a05aecc132 (diff) |
Refactor primary IDE API
This introduces the new type -- Semantics.
Semantics maps SyntaxNodes to various semantic info, such as type,
name resolution or macro expansions.
To do so, Semantics maintains a HashMap which maps every node it saw
to the file from which the node originated. This is enough to get all
the necessary hir bits just from syntax.
Diffstat (limited to 'crates/ra_ide/src/syntax_highlighting.rs')
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting.rs | 87 |
1 files changed, 39 insertions, 48 deletions
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 9bc3ad448..987476d2c 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -1,8 +1,11 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{HirFileId, InFile, Name, SourceAnalyzer, SourceBinder}; | 3 | use hir::{Name, Semantics}; |
4 | use ra_db::SourceDatabase; | 4 | use ra_db::SourceDatabase; |
5 | use ra_ide_db::{defs::NameDefinition, RootDatabase}; | 5 | use ra_ide_db::{ |
6 | defs::{classify_name, NameDefinition}, | ||
7 | RootDatabase, | ||
8 | }; | ||
6 | use ra_prof::profile; | 9 | use ra_prof::profile; |
7 | use ra_syntax::{ | 10 | use ra_syntax::{ |
8 | ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, | 11 | ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, |
@@ -10,11 +13,7 @@ use ra_syntax::{ | |||
10 | }; | 13 | }; |
11 | use rustc_hash::FxHashMap; | 14 | use rustc_hash::FxHashMap; |
12 | 15 | ||
13 | use crate::{ | 16 | use crate::{references::classify_name_ref, FileId}; |
14 | expand::descend_into_macros_with_analyzer, | ||
15 | references::{classify_name, classify_name_ref}, | ||
16 | FileId, | ||
17 | }; | ||
18 | 17 | ||
19 | pub mod tags { | 18 | pub mod tags { |
20 | pub const FIELD: &str = "field"; | 19 | pub const FIELD: &str = "field"; |
@@ -73,14 +72,11 @@ pub(crate) fn highlight( | |||
73 | range: Option<TextRange>, | 72 | range: Option<TextRange>, |
74 | ) -> Vec<HighlightedRange> { | 73 | ) -> Vec<HighlightedRange> { |
75 | let _p = profile("highlight"); | 74 | let _p = profile("highlight"); |
75 | let sema = Semantics::new(db); | ||
76 | let root = sema.parse(file_id).syntax().clone(); | ||
76 | 77 | ||
77 | let parse = db.parse(file_id); | ||
78 | let root = parse.tree().syntax().clone(); | ||
79 | |||
80 | let mut sb = SourceBinder::new(db); | ||
81 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); | 78 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); |
82 | let mut res = Vec::new(); | 79 | let mut res = Vec::new(); |
83 | let analyzer = sb.analyze(InFile::new(file_id.into(), &root), None); | ||
84 | 80 | ||
85 | let mut in_macro_call = None; | 81 | let mut in_macro_call = None; |
86 | 82 | ||
@@ -105,7 +101,7 @@ pub(crate) fn highlight( | |||
105 | match node.kind() { | 101 | match node.kind() { |
106 | MACRO_CALL => { | 102 | MACRO_CALL => { |
107 | in_macro_call = Some(node.clone()); | 103 | in_macro_call = Some(node.clone()); |
108 | if let Some(range) = highlight_macro(InFile::new(file_id.into(), node)) { | 104 | if let Some(range) = highlight_macro(node) { |
109 | res.push(HighlightedRange { | 105 | res.push(HighlightedRange { |
110 | range, | 106 | range, |
111 | tag: tags::MACRO, | 107 | tag: tags::MACRO, |
@@ -116,10 +112,9 @@ pub(crate) fn highlight( | |||
116 | _ if in_macro_call.is_some() => { | 112 | _ if in_macro_call.is_some() => { |
117 | if let Some(token) = node.as_token() { | 113 | if let Some(token) = node.as_token() { |
118 | if let Some((tag, binding_hash)) = highlight_token_tree( | 114 | if let Some((tag, binding_hash)) = highlight_token_tree( |
119 | &mut sb, | 115 | &sema, |
120 | &analyzer, | ||
121 | &mut bindings_shadow_count, | 116 | &mut bindings_shadow_count, |
122 | InFile::new(file_id.into(), token.clone()), | 117 | token.clone(), |
123 | ) { | 118 | ) { |
124 | res.push(HighlightedRange { | 119 | res.push(HighlightedRange { |
125 | range: node.text_range(), | 120 | range: node.text_range(), |
@@ -130,11 +125,9 @@ pub(crate) fn highlight( | |||
130 | } | 125 | } |
131 | } | 126 | } |
132 | _ => { | 127 | _ => { |
133 | if let Some((tag, binding_hash)) = highlight_node( | 128 | if let Some((tag, binding_hash)) = |
134 | &mut sb, | 129 | highlight_node(&sema, &mut bindings_shadow_count, node.clone()) |
135 | &mut bindings_shadow_count, | 130 | { |
136 | InFile::new(file_id.into(), node.clone()), | ||
137 | ) { | ||
138 | res.push(HighlightedRange { | 131 | res.push(HighlightedRange { |
139 | range: node.text_range(), | 132 | range: node.text_range(), |
140 | tag, | 133 | tag, |
@@ -161,8 +154,8 @@ pub(crate) fn highlight( | |||
161 | res | 154 | res |
162 | } | 155 | } |
163 | 156 | ||
164 | fn highlight_macro(node: InFile<SyntaxElement>) -> Option<TextRange> { | 157 | fn highlight_macro(node: SyntaxElement) -> Option<TextRange> { |
165 | let macro_call = ast::MacroCall::cast(node.value.as_node()?.clone())?; | 158 | let macro_call = ast::MacroCall::cast(node.as_node()?.clone())?; |
166 | let path = macro_call.path()?; | 159 | let path = macro_call.path()?; |
167 | let name_ref = path.segment()?.name_ref()?; | 160 | let name_ref = path.segment()?.name_ref()?; |
168 | 161 | ||
@@ -179,35 +172,34 @@ fn highlight_macro(node: InFile<SyntaxElement>) -> Option<TextRange> { | |||
179 | } | 172 | } |
180 | 173 | ||
181 | fn highlight_token_tree( | 174 | fn highlight_token_tree( |
182 | sb: &mut SourceBinder<RootDatabase>, | 175 | sema: &Semantics<RootDatabase>, |
183 | analyzer: &SourceAnalyzer, | ||
184 | bindings_shadow_count: &mut FxHashMap<Name, u32>, | 176 | bindings_shadow_count: &mut FxHashMap<Name, u32>, |
185 | token: InFile<SyntaxToken>, | 177 | token: SyntaxToken, |
186 | ) -> Option<(&'static str, Option<u64>)> { | 178 | ) -> Option<(&'static str, Option<u64>)> { |
187 | if token.value.parent().kind() != TOKEN_TREE { | 179 | if token.parent().kind() != TOKEN_TREE { |
188 | return None; | 180 | return None; |
189 | } | 181 | } |
190 | let token = descend_into_macros_with_analyzer(sb.db, analyzer, token); | 182 | let token = sema.descend_into_macros(token.clone()); |
191 | let expanded = { | 183 | let expanded = { |
192 | let parent = token.value.parent(); | 184 | let parent = token.parent(); |
193 | // We only care Name and Name_ref | 185 | // We only care Name and Name_ref |
194 | match (token.value.kind(), parent.kind()) { | 186 | match (token.kind(), parent.kind()) { |
195 | (IDENT, NAME) | (IDENT, NAME_REF) => token.with_value(parent.into()), | 187 | (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), |
196 | _ => token.map(|it| it.into()), | 188 | _ => token.into(), |
197 | } | 189 | } |
198 | }; | 190 | }; |
199 | 191 | ||
200 | highlight_node(sb, bindings_shadow_count, expanded) | 192 | highlight_node(sema, bindings_shadow_count, expanded) |
201 | } | 193 | } |
202 | 194 | ||
203 | fn highlight_node( | 195 | fn highlight_node( |
204 | sb: &mut SourceBinder<RootDatabase>, | 196 | sema: &Semantics<RootDatabase>, |
205 | bindings_shadow_count: &mut FxHashMap<Name, u32>, | 197 | bindings_shadow_count: &mut FxHashMap<Name, u32>, |
206 | node: InFile<SyntaxElement>, | 198 | node: SyntaxElement, |
207 | ) -> Option<(&'static str, Option<u64>)> { | 199 | ) -> Option<(&'static str, Option<u64>)> { |
208 | let db = sb.db; | 200 | let db = sema.db; |
209 | let mut binding_hash = None; | 201 | let mut binding_hash = None; |
210 | let tag = match node.value.kind() { | 202 | let tag = match node.kind() { |
211 | FN_DEF => { | 203 | FN_DEF => { |
212 | bindings_shadow_count.clear(); | 204 | bindings_shadow_count.clear(); |
213 | return None; | 205 | return None; |
@@ -216,19 +208,18 @@ fn highlight_node( | |||
216 | STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, | 208 | STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, |
217 | ATTR => tags::LITERAL_ATTRIBUTE, | 209 | ATTR => tags::LITERAL_ATTRIBUTE, |
218 | // Special-case field init shorthand | 210 | // Special-case field init shorthand |
219 | NAME_REF if node.value.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, | 211 | NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, |
220 | NAME_REF if node.value.ancestors().any(|it| it.kind() == ATTR) => return None, | 212 | NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => return None, |
221 | NAME_REF => { | 213 | NAME_REF => { |
222 | let name_ref = node.value.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); | 214 | let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); |
223 | let name_kind = classify_name_ref(sb, node.with_value(&name_ref)); | 215 | let name_kind = classify_name_ref(sema, &name_ref); |
224 | match name_kind { | 216 | match name_kind { |
225 | Some(name_kind) => { | 217 | Some(name_kind) => { |
226 | if let NameDefinition::Local(local) = &name_kind { | 218 | if let NameDefinition::Local(local) = &name_kind { |
227 | if let Some(name) = local.name(db) { | 219 | if let Some(name) = local.name(db) { |
228 | let shadow_count = | 220 | let shadow_count = |
229 | bindings_shadow_count.entry(name.clone()).or_default(); | 221 | bindings_shadow_count.entry(name.clone()).or_default(); |
230 | binding_hash = | 222 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) |
231 | Some(calc_binding_hash(node.file_id, &name, *shadow_count)) | ||
232 | } | 223 | } |
233 | }; | 224 | }; |
234 | 225 | ||
@@ -238,14 +229,14 @@ fn highlight_node( | |||
238 | } | 229 | } |
239 | } | 230 | } |
240 | NAME => { | 231 | NAME => { |
241 | let name = node.value.as_node().cloned().and_then(ast::Name::cast).unwrap(); | 232 | let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); |
242 | let name_kind = classify_name(sb, node.with_value(&name)); | 233 | let name_kind = classify_name(sema, &name); |
243 | 234 | ||
244 | if let Some(NameDefinition::Local(local)) = &name_kind { | 235 | if let Some(NameDefinition::Local(local)) = &name_kind { |
245 | if let Some(name) = local.name(db) { | 236 | if let Some(name) = local.name(db) { |
246 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); | 237 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); |
247 | *shadow_count += 1; | 238 | *shadow_count += 1; |
248 | binding_hash = Some(calc_binding_hash(node.file_id, &name, *shadow_count)) | 239 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) |
249 | } | 240 | } |
250 | }; | 241 | }; |
251 | 242 | ||
@@ -272,7 +263,7 @@ fn highlight_node( | |||
272 | 263 | ||
273 | return Some((tag, binding_hash)); | 264 | return Some((tag, binding_hash)); |
274 | 265 | ||
275 | fn calc_binding_hash(file_id: HirFileId, name: &Name, shadow_count: u32) -> u64 { | 266 | fn calc_binding_hash(name: &Name, shadow_count: u32) -> u64 { |
276 | fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { | 267 | fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { |
277 | use std::{collections::hash_map::DefaultHasher, hash::Hasher}; | 268 | use std::{collections::hash_map::DefaultHasher, hash::Hasher}; |
278 | 269 | ||
@@ -281,7 +272,7 @@ fn highlight_node( | |||
281 | hasher.finish() | 272 | hasher.finish() |
282 | } | 273 | } |
283 | 274 | ||
284 | hash((file_id, name, shadow_count)) | 275 | hash((name, shadow_count)) |
285 | } | 276 | } |
286 | } | 277 | } |
287 | 278 | ||