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::CrateRoot => "crate_root",
148                SymbolKind::Derive => "derive",
149                SymbolKind::DeriveHelper => "derive_helper",
150                SymbolKind::Enum => "enum",
151                SymbolKind::Field => "field",
152                SymbolKind::Function => "function",
153                SymbolKind::Impl => "self_type",
154                SymbolKind::InlineAsmRegOrRegClass => "reg",
155                SymbolKind::Label => "label",
156                SymbolKind::LifetimeParam => "lifetime",
157                SymbolKind::Local => "variable",
158                SymbolKind::Macro => "macro",
159                SymbolKind::Method => "method",
160                SymbolKind::ProcMacro => "proc_macro",
161                SymbolKind::Module => "module",
162                SymbolKind::SelfParam => "self_keyword",
163                SymbolKind::SelfType => "self_type_keyword",
164                SymbolKind::Static => "static",
165                SymbolKind::Struct => "struct",
166                SymbolKind::ToolModule => "tool_module",
167                SymbolKind::Trait => "trait",
168                SymbolKind::TypeAlias => "type_alias",
169                SymbolKind::TypeParam => "type_param",
170                SymbolKind::Union => "union",
171                SymbolKind::ValueParam => "value_param",
172                SymbolKind::Variant => "enum_variant",
173            },
174            HlTag::AttributeBracket => "attribute_bracket",
175            HlTag::BoolLiteral => "bool_literal",
176            HlTag::BuiltinType => "builtin_type",
177            HlTag::ByteLiteral => "byte_literal",
178            HlTag::CharLiteral => "char_literal",
179            HlTag::Comment => "comment",
180            HlTag::EscapeSequence => "escape_sequence",
181            HlTag::InvalidEscapeSequence => "invalid_escape_sequence",
182            HlTag::FormatSpecifier => "format_specifier",
183            HlTag::Keyword => "keyword",
184            HlTag::Punctuation(punct) => match punct {
185                HlPunct::Bracket => "bracket",
186                HlPunct::Brace => "brace",
187                HlPunct::Parenthesis => "parenthesis",
188                HlPunct::Angle => "angle",
189                HlPunct::Comma => "comma",
190                HlPunct::Dot => "dot",
191                HlPunct::Colon => "colon",
192                HlPunct::Semi => "semicolon",
193                HlPunct::MacroBang => "macro_bang",
194                HlPunct::Other => "punctuation",
195            },
196            HlTag::NumericLiteral => "numeric_literal",
197            HlTag::Operator(op) => match op {
198                HlOperator::Bitwise => "bitwise",
199                HlOperator::Arithmetic => "arithmetic",
200                HlOperator::Logical => "logical",
201                HlOperator::Comparison => "comparison",
202                HlOperator::Negation => "negation",
203                HlOperator::Other => "operator",
204            },
205            HlTag::StringLiteral => "string_literal",
206            HlTag::UnresolvedReference => "unresolved_reference",
207            HlTag::None => "none",
208        }
209    }
210}
211
212impl fmt::Display for HlTag {
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        fmt::Display::fmt(self.as_str(), f)
215    }
216}
217
218impl HlMod {
219    const ALL: &'static [HlMod; HlMod::Unsafe as usize + 1] = &[
220        HlMod::Associated,
221        HlMod::Async,
222        HlMod::Attribute,
223        HlMod::Callable,
224        HlMod::Const,
225        HlMod::Consuming,
226        HlMod::ControlFlow,
227        HlMod::CrateRoot,
228        HlMod::DefaultLibrary,
229        HlMod::Definition,
230        HlMod::Deprecated,
231        HlMod::Documentation,
232        HlMod::Injected,
233        HlMod::IntraDocLink,
234        HlMod::Library,
235        HlMod::Macro,
236        HlMod::Mutable,
237        HlMod::ProcMacro,
238        HlMod::Public,
239        HlMod::Reference,
240        HlMod::Static,
241        HlMod::Trait,
242        HlMod::Unsafe,
243    ];
244
245    fn as_str(self) -> &'static str {
246        match self {
247            HlMod::Associated => "associated",
248            HlMod::Async => "async",
249            HlMod::Attribute => "attribute",
250            HlMod::Callable => "callable",
251            HlMod::Consuming => "consuming",
252            HlMod::Const => "const",
253            HlMod::ControlFlow => "control",
254            HlMod::CrateRoot => "crate_root",
255            HlMod::DefaultLibrary => "default_library",
256            HlMod::Definition => "declaration",
257            HlMod::Deprecated => "deprecated",
258            HlMod::Documentation => "documentation",
259            HlMod::Injected => "injected",
260            HlMod::IntraDocLink => "intra_doc_link",
261            HlMod::Library => "library",
262            HlMod::Macro => "macro",
263            HlMod::ProcMacro => "proc_macro",
264            HlMod::Mutable => "mutable",
265            HlMod::Public => "public",
266            HlMod::Reference => "reference",
267            HlMod::Static => "static",
268            HlMod::Trait => "trait",
269            HlMod::Unsafe => "unsafe",
270        }
271    }
272
273    fn mask(self) -> u32 {
274        debug_assert!(Self::ALL.len() <= 32, "HlMod::mask is not enough to cover all variants");
275        1 << (self as u32)
276    }
277}
278
279impl fmt::Display for HlMod {
280    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281        fmt::Display::fmt(self.as_str(), f)
282    }
283}
284
285impl fmt::Display for Highlight {
286    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287        self.tag.fmt(f)?;
288        for modifier in self.mods.iter() {
289            f.write_char('.')?;
290            modifier.fmt(f)?;
291        }
292        Ok(())
293    }
294}
295
296impl From<HlTag> for Highlight {
297    fn from(tag: HlTag) -> Highlight {
298        Highlight::new(tag)
299    }
300}
301
302impl From<HlOperator> for Highlight {
303    fn from(op: HlOperator) -> Highlight {
304        Highlight::new(HlTag::Operator(op))
305    }
306}
307
308impl From<HlPunct> for Highlight {
309    fn from(punct: HlPunct) -> Highlight {
310        Highlight::new(HlTag::Punctuation(punct))
311    }
312}
313
314impl From<SymbolKind> for Highlight {
315    fn from(sym: SymbolKind) -> Highlight {
316        Highlight::new(HlTag::Symbol(sym))
317    }
318}
319
320impl Highlight {
321    pub(crate) fn new(tag: HlTag) -> Highlight {
322        Highlight { tag, mods: HlMods::default() }
323    }
324    pub fn is_empty(&self) -> bool {
325        self.tag == HlTag::None && self.mods.is_empty()
326    }
327}
328
329impl ops::BitOr<HlMod> for HlTag {
330    type Output = Highlight;
331
332    fn bitor(self, rhs: HlMod) -> Highlight {
333        Highlight::new(self) | rhs
334    }
335}
336
337impl ops::BitOrAssign<HlMod> for HlMods {
338    fn bitor_assign(&mut self, rhs: HlMod) {
339        self.0 |= rhs.mask();
340    }
341}
342
343impl ops::BitOrAssign<HlMod> for Highlight {
344    fn bitor_assign(&mut self, rhs: HlMod) {
345        self.mods |= rhs;
346    }
347}
348
349impl ops::BitOr<HlMod> for Highlight {
350    type Output = Highlight;
351
352    fn bitor(mut self, rhs: HlMod) -> Highlight {
353        self |= rhs;
354        self
355    }
356}
357
358impl HlMods {
359    pub fn is_empty(&self) -> bool {
360        self.0 == 0
361    }
362
363    pub fn contains(self, m: HlMod) -> bool {
364        self.0 & m.mask() == m.mask()
365    }
366
367    pub fn iter(self) -> impl Iterator<Item = HlMod> {
368        HlMod::ALL.iter().copied().filter(move |it| self.0 & it.mask() == it.mask())
369    }
370}