ra_ap_rust_analyzer/lsp/
semantic_tokens.rs

1//! Semantic Tokens helpers
2
3use std::ops;
4
5use lsp_types::{
6    Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens,
7    SemanticTokensEdit,
8};
9
10macro_rules! define_semantic_token_types {
11    (
12        standard {
13            $($standard:ident),*$(,)?
14        }
15        custom {
16            $(($custom:ident, $string:literal) $(=> $fallback:ident)?),*$(,)?
17        }
18
19    ) => {
20        pub(crate) mod types {
21            use super::SemanticTokenType;
22            $(pub(crate) const $standard: SemanticTokenType = SemanticTokenType::$standard;)*
23            $(pub(crate) const $custom: SemanticTokenType = SemanticTokenType::new($string);)*
24        }
25
26        pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
27            $(self::types::$standard,)*
28            $(self::types::$custom),*
29        ];
30
31        pub(crate) fn standard_fallback_type(token: SemanticTokenType) -> Option<SemanticTokenType> {
32            use self::types::*;
33            $(
34                if token == $custom {
35                    None $(.or(Some(self::types::$fallback)))?
36                } else
37            )*
38            { Some(token )}
39        }
40    };
41}
42
43define_semantic_token_types![
44    standard {
45        COMMENT,
46        DECORATOR,
47        ENUM_MEMBER,
48        ENUM,
49        FUNCTION,
50        INTERFACE,
51        KEYWORD,
52        MACRO,
53        METHOD,
54        NAMESPACE,
55        NUMBER,
56        OPERATOR,
57        PARAMETER,
58        PROPERTY,
59        STRING,
60        STRUCT,
61        TYPE_PARAMETER,
62        VARIABLE,
63        TYPE,
64    }
65
66    custom {
67        (ANGLE, "angle"),
68        (ARITHMETIC, "arithmetic") => OPERATOR,
69        (ATTRIBUTE_BRACKET, "attributeBracket") => DECORATOR,
70        (ATTRIBUTE, "attribute") => DECORATOR,
71        (BITWISE, "bitwise") => OPERATOR,
72        (BOOLEAN, "boolean"),
73        (BRACE, "brace"),
74        (BRACKET, "bracket"),
75        (BUILTIN_ATTRIBUTE, "builtinAttribute") => DECORATOR,
76        (BUILTIN_TYPE, "builtinType") => TYPE,
77        (CHAR, "character") => STRING,
78        (COLON, "colon"),
79        (COMMA, "comma"),
80        (COMPARISON, "comparison") => OPERATOR,
81        (CONST_PARAMETER, "constParameter"),
82        (CONST, "const") => VARIABLE,
83        (DERIVE_HELPER, "deriveHelper") => DECORATOR,
84        (DERIVE, "derive") => DECORATOR,
85        (DOT, "dot"),
86        (ESCAPE_SEQUENCE, "escapeSequence") => STRING,
87        (FORMAT_SPECIFIER, "formatSpecifier") => STRING,
88        (GENERIC, "generic") => TYPE_PARAMETER,
89        (INVALID_ESCAPE_SEQUENCE, "invalidEscapeSequence") => STRING,
90        (LABEL, "label"),
91        (LIFETIME, "lifetime"),
92        (LOGICAL, "logical") => OPERATOR,
93        (MACRO_BANG, "macroBang") => MACRO,
94        (PARENTHESIS, "parenthesis"),
95        (PROC_MACRO, "procMacro") => MACRO,
96        (PUNCTUATION, "punctuation"),
97        (SELF_KEYWORD, "selfKeyword") => KEYWORD,
98        (SELF_TYPE_KEYWORD, "selfTypeKeyword") => KEYWORD,
99        (SEMICOLON, "semicolon"),
100        (STATIC, "static") => VARIABLE,
101        (TOOL_MODULE, "toolModule") => DECORATOR,
102        (TYPE_ALIAS, "typeAlias") => TYPE,
103        (UNION, "union") => TYPE,
104        (UNRESOLVED_REFERENCE, "unresolvedReference"),
105    }
106];
107
108macro_rules! count_tts {
109    () => {0usize};
110    ($_head:tt $($tail:tt)*) => {1usize + count_tts!($($tail)*)};
111}
112macro_rules! define_semantic_token_modifiers {
113    (
114        standard {
115            $($standard:ident),*$(,)?
116        }
117        custom {
118            $(($custom:ident, $string:literal)),*$(,)?
119        }
120
121    ) => {
122        pub(crate) mod modifiers {
123            use super::SemanticTokenModifier;
124
125            $(pub(crate) const $standard: SemanticTokenModifier = SemanticTokenModifier::$standard;)*
126            $(pub(crate) const $custom: SemanticTokenModifier = SemanticTokenModifier::new($string);)*
127        }
128
129        pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
130            $(SemanticTokenModifier::$standard,)*
131            $(self::modifiers::$custom),*
132        ];
133
134        const LAST_STANDARD_MOD: usize = count_tts!($($standard)*);
135    };
136}
137
138define_semantic_token_modifiers![
139    standard {
140        ASYNC,
141        DOCUMENTATION,
142        DECLARATION,
143        STATIC,
144        DEFAULT_LIBRARY,
145    }
146    custom {
147        (ASSOCIATED, "associated"),
148        (ATTRIBUTE_MODIFIER, "attribute"),
149        (CALLABLE, "callable"),
150        (CONSTANT, "constant"),
151        (CONSUMING, "consuming"),
152        (CONTROL_FLOW, "controlFlow"),
153        (CRATE_ROOT, "crateRoot"),
154        (INJECTED, "injected"),
155        (INTRA_DOC_LINK, "intraDocLink"),
156        (LIBRARY, "library"),
157        (MACRO_MODIFIER, "macro"),
158        (MUTABLE, "mutable"),
159        (PROC_MACRO_MODIFIER, "procMacro"),
160        (PUBLIC, "public"),
161        (REFERENCE, "reference"),
162        (TRAIT_MODIFIER, "trait"),
163        (UNSAFE, "unsafe"),
164    }
165];
166
167#[derive(Default)]
168pub(crate) struct ModifierSet(pub(crate) u32);
169
170impl ModifierSet {
171    pub(crate) fn standard_fallback(&mut self) {
172        // Remove all non standard modifiers
173        self.0 &= !(!0u32 << LAST_STANDARD_MOD)
174    }
175}
176
177impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet {
178    fn bitor_assign(&mut self, rhs: SemanticTokenModifier) {
179        let idx = SUPPORTED_MODIFIERS.iter().position(|it| it == &rhs).unwrap();
180        self.0 |= 1 << idx;
181    }
182}
183
184/// Tokens are encoded relative to each other.
185///
186/// This is a direct port of <https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45>
187pub(crate) struct SemanticTokensBuilder {
188    id: String,
189    prev_line: u32,
190    prev_char: u32,
191    data: Vec<SemanticToken>,
192}
193
194impl SemanticTokensBuilder {
195    pub(crate) fn new(id: String) -> Self {
196        SemanticTokensBuilder { id, prev_line: 0, prev_char: 0, data: Default::default() }
197    }
198
199    /// Push a new token onto the builder
200    pub(crate) fn push(&mut self, range: Range, token_index: u32, modifier_bitset: u32) {
201        let mut push_line = range.start.line;
202        let mut push_char = range.start.character;
203
204        if !self.data.is_empty() {
205            push_line -= self.prev_line;
206            if push_line == 0 {
207                push_char -= self.prev_char;
208            }
209        }
210
211        // A token cannot be multiline
212        let token_len = range.end.character - range.start.character;
213
214        let token = SemanticToken {
215            delta_line: push_line,
216            delta_start: push_char,
217            length: token_len,
218            token_type: token_index,
219            token_modifiers_bitset: modifier_bitset,
220        };
221
222        self.data.push(token);
223
224        self.prev_line = range.start.line;
225        self.prev_char = range.start.character;
226    }
227
228    pub(crate) fn build(self) -> SemanticTokens {
229        SemanticTokens { result_id: Some(self.id), data: self.data }
230    }
231}
232
233pub(crate) fn diff_tokens(old: &[SemanticToken], new: &[SemanticToken]) -> Vec<SemanticTokensEdit> {
234    let offset = new.iter().zip(old.iter()).take_while(|&(n, p)| n == p).count();
235
236    let (_, old) = old.split_at(offset);
237    let (_, new) = new.split_at(offset);
238
239    let offset_from_end =
240        new.iter().rev().zip(old.iter().rev()).take_while(|&(n, p)| n == p).count();
241
242    let (old, _) = old.split_at(old.len() - offset_from_end);
243    let (new, _) = new.split_at(new.len() - offset_from_end);
244
245    if old.is_empty() && new.is_empty() {
246        vec![]
247    } else {
248        // The lsp data field is actually a byte-diff but we
249        // travel in tokens so `start` and `delete_count` are in multiples of the
250        // serialized size of `SemanticToken`.
251        vec![SemanticTokensEdit {
252            start: 5 * offset as u32,
253            delete_count: 5 * old.len() as u32,
254            data: Some(new.into()),
255        }]
256    }
257}
258
259pub(crate) fn type_index(ty: SemanticTokenType) -> u32 {
260    SUPPORTED_TYPES.iter().position(|it| *it == ty).unwrap() as u32
261}
262
263#[cfg(test)]
264mod tests {
265    use super::*;
266
267    fn from(t: (u32, u32, u32, u32, u32)) -> SemanticToken {
268        SemanticToken {
269            delta_line: t.0,
270            delta_start: t.1,
271            length: t.2,
272            token_type: t.3,
273            token_modifiers_bitset: t.4,
274        }
275    }
276
277    #[test]
278    fn test_diff_insert_at_end() {
279        let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
280        let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10)), from((11, 12, 13, 14, 15))];
281
282        let edits = diff_tokens(&before, &after);
283        assert_eq!(
284            edits[0],
285            SemanticTokensEdit {
286                start: 10,
287                delete_count: 0,
288                data: Some(vec![from((11, 12, 13, 14, 15))])
289            }
290        );
291    }
292
293    #[test]
294    fn test_diff_insert_at_beginning() {
295        let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
296        let after = [from((11, 12, 13, 14, 15)), from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
297
298        let edits = diff_tokens(&before, &after);
299        assert_eq!(
300            edits[0],
301            SemanticTokensEdit {
302                start: 0,
303                delete_count: 0,
304                data: Some(vec![from((11, 12, 13, 14, 15))])
305            }
306        );
307    }
308
309    #[test]
310    fn test_diff_insert_in_middle() {
311        let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
312        let after = [
313            from((1, 2, 3, 4, 5)),
314            from((10, 20, 30, 40, 50)),
315            from((60, 70, 80, 90, 100)),
316            from((6, 7, 8, 9, 10)),
317        ];
318
319        let edits = diff_tokens(&before, &after);
320        assert_eq!(
321            edits[0],
322            SemanticTokensEdit {
323                start: 5,
324                delete_count: 0,
325                data: Some(vec![from((10, 20, 30, 40, 50)), from((60, 70, 80, 90, 100))])
326            }
327        );
328    }
329
330    #[test]
331    fn test_diff_remove_from_end() {
332        let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10)), from((11, 12, 13, 14, 15))];
333        let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
334
335        let edits = diff_tokens(&before, &after);
336        assert_eq!(edits[0], SemanticTokensEdit { start: 10, delete_count: 5, data: Some(vec![]) });
337    }
338
339    #[test]
340    fn test_diff_remove_from_beginning() {
341        let before = [from((11, 12, 13, 14, 15)), from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
342        let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
343
344        let edits = diff_tokens(&before, &after);
345        assert_eq!(edits[0], SemanticTokensEdit { start: 0, delete_count: 5, data: Some(vec![]) });
346    }
347
348    #[test]
349    fn test_diff_remove_from_middle() {
350        let before = [
351            from((1, 2, 3, 4, 5)),
352            from((10, 20, 30, 40, 50)),
353            from((60, 70, 80, 90, 100)),
354            from((6, 7, 8, 9, 10)),
355        ];
356        let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
357
358        let edits = diff_tokens(&before, &after);
359        assert_eq!(edits[0], SemanticTokensEdit { start: 5, delete_count: 10, data: Some(vec![]) });
360    }
361}