aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/syntax_highlighting.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-02-18 17:35:10 +0000
committerAleksey Kladov <[email protected]>2020-02-26 11:55:50 +0000
commitc3a4c4429de83450654795534e64e878a774a088 (patch)
tree12d89798f61b276f8bd640db07276a7d4e92b1c2 /crates/ra_ide/src/syntax_highlighting.rs
parent04deae3dba7c9b7054f7a1d64e4b93a05aecc132 (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.rs87
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
3use hir::{HirFileId, InFile, Name, SourceAnalyzer, SourceBinder}; 3use hir::{Name, Semantics};
4use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
5use ra_ide_db::{defs::NameDefinition, RootDatabase}; 5use ra_ide_db::{
6 defs::{classify_name, NameDefinition},
7 RootDatabase,
8};
6use ra_prof::profile; 9use ra_prof::profile;
7use ra_syntax::{ 10use 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};
11use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
12 15
13use crate::{ 16use 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
19pub mod tags { 18pub 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
164fn highlight_macro(node: InFile<SyntaxElement>) -> Option<TextRange> { 157fn 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
181fn highlight_token_tree( 174fn 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
203fn highlight_node( 195fn 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