diff options
Diffstat (limited to 'crates/ra_ide_api/src/syntax_highlighting.rs')
-rw-r--r-- | crates/ra_ide_api/src/syntax_highlighting.rs | 168 |
1 files changed, 67 insertions, 101 deletions
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs index 33f3caceb..d53a759ee 100644 --- a/crates/ra_ide_api/src/syntax_highlighting.rs +++ b/crates/ra_ide_api/src/syntax_highlighting.rs | |||
@@ -2,19 +2,17 @@ | |||
2 | 2 | ||
3 | use rustc_hash::{FxHashMap, FxHashSet}; | 3 | use rustc_hash::{FxHashMap, FxHashSet}; |
4 | 4 | ||
5 | use hir::{Mutability, Ty}; | 5 | use hir::{Mutability, Name}; |
6 | use ra_db::SourceDatabase; | 6 | use ra_db::SourceDatabase; |
7 | use ra_prof::profile; | 7 | use ra_prof::profile; |
8 | use ra_syntax::{ | 8 | use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T}; |
9 | ast::{self, NameOwner}, | ||
10 | AstNode, Direction, SmolStr, SyntaxElement, SyntaxKind, | ||
11 | SyntaxKind::*, | ||
12 | TextRange, T, | ||
13 | }; | ||
14 | 9 | ||
15 | use crate::{ | 10 | use crate::{ |
16 | db::RootDatabase, | 11 | db::RootDatabase, |
17 | references::{classify_name_ref, NameKind::*}, | 12 | references::{ |
13 | classify_name, classify_name_ref, | ||
14 | NameKind::{self, *}, | ||
15 | }, | ||
18 | FileId, | 16 | FileId, |
19 | }; | 17 | }; |
20 | 18 | ||
@@ -40,32 +38,12 @@ fn is_control_keyword(kind: SyntaxKind) -> bool { | |||
40 | } | 38 | } |
41 | } | 39 | } |
42 | 40 | ||
43 | fn is_variable_mutable( | ||
44 | db: &RootDatabase, | ||
45 | analyzer: &hir::SourceAnalyzer, | ||
46 | pat: ast::BindPat, | ||
47 | ) -> bool { | ||
48 | if pat.is_mutable() { | ||
49 | return true; | ||
50 | } | ||
51 | |||
52 | let ty = analyzer.type_of_pat(db, &pat.into()).unwrap_or(Ty::Unknown); | ||
53 | if let Some((_, mutability)) = ty.as_reference() { | ||
54 | match mutability { | ||
55 | Mutability::Shared => false, | ||
56 | Mutability::Mut => true, | ||
57 | } | ||
58 | } else { | ||
59 | false | ||
60 | } | ||
61 | } | ||
62 | |||
63 | pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> { | 41 | pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> { |
64 | let _p = profile("highlight"); | 42 | let _p = profile("highlight"); |
65 | let parse = db.parse(file_id); | 43 | let parse = db.parse(file_id); |
66 | let root = parse.tree().syntax().clone(); | 44 | let root = parse.tree().syntax().clone(); |
67 | 45 | ||
68 | fn calc_binding_hash(file_id: FileId, text: &SmolStr, shadow_count: u32) -> u64 { | 46 | fn calc_binding_hash(file_id: FileId, name: &Name, shadow_count: u32) -> u64 { |
69 | fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { | 47 | fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { |
70 | use std::{collections::hash_map::DefaultHasher, hash::Hasher}; | 48 | use std::{collections::hash_map::DefaultHasher, hash::Hasher}; |
71 | 49 | ||
@@ -74,13 +52,13 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
74 | hasher.finish() | 52 | hasher.finish() |
75 | } | 53 | } |
76 | 54 | ||
77 | hash((file_id, text, shadow_count)) | 55 | hash((file_id, name, shadow_count)) |
78 | } | 56 | } |
79 | 57 | ||
80 | // Visited nodes to handle highlighting priorities | 58 | // Visited nodes to handle highlighting priorities |
81 | // FIXME: retain only ranges here | 59 | // FIXME: retain only ranges here |
82 | let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default(); | 60 | let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default(); |
83 | let mut bindings_shadow_count: FxHashMap<SmolStr, u32> = FxHashMap::default(); | 61 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); |
84 | 62 | ||
85 | let mut res = Vec::new(); | 63 | let mut res = Vec::new(); |
86 | for node in root.descendants_with_tokens() { | 64 | for node in root.descendants_with_tokens() { |
@@ -100,81 +78,38 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
100 | if node.ancestors().any(|it| it.kind() == ATTR) { | 78 | if node.ancestors().any(|it| it.kind() == ATTR) { |
101 | continue; | 79 | continue; |
102 | } | 80 | } |
103 | if let Some(name_ref) = node.as_node().cloned().and_then(ast::NameRef::cast) { | ||
104 | let name_kind = classify_name_ref(db, file_id, &name_ref).map(|d| d.kind); | ||
105 | match name_kind { | ||
106 | Some(Macro(_)) => "macro", | ||
107 | Some(Field(_)) => "field", | ||
108 | Some(AssocItem(hir::AssocItem::Function(_))) => "function", | ||
109 | Some(AssocItem(hir::AssocItem::Const(_))) => "constant", | ||
110 | Some(AssocItem(hir::AssocItem::TypeAlias(_))) => "type", | ||
111 | Some(Def(hir::ModuleDef::Module(_))) => "module", | ||
112 | Some(Def(hir::ModuleDef::Function(_))) => "function", | ||
113 | Some(Def(hir::ModuleDef::Adt(_))) => "type", | ||
114 | Some(Def(hir::ModuleDef::EnumVariant(_))) => "constant", | ||
115 | Some(Def(hir::ModuleDef::Const(_))) => "constant", | ||
116 | Some(Def(hir::ModuleDef::Static(_))) => "constant", | ||
117 | Some(Def(hir::ModuleDef::Trait(_))) => "type", | ||
118 | Some(Def(hir::ModuleDef::TypeAlias(_))) => "type", | ||
119 | Some(Def(hir::ModuleDef::BuiltinType(_))) => "type", | ||
120 | Some(SelfType(_)) => "type", | ||
121 | Some(Pat((_, ptr))) => { | ||
122 | let pat = ptr.to_node(&root); | ||
123 | if let Some(name) = pat.name() { | ||
124 | let text = name.text(); | ||
125 | let shadow_count = | ||
126 | bindings_shadow_count.entry(text.clone()).or_default(); | ||
127 | binding_hash = | ||
128 | Some(calc_binding_hash(file_id, &text, *shadow_count)) | ||
129 | } | ||
130 | 81 | ||
131 | let analyzer = | 82 | let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); |
132 | hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); | 83 | let name_kind = classify_name_ref(db, file_id, &name_ref).map(|d| d.kind); |
133 | if is_variable_mutable(db, &analyzer, ptr.to_node(&root)) { | 84 | |
134 | "variable.mut" | 85 | if let Some(Local(local)) = &name_kind { |
135 | } else { | 86 | if let Some(name) = local.name(db) { |
136 | "variable" | 87 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); |
137 | } | 88 | binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count)) |
138 | } | ||
139 | Some(SelfParam(_)) => "type", | ||
140 | Some(GenericParam(_)) => "type", | ||
141 | None => "text", | ||
142 | } | 89 | } |
143 | } else { | 90 | }; |
144 | "text" | 91 | |
145 | } | 92 | name_kind.map_or("text", |it| highlight_name(db, it)) |
146 | } | 93 | } |
147 | NAME => { | 94 | NAME => { |
148 | if let Some(name) = node.as_node().cloned().and_then(ast::Name::cast) { | 95 | let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); |
149 | let analyzer = hir::SourceAnalyzer::new(db, file_id, name.syntax(), None); | 96 | let name_kind = classify_name(db, file_id, &name).map(|d| d.kind); |
150 | if let Some(pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { | 97 | |
151 | if let Some(name) = pat.name() { | 98 | if let Some(Local(local)) = &name_kind { |
152 | let text = name.text(); | 99 | if let Some(name) = local.name(db) { |
153 | let shadow_count = | 100 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); |
154 | bindings_shadow_count.entry(text.clone()).or_default(); | 101 | *shadow_count += 1; |
155 | *shadow_count += 1; | 102 | binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count)) |
156 | binding_hash = Some(calc_binding_hash(file_id, &text, *shadow_count)) | ||
157 | } | ||
158 | |||
159 | if is_variable_mutable(db, &analyzer, pat) { | ||
160 | "variable.mut" | ||
161 | } else { | ||
162 | "variable" | ||
163 | } | ||
164 | } else { | ||
165 | name.syntax() | ||
166 | .parent() | ||
167 | .map(|x| match x.kind() { | ||
168 | TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => { | ||
169 | "type" | ||
170 | } | ||
171 | RECORD_FIELD_DEF => "field", | ||
172 | _ => "function", | ||
173 | }) | ||
174 | .unwrap_or("function") | ||
175 | } | 103 | } |
176 | } else { | 104 | }; |
177 | "text" | 105 | |
106 | match name_kind { | ||
107 | Some(name_kind) => highlight_name(db, name_kind), | ||
108 | None => name.syntax().parent().map_or("function", |x| match x.kind() { | ||
109 | TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => "type", | ||
110 | RECORD_FIELD_DEF => "field", | ||
111 | _ => "function", | ||
112 | }), | ||
178 | } | 113 | } |
179 | } | 114 | } |
180 | INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal", | 115 | INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal", |
@@ -272,6 +207,37 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo | |||
272 | buf | 207 | buf |
273 | } | 208 | } |
274 | 209 | ||
210 | fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str { | ||
211 | match name_kind { | ||
212 | Macro(_) => "macro", | ||
213 | Field(_) => "field", | ||
214 | AssocItem(hir::AssocItem::Function(_)) => "function", | ||
215 | AssocItem(hir::AssocItem::Const(_)) => "constant", | ||
216 | AssocItem(hir::AssocItem::TypeAlias(_)) => "type", | ||
217 | Def(hir::ModuleDef::Module(_)) => "module", | ||
218 | Def(hir::ModuleDef::Function(_)) => "function", | ||
219 | Def(hir::ModuleDef::Adt(_)) => "type", | ||
220 | Def(hir::ModuleDef::EnumVariant(_)) => "constant", | ||
221 | Def(hir::ModuleDef::Const(_)) => "constant", | ||
222 | Def(hir::ModuleDef::Static(_)) => "constant", | ||
223 | Def(hir::ModuleDef::Trait(_)) => "type", | ||
224 | Def(hir::ModuleDef::TypeAlias(_)) => "type", | ||
225 | Def(hir::ModuleDef::BuiltinType(_)) => "type", | ||
226 | SelfType(_) => "type", | ||
227 | GenericParam(_) => "type", | ||
228 | Local(local) => { | ||
229 | if local.is_mut(db) { | ||
230 | "variable.mut" | ||
231 | } else { | ||
232 | match local.ty(db).as_reference() { | ||
233 | Some((_, Mutability::Mut)) => "variable.mut", | ||
234 | _ => "variable", | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | } | ||
239 | } | ||
240 | |||
275 | //FIXME: like, real html escaping | 241 | //FIXME: like, real html escaping |
276 | fn html_escape(text: &str) -> String { | 242 | fn html_escape(text: &str) -> String { |
277 | text.replace("<", "<").replace(">", ">") | 243 | text.replace("<", "<").replace(">", ">") |