aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-02-27 08:32:44 +0000
committerGitHub <[email protected]>2020-02-27 08:32:44 +0000
commit2180591593b4285b1765eb105c285c82727b2a6f (patch)
treeea276adbcf0c3fac4b457a2af49a2a7782585b87
parentac52a4109f6ada3241d84fd89ad28420bdfad335 (diff)
parenta8e68ff814801a72ecac6cd918a3e7fbb25d11c0 (diff)
Merge #3335
3335: Refactor highlighting a bit r=matklad a=matklad bors r+ Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r--crates/ra_ide/src/lib.rs4
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html52
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html14
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs197
-rw-r--r--crates/ra_ide/src/syntax_highlighting/highlight.rs163
-rw-r--r--crates/ra_ide/src/syntax_highlighting/highlight_tag.rs43
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs104
-rw-r--r--crates/rust-analyzer/src/caps.rs7
-rw-r--r--crates/rust-analyzer/src/conv.rs111
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs22
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs38
-rw-r--r--editors/code/package.json28
12 files changed, 478 insertions, 305 deletions
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index d74d32453..d509de14e 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -74,7 +74,9 @@ pub use crate::{
74 runnables::{Runnable, RunnableKind, TestId}, 74 runnables::{Runnable, RunnableKind, TestId},
75 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 75 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
76 ssr::SsrError, 76 ssr::SsrError,
77 syntax_highlighting::{HighlightTag, HighlightedRange}, 77 syntax_highlighting::{
78 Highlight, HighlightModifier, HighlightModifiers, HighlightTag, HighlightedRange,
79 },
78}; 80};
79 81
80pub use hir::Documentation; 82pub use hir::Documentation;
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index a02dbaf2f..51851763e 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -10,29 +10,29 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.parameter { color: #94BFF3; } 10.parameter { color: #94BFF3; }
11.text { color: #DCDCCC; } 11.text { color: #DCDCCC; }
12.type { color: #7CB8BB; } 12.type { color: #7CB8BB; }
13.type\.builtin { color: #8CD0D3; } 13.type.builtin { color: #8CD0D3; }
14.type\.param { color: #20999D; } 14.type.param { color: #20999D; }
15.attribute { color: #94BFF3; } 15.attribute { color: #94BFF3; }
16.literal { color: #BFEBBF; } 16.literal { color: #BFEBBF; }
17.literal\.numeric { color: #6A8759; } 17.literal.numeric { color: #6A8759; }
18.macro { color: #94BFF3; } 18.macro { color: #94BFF3; }
19.module { color: #AFD8AF; } 19.module { color: #AFD8AF; }
20.variable { color: #DCDCCC; } 20.variable { color: #DCDCCC; }
21.variable\.mut { color: #DCDCCC; text-decoration: underline; } 21.variable.mut { color: #DCDCCC; text-decoration: underline; }
22 22
23.keyword { color: #F0DFAF; } 23.keyword { color: #F0DFAF; }
24.keyword\.unsafe { color: #DFAF8F; } 24.keyword.unsafe { color: #DFAF8F; }
25.keyword\.control { color: #F0DFAF; font-weight: bold; } 25.keyword.control { color: #F0DFAF; font-weight: bold; }
26</style> 26</style>
27<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> 27<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>
28<span class="keyword">struct</span> <span class="type">Foo</span> { 28<span class="keyword">struct</span> <span class="type">Foo</span> {
29 <span class="keyword">pub</span> <span class="field">x</span>: <span class="type.builtin">i32</span>, 29 <span class="keyword">pub</span> <span class="field">x</span>: <span class="type builtin">i32</span>,
30 <span class="keyword">pub</span> <span class="field">y</span>: <span class="type.builtin">i32</span>, 30 <span class="keyword">pub</span> <span class="field">y</span>: <span class="type builtin">i32</span>,
31} 31}
32 32
33<span class="keyword">fn</span> <span class="function">foo</span>&lt;<span class="type.param">T</span>&gt;() -&gt; <span class="type.param">T</span> { 33<span class="keyword">fn</span> <span class="function">foo</span>&lt;<span class="type param">T</span>&gt;() -&gt; <span class="type param">T</span> {
34 <span class="macro">unimplemented</span><span class="macro">!</span>(); 34 <span class="macro">unimplemented</span><span class="macro">!</span>();
35 <span class="function">foo</span>::&lt;<span class="type.builtin">i32</span>&gt;(); 35 <span class="function">foo</span>::&lt;<span class="type builtin">i32</span>&gt;();
36} 36}
37 37
38<span class="macro">macro_rules</span><span class="macro">!</span> def_fn { 38<span class="macro">macro_rules</span><span class="macro">!</span> def_fn {
@@ -40,33 +40,33 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
40} 40}
41 41
42<span class="macro">def_fn</span><span class="macro">!</span>{ 42<span class="macro">def_fn</span><span class="macro">!</span>{
43 <span class="keyword">fn</span> <span class="function">bar</span>() -&gt; <span class="type.builtin">u32</span> { 43 <span class="keyword">fn</span> <span class="function">bar</span>() -&gt; <span class="type builtin">u32</span> {
44 <span class="literal.numeric">100</span> 44 <span class="literal numeric">100</span>
45 } 45 }
46} 46}
47 47
48<span class="comment">// comment</span> 48<span class="comment">// comment</span>
49<span class="keyword">fn</span> <span class="function">main</span>() { 49<span class="keyword">fn</span> <span class="function">main</span>() {
50 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal.numeric">92</span>); 50 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal numeric">92</span>);
51 51
52 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">vec</span> = Vec::new(); 52 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable mutable">vec</span> = Vec::new();
53 <span class="keyword.control">if</span> <span class="keyword">true</span> { 53 <span class="keyword control">if</span> <span class="keyword">true</span> {
54 <span class="keyword">let</span> <span class="variable">x</span> = <span class="literal.numeric">92</span>; 54 <span class="keyword">let</span> <span class="variable">x</span> = <span class="literal numeric">92</span>;
55 <span class="variable.mut">vec</span>.push(<span class="type">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="literal.numeric">1</span> }); 55 <span class="variable mutable">vec</span>.push(<span class="type">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="literal numeric">1</span> });
56 } 56 }
57 <span class="keyword.unsafe">unsafe</span> { <span class="variable.mut">vec</span>.set_len(<span class="literal.numeric">0</span>); } 57 <span class="keyword unsafe">unsafe</span> { <span class="variable mutable">vec</span>.set_len(<span class="literal numeric">0</span>); }
58 58
59 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">x</span> = <span class="literal.numeric">42</span>; 59 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable mutable">x</span> = <span class="literal numeric">42</span>;
60 <span class="keyword">let</span> <span class="variable.mut">y</span> = &<span class="keyword">mut</span> <span class="variable.mut">x</span>; 60 <span class="keyword">let</span> <span class="variable mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>;
61 <span class="keyword">let</span> <span class="variable">z</span> = &<span class="variable.mut">y</span>; 61 <span class="keyword">let</span> <span class="variable">z</span> = &<span class="variable mutable">y</span>;
62 62
63 <span class="variable.mut">y</span>; 63 <span class="variable mutable">y</span>;
64} 64}
65 65
66<span class="keyword">enum</span> <span class="type">E</span>&lt;<span class="type.param">X</span>&gt; { 66<span class="keyword">enum</span> <span class="type">E</span>&lt;<span class="type param">X</span>&gt; {
67 <span class="constant">V</span>(<span class="type.param">X</span>) 67 <span class="constant">V</span>(<span class="type param">X</span>)
68} 68}
69 69
70<span class="keyword">impl</span>&lt;<span class="type.param">X</span>&gt; <span class="type">E</span>&lt;<span class="type.param">X</span>&gt; { 70<span class="keyword">impl</span>&lt;<span class="type param">X</span>&gt; <span class="type">E</span>&lt;<span class="type param">X</span>&gt; {
71 <span class="keyword">fn</span> <span class="function">new</span>&lt;<span class="type.param">T</span>&gt;() -&gt; <span class="type">E</span>&lt;<span class="type.param">T</span>&gt; {} 71 <span class="keyword">fn</span> <span class="function">new</span>&lt;<span class="type param">T</span>&gt;() -&gt; <span class="type">E</span>&lt;<span class="type param">T</span>&gt; {}
72}</code></pre> \ No newline at end of file 72}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index d6a7da953..1f869867f 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -10,19 +10,19 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.parameter { color: #94BFF3; } 10.parameter { color: #94BFF3; }
11.text { color: #DCDCCC; } 11.text { color: #DCDCCC; }
12.type { color: #7CB8BB; } 12.type { color: #7CB8BB; }
13.type\.builtin { color: #8CD0D3; } 13.type.builtin { color: #8CD0D3; }
14.type\.param { color: #20999D; } 14.type.param { color: #20999D; }
15.attribute { color: #94BFF3; } 15.attribute { color: #94BFF3; }
16.literal { color: #BFEBBF; } 16.literal { color: #BFEBBF; }
17.literal\.numeric { color: #6A8759; } 17.literal.numeric { color: #6A8759; }
18.macro { color: #94BFF3; } 18.macro { color: #94BFF3; }
19.module { color: #AFD8AF; } 19.module { color: #AFD8AF; }
20.variable { color: #DCDCCC; } 20.variable { color: #DCDCCC; }
21.variable\.mut { color: #DCDCCC; text-decoration: underline; } 21.variable.mut { color: #DCDCCC; text-decoration: underline; }
22 22
23.keyword { color: #F0DFAF; } 23.keyword { color: #F0DFAF; }
24.keyword\.unsafe { color: #DFAF8F; } 24.keyword.unsafe { color: #DFAF8F; }
25.keyword\.control { color: #F0DFAF; font-weight: bold; } 25.keyword.control { color: #F0DFAF; font-weight: bold; }
26</style> 26</style>
27<pre><code><span class="keyword">fn</span> <span class="function">main</span>() { 27<pre><code><span class="keyword">fn</span> <span class="function">main</span>() {
28 <span class="keyword">let</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string">"hello"</span>; 28 <span class="keyword">let</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string">"hello"</span>;
@@ -34,5 +34,5 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
34} 34}
35 35
36<span class="keyword">fn</span> <span class="function">bar</span>() { 36<span class="keyword">fn</span> <span class="function">bar</span>() {
37 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string">"hello"</span>; 37 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable mutable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string">"hello"</span>;
38}</code></pre> \ No newline at end of file 38}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index d422930bf..4e95b9ce5 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -1,9 +1,9 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3mod highlight_tag; 3mod highlight;
4mod html;
4 5
5use hir::{Name, Semantics}; 6use hir::{Name, Semantics};
6use ra_db::SourceDatabase;
7use ra_ide_db::{ 7use ra_ide_db::{
8 defs::{classify_name, NameDefinition}, 8 defs::{classify_name, NameDefinition},
9 RootDatabase, 9 RootDatabase,
@@ -17,12 +17,14 @@ use rustc_hash::FxHashMap;
17 17
18use crate::{references::classify_name_ref, FileId}; 18use crate::{references::classify_name_ref, FileId};
19 19
20pub use highlight_tag::HighlightTag; 20pub use highlight::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag};
21
22pub(crate) use html::highlight_as_html;
21 23
22#[derive(Debug)] 24#[derive(Debug)]
23pub struct HighlightedRange { 25pub struct HighlightedRange {
24 pub range: TextRange, 26 pub range: TextRange,
25 pub tag: HighlightTag, 27 pub highlight: Highlight,
26 pub binding_hash: Option<u64>, 28 pub binding_hash: Option<u64>,
27} 29}
28 30
@@ -79,33 +81,33 @@ pub(crate) fn highlight(
79 if let Some(range) = highlight_macro(node) { 81 if let Some(range) = highlight_macro(node) {
80 res.push(HighlightedRange { 82 res.push(HighlightedRange {
81 range, 83 range,
82 tag: HighlightTag::MACRO, 84 highlight: HighlightTag::Macro.into(),
83 binding_hash: None, 85 binding_hash: None,
84 }); 86 });
85 } 87 }
86 } 88 }
87 _ if in_macro_call.is_some() => { 89 _ if in_macro_call.is_some() => {
88 if let Some(token) = node.as_token() { 90 if let Some(token) = node.as_token() {
89 if let Some((tag, binding_hash)) = highlight_token_tree( 91 if let Some((highlight, binding_hash)) = highlight_token_tree(
90 &sema, 92 &sema,
91 &mut bindings_shadow_count, 93 &mut bindings_shadow_count,
92 token.clone(), 94 token.clone(),
93 ) { 95 ) {
94 res.push(HighlightedRange { 96 res.push(HighlightedRange {
95 range: node.text_range(), 97 range: node.text_range(),
96 tag, 98 highlight,
97 binding_hash, 99 binding_hash,
98 }); 100 });
99 } 101 }
100 } 102 }
101 } 103 }
102 _ => { 104 _ => {
103 if let Some((tag, binding_hash)) = 105 if let Some((highlight, binding_hash)) =
104 highlight_node(&sema, &mut bindings_shadow_count, node.clone()) 106 highlight_node(&sema, &mut bindings_shadow_count, node.clone())
105 { 107 {
106 res.push(HighlightedRange { 108 res.push(HighlightedRange {
107 range: node.text_range(), 109 range: node.text_range(),
108 tag, 110 highlight,
109 binding_hash, 111 binding_hash,
110 }); 112 });
111 } 113 }
@@ -150,7 +152,7 @@ fn highlight_token_tree(
150 sema: &Semantics<RootDatabase>, 152 sema: &Semantics<RootDatabase>,
151 bindings_shadow_count: &mut FxHashMap<Name, u32>, 153 bindings_shadow_count: &mut FxHashMap<Name, u32>,
152 token: SyntaxToken, 154 token: SyntaxToken,
153) -> Option<(HighlightTag, Option<u64>)> { 155) -> Option<(Highlight, Option<u64>)> {
154 if token.parent().kind() != TOKEN_TREE { 156 if token.parent().kind() != TOKEN_TREE {
155 return None; 157 return None;
156 } 158 }
@@ -171,19 +173,21 @@ fn highlight_node(
171 sema: &Semantics<RootDatabase>, 173 sema: &Semantics<RootDatabase>,
172 bindings_shadow_count: &mut FxHashMap<Name, u32>, 174 bindings_shadow_count: &mut FxHashMap<Name, u32>,
173 node: SyntaxElement, 175 node: SyntaxElement,
174) -> Option<(HighlightTag, Option<u64>)> { 176) -> Option<(Highlight, Option<u64>)> {
175 let db = sema.db; 177 let db = sema.db;
176 let mut binding_hash = None; 178 let mut binding_hash = None;
177 let tag = match node.kind() { 179 let highlight: Highlight = match node.kind() {
178 FN_DEF => { 180 FN_DEF => {
179 bindings_shadow_count.clear(); 181 bindings_shadow_count.clear();
180 return None; 182 return None;
181 } 183 }
182 COMMENT => HighlightTag::LITERAL_COMMENT, 184 COMMENT => HighlightTag::Comment.into(),
183 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::LITERAL_STRING, 185 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::LiteralString.into(),
184 ATTR => HighlightTag::LITERAL_ATTRIBUTE, 186 ATTR => HighlightTag::Attribute.into(),
185 // Special-case field init shorthand 187 // Special-case field init shorthand
186 NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => HighlightTag::FIELD, 188 NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => {
189 HighlightTag::Field.into()
190 }
187 NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => return None, 191 NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => return None,
188 NAME_REF => { 192 NAME_REF => {
189 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); 193 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
@@ -217,26 +221,30 @@ fn highlight_node(
217 221
218 match name_kind { 222 match name_kind {
219 Some(name_kind) => highlight_name(db, name_kind), 223 Some(name_kind) => highlight_name(db, name_kind),
220 None => name.syntax().parent().map_or(HighlightTag::FUNCTION, |x| match x.kind() { 224 None => name.syntax().parent().map_or(HighlightTag::Function.into(), |x| {
221 STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => HighlightTag::TYPE, 225 match x.kind() {
222 TYPE_PARAM => HighlightTag::TYPE_PARAM, 226 STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => {
223 RECORD_FIELD_DEF => HighlightTag::FIELD, 227 HighlightTag::Type.into()
224 _ => HighlightTag::FUNCTION, 228 }
229 TYPE_PARAM => HighlightTag::TypeParam.into(),
230 RECORD_FIELD_DEF => HighlightTag::Field.into(),
231 _ => HighlightTag::Function.into(),
232 }
225 }), 233 }),
226 } 234 }
227 } 235 }
228 INT_NUMBER | FLOAT_NUMBER => HighlightTag::LITERAL_NUMERIC, 236 INT_NUMBER | FLOAT_NUMBER => HighlightTag::LiteralNumeric.into(),
229 BYTE => HighlightTag::LITERAL_BYTE, 237 BYTE => HighlightTag::LiteralByte.into(),
230 CHAR => HighlightTag::LITERAL_CHAR, 238 CHAR => HighlightTag::LiteralChar.into(),
231 LIFETIME => HighlightTag::TYPE_LIFETIME, 239 LIFETIME => HighlightTag::TypeLifetime.into(),
232 T![unsafe] => HighlightTag::KEYWORD_UNSAFE, 240 T![unsafe] => HighlightTag::Keyword | HighlightModifier::Unsafe,
233 k if is_control_keyword(k) => HighlightTag::KEYWORD_CONTROL, 241 k if is_control_keyword(k) => HighlightTag::Keyword | HighlightModifier::Control,
234 k if k.is_keyword() => HighlightTag::KEYWORD, 242 k if k.is_keyword() => HighlightTag::Keyword.into(),
235 243
236 _ => return None, 244 _ => return None,
237 }; 245 };
238 246
239 return Some((tag, binding_hash)); 247 return Some((highlight, binding_hash));
240 248
241 fn calc_binding_hash(name: &Name, shadow_count: u32) -> u64 { 249 fn calc_binding_hash(name: &Name, shadow_count: u32) -> u64 {
242 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { 250 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
@@ -251,123 +259,34 @@ fn highlight_node(
251 } 259 }
252} 260}
253 261
254pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { 262fn highlight_name(db: &RootDatabase, def: NameDefinition) -> Highlight {
255 let parse = db.parse(file_id);
256
257 fn rainbowify(seed: u64) -> String {
258 use rand::prelude::*;
259 let mut rng = SmallRng::seed_from_u64(seed);
260 format!(
261 "hsl({h},{s}%,{l}%)",
262 h = rng.gen_range::<u16, _, _>(0, 361),
263 s = rng.gen_range::<u16, _, _>(42, 99),
264 l = rng.gen_range::<u16, _, _>(40, 91),
265 )
266 }
267
268 let mut ranges = highlight(db, file_id, None);
269 ranges.sort_by_key(|it| it.range.start());
270 // quick non-optimal heuristic to intersect token ranges and highlighted ranges
271 let mut frontier = 0;
272 let mut could_intersect: Vec<&HighlightedRange> = Vec::new();
273
274 let mut buf = String::new();
275 buf.push_str(&STYLE);
276 buf.push_str("<pre><code>");
277 let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token());
278 for token in tokens {
279 could_intersect.retain(|it| token.text_range().start() <= it.range.end());
280 while let Some(r) = ranges.get(frontier) {
281 if r.range.start() <= token.text_range().end() {
282 could_intersect.push(r);
283 frontier += 1;
284 } else {
285 break;
286 }
287 }
288 let text = html_escape(&token.text());
289 let ranges = could_intersect
290 .iter()
291 .filter(|it| token.text_range().is_subrange(&it.range))
292 .collect::<Vec<_>>();
293 if ranges.is_empty() {
294 buf.push_str(&text);
295 } else {
296 let classes = ranges.iter().map(|x| x.tag.to_string()).collect::<Vec<_>>().join(" ");
297 let binding_hash = ranges.first().and_then(|x| x.binding_hash);
298 let color = match (rainbow, binding_hash) {
299 (true, Some(hash)) => format!(
300 " data-binding-hash=\"{}\" style=\"color: {};\"",
301 hash,
302 rainbowify(hash)
303 ),
304 _ => "".into(),
305 };
306 buf.push_str(&format!("<span class=\"{}\"{}>{}</span>", classes, color, text));
307 }
308 }
309 buf.push_str("</code></pre>");
310 buf
311}
312
313fn highlight_name(db: &RootDatabase, def: NameDefinition) -> HighlightTag {
314 match def { 263 match def {
315 NameDefinition::Macro(_) => HighlightTag::MACRO, 264 NameDefinition::Macro(_) => HighlightTag::Macro,
316 NameDefinition::StructField(_) => HighlightTag::FIELD, 265 NameDefinition::StructField(_) => HighlightTag::Field,
317 NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => HighlightTag::MODULE, 266 NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => HighlightTag::Module,
318 NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => HighlightTag::FUNCTION, 267 NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => HighlightTag::Function,
319 NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => HighlightTag::TYPE, 268 NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => HighlightTag::Type,
320 NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => HighlightTag::CONSTANT, 269 NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => HighlightTag::Constant,
321 NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => HighlightTag::CONSTANT, 270 NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => HighlightTag::Constant,
322 NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => HighlightTag::CONSTANT, 271 NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => HighlightTag::Constant,
323 NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => HighlightTag::TYPE, 272 NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => HighlightTag::Type,
324 NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => HighlightTag::TYPE, 273 NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => HighlightTag::Type,
325 NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => HighlightTag::TYPE_BUILTIN, 274 NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => {
326 NameDefinition::SelfType(_) => HighlightTag::TYPE_SELF, 275 return HighlightTag::Type | HighlightModifier::Builtin
327 NameDefinition::TypeParam(_) => HighlightTag::TYPE_PARAM, 276 }
277 NameDefinition::SelfType(_) => HighlightTag::TypeSelf,
278 NameDefinition::TypeParam(_) => HighlightTag::TypeParam,
328 NameDefinition::Local(local) => { 279 NameDefinition::Local(local) => {
280 let mut h = Highlight::new(HighlightTag::Variable);
329 if local.is_mut(db) || local.ty(db).is_mutable_reference() { 281 if local.is_mut(db) || local.ty(db).is_mutable_reference() {
330 HighlightTag::VARIABLE_MUT 282 h |= HighlightModifier::Mutable;
331 } else {
332 HighlightTag::VARIABLE
333 } 283 }
284 return h;
334 } 285 }
335 } 286 }
287 .into()
336} 288}
337 289
338//FIXME: like, real html escaping
339fn html_escape(text: &str) -> String {
340 text.replace("<", "&lt;").replace(">", "&gt;")
341}
342
343const STYLE: &str = "
344<style>
345body { margin: 0; }
346pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
347
348.comment { color: #7F9F7F; }
349.string { color: #CC9393; }
350.field { color: #94BFF3; }
351.function { color: #93E0E3; }
352.parameter { color: #94BFF3; }
353.text { color: #DCDCCC; }
354.type { color: #7CB8BB; }
355.type\\.builtin { color: #8CD0D3; }
356.type\\.param { color: #20999D; }
357.attribute { color: #94BFF3; }
358.literal { color: #BFEBBF; }
359.literal\\.numeric { color: #6A8759; }
360.macro { color: #94BFF3; }
361.module { color: #AFD8AF; }
362.variable { color: #DCDCCC; }
363.variable\\.mut { color: #DCDCCC; text-decoration: underline; }
364
365.keyword { color: #F0DFAF; }
366.keyword\\.unsafe { color: #DFAF8F; }
367.keyword\\.control { color: #F0DFAF; font-weight: bold; }
368</style>
369";
370
371#[cfg(test)] 290#[cfg(test)]
372mod tests { 291mod tests {
373 use std::fs; 292 use std::fs;
@@ -498,6 +417,6 @@ fn bar() {
498 }) 417 })
499 .unwrap(); 418 .unwrap();
500 419
501 assert_eq!(&highlights[0].tag.to_string(), "field"); 420 assert_eq!(&highlights[0].highlight.to_string(), "field");
502 } 421 }
503} 422}
diff --git a/crates/ra_ide/src/syntax_highlighting/highlight.rs b/crates/ra_ide/src/syntax_highlighting/highlight.rs
new file mode 100644
index 000000000..383c74c98
--- /dev/null
+++ b/crates/ra_ide/src/syntax_highlighting/highlight.rs
@@ -0,0 +1,163 @@
1//! Defines token tags we use for syntax highlighting.
2//! A tag is not unlike a CSS class.
3
4use std::{fmt, ops};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
7pub struct Highlight {
8 pub tag: HighlightTag,
9 pub modifiers: HighlightModifiers,
10}
11
12#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
13pub struct HighlightModifiers(u32);
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
16pub enum HighlightTag {
17 Field,
18 Function,
19 Module,
20 Constant,
21 Macro,
22 Variable,
23
24 Type,
25 TypeSelf,
26 TypeParam,
27 TypeLifetime,
28
29 LiteralByte,
30 LiteralNumeric,
31 LiteralChar,
32
33 Comment,
34 LiteralString,
35 Attribute,
36
37 Keyword,
38}
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
41#[repr(u8)]
42pub enum HighlightModifier {
43 Mutable = 0,
44 Unsafe,
45 /// Used with keywords like `if` and `break`.
46 Control,
47 Builtin,
48}
49
50impl HighlightTag {
51 fn as_str(self) -> &'static str {
52 match self {
53 HighlightTag::Field => "field",
54 HighlightTag::Function => "function",
55 HighlightTag::Module => "module",
56 HighlightTag::Constant => "constant",
57 HighlightTag::Macro => "macro",
58 HighlightTag::Variable => "variable",
59 HighlightTag::Type => "type",
60 HighlightTag::TypeSelf => "type.self",
61 HighlightTag::TypeParam => "type.param",
62 HighlightTag::TypeLifetime => "type.lifetime",
63 HighlightTag::LiteralByte => "literal.byte",
64 HighlightTag::LiteralNumeric => "literal.numeric",
65 HighlightTag::LiteralChar => "literal.char",
66 HighlightTag::Comment => "comment",
67 HighlightTag::LiteralString => "string",
68 HighlightTag::Attribute => "attribute",
69 HighlightTag::Keyword => "keyword",
70 }
71 }
72}
73
74impl fmt::Display for HighlightTag {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 fmt::Display::fmt(self.as_str(), f)
77 }
78}
79
80impl HighlightModifier {
81 const ALL: &'static [HighlightModifier] = &[
82 HighlightModifier::Mutable,
83 HighlightModifier::Unsafe,
84 HighlightModifier::Control,
85 HighlightModifier::Builtin,
86 ];
87
88 fn as_str(self) -> &'static str {
89 match self {
90 HighlightModifier::Mutable => "mutable",
91 HighlightModifier::Unsafe => "unsafe",
92 HighlightModifier::Control => "control",
93 HighlightModifier::Builtin => "builtin",
94 }
95 }
96
97 fn mask(self) -> u32 {
98 1 << (self as u32)
99 }
100}
101
102impl fmt::Display for HighlightModifier {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 fmt::Display::fmt(self.as_str(), f)
105 }
106}
107
108impl fmt::Display for Highlight {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 write!(f, "{}", self.tag)?;
111 for modifier in self.modifiers.iter() {
112 write!(f, ".{}", modifier)?
113 }
114 Ok(())
115 }
116}
117
118impl From<HighlightTag> for Highlight {
119 fn from(tag: HighlightTag) -> Highlight {
120 Highlight::new(tag)
121 }
122}
123
124impl Highlight {
125 pub(crate) fn new(tag: HighlightTag) -> Highlight {
126 Highlight { tag, modifiers: HighlightModifiers::default() }
127 }
128}
129
130impl ops::BitOr<HighlightModifier> for HighlightTag {
131 type Output = Highlight;
132
133 fn bitor(self, rhs: HighlightModifier) -> Highlight {
134 Highlight::new(self) | rhs
135 }
136}
137
138impl ops::BitOrAssign<HighlightModifier> for HighlightModifiers {
139 fn bitor_assign(&mut self, rhs: HighlightModifier) {
140 self.0 |= rhs.mask();
141 }
142}
143
144impl ops::BitOrAssign<HighlightModifier> for Highlight {
145 fn bitor_assign(&mut self, rhs: HighlightModifier) {
146 self.modifiers |= rhs;
147 }
148}
149
150impl ops::BitOr<HighlightModifier> for Highlight {
151 type Output = Highlight;
152
153 fn bitor(mut self, rhs: HighlightModifier) -> Highlight {
154 self |= rhs;
155 self
156 }
157}
158
159impl HighlightModifiers {
160 pub fn iter(self) -> impl Iterator<Item = HighlightModifier> {
161 HighlightModifier::ALL.iter().copied().filter(move |it| self.0 & it.mask() == it.mask())
162 }
163}
diff --git a/crates/ra_ide/src/syntax_highlighting/highlight_tag.rs b/crates/ra_ide/src/syntax_highlighting/highlight_tag.rs
deleted file mode 100644
index af1ac07b3..000000000
--- a/crates/ra_ide/src/syntax_highlighting/highlight_tag.rs
+++ /dev/null
@@ -1,43 +0,0 @@
1//! Defines token tags we use for syntax highlighting.
2//! A tag is not unlike a CSS class.
3
4use std::fmt;
5
6#[derive(Debug, PartialEq, Eq, Clone, Copy)]
7pub struct HighlightTag(&'static str);
8
9impl fmt::Display for HighlightTag {
10 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11 fmt::Display::fmt(self.0, f)
12 }
13}
14
15#[rustfmt::skip]
16impl HighlightTag {
17 pub const FIELD: HighlightTag = HighlightTag("field");
18 pub const FUNCTION: HighlightTag = HighlightTag("function");
19 pub const MODULE: HighlightTag = HighlightTag("module");
20 pub const CONSTANT: HighlightTag = HighlightTag("constant");
21 pub const MACRO: HighlightTag = HighlightTag("macro");
22
23 pub const VARIABLE: HighlightTag = HighlightTag("variable");
24 pub const VARIABLE_MUT: HighlightTag = HighlightTag("variable.mut");
25
26 pub const TYPE: HighlightTag = HighlightTag("type");
27 pub const TYPE_BUILTIN: HighlightTag = HighlightTag("type.builtin");
28 pub const TYPE_SELF: HighlightTag = HighlightTag("type.self");
29 pub const TYPE_PARAM: HighlightTag = HighlightTag("type.param");
30 pub const TYPE_LIFETIME: HighlightTag = HighlightTag("type.lifetime");
31
32 pub const LITERAL_BYTE: HighlightTag = HighlightTag("literal.byte");
33 pub const LITERAL_NUMERIC: HighlightTag = HighlightTag("literal.numeric");
34 pub const LITERAL_CHAR: HighlightTag = HighlightTag("literal.char");
35
36 pub const LITERAL_COMMENT: HighlightTag = HighlightTag("comment");
37 pub const LITERAL_STRING: HighlightTag = HighlightTag("string");
38 pub const LITERAL_ATTRIBUTE: HighlightTag = HighlightTag("attribute");
39
40 pub const KEYWORD: HighlightTag = HighlightTag("keyword");
41 pub const KEYWORD_UNSAFE: HighlightTag = HighlightTag("keyword.unsafe");
42 pub const KEYWORD_CONTROL: HighlightTag = HighlightTag("keyword.control");
43}
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
new file mode 100644
index 000000000..210d9a57b
--- /dev/null
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -0,0 +1,104 @@
1//! Renders a bit of code as HTML.
2
3use ra_db::SourceDatabase;
4use ra_syntax::AstNode;
5
6use crate::{FileId, HighlightedRange, RootDatabase};
7
8use super::highlight;
9
10pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
11 let parse = db.parse(file_id);
12
13 fn rainbowify(seed: u64) -> String {
14 use rand::prelude::*;
15 let mut rng = SmallRng::seed_from_u64(seed);
16 format!(
17 "hsl({h},{s}%,{l}%)",
18 h = rng.gen_range::<u16, _, _>(0, 361),
19 s = rng.gen_range::<u16, _, _>(42, 99),
20 l = rng.gen_range::<u16, _, _>(40, 91),
21 )
22 }
23
24 let mut ranges = highlight(db, file_id, None);
25 ranges.sort_by_key(|it| it.range.start());
26 // quick non-optimal heuristic to intersect token ranges and highlighted ranges
27 let mut frontier = 0;
28 let mut could_intersect: Vec<&HighlightedRange> = Vec::new();
29
30 let mut buf = String::new();
31 buf.push_str(&STYLE);
32 buf.push_str("<pre><code>");
33 let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token());
34 for token in tokens {
35 could_intersect.retain(|it| token.text_range().start() <= it.range.end());
36 while let Some(r) = ranges.get(frontier) {
37 if r.range.start() <= token.text_range().end() {
38 could_intersect.push(r);
39 frontier += 1;
40 } else {
41 break;
42 }
43 }
44 let text = html_escape(&token.text());
45 let ranges = could_intersect
46 .iter()
47 .filter(|it| token.text_range().is_subrange(&it.range))
48 .collect::<Vec<_>>();
49 if ranges.is_empty() {
50 buf.push_str(&text);
51 } else {
52 let classes = ranges
53 .iter()
54 .map(|it| it.highlight.to_string().replace('.', " "))
55 .collect::<Vec<_>>()
56 .join(" ");
57 let binding_hash = ranges.first().and_then(|x| x.binding_hash);
58 let color = match (rainbow, binding_hash) {
59 (true, Some(hash)) => format!(
60 " data-binding-hash=\"{}\" style=\"color: {};\"",
61 hash,
62 rainbowify(hash)
63 ),
64 _ => "".into(),
65 };
66 buf.push_str(&format!("<span class=\"{}\"{}>{}</span>", classes, color, text));
67 }
68 }
69 buf.push_str("</code></pre>");
70 buf
71}
72
73//FIXME: like, real html escaping
74fn html_escape(text: &str) -> String {
75 text.replace("<", "&lt;").replace(">", "&gt;")
76}
77
78const STYLE: &str = "
79<style>
80body { margin: 0; }
81pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
82
83.comment { color: #7F9F7F; }
84.string { color: #CC9393; }
85.field { color: #94BFF3; }
86.function { color: #93E0E3; }
87.parameter { color: #94BFF3; }
88.text { color: #DCDCCC; }
89.type { color: #7CB8BB; }
90.type.builtin { color: #8CD0D3; }
91.type.param { color: #20999D; }
92.attribute { color: #94BFF3; }
93.literal { color: #BFEBBF; }
94.literal.numeric { color: #6A8759; }
95.macro { color: #94BFF3; }
96.module { color: #AFD8AF; }
97.variable { color: #DCDCCC; }
98.variable.mut { color: #DCDCCC; text-decoration: underline; }
99
100.keyword { color: #F0DFAF; }
101.keyword.unsafe { color: #DFAF8F; }
102.keyword.control { color: #F0DFAF; font-weight: bold; }
103</style>
104";
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index db82eeb1c..759bceb32 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -63,11 +63,8 @@ pub fn server_capabilities() -> ServerCapabilities {
63 semantic_tokens_provider: Some( 63 semantic_tokens_provider: Some(
64 SemanticTokensOptions { 64 SemanticTokensOptions {
65 legend: SemanticTokensLegend { 65 legend: SemanticTokensLegend {
66 token_types: semantic_tokens::supported_token_types().iter().cloned().collect(), 66 token_types: semantic_tokens::SUPPORTED_TYPES.iter().cloned().collect(),
67 token_modifiers: semantic_tokens::supported_token_modifiers() 67 token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.iter().cloned().collect(),
68 .iter()
69 .cloned()
70 .collect(),
71 }, 68 },
72 69
73 document_provider: Some(SemanticTokensDocumentProvider::Bool(true)), 70 document_provider: Some(SemanticTokensDocumentProvider::Bool(true)),
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index 5596967bd..b012f5dd5 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -10,14 +10,21 @@ use lsp_types::{
10}; 10};
11use ra_ide::{ 11use ra_ide::{
12 translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, 12 translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition,
13 FileRange, FileSystemEdit, Fold, FoldKind, HighlightTag, InsertTextFormat, LineCol, LineIndex, 13 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag,
14 NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit, 14 InsertTextFormat, LineCol, LineIndex, NavigationTarget, RangeInfo, ReferenceAccess, Severity,
15 SourceChange, SourceFileEdit,
15}; 16};
16use ra_syntax::{SyntaxKind, TextRange, TextUnit}; 17use ra_syntax::{SyntaxKind, TextRange, TextUnit};
17use ra_text_edit::{AtomTextEdit, TextEdit}; 18use ra_text_edit::{AtomTextEdit, TextEdit};
18use ra_vfs::LineEndings; 19use ra_vfs::LineEndings;
19 20
20use crate::{req, semantic_tokens, world::WorldSnapshot, Result}; 21use crate::{
22 req,
23 semantic_tokens::{self, ModifierSet, BUILTIN, CONSTANT, CONTROL, MUTABLE, UNSAFE},
24 world::WorldSnapshot,
25 Result,
26};
27use semantic_tokens::ATTRIBUTE;
21 28
22pub trait Conv { 29pub trait Conv {
23 type Output; 30 type Output;
@@ -303,74 +310,52 @@ impl ConvWith<&FoldConvCtx<'_>> for Fold {
303 } 310 }
304} 311}
305 312
306impl Conv for HighlightTag { 313impl Conv for Highlight {
307 type Output = (SemanticTokenType, Vec<SemanticTokenModifier>); 314 type Output = (u32, u32);
308
309 fn conv(self) -> (SemanticTokenType, Vec<SemanticTokenModifier>) {
310 let token_type: SemanticTokenType = match self {
311 HighlightTag::FIELD => SemanticTokenType::MEMBER,
312 HighlightTag::FUNCTION => SemanticTokenType::FUNCTION,
313 HighlightTag::MODULE => SemanticTokenType::NAMESPACE,
314 HighlightTag::CONSTANT => {
315 return (
316 SemanticTokenType::VARIABLE,
317 vec![SemanticTokenModifier::STATIC, SemanticTokenModifier::READONLY],
318 )
319 }
320 HighlightTag::MACRO => SemanticTokenType::MACRO,
321
322 HighlightTag::VARIABLE => {
323 return (SemanticTokenType::VARIABLE, vec![SemanticTokenModifier::READONLY])
324 }
325 HighlightTag::VARIABLE_MUT => SemanticTokenType::VARIABLE,
326 315
327 HighlightTag::TYPE => SemanticTokenType::TYPE, 316 fn conv(self) -> Self::Output {
328 HighlightTag::TYPE_BUILTIN => SemanticTokenType::TYPE, 317 let mut mods = ModifierSet::default();
329 HighlightTag::TYPE_SELF => { 318 let type_ = match self.tag {
330 return (SemanticTokenType::TYPE, vec![SemanticTokenModifier::REFERENCE]) 319 HighlightTag::Field => SemanticTokenType::MEMBER,
320 HighlightTag::Function => SemanticTokenType::FUNCTION,
321 HighlightTag::Module => SemanticTokenType::NAMESPACE,
322 HighlightTag::Constant => {
323 mods |= SemanticTokenModifier::STATIC;
324 mods |= SemanticTokenModifier::READONLY;
325 CONSTANT
331 } 326 }
332 HighlightTag::TYPE_PARAM => SemanticTokenType::TYPE_PARAMETER, 327 HighlightTag::Macro => SemanticTokenType::MACRO,
333 HighlightTag::TYPE_LIFETIME => { 328 HighlightTag::Variable => SemanticTokenType::VARIABLE,
334 return (SemanticTokenType::LABEL, vec![SemanticTokenModifier::REFERENCE]) 329 HighlightTag::Type => SemanticTokenType::TYPE,
330 HighlightTag::TypeSelf => {
331 mods |= SemanticTokenModifier::REFERENCE;
332 SemanticTokenType::TYPE
335 } 333 }
336 334 HighlightTag::TypeParam => SemanticTokenType::TYPE_PARAMETER,
337 HighlightTag::LITERAL_BYTE => SemanticTokenType::NUMBER, 335 HighlightTag::TypeLifetime => {
338 HighlightTag::LITERAL_NUMERIC => SemanticTokenType::NUMBER, 336 mods |= SemanticTokenModifier::REFERENCE;
339 HighlightTag::LITERAL_CHAR => SemanticTokenType::NUMBER, 337 SemanticTokenType::LABEL
340
341 HighlightTag::LITERAL_COMMENT => {
342 return (SemanticTokenType::COMMENT, vec![SemanticTokenModifier::DOCUMENTATION])
343 } 338 }
344 339 HighlightTag::LiteralByte => SemanticTokenType::NUMBER,
345 HighlightTag::LITERAL_STRING => SemanticTokenType::STRING, 340 HighlightTag::LiteralNumeric => SemanticTokenType::NUMBER,
346 HighlightTag::LITERAL_ATTRIBUTE => SemanticTokenType::KEYWORD, 341 HighlightTag::LiteralChar => SemanticTokenType::NUMBER,
347 342 HighlightTag::Comment => SemanticTokenType::COMMENT,
348 HighlightTag::KEYWORD => SemanticTokenType::KEYWORD, 343 HighlightTag::LiteralString => SemanticTokenType::STRING,
349 HighlightTag::KEYWORD_UNSAFE => SemanticTokenType::KEYWORD, 344 HighlightTag::Attribute => ATTRIBUTE,
350 HighlightTag::KEYWORD_CONTROL => SemanticTokenType::KEYWORD, 345 HighlightTag::Keyword => SemanticTokenType::KEYWORD,
351 unknown => panic!("Unknown semantic token: {}", unknown),
352 }; 346 };
353 347
354 (token_type, vec![]) 348 for modifier in self.modifiers.iter() {
355 } 349 let modifier = match modifier {
356} 350 HighlightModifier::Mutable => MUTABLE,
357 351 HighlightModifier::Unsafe => UNSAFE,
358impl Conv for (SemanticTokenType, Vec<SemanticTokenModifier>) { 352 HighlightModifier::Control => CONTROL,
359 type Output = (u32, u32); 353 HighlightModifier::Builtin => BUILTIN,
360 354 };
361 fn conv(self) -> Self::Output { 355 mods |= modifier;
362 let token_index =
363 semantic_tokens::supported_token_types().iter().position(|it| *it == self.0).unwrap();
364 let mut token_modifier_bitset = 0;
365 for modifier in self.1.iter() {
366 let modifier_index = semantic_tokens::supported_token_modifiers()
367 .iter()
368 .position(|it| it == modifier)
369 .unwrap();
370 token_modifier_bitset |= 1 << modifier_index;
371 } 356 }
372 357
373 (token_index as u32, token_modifier_bitset as u32) 358 (semantic_tokens::type_index(type_), mods.0)
374 } 359 }
375} 360}
376 361
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index e9f1c4f4b..9ed53169c 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -16,9 +16,9 @@ use lsp_types::{
16 CodeAction, CodeActionOrCommand, CodeActionResponse, CodeLens, Command, CompletionItem, 16 CodeAction, CodeActionOrCommand, CodeActionResponse, CodeLens, Command, CompletionItem,
17 Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, 17 Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange,
18 FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, 18 FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position,
19 PrepareRenameResponse, Range, RenameParams, SemanticTokenModifier, SemanticTokenType, 19 PrepareRenameResponse, Range, RenameParams, SemanticTokens, SemanticTokensParams,
20 SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, 20 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
21 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit, 21 TextDocumentIdentifier, TextEdit, WorkspaceEdit,
22}; 22};
23use ra_ide::{ 23use ra_ide::{
24 AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, 24 AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
@@ -954,7 +954,7 @@ fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>>
954 .into_iter() 954 .into_iter()
955 .map(|h| Decoration { 955 .map(|h| Decoration {
956 range: h.range.conv_with(&line_index), 956 range: h.range.conv_with(&line_index),
957 tag: h.tag.to_string(), 957 tag: h.highlight.to_string(),
958 binding_hash: h.binding_hash.map(|x| x.to_string()), 958 binding_hash: h.binding_hash.map(|x| x.to_string()),
959 }) 959 })
960 .collect(); 960 .collect();
@@ -1082,10 +1082,9 @@ pub fn handle_semantic_tokens(
1082 1082
1083 let mut builder = SemanticTokensBuilder::default(); 1083 let mut builder = SemanticTokensBuilder::default();
1084 1084
1085 for h in world.analysis().highlight(file_id)?.into_iter() { 1085 for highlight_range in world.analysis().highlight(file_id)?.into_iter() {
1086 let type_and_modifiers: (SemanticTokenType, Vec<SemanticTokenModifier>) = h.tag.conv(); 1086 let (token_type, token_modifiers) = highlight_range.highlight.conv();
1087 let (token_type, token_modifiers) = type_and_modifiers.conv(); 1087 builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers);
1088 builder.push(h.range.conv_with(&line_index), token_type, token_modifiers);
1089 } 1088 }
1090 1089
1091 let tokens = SemanticTokens { data: builder.build(), ..Default::default() }; 1090 let tokens = SemanticTokens { data: builder.build(), ..Default::default() };
@@ -1104,10 +1103,9 @@ pub fn handle_semantic_tokens_range(
1104 1103
1105 let mut builder = SemanticTokensBuilder::default(); 1104 let mut builder = SemanticTokensBuilder::default();
1106 1105
1107 for h in world.analysis().highlight_range(frange)?.into_iter() { 1106 for highlight_range in world.analysis().highlight_range(frange)?.into_iter() {
1108 let type_and_modifiers: (SemanticTokenType, Vec<SemanticTokenModifier>) = h.tag.conv(); 1107 let (token_type, token_modifiers) = highlight_range.highlight.conv();
1109 let (token_type, token_modifiers) = type_and_modifiers.conv(); 1108 builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers);
1110 builder.push(h.range.conv_with(&line_index), token_type, token_modifiers);
1111 } 1109 }
1112 1110
1113 let tokens = SemanticTokens { data: builder.build(), ..Default::default() }; 1111 let tokens = SemanticTokens { data: builder.build(), ..Default::default() };
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index e6a8eb146..d8362409d 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -1,8 +1,18 @@
1//! Semantic Tokens helpers 1//! Semantic Tokens helpers
2 2
3use std::ops;
4
3use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType}; 5use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType};
4 6
5const SUPPORTED_TYPES: &[SemanticTokenType] = &[ 7pub(crate) const ATTRIBUTE: SemanticTokenType = SemanticTokenType::new("attribute");
8pub(crate) const CONSTANT: SemanticTokenType = SemanticTokenType::new("constant");
9
10pub(crate) const MUTABLE: SemanticTokenModifier = SemanticTokenModifier::new("mutable");
11pub(crate) const UNSAFE: SemanticTokenModifier = SemanticTokenModifier::new("unsafe");
12pub(crate) const CONTROL: SemanticTokenModifier = SemanticTokenModifier::new("control");
13pub(crate) const BUILTIN: SemanticTokenModifier = SemanticTokenModifier::new("builtin");
14
15pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
6 SemanticTokenType::COMMENT, 16 SemanticTokenType::COMMENT,
7 SemanticTokenType::KEYWORD, 17 SemanticTokenType::KEYWORD,
8 SemanticTokenType::STRING, 18 SemanticTokenType::STRING,
@@ -23,9 +33,11 @@ const SUPPORTED_TYPES: &[SemanticTokenType] = &[
23 SemanticTokenType::VARIABLE, 33 SemanticTokenType::VARIABLE,
24 SemanticTokenType::PARAMETER, 34 SemanticTokenType::PARAMETER,
25 SemanticTokenType::LABEL, 35 SemanticTokenType::LABEL,
36 ATTRIBUTE,
37 CONSTANT,
26]; 38];
27 39
28const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[ 40pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
29 SemanticTokenModifier::DOCUMENTATION, 41 SemanticTokenModifier::DOCUMENTATION,
30 SemanticTokenModifier::DECLARATION, 42 SemanticTokenModifier::DECLARATION,
31 SemanticTokenModifier::DEFINITION, 43 SemanticTokenModifier::DEFINITION,
@@ -36,16 +48,20 @@ const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
36 SemanticTokenModifier::ASYNC, 48 SemanticTokenModifier::ASYNC,
37 SemanticTokenModifier::VOLATILE, 49 SemanticTokenModifier::VOLATILE,
38 SemanticTokenModifier::READONLY, 50 SemanticTokenModifier::READONLY,
51 MUTABLE,
52 UNSAFE,
53 CONTROL,
54 BUILTIN,
39]; 55];
40 56
41/// Token types that the server supports 57#[derive(Default)]
42pub(crate) fn supported_token_types() -> &'static [SemanticTokenType] { 58pub(crate) struct ModifierSet(pub(crate) u32);
43 SUPPORTED_TYPES
44}
45 59
46/// Token modifiers that the server supports 60impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet {
47pub(crate) fn supported_token_modifiers() -> &'static [SemanticTokenModifier] { 61 fn bitor_assign(&mut self, rhs: SemanticTokenModifier) {
48 SUPPORTED_MODIFIERS 62 let idx = SUPPORTED_MODIFIERS.iter().position(|it| it == &rhs).unwrap();
63 self.0 |= 1 << idx;
64 }
49} 65}
50 66
51/// Tokens are encoded relative to each other. 67/// Tokens are encoded relative to each other.
@@ -92,3 +108,7 @@ impl SemanticTokensBuilder {
92 self.data 108 self.data
93 } 109 }
94} 110}
111
112pub fn type_index(type_: SemanticTokenType) -> u32 {
113 SUPPORTED_TYPES.iter().position(|it| *it == type_).unwrap() as u32
114}
diff --git a/editors/code/package.json b/editors/code/package.json
index c5f9d50ac..ad1101603 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -380,6 +380,28 @@
380 } 380 }
381 } 381 }
382 ], 382 ],
383 "semanticTokenTypes": [
384 {
385 "id": "attribute"
386 },
387 {
388 "id": "constant"
389 }
390 ],
391 "semanticTokenModifiers": [
392 {
393 "id": "mutable"
394 },
395 {
396 "id": "unsafe"
397 },
398 {
399 "id": "control"
400 },
401 {
402 "id": "builtin"
403 }
404 ],
383 "semanticTokenStyleDefaults": [ 405 "semanticTokenStyleDefaults": [
384 { 406 {
385 "selector": "*.mutable", 407 "selector": "*.mutable",
@@ -392,6 +414,12 @@
392 "highContrast": { 414 "highContrast": {
393 "fontStyle": "underline" 415 "fontStyle": "underline"
394 } 416 }
417 },
418 {
419 "selector": "constant",
420 "scope": [
421 "entity.name.constant"
422 ]
395 } 423 }
396 ] 424 ]
397 } 425 }