Skip to content

Commit ead603b

Browse files
implement semantic highlighting
1 parent 5d787f1 commit ead603b

File tree

7 files changed

+370
-72
lines changed

7 files changed

+370
-72
lines changed

ra-wasm/src/lib.rs

+17-17
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@ use ide_db::{
1616
SnippetCap,
1717
},
1818
};
19+
use return_types::*;
1920
use wasm_bindgen::prelude::*;
20-
21-
mod to_proto;
21+
pub use wasm_bindgen_rayon::init_thread_pool;
2222

2323
mod return_types;
24-
use return_types::*;
25-
26-
pub use wasm_bindgen_rayon::init_thread_pool;
24+
mod semantic_tokens;
25+
mod to_proto;
2726

2827
#[wasm_bindgen(start)]
2928
pub fn start() {
@@ -139,17 +138,6 @@ impl WorldState {
139138

140139
let line_index = self.analysis().file_line_index(self.file_id).unwrap();
141140

142-
let highlights: Vec<_> = self
143-
.analysis()
144-
.highlight(file_id)
145-
.unwrap()
146-
.into_iter()
147-
.map(|hl| Highlight {
148-
tag: Some(hl.highlight.tag.to_string()),
149-
range: to_proto::text_range(hl.range, &line_index),
150-
})
151-
.collect();
152-
153141
let config = DiagnosticsConfig::default();
154142

155143
let diagnostics: Vec<_> = self
@@ -171,7 +159,19 @@ impl WorldState {
171159
})
172160
.collect();
173161

174-
serde_wasm_bindgen::to_value(&UpdateResult { diagnostics, highlights }).unwrap()
162+
serde_wasm_bindgen::to_value(&UpdateResult { diagnostics }).unwrap()
163+
}
164+
165+
pub fn semantic_tokens(&self) -> Vec<u32> {
166+
log::warn!("semantic_tokens");
167+
// let mut builder = SemanticTokensBuilder::new();
168+
let line_index = self.analysis().file_line_index(self.file_id).unwrap();
169+
let file_text = self.analysis().file_text(self.file_id).unwrap();
170+
to_proto::semantic_tokens(
171+
&file_text,
172+
&line_index,
173+
self.analysis().highlight(self.file_id).unwrap(),
174+
)
175175
}
176176

177177
pub fn inlay_hints(&self) -> JsValue {

ra-wasm/src/return_types.rs

-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ pub struct TextEdit {
6262
#[derive(Serialize)]
6363
pub struct UpdateResult {
6464
pub diagnostics: Vec<Diagnostic>,
65-
pub highlights: Vec<Highlight>,
6665
}
6766

6867
#[derive(Serialize)]

ra-wasm/src/semantic_tokens.rs

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//! Semantic Tokens helpers
2+
#![allow(non_camel_case_types)]
3+
4+
use std::ops;
5+
6+
use crate::return_types;
7+
8+
#[repr(u8)]
9+
#[allow(dead_code)]
10+
pub(crate) enum SemanticTokenType {
11+
COMMENT,
12+
STRING,
13+
KEYWORD,
14+
NUMBER,
15+
REGEXP,
16+
OPERATOR,
17+
NAMESPACE,
18+
TYPE,
19+
STRUCT,
20+
CLASS,
21+
INTERFACE,
22+
ENUM,
23+
TYPE_PARAMETER,
24+
FUNCTION,
25+
MEMBER,
26+
MACRO,
27+
VARIABLE,
28+
PARAMETER,
29+
PROPERTY,
30+
LABEL,
31+
UNSUPPORTED,
32+
}
33+
34+
macro_rules! define_semantic_token_modifiers {
35+
($($ident:ident),*$(,)?) => {
36+
#[derive(PartialEq)]
37+
pub(crate) enum SemanticTokenModifier {
38+
$($ident),*
39+
}
40+
41+
pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
42+
$(SemanticTokenModifier::$ident),*
43+
];
44+
};
45+
}
46+
47+
define_semantic_token_modifiers![
48+
DOCUMENTATION,
49+
DECLARATION,
50+
DEFINITION,
51+
STATIC,
52+
ABSTRACT,
53+
DEPRECATED,
54+
READONLY,
55+
DEFAULT_LIBRARY,
56+
// custom
57+
ASYNC,
58+
ATTRIBUTE_MODIFIER,
59+
CALLABLE,
60+
CONSTANT,
61+
CONSUMING,
62+
CONTROL_FLOW,
63+
CRATE_ROOT,
64+
INJECTED,
65+
INTRA_DOC_LINK,
66+
LIBRARY,
67+
MUTABLE,
68+
PUBLIC,
69+
REFERENCE,
70+
TRAIT_MODIFIER,
71+
UNSAFE,
72+
];
73+
74+
#[derive(Default)]
75+
pub(crate) struct ModifierSet(pub(crate) u32);
76+
77+
impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet {
78+
fn bitor_assign(&mut self, rhs: SemanticTokenModifier) {
79+
let idx = SUPPORTED_MODIFIERS.iter().position(|it| it == &rhs).unwrap();
80+
self.0 |= 1 << idx;
81+
}
82+
}
83+
84+
/// Tokens are encoded relative to each other.
85+
///
86+
/// This is a direct port of <https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45>
87+
pub(crate) struct SemanticTokensBuilder {
88+
prev_line: u32,
89+
prev_char: u32,
90+
data: Vec<u32>,
91+
}
92+
93+
impl SemanticTokensBuilder {
94+
pub(crate) fn new() -> Self {
95+
SemanticTokensBuilder { prev_line: 0, prev_char: 0, data: Vec::new() }
96+
}
97+
98+
/// Push a new token onto the builder
99+
pub(crate) fn push(
100+
&mut self,
101+
range: return_types::Range,
102+
token_index: u32,
103+
modifier_bitset: u32,
104+
) {
105+
let mut push_line = range.startLineNumber - 1;
106+
let mut push_char = range.startColumn - 1;
107+
108+
if !self.data.is_empty() {
109+
push_line -= self.prev_line;
110+
if push_line == 0 {
111+
push_char -= self.prev_char;
112+
}
113+
}
114+
115+
// A token cannot be multiline
116+
let token_len = range.endColumn - range.startColumn;
117+
118+
let token = [push_line, push_char, token_len, token_index, modifier_bitset];
119+
120+
self.data.extend_from_slice(&token);
121+
122+
self.prev_line = range.startLineNumber - 1;
123+
self.prev_char = range.startColumn - 1;
124+
}
125+
126+
pub(crate) fn build(self) -> Vec<u32> {
127+
self.data
128+
}
129+
}
130+
131+
pub(crate) fn type_index(ty: SemanticTokenType) -> u32 {
132+
ty as u32
133+
}

ra-wasm/src/to_proto.rs

+121-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! Conversion of rust-analyzer specific types to return_types equivalents.
2-
use crate::return_types;
2+
use crate::{return_types, semantic_tokens};
33

44
pub(crate) fn text_range(
55
range: ide::TextRange,
@@ -229,3 +229,123 @@ fn markdown_string(s: &str) -> return_types::MarkdownString {
229229

230230
return_types::MarkdownString { value: processed_lines.join("\n") }
231231
}
232+
233+
pub(crate) type SemanticTokens = Vec<u32>;
234+
235+
pub(crate) fn semantic_tokens(
236+
text: &str,
237+
line_index: &ide::LineIndex,
238+
highlights: Vec<ide::HlRange>,
239+
) -> SemanticTokens {
240+
let mut builder = semantic_tokens::SemanticTokensBuilder::new();
241+
242+
for highlight_range in highlights {
243+
if highlight_range.highlight.is_empty() {
244+
continue;
245+
}
246+
let (ty, mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
247+
let token_index = semantic_tokens::type_index(ty);
248+
let modifier_bitset = mods.0;
249+
250+
for mut text_range in line_index.lines(highlight_range.range) {
251+
if text[text_range].ends_with('\n') {
252+
text_range = ide::TextRange::new(
253+
text_range.start(),
254+
text_range.end() - ide::TextSize::of('\n'),
255+
);
256+
}
257+
let range = self::text_range(text_range, line_index);
258+
259+
builder.push(range, token_index, modifier_bitset);
260+
}
261+
}
262+
263+
builder.build()
264+
}
265+
266+
fn semantic_token_type_and_modifiers(
267+
highlight: ide::Highlight,
268+
) -> (semantic_tokens::SemanticTokenType, semantic_tokens::ModifierSet) {
269+
use ide::{HlMod, HlTag, SymbolKind};
270+
use semantic_tokens::*;
271+
let mut mods = ModifierSet::default();
272+
let type_ = match highlight.tag {
273+
HlTag::Symbol(symbol) => match symbol {
274+
SymbolKind::Module => SemanticTokenType::NAMESPACE,
275+
SymbolKind::Impl => SemanticTokenType::TYPE,
276+
SymbolKind::Field => SemanticTokenType::PROPERTY,
277+
SymbolKind::TypeParam => SemanticTokenType::TYPE_PARAMETER,
278+
SymbolKind::ConstParam => SemanticTokenType::PARAMETER,
279+
SymbolKind::LifetimeParam => SemanticTokenType::TYPE_PARAMETER,
280+
SymbolKind::Label => SemanticTokenType::LABEL,
281+
SymbolKind::ValueParam => SemanticTokenType::PARAMETER,
282+
SymbolKind::SelfParam => SemanticTokenType::KEYWORD,
283+
SymbolKind::Local => SemanticTokenType::VARIABLE,
284+
SymbolKind::Function => {
285+
if highlight.mods.contains(HlMod::Associated) {
286+
SemanticTokenType::MEMBER
287+
} else {
288+
SemanticTokenType::FUNCTION
289+
}
290+
}
291+
SymbolKind::Const => {
292+
mods |= SemanticTokenModifier::CONSTANT;
293+
mods |= SemanticTokenModifier::STATIC;
294+
SemanticTokenType::VARIABLE
295+
}
296+
SymbolKind::Static => {
297+
mods |= SemanticTokenModifier::STATIC;
298+
SemanticTokenType::VARIABLE
299+
}
300+
SymbolKind::Struct => SemanticTokenType::TYPE,
301+
SymbolKind::Enum => SemanticTokenType::TYPE,
302+
SymbolKind::Variant => SemanticTokenType::MEMBER,
303+
SymbolKind::Union => SemanticTokenType::TYPE,
304+
SymbolKind::TypeAlias => SemanticTokenType::TYPE,
305+
SymbolKind::Trait => SemanticTokenType::INTERFACE,
306+
SymbolKind::Macro => SemanticTokenType::MACRO,
307+
},
308+
HlTag::Attribute => SemanticTokenType::UNSUPPORTED,
309+
HlTag::BoolLiteral => SemanticTokenType::NUMBER,
310+
HlTag::BuiltinAttr => SemanticTokenType::UNSUPPORTED,
311+
HlTag::BuiltinType => SemanticTokenType::TYPE,
312+
HlTag::ByteLiteral | HlTag::NumericLiteral => SemanticTokenType::NUMBER,
313+
HlTag::CharLiteral => SemanticTokenType::STRING,
314+
HlTag::Comment => SemanticTokenType::COMMENT,
315+
HlTag::EscapeSequence => SemanticTokenType::NUMBER,
316+
HlTag::FormatSpecifier => SemanticTokenType::MACRO,
317+
HlTag::Keyword => SemanticTokenType::KEYWORD,
318+
HlTag::None => SemanticTokenType::UNSUPPORTED,
319+
HlTag::Operator(_op) => SemanticTokenType::OPERATOR,
320+
HlTag::StringLiteral => SemanticTokenType::STRING,
321+
HlTag::UnresolvedReference => SemanticTokenType::UNSUPPORTED,
322+
HlTag::Punctuation(_punct) => SemanticTokenType::OPERATOR,
323+
};
324+
325+
for modifier in highlight.mods.iter() {
326+
let modifier = match modifier {
327+
HlMod::Associated => continue,
328+
HlMod::Async => SemanticTokenModifier::ASYNC,
329+
HlMod::Attribute => SemanticTokenModifier::ATTRIBUTE_MODIFIER,
330+
HlMod::Callable => SemanticTokenModifier::CALLABLE,
331+
HlMod::Consuming => SemanticTokenModifier::CONSUMING,
332+
HlMod::ControlFlow => SemanticTokenModifier::CONTROL_FLOW,
333+
HlMod::CrateRoot => SemanticTokenModifier::CRATE_ROOT,
334+
HlMod::DefaultLibrary => SemanticTokenModifier::DEFAULT_LIBRARY,
335+
HlMod::Definition => SemanticTokenModifier::DECLARATION,
336+
HlMod::Documentation => SemanticTokenModifier::DOCUMENTATION,
337+
HlMod::Injected => SemanticTokenModifier::INJECTED,
338+
HlMod::IntraDocLink => SemanticTokenModifier::INTRA_DOC_LINK,
339+
HlMod::Library => SemanticTokenModifier::LIBRARY,
340+
HlMod::Mutable => SemanticTokenModifier::MUTABLE,
341+
HlMod::Public => SemanticTokenModifier::PUBLIC,
342+
HlMod::Reference => SemanticTokenModifier::REFERENCE,
343+
HlMod::Static => SemanticTokenModifier::STATIC,
344+
HlMod::Trait => SemanticTokenModifier::TRAIT_MODIFIER,
345+
HlMod::Unsafe => SemanticTokenModifier::UNSAFE,
346+
};
347+
mods |= modifier;
348+
}
349+
350+
(type_, mods)
351+
}

www/example-code.rs

+25-4
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,47 @@
11
use std::ops::Range;
22

3-
fn gav(x: i32, y: i32) -> i64 {
3+
unsafe fn gav(x: i32, y: i32) -> i64 {
44
(x - y) * (x + y)
55
}
66

7+
#[derive(Debug)]
8+
struct Gen<G, 'a> {
9+
g: &'a G
10+
}
11+
12+
impl<G, 'a> Gen<G, 'a> {
13+
/// Create a new `Gen`
14+
/// ```
15+
/// let mut gen = Gen::new(&mut something);
16+
/// ```
17+
fn new(g: &mut G) -> Self {
18+
Gen { g }
19+
}
20+
21+
fn do(&mut self) -> () { }
22+
}
23+
724
fn main() {
825
let num = 5;
926
let a = vec![1, 2, 3];
1027
let b = Some(2);
1128
let c = None;
1229
let d = Range { start: 1, end: num };
1330
let e = 1..num;
14-
let f = "sssss".to_string();
31+
let mut f = "sssss".to_string();
32+
let x = &mut f;
1533
for a in d {
1634
for b in e {
17-
let c = gav(gav(a, b), a);
35+
let c = unsafe { gav(gav(a, b), a) };
1836
assert_eq!(gav(a, b), a * a - b * b);
1937
}
2038
}
39+
40+
let mut gen = Gen::new(&mut f);
2141
let f = d
2242
.reduce(|a, b| {
23-
println!("{}", a);
43+
gen.do();
44+
println!("value: {}", a);
2445
a * b
2546
})
2647
.unwrap();

0 commit comments

Comments
 (0)