]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/src/semantic_tokens.rs
Merge #7777
[rust.git] / crates / rust-analyzer / src / semantic_tokens.rs
1 //! Semantic Tokens helpers
2
3 use std::ops;
4
5 use lsp_types::{
6     Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens,
7     SemanticTokensEdit,
8 };
9
10 macro_rules! define_semantic_token_types {
11     ($(($ident:ident, $string:literal)),*$(,)?) => {
12         $(pub(crate) const $ident: SemanticTokenType = SemanticTokenType::new($string);)*
13
14         pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
15             SemanticTokenType::COMMENT,
16             SemanticTokenType::KEYWORD,
17             SemanticTokenType::STRING,
18             SemanticTokenType::NUMBER,
19             SemanticTokenType::REGEXP,
20             SemanticTokenType::OPERATOR,
21             SemanticTokenType::NAMESPACE,
22             SemanticTokenType::TYPE,
23             SemanticTokenType::STRUCT,
24             SemanticTokenType::CLASS,
25             SemanticTokenType::INTERFACE,
26             SemanticTokenType::ENUM,
27             SemanticTokenType::ENUM_MEMBER,
28             SemanticTokenType::TYPE_PARAMETER,
29             SemanticTokenType::FUNCTION,
30             SemanticTokenType::METHOD,
31             SemanticTokenType::PROPERTY,
32             SemanticTokenType::MACRO,
33             SemanticTokenType::VARIABLE,
34             SemanticTokenType::PARAMETER,
35             $($ident),*
36         ];
37     };
38 }
39
40 define_semantic_token_types![
41     (ANGLE, "angle"),
42     (ATTRIBUTE, "attribute"),
43     (BOOLEAN, "boolean"),
44     (BRACE, "brace"),
45     (BRACKET, "bracket"),
46     (BUILTIN_TYPE, "builtinType"),
47     (CHAR_LITERAL, "characterLiteral"),
48     (COMMA, "comma"),
49     (COLON, "colon"),
50     (DOT, "dot"),
51     (ESCAPE_SEQUENCE, "escapeSequence"),
52     (FORMAT_SPECIFIER, "formatSpecifier"),
53     (GENERIC, "generic"),
54     (CONST_PARAMETER, "constParameter"),
55     (LIFETIME, "lifetime"),
56     (LABEL, "label"),
57     (PARENTHESIS, "parenthesis"),
58     (PUNCTUATION, "punctuation"),
59     (SELF_KEYWORD, "selfKeyword"),
60     (SEMICOLON, "semicolon"),
61     (TYPE_ALIAS, "typeAlias"),
62     (UNION, "union"),
63     (UNRESOLVED_REFERENCE, "unresolvedReference"),
64 ];
65
66 macro_rules! define_semantic_token_modifiers {
67     ($(($ident:ident, $string:literal)),*$(,)?) => {
68         $(pub(crate) const $ident: SemanticTokenModifier = SemanticTokenModifier::new($string);)*
69
70         pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
71             SemanticTokenModifier::DOCUMENTATION,
72             SemanticTokenModifier::DECLARATION,
73             SemanticTokenModifier::DEFINITION,
74             SemanticTokenModifier::STATIC,
75             SemanticTokenModifier::ABSTRACT,
76             SemanticTokenModifier::DEPRECATED,
77             SemanticTokenModifier::READONLY,
78             $($ident),*
79         ];
80     };
81 }
82
83 define_semantic_token_modifiers![
84     (CONSTANT, "constant"),
85     (CONTROL_FLOW, "controlFlow"),
86     (INJECTED, "injected"),
87     (MUTABLE, "mutable"),
88     (CONSUMING, "consuming"),
89     (UNSAFE, "unsafe"),
90     (ATTRIBUTE_MODIFIER, "attribute"),
91     (CALLABLE, "callable"),
92 ];
93
94 #[derive(Default)]
95 pub(crate) struct ModifierSet(pub(crate) u32);
96
97 impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet {
98     fn bitor_assign(&mut self, rhs: SemanticTokenModifier) {
99         let idx = SUPPORTED_MODIFIERS.iter().position(|it| it == &rhs).unwrap();
100         self.0 |= 1 << idx;
101     }
102 }
103
104 /// Tokens are encoded relative to each other.
105 ///
106 /// This is a direct port of https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45
107 pub(crate) struct SemanticTokensBuilder {
108     id: String,
109     prev_line: u32,
110     prev_char: u32,
111     data: Vec<SemanticToken>,
112 }
113
114 impl SemanticTokensBuilder {
115     pub(crate) fn new(id: String) -> Self {
116         SemanticTokensBuilder { id, prev_line: 0, prev_char: 0, data: Default::default() }
117     }
118
119     /// Push a new token onto the builder
120     pub(crate) fn push(&mut self, range: Range, token_index: u32, modifier_bitset: u32) {
121         let mut push_line = range.start.line as u32;
122         let mut push_char = range.start.character as u32;
123
124         if !self.data.is_empty() {
125             push_line -= self.prev_line;
126             if push_line == 0 {
127                 push_char -= self.prev_char;
128             }
129         }
130
131         // A token cannot be multiline
132         let token_len = range.end.character - range.start.character;
133
134         let token = SemanticToken {
135             delta_line: push_line,
136             delta_start: push_char,
137             length: token_len as u32,
138             token_type: token_index,
139             token_modifiers_bitset: modifier_bitset,
140         };
141
142         self.data.push(token);
143
144         self.prev_line = range.start.line as u32;
145         self.prev_char = range.start.character as u32;
146     }
147
148     pub(crate) fn build(self) -> SemanticTokens {
149         SemanticTokens { result_id: Some(self.id), data: self.data }
150     }
151 }
152
153 pub(crate) fn diff_tokens(old: &[SemanticToken], new: &[SemanticToken]) -> Vec<SemanticTokensEdit> {
154     let offset = new.iter().zip(old.iter()).take_while(|&(n, p)| n == p).count();
155
156     let (_, old) = old.split_at(offset);
157     let (_, new) = new.split_at(offset);
158
159     let offset_from_end =
160         new.iter().rev().zip(old.iter().rev()).take_while(|&(n, p)| n == p).count();
161
162     let (old, _) = old.split_at(old.len() - offset_from_end);
163     let (new, _) = new.split_at(new.len() - offset_from_end);
164
165     if old.is_empty() && new.is_empty() {
166         vec![]
167     } else {
168         // The lsp data field is actually a byte-diff but we
169         // travel in tokens so `start` and `delete_count` are in multiples of the
170         // serialized size of `SemanticToken`.
171         vec![SemanticTokensEdit {
172             start: 5 * offset as u32,
173             delete_count: 5 * old.len() as u32,
174             data: Some(new.into()),
175         }]
176     }
177 }
178
179 pub(crate) fn type_index(type_: SemanticTokenType) -> u32 {
180     SUPPORTED_TYPES.iter().position(|it| *it == type_).unwrap() as u32
181 }
182
183 #[cfg(test)]
184 mod tests {
185     use super::*;
186
187     fn from(t: (u32, u32, u32, u32, u32)) -> SemanticToken {
188         SemanticToken {
189             delta_line: t.0,
190             delta_start: t.1,
191             length: t.2,
192             token_type: t.3,
193             token_modifiers_bitset: t.4,
194         }
195     }
196
197     #[test]
198     fn test_diff_insert_at_end() {
199         let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
200         let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10)), from((11, 12, 13, 14, 15))];
201
202         let edits = diff_tokens(&before, &after);
203         assert_eq!(
204             edits[0],
205             SemanticTokensEdit {
206                 start: 10,
207                 delete_count: 0,
208                 data: Some(vec![from((11, 12, 13, 14, 15))])
209             }
210         );
211     }
212
213     #[test]
214     fn test_diff_insert_at_beginning() {
215         let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
216         let after = [from((11, 12, 13, 14, 15)), from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
217
218         let edits = diff_tokens(&before, &after);
219         assert_eq!(
220             edits[0],
221             SemanticTokensEdit {
222                 start: 0,
223                 delete_count: 0,
224                 data: Some(vec![from((11, 12, 13, 14, 15))])
225             }
226         );
227     }
228
229     #[test]
230     fn test_diff_insert_in_middle() {
231         let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
232         let after = [
233             from((1, 2, 3, 4, 5)),
234             from((10, 20, 30, 40, 50)),
235             from((60, 70, 80, 90, 100)),
236             from((6, 7, 8, 9, 10)),
237         ];
238
239         let edits = diff_tokens(&before, &after);
240         assert_eq!(
241             edits[0],
242             SemanticTokensEdit {
243                 start: 5,
244                 delete_count: 0,
245                 data: Some(vec![from((10, 20, 30, 40, 50)), from((60, 70, 80, 90, 100))])
246             }
247         );
248     }
249
250     #[test]
251     fn test_diff_remove_from_end() {
252         let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10)), from((11, 12, 13, 14, 15))];
253         let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
254
255         let edits = diff_tokens(&before, &after);
256         assert_eq!(edits[0], SemanticTokensEdit { start: 10, delete_count: 5, data: Some(vec![]) });
257     }
258
259     #[test]
260     fn test_diff_remove_from_beginning() {
261         let before = [from((11, 12, 13, 14, 15)), from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
262         let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
263
264         let edits = diff_tokens(&before, &after);
265         assert_eq!(edits[0], SemanticTokensEdit { start: 0, delete_count: 5, data: Some(vec![]) });
266     }
267
268     #[test]
269     fn test_diff_remove_from_middle() {
270         let before = [
271             from((1, 2, 3, 4, 5)),
272             from((10, 20, 30, 40, 50)),
273             from((60, 70, 80, 90, 100)),
274             from((6, 7, 8, 9, 10)),
275         ];
276         let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
277
278         let edits = diff_tokens(&before, &after);
279         assert_eq!(edits[0], SemanticTokensEdit { start: 5, delete_count: 10, data: Some(vec![]) });
280     }
281 }