Skip to main content

csskit_highlight/
lib.rs

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