csskit_highlight/
lib.rs

1#![deny(warnings)]
2use bitmask_enum::bitmask;
3use chromashift::Hex;
4use core::fmt;
5use css_lexer::Span;
6use std::collections::HashMap;
7use strum::{Display, VariantNames};
8
9mod css;
10
11#[cfg(any(feature = "anstyle", feature = "owo-colors"))]
12mod default_ansi_theme;
13#[cfg(any(feature = "anstyle", feature = "owo-colors"))]
14pub use default_ansi_theme::{AnsiTheme, DefaultAnsiTheme};
15
16#[cfg(any(feature = "anstyle", feature = "owo-colors"))]
17mod ansi_highlight_cursor_stream;
18#[cfg(any(feature = "anstyle", feature = "owo-colors"))]
19pub use ansi_highlight_cursor_stream::AnsiHighlightCursorStream;
20
21#[cfg(feature = "miette")]
22mod highlight;
23#[cfg(feature = "miette")]
24pub use highlight::CssHighlighter;
25
26#[cfg(test)]
27mod test_helpers;
28#[cfg(test)]
29mod tests;
30
31// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#semanticTokenTypes
32#[derive(Display, VariantNames, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub enum SemanticKind {
34	None,
35
36	/* Selector Elements */
37	Id,
38	Tag,
39	Class,
40	Wildcard,
41	Attribute,
42	Namespace,
43	Combinator,
44	PseudoClass,
45	PseudoElement,
46	LegacyPseudoElement,
47	FunctionalPseudoClass,
48	FunctionalPseudoElement,
49
50	/* Rule Elements */
51	AtKeyword,
52	Prelude,
53
54	/* Property Declarations */
55	Declaration,
56	StyleValueKeyword,
57	StyleValueDimension,
58	StyleValueNumber,
59	StyleValueString,
60	StyleValueUrl,
61	StyleValueColor,
62	StyleValueFunction,
63	StyleValueImportant,
64
65	Punctuation,
66}
67
68impl SemanticKind {
69	pub fn bits(&self) -> u8 {
70		*self as u8
71	}
72}
73
74// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#semanticTokenModifiers
75#[derive(VariantNames)]
76#[bitmask(u8)]
77pub enum SemanticModifier {
78	Unknown,
79	Deprecated,
80	Experimental,
81	Vendor,
82	Custom,
83}
84
85impl fmt::Display for SemanticModifier {
86	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87		if self.contains(Self::Unknown) {
88			write!(f, " unknown")?;
89		}
90		if self.contains(Self::Deprecated) {
91			write!(f, " deprecated")?;
92		}
93		if self.contains(Self::Experimental) {
94			write!(f, " experimental")?;
95		}
96		if self.contains(Self::Experimental) {
97			write!(f, " vendor")?;
98		}
99		if self.contains(Self::Custom) {
100			write!(f, " custom")?;
101		}
102		Ok(())
103	}
104}
105
106#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
107pub enum SemanticDecoration {
108	None,
109	BackgroundColor(Hex),
110}
111
112#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
113pub struct Highlight {
114	kind: SemanticKind,
115	modifier: SemanticModifier,
116	decoration: SemanticDecoration,
117	span: Span,
118}
119
120impl Highlight {
121	#[inline(always)]
122	pub fn span(&self) -> Span {
123		self.span
124	}
125
126	#[inline(always)]
127	pub fn modifier(&self) -> SemanticModifier {
128		self.modifier
129	}
130
131	#[inline(always)]
132	pub fn kind(&self) -> SemanticKind {
133		self.kind
134	}
135
136	#[inline(always)]
137	pub fn decoration(&self) -> SemanticDecoration {
138		self.decoration
139	}
140}
141
142#[derive(Default)]
143pub struct TokenHighlighter {
144	highlights: HashMap<Span, Highlight>,
145}
146
147impl TokenHighlighter {
148	pub fn new() -> Self {
149		Self { highlights: HashMap::new() }
150	}
151
152	pub fn get(&self, span: Span) -> Option<&Highlight> {
153		self.highlights.get(&span)
154	}
155
156	pub fn highlights(&self) -> impl Iterator<Item = &Highlight> {
157		self.highlights.values()
158	}
159
160	fn insert(&mut self, span: Span, kind: SemanticKind, modifier: SemanticModifier) {
161		self.insert_with_decoration(span, kind, modifier, SemanticDecoration::None);
162	}
163
164	fn insert_with_decoration(
165		&mut self,
166		span: Span,
167		kind: SemanticKind,
168		modifier: SemanticModifier,
169		decoration: SemanticDecoration,
170	) {
171		self.highlights.insert(span, Highlight { span, kind, modifier, decoration });
172	}
173}