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    /// Doc-strings like this one.
71    Documentation,
72    /// Highlighting injection like rust code in doc strings or ra_fixture.
73    Injected,
74    /// Used for intra doc links in doc injection.
75    IntraDocLink,
76    /// Used for items from other crates.
77    Library,
78    /// Used to differentiate individual elements within macro calls.
79    Macro,
80    /// Used to differentiate individual elements within proc-macro calls.
81    ProcMacro,
82    /// Mutable binding.
83    Mutable,
84    /// Used for public items.
85    Public,
86    /// Immutable reference.
87    Reference,
88    /// Used for associated items, except Methods. (Some languages call these static members)
89    Static,
90    /// Used for items in traits and trait impls.
91    Trait,
92    // Keep this last!
93    /// Used for unsafe functions, unsafe traits, mutable statics, union accesses and unsafe operations.
94    Unsafe,
95}
96
97#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
98pub enum HlPunct {
99    /// []
100    Bracket,
101    /// {}
102    Brace,
103    /// ()
104    Parenthesis,
105    /// <>
106    Angle,
107    /// ,
108    Comma,
109    /// .
110    Dot,
111    /// :
112    Colon,
113    /// ;
114    Semi,
115    /// ! (only for macro calls)
116    MacroBang,
117    /// Other punctutations
118    Other,
119}
120
121#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
122pub enum HlOperator {
123    /// |, &, !, ^, |=, &=, ^=
124    Bitwise,
125    /// +, -, *, /, +=, -=, *=, /=
126    Arithmetic,
127    /// &&, ||, !
128    Logical,
129    /// >, <, ==, >=, <=, !=
130    Comparison,
131    /// Other operators
132    Other,
133}
134
135impl HlTag {
136    fn as_str(self) -> &'static str {
137        match self {
138            HlTag::Symbol(symbol) => match symbol {
139                SymbolKind::Attribute => "attribute",
140                SymbolKind::BuiltinAttr => "builtin_attr",
141                SymbolKind::Const => "constant",
142                SymbolKind::ConstParam => "const_param",
143                SymbolKind::Derive => "derive",
144                SymbolKind::DeriveHelper => "derive_helper",
145                SymbolKind::Enum => "enum",
146                SymbolKind::Field => "field",
147                SymbolKind::Function => "function",
148                SymbolKind::Impl => "self_type",
149                SymbolKind::InlineAsmRegOrRegClass => "reg",
150                SymbolKind::Label => "label",
151                SymbolKind::LifetimeParam => "lifetime",
152                SymbolKind::Local => "variable",
153                SymbolKind::Macro => "macro",
154                SymbolKind::Method => "method",
155                SymbolKind::ProcMacro => "proc_macro",
156                SymbolKind::Module => "module",
157                SymbolKind::SelfParam => "self_keyword",
158                SymbolKind::SelfType => "self_type_keyword",
159                SymbolKind::Static => "static",
160                SymbolKind::Struct => "struct",
161                SymbolKind::ToolModule => "tool_module",
162                SymbolKind::Trait => "trait",
163                SymbolKind::TypeAlias => "type_alias",
164                SymbolKind::TypeParam => "type_param",
165                SymbolKind::Union => "union",
166                SymbolKind::ValueParam => "value_param",
167                SymbolKind::Variant => "enum_variant",
168            },
169            HlTag::AttributeBracket => "attribute_bracket",
170            HlTag::BoolLiteral => "bool_literal",
171            HlTag::BuiltinType => "builtin_type",
172            HlTag::ByteLiteral => "byte_literal",
173            HlTag::CharLiteral => "char_literal",
174            HlTag::Comment => "comment",
175            HlTag::EscapeSequence => "escape_sequence",
176            HlTag::InvalidEscapeSequence => "invalid_escape_sequence",
177            HlTag::FormatSpecifier => "format_specifier",
178            HlTag::Keyword => "keyword",
179            HlTag::Punctuation(punct) => match punct {
180                HlPunct::Bracket => "bracket",
181                HlPunct::Brace => "brace",
182                HlPunct::Parenthesis => "parenthesis",
183                HlPunct::Angle => "angle",
184                HlPunct::Comma => "comma",
185                HlPunct::Dot => "dot",
186                HlPunct::Colon => "colon",
187                HlPunct::Semi => "semicolon",
188                HlPunct::MacroBang => "macro_bang",
189                HlPunct::Other => "punctuation",
190            },
191            HlTag::NumericLiteral => "numeric_literal",
192            HlTag::Operator(op) => match op {
193                HlOperator::Bitwise => "bitwise",
194                HlOperator::Arithmetic => "arithmetic",
195                HlOperator::Logical => "logical",
196                HlOperator::Comparison => "comparison",
197                HlOperator::Other => "operator",
198            },
199            HlTag::StringLiteral => "string_literal",
200            HlTag::UnresolvedReference => "unresolved_reference",
201            HlTag::None => "none",
202        }
203    }
204}
205
206impl fmt::Display for HlTag {
207    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208        fmt::Display::fmt(self.as_str(), f)
209    }
210}
211
212impl HlMod {
213    const ALL: &'static [HlMod; HlMod::Unsafe as usize + 1] = &[
214        HlMod::Associated,
215        HlMod::Async,
216        HlMod::Attribute,
217        HlMod::Callable,
218        HlMod::Const,
219        HlMod::Consuming,
220        HlMod::ControlFlow,
221        HlMod::CrateRoot,
222        HlMod::DefaultLibrary,
223        HlMod::Definition,
224        HlMod::Documentation,
225        HlMod::Injected,
226        HlMod::IntraDocLink,
227        HlMod::Library,
228        HlMod::Macro,
229        HlMod::Mutable,
230        HlMod::ProcMacro,
231        HlMod::Public,
232        HlMod::Reference,
233        HlMod::Static,
234        HlMod::Trait,
235        HlMod::Unsafe,
236    ];
237
238    fn as_str(self) -> &'static str {
239        match self {
240            HlMod::Associated => "associated",
241            HlMod::Async => "async",
242            HlMod::Attribute => "attribute",
243            HlMod::Callable => "callable",
244            HlMod::Consuming => "consuming",
245            HlMod::Const => "const",
246            HlMod::ControlFlow => "control",
247            HlMod::CrateRoot => "crate_root",
248            HlMod::DefaultLibrary => "default_library",
249            HlMod::Definition => "declaration",
250            HlMod::Documentation => "documentation",
251            HlMod::Injected => "injected",
252            HlMod::IntraDocLink => "intra_doc_link",
253            HlMod::Library => "library",
254            HlMod::Macro => "macro",
255            HlMod::ProcMacro => "proc_macro",
256            HlMod::Mutable => "mutable",
257            HlMod::Public => "public",
258            HlMod::Reference => "reference",
259            HlMod::Static => "static",
260            HlMod::Trait => "trait",
261            HlMod::Unsafe => "unsafe",
262        }
263    }
264
265    fn mask(self) -> u32 {
266        debug_assert!(Self::ALL.len() <= 32, "HlMod::mask is not enough to cover all variants");
267        1 << (self as u32)
268    }
269}
270
271impl fmt::Display for HlMod {
272    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273        fmt::Display::fmt(self.as_str(), f)
274    }
275}
276
277impl fmt::Display for Highlight {
278    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279        self.tag.fmt(f)?;
280        for modifier in self.mods.iter() {
281            f.write_char('.')?;
282            modifier.fmt(f)?;
283        }
284        Ok(())
285    }
286}
287
288impl From<HlTag> for Highlight {
289    fn from(tag: HlTag) -> Highlight {
290        Highlight::new(tag)
291    }
292}
293
294impl From<HlOperator> for Highlight {
295    fn from(op: HlOperator) -> Highlight {
296        Highlight::new(HlTag::Operator(op))
297    }
298}
299
300impl From<HlPunct> for Highlight {
301    fn from(punct: HlPunct) -> Highlight {
302        Highlight::new(HlTag::Punctuation(punct))
303    }
304}
305
306impl From<SymbolKind> for Highlight {
307    fn from(sym: SymbolKind) -> Highlight {
308        Highlight::new(HlTag::Symbol(sym))
309    }
310}
311
312impl Highlight {
313    pub(crate) fn new(tag: HlTag) -> Highlight {
314        Highlight { tag, mods: HlMods::default() }
315    }
316    pub fn is_empty(&self) -> bool {
317        self.tag == HlTag::None && self.mods.is_empty()
318    }
319}
320
321impl ops::BitOr<HlMod> for HlTag {
322    type Output = Highlight;
323
324    fn bitor(self, rhs: HlMod) -> Highlight {
325        Highlight::new(self) | rhs
326    }
327}
328
329impl ops::BitOrAssign<HlMod> for HlMods {
330    fn bitor_assign(&mut self, rhs: HlMod) {
331        self.0 |= rhs.mask();
332    }
333}
334
335impl ops::BitOrAssign<HlMod> for Highlight {
336    fn bitor_assign(&mut self, rhs: HlMod) {
337        self.mods |= rhs;
338    }
339}
340
341impl ops::BitOr<HlMod> for Highlight {
342    type Output = Highlight;
343
344    fn bitor(mut self, rhs: HlMod) -> Highlight {
345        self |= rhs;
346        self
347    }
348}
349
350impl HlMods {
351    pub fn is_empty(&self) -> bool {
352        self.0 == 0
353    }
354
355    pub fn contains(self, m: HlMod) -> bool {
356        self.0 & m.mask() == m.mask()
357    }
358
359    pub fn iter(self) -> impl Iterator<Item = HlMod> {
360        HlMod::ALL.iter().copied().filter(move |it| self.0 & it.mask() == it.mask())
361    }
362}