1use css_ast::{
2 CSSInt, Color, CssMetadata, Declaration, DeclarationValue, NodeId, PropertyRule, QueryableNode, StyleRule,
3 ToChromashift, Visit,
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::ContainerRule
45 | NodeId::PageRule
46 | NodeId::LayerRule
47 | NodeId::MarginRule
48 | NodeId::WebkitKeyframesRule
49 | NodeId::DocumentRule
50 | NodeId::MozDocumentRule
51 | NodeId::PropertyRule
52 | NodeId::CounterStyleRule
53 | NodeId::NamespaceRule
54 | NodeId::StartingStyleRule => Some(SemanticKind::AtKeyword),
55
56 NodeId::Color => Some(SemanticKind::StyleValueColor),
58 NodeId::Url => Some(SemanticKind::StyleValueUrl),
59 NodeId::Length
60 | NodeId::LengthPercentage
61 | NodeId::Angle
62 | NodeId::Time
63 | NodeId::Flex
64 | NodeId::Percentage
65 | NodeId::Decibel => Some(SemanticKind::StyleValueDimension),
66
67 _ => None,
69 }
70 }
71}
72
73impl From<&CssMetadata> for SemanticModifier {
74 fn from(metadata: &CssMetadata) -> Self {
75 use css_ast::NodeKinds;
76 let mut modifier = SemanticModifier::none();
77
78 if metadata.node_kinds.contains(NodeKinds::Unknown) {
79 modifier |= SemanticModifier::Unknown;
80 }
81 if metadata.node_kinds.contains(NodeKinds::Deprecated) {
82 modifier |= SemanticModifier::Deprecated;
83 }
84 if metadata.node_kinds.contains(NodeKinds::Experimental) {
85 modifier |= SemanticModifier::Experimental;
86 }
87 if metadata.node_kinds.contains(NodeKinds::NonStandard) {
88 modifier |= SemanticModifier::Vendor;
89 }
90 if metadata.node_kinds.contains(NodeKinds::Custom) {
91 modifier |= SemanticModifier::Custom;
92 }
93 if metadata.has_vendor_prefixes() {
94 modifier |= SemanticModifier::Vendor;
95 }
96
97 modifier
98 }
99}
100
101impl Visit for TokenHighlighter {
102 fn visit_queryable_node<T: QueryableNode>(&mut self, node: &T) {
103 let node_id = node.node_id();
104 let metadata = node.metadata();
105 let modifier = SemanticModifier::from(&metadata);
106 let kind = SemanticKind::from_node_id(node_id).unwrap_or(SemanticKind::None);
107
108 if !modifier.is_none() || kind != SemanticKind::None {
109 self.insert(node.to_span(), kind, modifier);
110 }
111 }
112
113 fn visit_style_rule<'a>(&mut self, rule: &StyleRule<'a>) {
115 self.insert(rule.rule.block.open_curly.to_span(), SemanticKind::Punctuation, SemanticModifier::none());
116 if let Some(close) = rule.rule.block.close_curly {
117 self.insert(close.to_span(), SemanticKind::Punctuation, SemanticModifier::none());
118 }
119 }
120
121 fn visit_declaration<'a, T: DeclarationValue<'a, CssMetadata>>(
123 &mut self,
124 property: &Declaration<'a, T, CssMetadata>,
125 ) {
126 let metadata = property.metadata();
127 let modifier = SemanticModifier::from(&metadata);
128 self.insert(property.name.to_span(), SemanticKind::Declaration, modifier);
129 self.insert(property.colon.to_span(), SemanticKind::Punctuation, SemanticModifier::none());
130 }
131
132 fn visit_property_rule<'a>(&mut self, property: &PropertyRule<'a>) {
134 self.insert(property.name.to_span(), SemanticKind::AtKeyword, SemanticModifier::none());
135 self.insert(property.prelude.to_span(), SemanticKind::Declaration, SemanticModifier::Custom);
136 }
137
138 fn visit_color(&mut self, color: &Color) {
140 let metadata = color.metadata();
141 let modifier = SemanticModifier::from(&metadata);
142 if let Some(bg) = color.to_chromashift() {
143 let swatch = SemanticDecoration::BackgroundColor(bg.into());
144 self.insert_with_decoration(color.to_span(), SemanticKind::StyleValueColor, modifier, swatch);
145 } else {
146 self.insert(color.to_span(), SemanticKind::StyleValueColor, modifier);
147 }
148 }
149
150 fn visit_css_int(&mut self, int: &CSSInt) {
152 self.insert(int.to_span(), SemanticKind::StyleValueNumber, SemanticModifier::none());
153 }
154}