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