aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src
diff options
context:
space:
mode:
authorPascal Hertleif <[email protected]>2019-05-25 15:23:58 +0100
committerPascal Hertleif <[email protected]>2019-05-27 10:26:35 +0100
commit43d5a4965308ec4b594725c0bd02cb046bdb730c (patch)
tree3a6e2965b065e61310deaa4186a8cec6535fd244 /crates/ra_ide_api/src
parented89b0638b1dbf8f9a33d9a95e829e602142bb05 (diff)
More clever highlighting, incl draft for structs
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r--crates/ra_ide_api/src/lib.rs2
-rw-r--r--crates/ra_ide_api/src/snapshots/highlighting.html24
-rw-r--r--crates/ra_ide_api/src/snapshots/rainbow_highlighting.html27
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__highlighting.snap192
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__rainbow_highlighting.snap128
-rw-r--r--crates/ra_ide_api/src/syntax_highlighting.rs185
6 files changed, 151 insertions, 407 deletions
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index d3456d5b2..65a3b591a 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -464,7 +464,7 @@ impl Analysis {
464 464
465 /// Computes syntax highlighting for the given file. 465 /// Computes syntax highlighting for the given file.
466 pub fn highlight_as_html(&self, file_id: FileId) -> Cancelable<String> { 466 pub fn highlight_as_html(&self, file_id: FileId) -> Cancelable<String> {
467 self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id)) 467 self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, true))
468 } 468 }
469 469
470 /// Computes completions at the given position. 470 /// Computes completions at the given position.
diff --git a/crates/ra_ide_api/src/snapshots/highlighting.html b/crates/ra_ide_api/src/snapshots/highlighting.html
index bfc0a67b1..4f4ed62a1 100644
--- a/crates/ra_ide_api/src/snapshots/highlighting.html
+++ b/crates/ra_ide_api/src/snapshots/highlighting.html
@@ -1,10 +1,7 @@
1 1
2<style> 2<style>
3pre { 3body { margin: 0; }
4 color: #DCDCCC; 4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5 background-color: #3F3F3F;
6 font-size: 22px;
7}
8 5
9.comment { color: #7F9F7F; } 6.comment { color: #7F9F7F; }
10.string { color: #CC9393; } 7.string { color: #CC9393; }
@@ -19,13 +16,11 @@ pre {
19.keyword { color: #F0DFAF; } 16.keyword { color: #F0DFAF; }
20.keyword\.unsafe { color: #F0DFAF; font-weight: bold; } 17.keyword\.unsafe { color: #F0DFAF; font-weight: bold; }
21.keyword\.control { color: #DC8CC3; } 18.keyword\.control { color: #DC8CC3; }
22
23</style> 19</style>
24<pre><code> 20<pre><code><span class="attribute">#</span><span class="attribute">[</span><span class="attribute">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span>
25<span class="attribute">#</span><span class="attribute">[</span><span class="attribute">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span>
26<span class="keyword">struct</span> <span class="function">Foo</span> { 21<span class="keyword">struct</span> <span class="function">Foo</span> {
27 <span class="keyword">pub</span> <span class="function">x</span>: <span class="text">i32</span>, 22 <span class="keyword">pub</span> <span class="variable" data-binding-hash="12067179602561426350" style="color: hsl(78,84%,47%);">x</span>: <span class="text">i32</span>,
28 <span class="keyword">pub</span> <span class="function">y</span>: <span class="text">i32</span>, 23 <span class="keyword">pub</span> <span class="variable" data-binding-hash="15562018766631452210" style="color: hsl(318,95%,78%);">y</span>: <span class="text">i32</span>,
29} 24}
30 25
31<span class="keyword">fn</span> <span class="function">foo</span>&lt;<span class="type function">T</span>&gt;() -&gt; <span class="type">T</span> { 26<span class="keyword">fn</span> <span class="function">foo</span>&lt;<span class="type function">T</span>&gt;() -&gt; <span class="type">T</span> {
@@ -36,10 +31,9 @@ pre {
36<span class="keyword">fn</span> <span class="function">main</span>() { 31<span class="keyword">fn</span> <span class="function">main</span>() {
37 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal">92</span>); 32 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal">92</span>);
38 33
39 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="function">vec</span> = <span class="text">Vec</span>::<span class="text">new</span>(); 34 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable" data-binding-hash="9636295041291189729" style="color: hsl(51,57%,74%);">vec</span> = <span class="text">Vec</span>::<span class="text">new</span>();
40 <span class="keyword.control">if</span> <span class="keyword">true</span> { 35 <span class="keyword.control">if</span> <span class="keyword">true</span> {
41 <span class="text">vec</span>.<span class="text">push</span>(<span class="type">Foo</span> { <span class="field">x</span>: <span class="literal">0</span>, <span class="field">y</span>: <span class="literal">1</span> }); 36 <span class="variable" data-binding-hash="8496027264380925433" style="color: hsl(18,48%,55%);">vec</span>.<span class="text">push</span>(<span class="type">Foo</span> { <span class="field" data-binding-hash="17368948500121423555" style="color: hsl(49,97%,70%);">x</span>: <span class="literal">0</span>, <span class="field" data-binding-hash="13663097548341495469" style="color: hsl(26,51%,46%);">y</span>: <span class="literal">1</span> });
42 } 37 }
43 <span class="keyword.unsafe">unsafe</span> { <span class="text">vec</span>.<span class="text">set_len</span>(<span class="literal">0</span>); } 38 <span class="keyword.unsafe">unsafe</span> { <span class="variable" data-binding-hash="8496027264380925433" style="color: hsl(18,48%,55%);">vec</span>.<span class="text">set_len</span>(<span class="literal">0</span>); }
44} 39}</code></pre> \ No newline at end of file
45</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide_api/src/snapshots/rainbow_highlighting.html b/crates/ra_ide_api/src/snapshots/rainbow_highlighting.html
new file mode 100644
index 000000000..729d129d0
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/rainbow_highlighting.html
@@ -0,0 +1,27 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.comment { color: #7F9F7F; }
7.string { color: #CC9393; }
8.function { color: #93E0E3; }
9.parameter { color: #94BFF3; }
10.builtin { color: #DD6718; }
11.text { color: #DCDCCC; }
12.attribute { color: #BFEBBF; }
13.literal { color: #DFAF8F; }
14.macro { color: #DFAF8F; }
15
16.keyword { color: #F0DFAF; }
17.keyword\.unsafe { color: #F0DFAF; font-weight: bold; }
18.keyword\.control { color: #DC8CC3; }
19</style>
20<pre><code><span class="keyword">fn</span> <span class="function">main</span>() {
21 <span class="keyword">let</span> <span class="variable" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span> = <span class="string">"hello"</span>;
22 <span class="keyword">let</span> <span class="variable" data-binding-hash="5695551762718493399" style="color: hsl(272,48%,45%);">x</span> = <span class="variable" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span>.<span class="text">to_string</span>();
23 <span class="keyword">let</span> <span class="variable" data-binding-hash="5435401749617022797" style="color: hsl(353,77%,74%);">y</span> = <span class="variable" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span>.<span class="text">to_string</span>();
24
25 <span class="keyword">let</span> <span class="variable" data-binding-hash="1903207544374197704" style="color: hsl(58,61%,61%);">x</span> = <span class="string">"other color please!"</span>;
26 <span class="keyword">let</span> <span class="variable" data-binding-hash="14878783531007968800" style="color: hsl(265,73%,83%);">y</span> = <span class="variable" data-binding-hash="1903207544374197704" style="color: hsl(58,61%,61%);">x</span>.<span class="text">to_string</span>();
27}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap
deleted file mode 100644
index e50003b3c..000000000
--- a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap
+++ /dev/null
@@ -1,192 +0,0 @@
1---
2created: "2019-05-25T11:24:53.486036Z"
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 8465336196764640996,
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 176272420896316891,
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 15061637676198917049,
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 14077410872302487760,
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 8379786015941272633,
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 5766414492220109266,
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__rainbow_highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__rainbow_highlighting.snap
deleted file mode 100644
index 84cd521a2..000000000
--- a/crates/ra_ide_api/src/snapshots/tests__rainbow_highlighting.snap
+++ /dev/null
@@ -1,128 +0,0 @@
1---
2created: "2019-05-25T11:21:56.117898Z"
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 5766414492220109266,
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 15975256018338854530,
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 176272420896316891,
48 ),
49 },
50 HighlightedRange {
51 range: [50; 55),
52 tag: "variable",
53 id: Some(
54 15975256018338854530,
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 15061637676198917049,
72 ),
73 },
74 HighlightedRange {
75 range: [81; 86),
76 tag: "variable",
77 id: Some(
78 15975256018338854530,
79 ),
80 },
81 HighlightedRange {
82 range: [87; 96),
83 tag: "text",
84 id: None,
85 },
86 HighlightedRange {
87 range: [105; 108),
88 tag: "keyword",
89 id: None,
90 },
91 HighlightedRange {
92 range: [109; 110),
93 tag: "variable",
94 id: Some(
95 1714508680417729339,
96 ),
97 },
98 HighlightedRange {
99 range: [113; 134),
100 tag: "string",
101 id: None,
102 },
103 HighlightedRange {
104 range: [140; 143),
105 tag: "keyword",
106 id: None,
107 },
108 HighlightedRange {
109 range: [144; 145),
110 tag: "variable",
111 id: Some(
112 15953336624848413466,
113 ),
114 },
115 HighlightedRange {
116 range: [148; 149),
117 tag: "variable",
118 id: Some(
119 1714508680417729339,
120 ),
121 },
122 HighlightedRange {
123 range: [150; 159),
124 tag: "text",
125 id: None,
126 },
127 ],
128)
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs
index 407fcda4a..8981c85e6 100644
--- a/crates/ra_ide_api/src/syntax_highlighting.rs
+++ b/crates/ra_ide_api/src/syntax_highlighting.rs
@@ -10,7 +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 pub binding_hash: Option<u64>,
14} 14}
15 15
16fn is_control_keyword(kind: SyntaxKind) -> bool { 16fn is_control_keyword(kind: SyntaxKind) -> bool {
@@ -30,15 +30,18 @@ fn is_control_keyword(kind: SyntaxKind) -> bool {
30 30
31pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> { 31pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
32 let _p = profile("highlight"); 32 let _p = profile("highlight");
33
34 let source_file = db.parse(file_id); 33 let source_file = db.parse(file_id);
35 34
36 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { 35 fn calc_binding_hash(file_id: FileId, text: &SmolStr, shadow_count: u32) -> u64 {
37 use std::{collections::hash_map::DefaultHasher, hash::Hasher}; 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 }
38 43
39 let mut hasher = DefaultHasher::new(); 44 hash((file_id, text, shadow_count))
40 x.hash(&mut hasher);
41 hasher.finish()
42 } 45 }
43 46
44 // Visited nodes to handle highlighting priorities 47 // Visited nodes to handle highlighting priorities
@@ -50,66 +53,92 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
50 if highlighted.contains(&node) { 53 if highlighted.contains(&node) {
51 continue; 54 continue;
52 } 55 }
53 let (tag, id) = match node.kind() { 56 let mut binding_hash = None;
54 COMMENT => ("comment", None), 57 let tag = match node.kind() {
55 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => ("string", None), 58 COMMENT => "comment",
56 ATTR => ("attribute", None), 59 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string",
60 ATTR => "attribute",
57 NAME_REF => { 61 NAME_REF => {
58 if let Some(name_ref) = node.as_ast_node::<ast::NameRef>() { 62 if let Some(name_ref) = node.as_node().and_then(ast::NameRef::cast) {
59 use crate::name_ref_kind::{classify_name_ref, NameRefKind::*}; 63 use crate::name_ref_kind::{classify_name_ref, NameRefKind::*};
60 use hir::{ModuleDef, ImplItem}; 64 use hir::{ModuleDef, ImplItem};
61 65
62 // FIXME: try to reuse the SourceAnalyzers 66 // FIXME: try to reuse the SourceAnalyzers
63 let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); 67 let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
64 match classify_name_ref(db, &analyzer, name_ref) { 68 match classify_name_ref(db, &analyzer, name_ref) {
65 Some(Method(_)) => ("function", None), 69 Some(Method(_)) => "function",
66 Some(Macro(_)) => ("macro", None), 70 Some(Macro(_)) => "macro",
67 Some(FieldAccess(_)) => ("field", None), 71 Some(FieldAccess(field)) => {
68 Some(AssocItem(ImplItem::Method(_))) => ("function", None), 72 let (hir_file_id, src) = field.source(db);
69 Some(AssocItem(ImplItem::Const(_))) => ("constant", None), 73 if let hir::FieldSource::Named(name) = src {
70 Some(AssocItem(ImplItem::TypeAlias(_))) => ("type", None), 74 let text = name.syntax().text().to_smol_string();
71 Some(Def(ModuleDef::Module(_))) => ("module", None), 75 let shadow_count = 0; // potentially even from different file
72 Some(Def(ModuleDef::Function(_))) => ("function", None), 76 binding_hash = Some(calc_binding_hash(hir_file_id.original_file(db), &text, shadow_count));
73 Some(Def(ModuleDef::Struct(_))) => ("type", None), 77 }
74 Some(Def(ModuleDef::Union(_))) => ("type", None), 78
75 Some(Def(ModuleDef::Enum(_))) => ("type", None), 79 "field"
76 Some(Def(ModuleDef::EnumVariant(_))) => ("constant", None), 80 },
77 Some(Def(ModuleDef::Const(_))) => ("constant", None), 81 Some(AssocItem(ImplItem::Method(_))) => "function",
78 Some(Def(ModuleDef::Static(_))) => ("constant", None), 82 Some(AssocItem(ImplItem::Const(_))) => "constant",
79 Some(Def(ModuleDef::Trait(_))) => ("type", None), 83 Some(AssocItem(ImplItem::TypeAlias(_))) => "type",
80 Some(Def(ModuleDef::TypeAlias(_))) => ("type", None), 84 Some(Def(ModuleDef::Module(_))) => "module",
81 Some(SelfType(_)) => ("type", None), 85 Some(Def(ModuleDef::Function(_))) => "function",
82 Some(Pat(ptr)) => ("variable", Some(hash({ 86 Some(Def(ModuleDef::Struct(_))) => "type",
83 let text = ptr.syntax_node_ptr().to_node(&source_file.syntax()).text().to_smol_string(); 87 Some(Def(ModuleDef::Union(_))) => "type",
84 let shadow_count = bindings_shadow_count.entry(text.clone()).or_default(); 88 Some(Def(ModuleDef::Enum(_))) => "type",
85 (text, shadow_count) 89 Some(Def(ModuleDef::EnumVariant(_))) => "constant",
86 }))), 90 Some(Def(ModuleDef::Const(_))) => "constant",
87 Some(SelfParam(_)) => ("type", None), 91 Some(Def(ModuleDef::Static(_))) => "constant",
88 Some(GenericParam(_)) => ("type", None), 92 Some(Def(ModuleDef::Trait(_))) => "type",
89 None => ("text", None), 93 Some(Def(ModuleDef::TypeAlias(_))) => "type",
94 Some(SelfType(_)) => "type",
95 Some(Pat(ptr)) => {
96 binding_hash = Some({
97 let text = ptr.syntax_node_ptr().to_node(&source_file.syntax()).text().to_smol_string();
98 let shadow_count = bindings_shadow_count.entry(text.clone()).or_default();
99 calc_binding_hash(file_id, &text, *shadow_count)
100 });
101
102 "variable"
103 },
104 Some(SelfParam(_)) => "type",
105 Some(GenericParam(_)) => "type",
106 None => "text",
90 } 107 }
91 } else { 108 } else {
92 ("text", None) 109 "text"
93 } 110 }
94 } 111 }
95 NAME => { 112 NAME => {
96 if let Some(name) = node.as_ast_node::<ast::Name>() { 113 if let Some(name) = node.as_node().and_then(ast::Name::cast) {
97 ("variable", Some(hash({ 114 if name.syntax().ancestors().any(|x| ast::BindPat::cast(x).is_some()) {
98 let text = name.syntax().text().to_smol_string(); 115 binding_hash = Some({
99 let shadow_count = bindings_shadow_count.entry(text.clone()).or_insert(1); 116 let text = name.syntax().text().to_smol_string();
100 *shadow_count += 1; 117 let shadow_count = bindings_shadow_count.entry(text.clone()).or_insert(0);
101 (text, shadow_count) 118 *shadow_count += 1;
102 }))) 119 calc_binding_hash(file_id, &text, *shadow_count)
120 });
121 "variable"
122 } else if name.syntax().ancestors().any(|x| ast::NamedFieldDef::cast(x).is_some()) {
123 binding_hash = Some({
124 let text = name.syntax().text().to_smol_string();
125 let shadow_count = 0;
126 calc_binding_hash(file_id, &text, shadow_count)
127 });
128 "variable"
129 } else {
130 "function"
131 }
103 } else { 132 } else {
104 ("text", None) 133 "text"
105 } 134 }
106 } 135 }
107 TYPE_ALIAS_DEF | TYPE_ARG | TYPE_PARAM => ("type", None), 136 TYPE_ALIAS_DEF | TYPE_ARG | TYPE_PARAM => "type",
108 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => ("literal", None), 137 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal",
109 LIFETIME => ("parameter", None), 138 LIFETIME => "parameter",
110 T![unsafe] => ("keyword.unsafe", None), 139 T![unsafe] => "keyword.unsafe",
111 k if is_control_keyword(k) => ("keyword.control", None), 140 k if is_control_keyword(k) => "keyword.control",
112 k if k.is_keyword() => ("keyword", None), 141 k if k.is_keyword() => "keyword",
113 _ => { 142 _ => {
114 // let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); 143 // let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
115 if let Some(macro_call) = node.as_node().and_then(ast::MacroCall::cast) { 144 if let Some(macro_call) = node.as_node().and_then(ast::MacroCall::cast) {
@@ -128,7 +157,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
128 res.push(HighlightedRange { 157 res.push(HighlightedRange {
129 range: TextRange::from_to(range_start, range_end), 158 range: TextRange::from_to(range_start, range_end),
130 tag: "macro", 159 tag: "macro",
131 id: None, 160 binding_hash: None,
132 }) 161 })
133 } 162 }
134 } 163 }
@@ -137,14 +166,24 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
137 continue; 166 continue;
138 } 167 }
139 }; 168 };
140 res.push(HighlightedRange { range: node.range(), tag, id }) 169 res.push(HighlightedRange { range: node.range(), tag, binding_hash })
141 } 170 }
142 res 171 res
143} 172}
144 173
145pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId) -> String { 174pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
146 let source_file = db.parse(file_id); 175 let source_file = db.parse(file_id);
147 176
177 fn rainbowify(seed: u64) -> String {
178 use rand::prelude::*;
179 let mut rng = SmallRng::seed_from_u64(seed);
180 format!("hsl({h},{s}%,{l}%)",
181 h = rng.gen_range::<u16, _, _>(0, 361),
182 s = rng.gen_range::<u16, _, _>(42, 99),
183 l = rng.gen_range::<u16, _, _>(40, 91),
184 )
185 }
186
148 let mut ranges = highlight(db, file_id); 187 let mut ranges = highlight(db, file_id);
149 ranges.sort_by_key(|it| it.range.start()); 188 ranges.sort_by_key(|it| it.range.start());
150 // quick non-optimal heuristic to intersect token ranges and highlighted ranges 189 // quick non-optimal heuristic to intersect token ranges and highlighted ranges
@@ -166,16 +205,20 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId) -> String {
166 } 205 }
167 } 206 }
168 let text = html_escape(&token.text()); 207 let text = html_escape(&token.text());
169 let classes = could_intersect 208 let ranges = could_intersect
170 .iter() 209 .iter()
171 .filter(|it| token.range().is_subrange(&it.range)) 210 .filter(|it| token.range().is_subrange(&it.range))
172 .map(|it| it.tag)
173 .collect::<Vec<_>>(); 211 .collect::<Vec<_>>();
174 if classes.is_empty() { 212 if ranges.is_empty() {
175 buf.push_str(&text); 213 buf.push_str(&text);
176 } else { 214 } else {
177 let classes = classes.join(" "); 215 let classes = ranges.iter().map(|x| x.tag).collect::<Vec<_>>().join(" ");
178 buf.push_str(&format!("<span class=\"{}\">{}</span>", classes, text)); 216 let binding_hash = ranges.first().and_then(|x| x.binding_hash);
217 let color = match (rainbow, binding_hash) {
218 (true, Some(hash)) => format!(" data-binding-hash=\"{}\" style=\"color: {};\"", hash, rainbowify(hash)),
219 _ => "".into()
220 };
221 buf.push_str(&format!("<span class=\"{}\"{}>{}</span>", classes, color, text));
179 } 222 }
180 } 223 }
181 buf.push_str("</code></pre>"); 224 buf.push_str("</code></pre>");
@@ -189,11 +232,8 @@ fn html_escape(text: &str) -> String {
189 232
190const STYLE: &str = " 233const STYLE: &str = "
191<style> 234<style>
192pre { 235body { margin: 0; }
193 color: #DCDCCC; 236pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
194 background-color: #3F3F3F;
195 font-size: 22px;
196}
197 237
198.comment { color: #7F9F7F; } 238.comment { color: #7F9F7F; }
199.string { color: #CC9393; } 239.string { color: #CC9393; }
@@ -208,7 +248,6 @@ pre {
208.keyword { color: #F0DFAF; } 248.keyword { color: #F0DFAF; }
209.keyword\\.unsafe { color: #F0DFAF; font-weight: bold; } 249.keyword\\.unsafe { color: #F0DFAF; font-weight: bold; }
210.keyword\\.control { color: #DC8CC3; } 250.keyword\\.control { color: #DC8CC3; }
211
212</style> 251</style>
213"; 252";
214 253
@@ -241,12 +280,12 @@ fn main() {
241 } 280 }
242 unsafe { vec.set_len(0); } 281 unsafe { vec.set_len(0); }
243} 282}
244"#, 283"#.trim(),
245 ); 284 );
246 let dst_file = project_dir().join("crates/ra_ide_api/src/snapshots/highlighting.html"); 285 let dst_file = project_dir().join("crates/ra_ide_api/src/snapshots/highlighting.html");
247 let actual_html = &analysis.highlight_as_html(file_id).unwrap(); 286 let actual_html = &analysis.highlight_as_html(file_id).unwrap();
248 let expected_html = &read_text(&dst_file); 287 let expected_html = &read_text(&dst_file);
249 // std::fs::write(dst_file, &actual_html).unwrap(); 288 std::fs::write(dst_file, &actual_html).unwrap();
250 assert_eq_text!(expected_html, actual_html); 289 assert_eq_text!(expected_html, actual_html);
251 } 290 }
252 291
@@ -261,9 +300,13 @@ fn main() {
261 300
262 let x = "other color please!"; 301 let x = "other color please!";
263 let y = x.to_string(); 302 let y = x.to_string();
264}"#, 303}
304"#.trim(),
265 ); 305 );
266 let result = analysis.highlight(file_id); 306 let dst_file = project_dir().join("crates/ra_ide_api/src/snapshots/rainbow_highlighting.html");
267 assert_debug_snapshot_matches!("rainbow_highlighting", result); 307 let actual_html = &analysis.highlight_as_html(file_id).unwrap();
308 let expected_html = &read_text(&dst_file);
309 std::fs::write(dst_file, &actual_html).unwrap();
310 assert_eq_text!(expected_html, actual_html);
268 } 311 }
269} 312}