ide/syntax_highlighting/
tags.rs

1//! Defines token tags we use for syntax highlighting.
2//! A tag is not unlike a CSS class.
3
4use std::{
5    fmt::{self, Write},
6    ops,
7};
8
9use ide_db::SymbolKind;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub struct Highlight {
13    pub tag: HlTag,
14    pub mods: HlMods,
15}
16
17#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
18pub struct HlMods(u32);
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
21pub enum HlTag {
22    Symbol(SymbolKind),
23
24    AttributeBracket,
25    BoolLiteral,
26    BuiltinType,
27    ByteLiteral,
28    CharLiteral,
29    Comment,
30    EscapeSequence,
31    FormatSpecifier,
32    InvalidEscapeSequence,
33    Keyword,
34    NumericLiteral,
35    Operator(HlOperator),
36    Punctuation(HlPunct),
37    StringLiteral,
38    UnresolvedReference,
39
40    // For things which don't have a specific highlight.
41    None,
42}
43
44// Don't forget to adjust the feature description in crates/ide/src/syntax_highlighting.rs.
45// And make sure to use the lsp strings used when converting to the protocol in crates\rust-analyzer\src\semantic_tokens.rs, not the names of the variants here.
46#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
47#[repr(u8)]
48pub enum HlMod {
49    /// Used for associated items.
50    Associated = 0,
51    /// Used with keywords like `async` and `await`.
52    Async,
53    /// Used to differentiate individual elements within attribute calls.
54    Attribute,
55    /// Callable item or value.
56    Callable,
57    /// Constant operation.
58    Const,
59    /// Value that is being consumed in a function call
60    Consuming,
61    /// Used with keywords like `if` and `break`.
62    ControlFlow,
63    /// Used for crate names, like `serde`.
64    CrateRoot,
65    /// Used for items from built-in crates (std, core, alloc, test and proc_macro).
66    DefaultLibrary,
67    /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is
68    /// not.
69    Definition,
70    /// Used for things with the `#[deprecated]` attribute.
71    Deprecated,
72    /// Doc-strings like this one.
73    Documentation,
74    /// Highlighting injection like rust code in doc strings or ra_fixture.
75    Injected,
76    /// Used for intra doc links in doc injection.
77    IntraDocLink,
78    /// Used for items from other crates.
79    Library,
80    /// Used to differentiate individual elements within macro calls.
81    Macro,
82    /// Used to differentiate individual elements within proc-macro calls.
83    ProcMacro,
84    /// Mutable binding.
85    Mutable,
86    /// Used for public items.
87    Public,
88    /// Immutable reference.
89    Reference,
90    /// Used for associated items, except Methods. (Some languages call these static members)
91    Static,
92    /// Used for items in traits and trait impls.
93    Trait,
94    // Keep this last!
95    /// Used for unsafe functions, unsafe traits, mutable statics, union accesses and unsafe operations.
96    Unsafe,
97}
98
99#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
100pub enum HlPunct {
101    /// []
102    Bracket,
103    /// {}
104    Brace,
105    /// ()
106    Parenthesis,
107    /// <>
108    Angle,
109    /// ,
110    Comma,
111    /// .
112    Dot,
113    /// :
114    Colon,
115    /// ;
116    Semi,
117    /// ! (only for macro calls)
118    MacroBang,
119    /// Other punctutations
120    Other,
121}
122
123#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
124pub enum HlOperator {
125    /// |, &, !, ^, |=, &=, ^=
126    Bitwise,
127    /// +, -, *, /, +=, -=, *=, /=
128    Arithmetic,
129    /// &&, ||
130    Logical,
131    /// !
132    Negation,
133    /// >, <, ==, >=, <=, !=
134    Comparison,
135    /// Other operators
136    Other,
137}
138
139impl HlTag {
140    fn as_str(self) -> &'static str {
141        match self {
142            HlTag::Symbol(symbol) => match symbol {
143                SymbolKind::Attribute => "attribute",
144                SymbolKind::BuiltinAttr => "builtin_attr",
145                SymbolKind::Const => "constant",
146                SymbolKind::ConstParam => "const_param",
147                SymbolKind::Derive => "derive",
148                SymbolKind::DeriveHelper => "derive_helper",
149                SymbolKind::Enum => "enum",
150                SymbolKind::Field => "field",
151                SymbolKind::Function => "function",
152                SymbolKind::Impl => "self_type",
153                SymbolKind::InlineAsmRegOrRegClass => "reg",
154                SymbolKind::Label => "label",
155                SymbolKind::LifetimeParam => "lifetime",
156                SymbolKind::Local => "variable",
157                SymbolKind::Macro => "macro",
158                SymbolKind::Method => "method",
159                SymbolKind::ProcMacro => "proc_macro",
160                SymbolKind::Module => "module",
161                SymbolKind::SelfParam => "self_keyword",
162                SymbolKind::SelfType => "self_type_keyword",
163                SymbolKind::Static => "static",
164                SymbolKind::Struct => "struct",
165                SymbolKind::ToolModule => "tool_module",
166                SymbolKind::Trait => "trait",
167                SymbolKind::TypeAlias => "type_alias",
168                SymbolKind::TypeParam => "type_param",
169                SymbolKind::Union => "union",
170                SymbolKind::ValueParam => "value_param",
171                SymbolKind::Variant => "enum_variant",
172            },
173            HlTag::AttributeBracket => "attribute_bracket",
174            HlTag::BoolLiteral => "bool_literal",
175            HlTag::BuiltinType => "builtin_type",
176            HlTag::ByteLiteral => "byte_literal",
177            HlTag::CharLiteral => "char_literal",
178            HlTag::Comment => "comment",
179            HlTag::EscapeSequence => "escape_sequence",
180            HlTag::InvalidEscapeSequence => "invalid_escape_sequence",
181            HlTag::FormatSpecifier => "format_specifier",
182            HlTag::Keyword => "keyword",
183            HlTag::Punctuation(punct) => match punct {
184                HlPunct::Bracket => "bracket",
185                HlPunct::Brace => "brace",
186                HlPunct::Parenthesis => "parenthesis",
187                HlPunct::Angle => "angle",
188                HlPunct::Comma => "comma",
189                HlPunct::Dot => "dot",
190                HlPunct::Colon => "colon",
191                HlPunct::Semi => "semicolon",
192                HlPunct::MacroBang => "macro_bang",
193                HlPunct::Other => "punctuation",
194            },
195            HlTag::NumericLiteral => "numeric_literal",
196            HlTag::Operator(op) => match op {
197                HlOperator::Bitwise => "bitwise",
198                HlOperator::Arithmetic => "arithmetic",
199                HlOperator::Logical => "logical",
200                HlOperator::Comparison => "comparison",
201                HlOperator::Negation => "negation",
202                HlOperator::Other => "operator",
203            },
204            HlTag::StringLiteral => "string_literal",
205            HlTag::UnresolvedReference => "unresolved_reference",
206            HlTag::None => "none",
207        }
208    }
209}
210
211impl fmt::Display for HlTag {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        fmt::Display::fmt(self.as_str(), f)
214    }
215}
216
217impl HlMod {
218    const ALL: &'static [HlMod; HlMod::Unsafe as usize + 1] = &[
219        HlMod::Associated,
220        HlMod::Async,
221        HlMod::Attribute,
222        HlMod::Callable,
223        HlMod::Const,
224        HlMod::Consuming,
225        HlMod::ControlFlow,
226        HlMod::CrateRoot,
227        HlMod::DefaultLibrary,
228        HlMod::Definition,
229        HlMod::Deprecated,
230        HlMod::Documentation,
231        HlMod::Injected,
232        HlMod::IntraDocLink,
233        HlMod::Library,
234        HlMod::Macro,
235        HlMod::Mutable,
236        HlMod::ProcMacro,
237        HlMod::Public,
238        HlMod::Reference,
239        HlMod::Static,
240        HlMod::Trait,
241        HlMod::Unsafe,
242    ];
243
244    fn as_str(self) -> &'static str {
245        match self {
246            HlMod::Associated => "associated",
247            HlMod::Async => "async",
248            HlMod::Attribute => "attribute",
249            HlMod::Callable => "callable",
250            HlMod::Consuming => "consuming",
251            HlMod::Const => "const",
252            HlMod::ControlFlow => "control",
253            HlMod::CrateRoot => "crate_root",
254            HlMod::DefaultLibrary => "default_library",
255            HlMod::Definition => "declaration",
256            HlMod::Deprecated => "deprecated",
257            HlMod::Documentation => "documentation",
258            HlMod::Injected => "injected",
259            HlMod::IntraDocLink => "intra_doc_link",
260            HlMod::Library => "library",
261            HlMod::Macro => "macro",
262            HlMod::ProcMacro => "proc_macro",
263            HlMod::Mutable => "mutable",
264            HlMod::Public => "public",
265            HlMod::Reference => "reference",
266            HlMod::Static => "static",
267            HlMod::Trait => "trait",
268            HlMod::Unsafe => "unsafe",
269        }
270    }
271
272    fn mask(self) -> u32 {
273        debug_assert!(Self::ALL.len() <= 32, "HlMod::mask is not enough to cover all variants");
274        1 << (self as u32)
275    }
276}
277
278impl fmt::Display for HlMod {
279    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280        fmt::Display::fmt(self.as_str(), f)
281    }
282}
283
284impl fmt::Display for Highlight {
285    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286        self.tag.fmt(f)?;
287        for modifier in self.mods.iter() {
288            f.write_char('.')?;
289            modifier.fmt(f)?;
290        }
291        Ok(())
292    }
293}
294
295impl From<HlTag> for Highlight {
296    fn from(tag: HlTag) -> Highlight {
297        Highlight::new(tag)
298    }
299}
300
301impl From<HlOperator> for Highlight {
302    fn from(op: HlOperator) -> Highlight {
303        Highlight::new(HlTag::Operator(op))
304    }
305}
306
307impl From<HlPunct> for Highlight {
308    fn from(punct: HlPunct) -> Highlight {
309        Highlight::new(HlTag::Punctuation(punct))
310    }
311}
312
313impl From<SymbolKind> for Highlight {
314    fn from(sym: SymbolKind) -> Highlight {
315        Highlight::new(HlTag::Symbol(sym))
316    }
317}
318
319impl Highlight {
320    pub(crate) fn new(tag: HlTag) -> Highlight {
321        Highlight { tag, mods: HlMods::default() }
322    }
323    pub fn is_empty(&self) -> bool {
324        self.tag == HlTag::None && self.mods.is_empty()
325    }
326}
327
328impl ops::BitOr<HlMod> for HlTag {
329    type Output = Highlight;
330
331    fn bitor(self, rhs: HlMod) -> Highlight {
332        Highlight::new(self) | rhs
333    }
334}
335
336impl ops::BitOrAssign<HlMod> for HlMods {
337    fn bitor_assign(&mut self, rhs: HlMod) {
338        self.0 |= rhs.mask();
339    }
340}
341
342impl ops::BitOrAssign<HlMod> for Highlight {
343    fn bitor_assign(&mut self, rhs: HlMod) {
344        self.mods |= rhs;
345    }
346}
347
348impl ops::BitOr<HlMod> for Highlight {
349    type Output = Highlight;
350
351    fn bitor(mut self, rhs: HlMod) -> Highlight {
352        self |= rhs;
353        self
354    }
355}
356
357impl HlMods {
358    pub fn is_empty(&self) -> bool {
359        self.0 == 0
360    }
361
362    pub fn contains(self, m: HlMod) -> bool {
363        self.0 & m.mask() == m.mask()
364    }
365
366    pub fn iter(self) -> impl Iterator<Item = HlMod> {
367        HlMod::ALL.iter().copied().filter(move |it| self.0 & it.mask() == it.mask())
368    }
369}