aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPascal Hertleif <[email protected]>2019-05-23 18:42:42 +0100
committerPascal Hertleif <[email protected]>2019-05-27 10:26:33 +0100
commit5bf3e949e8470a138a61c806769e1a329761cab6 (patch)
tree9885346944b4aa82804514580944673a53605ee2
parent4b48cff022a1606bde596f01fbf44361640b10d8 (diff)
Semantic highlighting spike
Very simple approach: For each identifier, set the hash of the range where it's defined as its 'id' and use it in the VSCode extension to generate unique colors. Thus, the generated colors are per-file. They are also quite fragile, and I'm not entirely sure why. Looks like we need to make sure the same ranges aren't overwritten by a later request?
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__highlighting.snap192
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap87
-rw-r--r--crates/ra_ide_api/src/syntax_highlighting.rs101
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs6
-rw-r--r--crates/ra_lsp_server/src/req.rs1
-rw-r--r--crates/ra_syntax/src/syntax_node.rs4
-rw-r--r--editors/code/package-lock.json10
-rw-r--r--editors/code/package.json2
-rw-r--r--editors/code/src/highlighting.ts45
9 files changed, 409 insertions, 39 deletions
diff --git a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap
new file mode 100644
index 000000000..208681f10
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap
@@ -0,0 +1,192 @@
1---
2created: "2019-05-25T10:53:54.439877Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/syntax_highlighting.rs
5expression: result
6---
7Ok(
8 [
9 HighlightedRange {
10 range: [1; 24),
11 tag: "attribute",
12 id: None,
13 },
14 HighlightedRange {
15 range: [25; 31),
16 tag: "keyword",
17 id: None,
18 },
19 HighlightedRange {
20 range: [32; 35),
21 tag: "variable",
22 id: Some(
23 461893210254723387,
24 ),
25 },
26 HighlightedRange {
27 range: [42; 45),
28 tag: "keyword",
29 id: None,
30 },
31 HighlightedRange {
32 range: [46; 47),
33 tag: "variable",
34 id: Some(
35 8312289520117458465,
36 ),
37 },
38 HighlightedRange {
39 range: [49; 52),
40 tag: "text",
41 id: None,
42 },
43 HighlightedRange {
44 range: [58; 61),
45 tag: "keyword",
46 id: None,
47 },
48 HighlightedRange {
49 range: [62; 63),
50 tag: "variable",
51 id: Some(
52 4497542318236667727,
53 ),
54 },
55 HighlightedRange {
56 range: [65; 68),
57 tag: "text",
58 id: None,
59 },
60 HighlightedRange {
61 range: [73; 75),
62 tag: "keyword",
63 id: None,
64 },
65 HighlightedRange {
66 range: [76; 79),
67 tag: "variable",
68 id: Some(
69 4506850079084802999,
70 ),
71 },
72 HighlightedRange {
73 range: [80; 81),
74 tag: "type",
75 id: None,
76 },
77 HighlightedRange {
78 range: [80; 81),
79 tag: "variable",
80 id: Some(
81 16968185728268100018,
82 ),
83 },
84 HighlightedRange {
85 range: [88; 89),
86 tag: "type",
87 id: None,
88 },
89 HighlightedRange {
90 range: [96; 110),
91 tag: "macro",
92 id: None,
93 },
94 HighlightedRange {
95 range: [117; 127),
96 tag: "comment",
97 id: None,
98 },
99 HighlightedRange {
100 range: [128; 130),
101 tag: "keyword",
102 id: None,
103 },
104 HighlightedRange {
105 range: [131; 135),
106 tag: "variable",
107 id: Some(
108 14467718814232352107,
109 ),
110 },
111 HighlightedRange {
112 range: [145; 153),
113 tag: "macro",
114 id: None,
115 },
116 HighlightedRange {
117 range: [154; 166),
118 tag: "string",
119 id: None,
120 },
121 HighlightedRange {
122 range: [168; 170),
123 tag: "literal",
124 id: None,
125 },
126 HighlightedRange {
127 range: [178; 181),
128 tag: "keyword",
129 id: None,
130 },
131 HighlightedRange {
132 range: [182; 185),
133 tag: "keyword",
134 id: None,
135 },
136 HighlightedRange {
137 range: [186; 189),
138 tag: "macro",
139 id: None,
140 },
141 HighlightedRange {
142 range: [197; 200),
143 tag: "macro",
144 id: None,
145 },
146 HighlightedRange {
147 range: [192; 195),
148 tag: "text",
149 id: None,
150 },
151 HighlightedRange {
152 range: [208; 211),
153 tag: "macro",
154 id: None,
155 },
156 HighlightedRange {
157 range: [212; 216),
158 tag: "macro",
159 id: None,
160 },
161 HighlightedRange {
162 range: [226; 227),
163 tag: "literal",
164 id: None,
165 },
166 HighlightedRange {
167 range: [232; 233),
168 tag: "literal",
169 id: None,
170 },
171 HighlightedRange {
172 range: [242; 248),
173 tag: "keyword.unsafe",
174 id: None,
175 },
176 HighlightedRange {
177 range: [251; 254),
178 tag: "text",
179 id: None,
180 },
181 HighlightedRange {
182 range: [255; 262),
183 tag: "text",
184 id: None,
185 },
186 HighlightedRange {
187 range: [263; 264),
188 tag: "literal",
189 id: None,
190 },
191 ],
192)
diff --git a/crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap
new file mode 100644
index 000000000..3b3fe32e9
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap
@@ -0,0 +1,87 @@
1---
2created: "2019-05-25T10:25:13.898113Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/syntax_highlighting.rs
5expression: result
6---
7Ok(
8 [
9 HighlightedRange {
10 range: [1; 3),
11 tag: "keyword",
12 id: None,
13 },
14 HighlightedRange {
15 range: [4; 8),
16 tag: "variable",
17 id: Some(
18 17119830160611610240,
19 ),
20 },
21 HighlightedRange {
22 range: [17; 20),
23 tag: "keyword",
24 id: None,
25 },
26 HighlightedRange {
27 range: [21; 26),
28 tag: "variable",
29 id: Some(
30 2744494144922727377,
31 ),
32 },
33 HighlightedRange {
34 range: [29; 36),
35 tag: "string",
36 id: None,
37 },
38 HighlightedRange {
39 range: [42; 45),
40 tag: "keyword",
41 id: None,
42 },
43 HighlightedRange {
44 range: [46; 47),
45 tag: "variable",
46 id: Some(
47 10375904121795371996,
48 ),
49 },
50 HighlightedRange {
51 range: [50; 55),
52 tag: "variable",
53 id: Some(
54 2744494144922727377,
55 ),
56 },
57 HighlightedRange {
58 range: [56; 65),
59 tag: "text",
60 id: None,
61 },
62 HighlightedRange {
63 range: [73; 76),
64 tag: "keyword",
65 id: None,
66 },
67 HighlightedRange {
68 range: [77; 78),
69 tag: "variable",
70 id: Some(
71 8228548264153724449,
72 ),
73 },
74 HighlightedRange {
75 range: [81; 86),
76 tag: "variable",
77 id: Some(
78 2744494144922727377,
79 ),
80 },
81 HighlightedRange {
82 range: [87; 96),
83 tag: "text",
84 id: None,
85 },
86 ],
87)
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs
index 87e053364..da000c0c3 100644
--- a/crates/ra_ide_api/src/syntax_highlighting.rs
+++ b/crates/ra_ide_api/src/syntax_highlighting.rs
@@ -10,6 +10,7 @@ use crate::{FileId, db::RootDatabase};
10pub struct HighlightedRange { 10pub struct HighlightedRange {
11 pub range: TextRange, 11 pub range: TextRange,
12 pub tag: &'static str, 12 pub tag: &'static str,
13 pub id: Option<u64>,
13} 14}
14 15
15fn is_control_keyword(kind: SyntaxKind) -> bool { 16fn is_control_keyword(kind: SyntaxKind) -> bool {
@@ -32,6 +33,14 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
32 33
33 let source_file = db.parse(file_id); 34 let source_file = db.parse(file_id);
34 35
36 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
37 use std::{collections::hash_map::DefaultHasher, hash::Hasher};
38
39 let mut hasher = DefaultHasher::new();
40 x.hash(&mut hasher);
41 hasher.finish()
42 }
43
35 // Visited nodes to handle highlighting priorities 44 // Visited nodes to handle highlighting priorities
36 let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default(); 45 let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default();
37 let mut res = Vec::new(); 46 let mut res = Vec::new();
@@ -39,52 +48,59 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
39 if highlighted.contains(&node) { 48 if highlighted.contains(&node) {
40 continue; 49 continue;
41 } 50 }
42 let tag = match node.kind() { 51 let (tag, id) = match node.kind() {
43 COMMENT => "comment", 52 COMMENT => ("comment", None),
44 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string", 53 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => ("string", None),
45 ATTR => "attribute", 54 ATTR => ("attribute", None),
46 NAME_REF => { 55 NAME_REF => {
47 if let Some(name_ref) = node.as_node().and_then(|n| ast::NameRef::cast(n)) { 56 if let Some(name_ref) = node.as_ast_node::<ast::NameRef>() {
48 use crate::name_ref_kind::{classify_name_ref, NameRefKind::*}; 57 use crate::name_ref_kind::{classify_name_ref, NameRefKind::*};
49 use hir::{ModuleDef, ImplItem}; 58 use hir::{ModuleDef, ImplItem};
50 59
51 // FIXME: try to reuse the SourceAnalyzers 60 // FIXME: try to reuse the SourceAnalyzers
52 let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); 61 let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
53 match classify_name_ref(db, &analyzer, name_ref) { 62 match classify_name_ref(db, &analyzer, name_ref) {
54 Some(Method(_)) => "function", 63 Some(Method(_)) => ("function", None),
55 Some(Macro(_)) => "macro", 64 Some(Macro(_)) => ("macro", None),
56 Some(FieldAccess(_)) => "field", 65 Some(FieldAccess(_)) => ("field", None),
57 Some(AssocItem(ImplItem::Method(_))) => "function", 66 Some(AssocItem(ImplItem::Method(_))) => ("function", None),
58 Some(AssocItem(ImplItem::Const(_))) => "constant", 67 Some(AssocItem(ImplItem::Const(_))) => ("constant", None),
59 Some(AssocItem(ImplItem::TypeAlias(_))) => "type", 68 Some(AssocItem(ImplItem::TypeAlias(_))) => ("type", None),
60 Some(Def(ModuleDef::Module(_))) => "module", 69 Some(Def(ModuleDef::Module(_))) => ("module", None),
61 Some(Def(ModuleDef::Function(_))) => "function", 70 Some(Def(ModuleDef::Function(_))) => ("function", None),
62 Some(Def(ModuleDef::Struct(_))) => "type", 71 Some(Def(ModuleDef::Struct(_))) => ("type", None),
63 Some(Def(ModuleDef::Union(_))) => "type", 72 Some(Def(ModuleDef::Union(_))) => ("type", None),
64 Some(Def(ModuleDef::Enum(_))) => "type", 73 Some(Def(ModuleDef::Enum(_))) => ("type", None),
65 Some(Def(ModuleDef::EnumVariant(_))) => "constant", 74 Some(Def(ModuleDef::EnumVariant(_))) => ("constant", None),
66 Some(Def(ModuleDef::Const(_))) => "constant", 75 Some(Def(ModuleDef::Const(_))) => ("constant", None),
67 Some(Def(ModuleDef::Static(_))) => "constant", 76 Some(Def(ModuleDef::Static(_))) => ("constant", None),
68 Some(Def(ModuleDef::Trait(_))) => "type", 77 Some(Def(ModuleDef::Trait(_))) => ("type", None),
69 Some(Def(ModuleDef::TypeAlias(_))) => "type", 78 Some(Def(ModuleDef::TypeAlias(_))) => ("type", None),
70 Some(SelfType(_)) => "type", 79 Some(SelfType(_)) => ("type", None),
71 Some(Pat(_)) => "text", 80 Some(Pat(ptr)) => ("variable", Some(hash(ptr.syntax_node_ptr().range()))),
72 Some(SelfParam(_)) => "type", 81 Some(SelfParam(_)) => ("type", None),
73 Some(GenericParam(_)) => "type", 82 Some(GenericParam(_)) => ("type", None),
74 None => "text", 83 None => ("text", None),
75 } 84 }
76 } else { 85 } else {
77 "text" 86 ("text", None)
78 } 87 }
79 } 88 }
80 NAME => "function", 89 NAME => {
81 TYPE_ALIAS_DEF | TYPE_ARG | TYPE_PARAM => "type", 90 if let Some(name) = node.as_ast_node::<ast::Name>() {
82 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal", 91 ("variable", Some(hash(name.syntax().range())))
83 LIFETIME => "parameter", 92 } else {
84 T![unsafe] => "keyword.unsafe", 93 ("text", None)
85 k if is_control_keyword(k) => "keyword.control", 94 }
86 k if k.is_keyword() => "keyword", 95 }
96 TYPE_ALIAS_DEF | TYPE_ARG | TYPE_PARAM => ("type", None),
97 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => ("literal", None),
98 LIFETIME => ("parameter", None),
99 T![unsafe] => ("keyword.unsafe", None),
100 k if is_control_keyword(k) => ("keyword.control", None),
101 k if k.is_keyword() => ("keyword", None),
87 _ => { 102 _ => {
103 // let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
88 if let Some(macro_call) = node.as_node().and_then(ast::MacroCall::cast) { 104 if let Some(macro_call) = node.as_node().and_then(ast::MacroCall::cast) {
89 if let Some(path) = macro_call.path() { 105 if let Some(path) = macro_call.path() {
90 if let Some(segment) = path.segment() { 106 if let Some(segment) = path.segment() {
@@ -101,6 +117,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
101 res.push(HighlightedRange { 117 res.push(HighlightedRange {
102 range: TextRange::from_to(range_start, range_end), 118 range: TextRange::from_to(range_start, range_end),
103 tag: "macro", 119 tag: "macro",
120 id: None,
104 }) 121 })
105 } 122 }
106 } 123 }
@@ -109,7 +126,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
109 continue; 126 continue;
110 } 127 }
111 }; 128 };
112 res.push(HighlightedRange { range: node.range(), tag }) 129 res.push(HighlightedRange { range: node.range(), tag, id })
113 } 130 }
114 res 131 res
115} 132}
@@ -221,4 +238,18 @@ fn main() {
221 // std::fs::write(dst_file, &actual_html).unwrap(); 238 // std::fs::write(dst_file, &actual_html).unwrap();
222 assert_eq_text!(expected_html, actual_html); 239 assert_eq_text!(expected_html, actual_html);
223 } 240 }
241
242 #[test]
243 fn test_sematic_highlighting() {
244 let (analysis, file_id) = single_file(
245 r#"
246fn main() {
247 let hello = "hello";
248 let x = hello.to_string();
249 let y = hello.to_string();
250}"#,
251 );
252 let result = analysis.highlight(file_id);
253 assert_debug_snapshot_matches!("sematic_highlighting", result);
254 }
224} 255}
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index a82ae696b..5dfd64ed4 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -872,7 +872,11 @@ fn highlight(world: &ServerWorld, file_id: FileId) -> Result<Vec<Decoration>> {
872 .analysis() 872 .analysis()
873 .highlight(file_id)? 873 .highlight(file_id)?
874 .into_iter() 874 .into_iter()
875 .map(|h| Decoration { range: h.range.conv_with(&line_index), tag: h.tag }) 875 .map(|h| Decoration {
876 range: h.range.conv_with(&line_index),
877 tag: h.tag,
878 id: h.id.map(|x| x.to_string()),
879 })
876 .collect(); 880 .collect();
877 Ok(res) 881 Ok(res)
878} 882}
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs
index 6090eb7b9..cea0e6ce7 100644
--- a/crates/ra_lsp_server/src/req.rs
+++ b/crates/ra_lsp_server/src/req.rs
@@ -129,6 +129,7 @@ pub struct PublishDecorationsParams {
129pub struct Decoration { 129pub struct Decoration {
130 pub range: Range, 130 pub range: Range,
131 pub tag: &'static str, 131 pub tag: &'static str,
132 pub id: Option<String>,
132} 133}
133 134
134pub enum ParentModule {} 135pub enum ParentModule {}
diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs
index 80054f529..89f92e0b7 100644
--- a/crates/ra_syntax/src/syntax_node.rs
+++ b/crates/ra_syntax/src/syntax_node.rs
@@ -523,6 +523,10 @@ impl<'a> SyntaxElement<'a> {
523 } 523 }
524 } 524 }
525 525
526 pub fn as_ast_node<T: AstNode>(&self) -> Option<&T> {
527 self.as_node().and_then(|x| <T as AstNode>::cast(x))
528 }
529
526 pub fn as_token(&self) -> Option<SyntaxToken<'a>> { 530 pub fn as_token(&self) -> Option<SyntaxToken<'a>> {
527 match self { 531 match self {
528 SyntaxElement::Node(_) => None, 532 SyntaxElement::Node(_) => None,
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 29cd260a4..6b3a12f91 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -36,6 +36,11 @@
36 "integrity": "sha512-Ja7d4s0qyGFxjGeDq5S7Si25OFibSAHUi6i17UWnwNnpitADN7hah9q0Tl25gxuV5R1u2Bx+np6w4LHXfHyj/g==", 36 "integrity": "sha512-Ja7d4s0qyGFxjGeDq5S7Si25OFibSAHUi6i17UWnwNnpitADN7hah9q0Tl25gxuV5R1u2Bx+np6w4LHXfHyj/g==",
37 "dev": true 37 "dev": true
38 }, 38 },
39 "@types/seedrandom": {
40 "version": "2.4.28",
41 "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.28.tgz",
42 "integrity": "sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA=="
43 },
39 "agent-base": { 44 "agent-base": {
40 "version": "4.2.1", 45 "version": "4.2.1",
41 "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", 46 "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
@@ -984,6 +989,11 @@
984 "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 989 "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
985 "dev": true 990 "dev": true
986 }, 991 },
992 "seedrandom": {
993 "version": "3.0.1",
994 "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.1.tgz",
995 "integrity": "sha512-1/02Y/rUeU1CJBAGLebiC5Lbo5FnB22gQbIFFYTLkwvp1xdABZJH1sn4ZT1MzXmPpzv+Rf/Lu2NcsLJiK4rcDg=="
996 },
987 "semver": { 997 "semver": {
988 "version": "5.7.0", 998 "version": "5.7.0",
989 "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", 999 "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
diff --git a/editors/code/package.json b/editors/code/package.json
index cde5fbcb8..d8ba914f5 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -31,11 +31,13 @@
31 "singleQuote": true 31 "singleQuote": true
32 }, 32 },
33 "dependencies": { 33 "dependencies": {
34 "seedrandom": "^3.0.1",
34 "vscode-languageclient": "^5.3.0-next.4" 35 "vscode-languageclient": "^5.3.0-next.4"
35 }, 36 },
36 "devDependencies": { 37 "devDependencies": {
37 "@types/mocha": "^5.2.6", 38 "@types/mocha": "^5.2.6",
38 "@types/node": "^10.14.5", 39 "@types/node": "^10.14.5",
40 "@types/seedrandom": "^2.4.28",
39 "prettier": "^1.17.0", 41 "prettier": "^1.17.0",
40 "shx": "^0.3.1", 42 "shx": "^0.3.1",
41 "tslint": "^5.16.0", 43 "tslint": "^5.16.0",
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts
index 8389d94b8..4597db08f 100644
--- a/editors/code/src/highlighting.ts
+++ b/editors/code/src/highlighting.ts
@@ -1,3 +1,4 @@
1import seedrandom = require('seedrandom');
1import * as vscode from 'vscode'; 2import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 3import * as lc from 'vscode-languageclient';
3 4
@@ -6,6 +7,20 @@ import { Server } from './server';
6export interface Decoration { 7export interface Decoration {
7 range: lc.Range; 8 range: lc.Range;
8 tag: string; 9 tag: string;
10 id?: string;
11}
12
13// Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76
14function fancify(seed: string, shade: 'light' | 'dark') {
15 const random = seedrandom(seed);
16 const randomInt = (min: number, max: number) => {
17 return Math.floor(random() * (max - min + 1)) + min;
18 };
19
20 const h = randomInt(0, 360);
21 const s = randomInt(42, 98);
22 const l = shade === 'light' ? randomInt(15, 40) : randomInt(40, 90);
23 return `hsl(${h},${s}%,${l}%)`;
9} 24}
10 25
11export class Highlighter { 26export class Highlighter {
@@ -76,6 +91,8 @@ export class Highlighter {
76 } 91 }
77 92
78 const byTag: Map<string, vscode.Range[]> = new Map(); 93 const byTag: Map<string, vscode.Range[]> = new Map();
94 const colorfulIdents: Map<string, vscode.Range[]> = new Map();
95
79 for (const tag of this.decorations.keys()) { 96 for (const tag of this.decorations.keys()) {
80 byTag.set(tag, []); 97 byTag.set(tag, []);
81 } 98 }
@@ -84,9 +101,23 @@ export class Highlighter {
84 if (!byTag.get(d.tag)) { 101 if (!byTag.get(d.tag)) {
85 continue; 102 continue;
86 } 103 }
87 byTag 104
88 .get(d.tag)! 105 if (d.id) {
89 .push(Server.client.protocol2CodeConverter.asRange(d.range)); 106 if (!colorfulIdents.has(d.id)) {
107 colorfulIdents.set(d.id, []);
108 }
109 colorfulIdents
110 .get(d.id)!
111 .push(
112 Server.client.protocol2CodeConverter.asRange(d.range)
113 );
114 } else {
115 byTag
116 .get(d.tag)!
117 .push(
118 Server.client.protocol2CodeConverter.asRange(d.range)
119 );
120 }
90 } 121 }
91 122
92 for (const tag of byTag.keys()) { 123 for (const tag of byTag.keys()) {
@@ -96,5 +127,13 @@ export class Highlighter {
96 const ranges = byTag.get(tag)!; 127 const ranges = byTag.get(tag)!;
97 editor.setDecorations(dec, ranges); 128 editor.setDecorations(dec, ranges);
98 } 129 }
130
131 for (const [hash, ranges] of colorfulIdents.entries()) {
132 const dec = vscode.window.createTextEditorDecorationType({
133 light: { color: fancify(hash, 'light') },
134 dark: { color: fancify(hash, 'dark') }
135 });
136 editor.setDecorations(dec, ranges);
137 }
99 } 138 }
100} 139}