aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/syntax_highlighting.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/syntax_highlighting.rs')
-rw-r--r--crates/ra_ide_api/src/syntax_highlighting.rs168
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
3use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::{FxHashMap, FxHashSet};
4 4
5use hir::{Mutability, Ty}; 5use hir::{Mutability, Name};
6use ra_db::SourceDatabase; 6use ra_db::SourceDatabase;
7use ra_prof::profile; 7use ra_prof::profile;
8use ra_syntax::{ 8use 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
15use crate::{ 10use 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
43fn 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
63pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> { 41pub(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
210fn 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
276fn html_escape(text: &str) -> String { 242fn html_escape(text: &str) -> String {
277 text.replace("<", "&lt;").replace(">", "&gt;") 243 text.replace("<", "&lt;").replace(">", "&gt;")