1use css_ast::{
2 CSSInt, Color, CssMetadata, Declaration, DeclarationValue, NodeId, NodeVisitor, PropertyRule, StyleRule,
3 ToChromashift, Visit, VisitNode, visitor,
4};
5use css_lexer::ToSpan;
6use css_parse::NodeWithMetadata;
7
8use crate::{SemanticDecoration, SemanticKind, SemanticModifier, TokenHighlighter};
9
10impl SemanticKind {
11 pub fn from_node_id(node_id: NodeId) -> Option<Self> {
14 use css_ast::NodeId;
15 match node_id {
16 NodeId::Tag
18 | NodeId::HtmlTag
19 | NodeId::HtmlNonConformingTag
20 | NodeId::HtmlNonStandardTag
21 | NodeId::SvgTag
22 | NodeId::MathmlTag
23 | NodeId::CustomElementTag
24 | NodeId::UnknownTag => Some(SemanticKind::Tag),
25 NodeId::Class => Some(SemanticKind::Class),
26 NodeId::Id => Some(SemanticKind::Id),
27 NodeId::PseudoClass
28 | NodeId::MozPseudoClass
29 | NodeId::MsPseudoClass
30 | NodeId::WebkitPseudoClass
31 | NodeId::OPseudoClass => Some(SemanticKind::PseudoClass),
32 NodeId::PseudoElement
33 | NodeId::MozPseudoElement
34 | NodeId::MsPseudoElement
35 | NodeId::WebkitPseudoElement
36 | NodeId::OPseudoElement => Some(SemanticKind::PseudoElement),
37 NodeId::Wildcard => Some(SemanticKind::Wildcard),
38
39 NodeId::MediaRule
41 | NodeId::KeyframesRule
42 | NodeId::SupportsRule
43 | NodeId::FontFaceRule
44 | NodeId::FontFeatureValuesRule
45 | NodeId::FontFeatureValueTypeRule
46 | NodeId::FontPaletteValuesRule
47 | NodeId::ContainerRule
48 | NodeId::PageRule
49 | NodeId::LayerRule
50 | NodeId::MarginRule
51 | NodeId::WebkitKeyframesRule
52 | NodeId::DocumentRule
53 | NodeId::MozDocumentRule
54 | NodeId::PropertyRule
55 | NodeId::CounterStyleRule
56 | NodeId::NamespaceRule
57 | NodeId::ColorProfileRule
58 | NodeId::ScopeRule
59 | NodeId::CustomMediaRule
60 | NodeId::StartingStyleRule => Some(SemanticKind::AtKeyword),
61
62 NodeId::Color => Some(SemanticKind::StyleValueColor),
64 NodeId::Url => Some(SemanticKind::StyleValueUrl),
65 NodeId::Length
66 | NodeId::LengthPercentage
67 | NodeId::Angle
68 | NodeId::Time
69 | NodeId::Flex
70 | NodeId::Percentage
71 | NodeId::Decibel => Some(SemanticKind::StyleValueDimension),
72
73 _ => None,
75 }
76 }
77}
78
79impl From<&CssMetadata> for SemanticModifier {
80 fn from(metadata: &CssMetadata) -> Self {
81 use css_ast::NodeKinds;
82 let mut modifier = SemanticModifier::none();
83
84 if metadata.node_kinds.contains(NodeKinds::Unknown) {
85 modifier |= SemanticModifier::Unknown;
86 }
87 if metadata.node_kinds.contains(NodeKinds::Deprecated) {
88 modifier |= SemanticModifier::Deprecated;
89 }
90 if metadata.node_kinds.contains(NodeKinds::Experimental) {
91 modifier |= SemanticModifier::Experimental;
92 }
93 if metadata.node_kinds.contains(NodeKinds::NonStandard) {
94 modifier |= SemanticModifier::Vendor;
95 }
96 if metadata.node_kinds.contains(NodeKinds::Custom) {
97 modifier |= SemanticModifier::Custom;
98 }
99 if metadata.has_vendor_prefixes() {
100 modifier |= SemanticModifier::Vendor;
101 }
102
103 modifier
104 }
105}
106
107#[visitor]
108impl Visit for TokenHighlighter {
109 fn enter_node(&mut self, query: VisitNode) {
110 let node_id = query.node_id.unwrap();
111 let modifier = SemanticModifier::from(&query.self_metadata());
112 let kind = SemanticKind::from_node_id(node_id).unwrap_or(SemanticKind::None);
113
114 if !modifier.is_none() || kind != SemanticKind::None {
115 self.insert(query.span(), kind, modifier);
116 }
117 }
118
119 fn visit_style_rule<'a>(&mut self, rule: &StyleRule<'a>) {
121 self.insert(rule.rule.block.open_curly.to_span(), SemanticKind::Punctuation, SemanticModifier::none());
122 if let Some(close) = rule.rule.block.close_curly {
123 self.insert(close.to_span(), SemanticKind::Punctuation, SemanticModifier::none());
124 }
125 }
126
127 fn enter_declaration<'a, T: DeclarationValue<'a, CssMetadata>>(
129 &mut self,
130 property: &Declaration<'a, T, CssMetadata>,
131 _query: css_ast::VisitNode,
132 ) {
133 let metadata = property.metadata();
134 let modifier = SemanticModifier::from(&metadata);
135 self.insert(property.name.to_span(), SemanticKind::Declaration, modifier);
136 self.insert(property.colon.to_span(), SemanticKind::Punctuation, SemanticModifier::none());
137 }
138
139 fn visit_property_rule<'a>(&mut self, property: &PropertyRule<'a>) {
141 self.insert(property.name.to_span(), SemanticKind::AtKeyword, SemanticModifier::none());
142 self.insert(property.prelude.to_span(), SemanticKind::Declaration, SemanticModifier::Custom);
143 }
144
145 fn visit_color<'b>(&mut self, color: &Color<'b>) {
147 let metadata = color.metadata();
148 let modifier = SemanticModifier::from(&metadata);
149 if let Some(bg) = color.to_chromashift() {
150 let swatch = SemanticDecoration::BackgroundColor(bg.into());
151 self.insert_with_decoration(color.to_span(), SemanticKind::StyleValueColor, modifier, swatch);
152 } else {
153 self.insert(color.to_span(), SemanticKind::StyleValueColor, modifier);
154 }
155 }
156
157 fn visit_css_int(&mut self, int: &CSSInt) {
159 self.insert(int.to_span(), SemanticKind::StyleValueNumber, SemanticModifier::none());
160 }
161}