Skip to main content

omena_syntax/
lib.rs

1//! Shared syntax vocabulary for the Omena CSS-family parser stack.
2//!
3//! This crate is intentionally substrate-only: it defines stable syntax kind
4//! ranges, CST integration, and shared lexical identity without constructing
5//! parser-owned syntax trees.
6//!
7//! syntax_kind_extraction_decision: keep `SyntaxKind` extracted in
8//! `omena-syntax`; parser, semantic, resolver, LSP, and checker layers consume
9//! this crate instead of re-declaring local node/token taxonomies.
10
11use cstree::{RawSyntaxKind, Syntax};
12
13pub mod ident;
14mod keyword;
15mod layer;
16mod selector;
17
18pub use keyword::{CssKeywordText, css_keyword};
19pub use layer::{CanonicalLayerIdentifierKeyV0, LayerPathV0};
20pub use selector::{
21    CanonicalCompoundSelectorV0, CanonicalSelectorAst, CanonicalSelectorBranchV0,
22    CanonicalSelectorCombinatorV0, CanonicalSelectorSpecificityWitnessV0, NestingTokenV0,
23};
24
25pub const TOKEN_START: u32 = 0x0000;
26pub const TOKEN_END: u32 = 0x03ff;
27pub const DIALECT_TOKEN_START: u32 = 0x0400;
28pub const DIALECT_TOKEN_END: u32 = 0x04ff;
29pub const NODE_START: u32 = 0x1000;
30pub const NODE_END: u32 = 0x13ff;
31pub const DIALECT_NODE_START: u32 = 0x1400;
32pub const DIALECT_NODE_END: u32 = 0x14ff;
33pub const BOGUS_START: u32 = 0x2000;
34pub const BOGUS_END: u32 = 0x20ff;
35pub const MARKER_START: u32 = 0x2100;
36pub const MARKER_END: u32 = 0x21ff;
37
38const _: () = {
39    assert!(TOKEN_END < DIALECT_TOKEN_START);
40    assert!(DIALECT_TOKEN_END < NODE_START);
41    assert!(NODE_END < DIALECT_NODE_START);
42    assert!(DIALECT_NODE_END < BOGUS_START);
43    assert!(BOGUS_END < MARKER_START);
44};
45
46pub type SyntaxNode<D = ()> = cstree::syntax::SyntaxNode<SyntaxKind, D>;
47pub type SyntaxToken<D = ()> = cstree::syntax::SyntaxToken<SyntaxKind, D>;
48pub type SyntaxElement<D = ()> = cstree::syntax::SyntaxElement<SyntaxKind, D>;
49pub type SyntaxElementRef<'a, D = ()> = cstree::syntax::SyntaxElementRef<'a, SyntaxKind, D>;
50
51macro_rules! syntax_kinds {
52    ($($name:ident = $value:literal,)+) => {
53        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
54        #[repr(u32)]
55        pub enum SyntaxKind {
56            $($name = $value,)+
57        }
58
59        impl SyntaxKind {
60            pub const ALL: &'static [Self] = &[$(Self::$name,)+];
61
62            pub const fn as_u32(self) -> u32 {
63                self as u32
64            }
65
66            pub fn from_raw_kind(raw: u32) -> Option<Self> {
67                match raw {
68                    $($value => Some(Self::$name),)+
69                    _ => None,
70                }
71            }
72
73            pub const fn is_token(self) -> bool {
74                let raw = self.as_u32();
75                (raw >= TOKEN_START && raw <= TOKEN_END)
76                    || (raw >= DIALECT_TOKEN_START && raw <= DIALECT_TOKEN_END)
77            }
78
79            pub const fn is_node(self) -> bool {
80                let raw = self.as_u32();
81                (raw >= NODE_START && raw <= NODE_END)
82                    || (raw >= DIALECT_NODE_START && raw <= DIALECT_NODE_END)
83            }
84
85            pub const fn is_bogus(self) -> bool {
86                let raw = self.as_u32();
87                raw >= BOGUS_START && raw <= BOGUS_END
88            }
89
90            pub const fn is_marker(self) -> bool {
91                let raw = self.as_u32();
92                raw >= MARKER_START && raw <= MARKER_END
93            }
94
95            pub const fn is_dialect_specific(self) -> bool {
96                let raw = self.as_u32();
97                (raw >= DIALECT_TOKEN_START && raw <= DIALECT_TOKEN_END)
98                    || (raw >= DIALECT_NODE_START && raw <= DIALECT_NODE_END)
99            }
100
101            pub const fn is_dialect(self) -> bool {
102                self.is_dialect_specific()
103            }
104
105            pub const fn is_trivia(self) -> bool {
106                matches!(
107                    self,
108                    Self::Whitespace
109                        | Self::LineComment
110                        | Self::BlockComment
111                        | Self::SassIndentedNewline
112                )
113            }
114        }
115    };
116}
117
118syntax_kinds! {
119    Whitespace = 0x0000,
120    LineComment = 0x0001,
121    BlockComment = 0x0002,
122    Ident = 0x0003,
123    Hash = 0x0004,
124    String = 0x0005,
125    BadString = 0x0006,
126    Url = 0x0007,
127    BadUrl = 0x0008,
128    Number = 0x0009,
129    Percentage = 0x000a,
130    Dimension = 0x000b,
131    UnicodeRange = 0x000c,
132    AtKeyword = 0x000d,
133    Delim = 0x000e,
134    Important = 0x000f,
135    Dot = 0x0010,
136    Comma = 0x0011,
137    Colon = 0x0012,
138    Semicolon = 0x0013,
139    LeftBrace = 0x0014,
140    RightBrace = 0x0015,
141    LeftParen = 0x0016,
142    RightParen = 0x0017,
143    LeftBracket = 0x0018,
144    RightBracket = 0x0019,
145    Plus = 0x001a,
146    Minus = 0x001b,
147    Star = 0x001c,
148    Slash = 0x001d,
149    Percent = 0x001e,
150    Equals = 0x001f,
151    Tilde = 0x0020,
152    Pipe = 0x0021,
153    Caret = 0x0022,
154    Dollar = 0x0023,
155    Ampersand = 0x0024,
156    GreaterThan = 0x0025,
157    LessThan = 0x0026,
158    PlusEquals = 0x0027,
159    MinusEquals = 0x0028,
160    StarEquals = 0x0029,
161    SlashEquals = 0x002a,
162    PipeEquals = 0x002b,
163    TildeEquals = 0x002c,
164    CaretEquals = 0x002d,
165    DollarEquals = 0x002e,
166    DoubleColon = 0x002f,
167    DoublePipe = 0x0030,
168    DoubleAmpersand = 0x0031,
169    Arrow = 0x0032,
170    IncludesMatch = 0x0033,
171    DashMatch = 0x0034,
172    PrefixMatch = 0x0035,
173    SuffixMatch = 0x0036,
174    SubstringMatch = 0x0037,
175    ColumnCombinator = 0x0038,
176    NestingSelector = 0x0039,
177    CustomPropertyName = 0x003a,
178    ClassName = 0x003b,
179    IdName = 0x003c,
180    KeywordAnd = 0x003d,
181    KeywordOr = 0x003e,
182    KeywordNot = 0x003f,
183    KeywordOnly = 0x0040,
184    KeywordFrom = 0x0041,
185    KeywordTo = 0x0042,
186    KeywordThrough = 0x0043,
187    KeywordImportant = 0x0044,
188    KeywordGlobal = 0x0045,
189    KeywordLocal = 0x0046,
190    KeywordExport = 0x0047,
191    KeywordImport = 0x0048,
192    KeywordComposes = 0x0049,
193    KeywordAs = 0x004a,
194    KeywordWith = 0x004b,
195    KeywordLayer = 0x004c,
196    KeywordSupports = 0x004d,
197    KeywordContainer = 0x004e,
198    KeywordScope = 0x004f,
199    KeywordMedia = 0x0050,
200    KeywordKeyframes = 0x0051,
201    KeywordCharset = 0x0052,
202    KeywordNamespace = 0x0053,
203    KeywordPage = 0x0054,
204    KeywordFontFace = 0x0055,
205    KeywordProperty = 0x0056,
206    KeywordStartingStyle = 0x0057,
207    KeywordWhen = 0x0058,
208    KeywordElse = 0x0059,
209    KeywordUse = 0x005a,
210    KeywordForward = 0x005b,
211    KeywordMixin = 0x005c,
212    KeywordInclude = 0x005d,
213    KeywordFunction = 0x005e,
214    KeywordReturn = 0x005f,
215    KeywordIf = 0x0060,
216    KeywordEach = 0x0061,
217    KeywordFor = 0x0062,
218    KeywordWhile = 0x0063,
219    KeywordIn = 0x0064,
220    Cdo = 0x0065,
221    Cdc = 0x0066,
222
223    ScssVariable = 0x0400,
224    ScssInterpolationStart = 0x0401,
225    ScssInterpolationEnd = 0x0402,
226    ScssSilentComment = 0x0403,
227    ScssPlaceholder = 0x0404,
228    ScssModuleNamespace = 0x0405,
229    SassIndentedNewline = 0x0406,
230    SassIndent = 0x0407,
231    SassDedent = 0x0408,
232    SassOptionalSemicolon = 0x0409,
233    LessVariable = 0x0410,
234    LessEscapedString = 0x0411,
235    LessDetachedRuleset = 0x0412,
236    LessMixinGuardWhen = 0x0413,
237    LessExtendKeyword = 0x0414,
238    LessNamespaceSeparator = 0x0415,
239    LessInterpolationStart = 0x0416,
240    LessInterpolationEnd = 0x0417,
241    LessPropertyVariableToken = 0x0418,
242    TemplateInterpolationStart = 0x0419,
243    TemplateInterpolationEnd = 0x041a,
244    TemplatePlaceholder = 0x041b,
245
246    Stylesheet = 0x1000,
247    Rule = 0x1001,
248    QualifiedRule = 0x1002,
249    Declaration = 0x1003,
250    DeclarationList = 0x1004,
251    RuleList = 0x1005,
252    SelectorList = 0x1006,
253    Selector = 0x1007,
254    ComplexSelector = 0x1008,
255    CompoundSelector = 0x1009,
256    ClassSelector = 0x100a,
257    IdSelector = 0x100b,
258    TypeSelector = 0x100c,
259    UniversalSelector = 0x100d,
260    AttributeSelector = 0x100e,
261    AttributeMatcher = 0x100f,
262    PseudoClassSelector = 0x1010,
263    PseudoElementSelector = 0x1011,
264    PseudoSelectorArgument = 0x1012,
265    NestingSelectorNode = 0x1013,
266    Combinator = 0x1014,
267    SelectorValue = 0x1015,
268    PropertyName = 0x1016,
269    CustomPropertyDeclaration = 0x1017,
270    Value = 0x1018,
271    ValueList = 0x1019,
272    FunctionCall = 0x101a,
273    FunctionArguments = 0x101b,
274    BinaryExpression = 0x101c,
275    UnaryExpression = 0x101d,
276    ParenthesizedExpression = 0x101e,
277    Interpolation = 0x101f,
278    DimensionValue = 0x1020,
279    ColorValue = 0x1021,
280    UrlValue = 0x1022,
281    VarFunction = 0x1023,
282    CalcFunction = 0x1024,
283    AtRule = 0x1025,
284    MediaRule = 0x1026,
285    SupportsRule = 0x1027,
286    ContainerRule = 0x1028,
287    LayerRule = 0x1029,
288    ScopeRule = 0x102a,
289    KeyframesRule = 0x102b,
290    KeyframeBlock = 0x102c,
291    FontFaceRule = 0x102d,
292    PageRule = 0x102e,
293    NamespaceRule = 0x102f,
294    ImportRule = 0x1030,
295    CharsetRule = 0x1031,
296    PropertyRule = 0x1032,
297    StartingStyleRule = 0x1033,
298    MediaQueryList = 0x1034,
299    MediaQuery = 0x1035,
300    MediaFeature = 0x1036,
301    SupportsCondition = 0x1037,
302    ContainerCondition = 0x1038,
303    LayerName = 0x1039,
304    ScopeRange = 0x103a,
305    CssModuleLocalBlock = 0x103b,
306    CssModuleGlobalBlock = 0x103c,
307    CssModuleExportBlock = 0x103d,
308    CssModuleImportBlock = 0x103e,
309    CssModuleComposesDeclaration = 0x103f,
310    CssModuleComposesTarget = 0x1040,
311    CssModuleFromClause = 0x1041,
312    TokenDefinition = 0x1042,
313    TokenReference = 0x1043,
314    Comment = 0x1044,
315    ErrorNode = 0x1045,
316    EnvFunction = 0x1046,
317    AttrFunction = 0x1047,
318    MathFunction = 0x1048,
319    PageMarginRule = 0x1049,
320    WhenRule = 0x104a,
321    ElseRule = 0x104b,
322    CounterStyleRule = 0x104c,
323    FontPaletteValuesRule = 0x104d,
324    ColorProfileRule = 0x104e,
325    PositionTryRule = 0x104f,
326    FontFeatureValuesRule = 0x1050,
327    FontFeatureValuesStylisticRule = 0x1051,
328    FontFeatureValuesStylesetRule = 0x1052,
329    FontFeatureValuesCharacterVariantRule = 0x1053,
330    FontFeatureValuesSwashRule = 0x1054,
331    FontFeatureValuesOrnamentsRule = 0x1055,
332    FontFeatureValuesAnnotationRule = 0x1056,
333    FontFeatureValuesHistoricalFormsRule = 0x1057,
334    ViewTransitionRule = 0x1058,
335    GradientFunction = 0x1059,
336    TransformFunction = 0x105a,
337    FilterFunction = 0x105b,
338    ImageFunction = 0x105c,
339    ShapeFunction = 0x105d,
340    AtRulePrelude = 0x105e,
341    NestRule = 0x105f,
342    CustomMediaRule = 0x1060,
343    IdentifierValue = 0x1061,
344    StringValue = 0x1062,
345    UnicodeRangeValue = 0x1063,
346    NumberValue = 0x1064,
347    PercentageValue = 0x1065,
348    BracketedValue = 0x1066,
349    ImportantAnnotation = 0x1067,
350    ComponentValue = 0x1068,
351    SimpleBlock = 0x1069,
352    ComponentValueList = 0x106a,
353    CommaSeparatedComponentValueList = 0x106b,
354    CustomPropertyValue = 0x106c,
355    AttributeName = 0x106d,
356    AttributeValue = 0x106e,
357    AttributeModifier = 0x106f,
358    NthSelectorArgument = 0x1070,
359    NthSelectorFormula = 0x1071,
360    NthSelectorOfSelectorList = 0x1072,
361    RelativeSelectorList = 0x1073,
362    RelativeSelector = 0x1074,
363    LanguageSelectorArgument = 0x1075,
364    LanguageTag = 0x1076,
365    DirectionalitySelectorArgument = 0x1077,
366    NamespacePrefix = 0x1078,
367    FunctionRule = 0x1079,
368    IfFunction = 0x107a,
369    IfRule = 0x107b,
370
371    ScssStylesheet = 0x1400,
372    ScssUseRule = 0x1401,
373    ScssForwardRule = 0x1402,
374    ScssMixinDeclaration = 0x1403,
375    ScssIncludeRule = 0x1404,
376    ScssFunctionDeclaration = 0x1405,
377    ScssReturnRule = 0x1406,
378    ScssVariableDeclaration = 0x1407,
379    ScssVariableReference = 0x1408,
380    ScssPlaceholderSelector = 0x1409,
381    ScssExtendRule = 0x140a,
382    ScssControlIf = 0x140b,
383    ScssControlElse = 0x140c,
384    ScssControlEach = 0x140d,
385    ScssControlFor = 0x140e,
386    ScssControlWhile = 0x140f,
387    ScssNestedProperty = 0x1410,
388    ScssModuleConfig = 0x1411,
389    SassIndentedBlock = 0x1412,
390    SassIndentedRule = 0x1413,
391    ScssAtRootRule = 0x1414,
392    ScssErrorRule = 0x1415,
393    ScssWarnRule = 0x1416,
394    ScssDebugRule = 0x1417,
395    ScssContentRule = 0x1418,
396    ScssVariableFlag = 0x1419,
397    LessStylesheet = 0x1420,
398    LessVariableDeclaration = 0x1421,
399    LessVariableReference = 0x1422,
400    LessMixinDeclaration = 0x1423,
401    LessMixinCall = 0x1424,
402    LessMixinGuard = 0x1425,
403    LessDetachedRulesetNode = 0x1426,
404    LessExtendRule = 0x1427,
405    LessNamespaceAccess = 0x1428,
406    LessPropertyVariable = 0x1429,
407    ScssMap = 0x142a,
408    ScssMapEntry = 0x142b,
409    ScssList = 0x142c,
410    ScssCondition = 0x142d,
411    LessCondition = 0x142e,
412
413    BogusToken = 0x2000,
414    BogusTrivia = 0x2001,
415    BogusRule = 0x2002,
416    BogusSelector = 0x2003,
417    BogusSelectorList = 0x2004,
418    BogusCompoundSelector = 0x2005,
419    BogusCombinator = 0x2006,
420    BogusDeclaration = 0x2007,
421    BogusDeclarationList = 0x2008,
422    BogusPropertyName = 0x2009,
423    BogusValue = 0x200a,
424    BogusValueList = 0x200b,
425    BogusFunctionCall = 0x200c,
426    BogusFunctionArguments = 0x200d,
427    BogusAtRule = 0x200e,
428    BogusMediaQuery = 0x200f,
429    BogusSupportsCondition = 0x2010,
430    BogusContainerCondition = 0x2011,
431    BogusLayerName = 0x2012,
432    BogusScopeRange = 0x2013,
433    BogusKeyframeBlock = 0x2014,
434    BogusCssModuleBlock = 0x2015,
435    BogusComposesDeclaration = 0x2016,
436    BogusComposesTarget = 0x2017,
437    BogusFromClause = 0x2018,
438    BogusInterpolation = 0x2019,
439    BogusScssVariable = 0x201a,
440    BogusScssMixin = 0x201b,
441    BogusScssFunction = 0x201c,
442    BogusScssControl = 0x201d,
443    BogusSassIndentation = 0x201e,
444    BogusLessVariable = 0x201f,
445    BogusLessMixin = 0x2020,
446    BogusLessGuard = 0x2021,
447    BogusLessDetachedRuleset = 0x2022,
448    BogusRecovery = 0x2023,
449    BogusScssModuleConfig = 0x2024,
450    BogusAtRulePrelude = 0x2025,
451    BogusBracketedValue = 0x2026,
452    BogusSimpleBlock = 0x2027,
453    BogusScssMap = 0x2028,
454    BogusScssMapEntry = 0x2029,
455    BogusScssList = 0x202a,
456    BogusScssCondition = 0x202b,
457    BogusLessCondition = 0x202c,
458
459    Root = 0x2100,
460    Eof = 0x2101,
461    Unknown = 0x21fe,
462    Tombstone = 0x21ff,
463}
464
465impl Syntax for SyntaxKind {
466    fn from_raw(raw: RawSyntaxKind) -> Self {
467        match Self::from_raw_kind(raw.0) {
468            Some(kind) => kind,
469            None => Self::Unknown,
470        }
471    }
472
473    fn into_raw(self) -> RawSyntaxKind {
474        RawSyntaxKind(self.as_u32())
475    }
476
477    fn static_text(self) -> Option<&'static str> {
478        match self {
479            Self::Dot => Some("."),
480            Self::Comma => Some(","),
481            Self::Colon => Some(":"),
482            Self::Semicolon => Some(";"),
483            Self::LeftBrace => Some("{"),
484            Self::RightBrace => Some("}"),
485            Self::LeftParen => Some("("),
486            Self::RightParen => Some(")"),
487            Self::LeftBracket => Some("["),
488            Self::RightBracket => Some("]"),
489            Self::Plus => Some("+"),
490            Self::Minus => Some("-"),
491            Self::Star => Some("*"),
492            Self::Slash => Some("/"),
493            Self::Percent => Some("%"),
494            Self::Equals => Some("="),
495            Self::Tilde => Some("~"),
496            Self::Pipe => Some("|"),
497            Self::Caret => Some("^"),
498            Self::Dollar => Some("$"),
499            Self::Ampersand => Some("&"),
500            Self::GreaterThan => Some(">"),
501            Self::LessThan => Some("<"),
502            Self::PlusEquals => Some("+="),
503            Self::MinusEquals => Some("-="),
504            Self::StarEquals => Some("*="),
505            Self::SlashEquals => Some("/="),
506            Self::PipeEquals => Some("|="),
507            Self::TildeEquals => Some("~="),
508            Self::CaretEquals => Some("^="),
509            Self::DollarEquals => Some("$="),
510            Self::DoubleColon => Some("::"),
511            Self::DoublePipe => Some("||"),
512            Self::DoubleAmpersand => Some("&&"),
513            Self::Arrow => Some("=>"),
514            Self::IncludesMatch => Some("~="),
515            Self::DashMatch => Some("|="),
516            Self::PrefixMatch => Some("^="),
517            Self::SuffixMatch => Some("$="),
518            Self::SubstringMatch => Some("*="),
519            Self::ColumnCombinator => Some("||"),
520            Self::KeywordAnd => Some("and"),
521            Self::KeywordOr => Some("or"),
522            Self::KeywordNot => Some("not"),
523            Self::KeywordOnly => Some("only"),
524            Self::KeywordFrom => Some("from"),
525            Self::KeywordTo => Some("to"),
526            Self::KeywordThrough => Some("through"),
527            Self::KeywordImportant => Some("important"),
528            Self::Cdo => Some("<!--"),
529            Self::Cdc => Some("-->"),
530            Self::KeywordGlobal => Some("global"),
531            Self::KeywordLocal => Some("local"),
532            Self::KeywordExport => Some("export"),
533            Self::KeywordImport => Some("import"),
534            Self::KeywordComposes => Some("composes"),
535            Self::KeywordAs => Some("as"),
536            Self::KeywordWith => Some("with"),
537            Self::KeywordLayer => Some("layer"),
538            Self::KeywordSupports => Some("supports"),
539            Self::KeywordContainer => Some("container"),
540            Self::KeywordScope => Some("scope"),
541            Self::KeywordMedia => Some("media"),
542            Self::KeywordKeyframes => Some("keyframes"),
543            Self::KeywordCharset => Some("charset"),
544            Self::KeywordNamespace => Some("namespace"),
545            Self::KeywordPage => Some("page"),
546            Self::KeywordFontFace => Some("font-face"),
547            Self::KeywordProperty => Some("property"),
548            Self::KeywordStartingStyle => Some("starting-style"),
549            Self::KeywordWhen => Some("when"),
550            Self::KeywordElse => Some("else"),
551            Self::KeywordUse => Some("use"),
552            Self::KeywordForward => Some("forward"),
553            Self::KeywordMixin => Some("mixin"),
554            Self::KeywordInclude => Some("include"),
555            Self::KeywordFunction => Some("function"),
556            Self::KeywordReturn => Some("return"),
557            Self::KeywordIf => Some("if"),
558            Self::KeywordEach => Some("each"),
559            Self::KeywordFor => Some("for"),
560            Self::KeywordWhile => Some("while"),
561            Self::KeywordIn => Some("in"),
562            Self::Eof => Some(""),
563            _ => None,
564        }
565    }
566}
567
568#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
569pub enum StyleDialect {
570    Css,
571    Scss,
572    Sass,
573    Less,
574}
575
576impl StyleDialect {
577    pub const ALL: &'static [Self] = &[Self::Css, Self::Scss, Self::Sass, Self::Less];
578}
579
580#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
581pub enum ModuleMode {
582    Plain,
583    CssModules,
584}
585
586impl ModuleMode {
587    pub const ALL: &'static [Self] = &[Self::Plain, Self::CssModules];
588}
589
590#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
591pub enum SymbolKind {
592    Class,
593    Id,
594    TypeSelector,
595    PlaceholderSelector,
596    Keyframes,
597    CustomProperty,
598    ScssVariable,
599    LessVariable,
600    Mixin,
601    Function,
602    ValueDeclaration,
603    ComposesTarget,
604    Namespace,
605    Layer,
606    Container,
607    Scope,
608    Import,
609    Export,
610    ModuleLocal,
611    ModuleGlobal,
612}
613
614impl SymbolKind {
615    pub const ALL: &'static [Self] = &[
616        Self::Class,
617        Self::Id,
618        Self::TypeSelector,
619        Self::PlaceholderSelector,
620        Self::Keyframes,
621        Self::CustomProperty,
622        Self::ScssVariable,
623        Self::LessVariable,
624        Self::Mixin,
625        Self::Function,
626        Self::ValueDeclaration,
627        Self::ComposesTarget,
628        Self::Namespace,
629        Self::Layer,
630        Self::Container,
631        Self::Scope,
632        Self::Import,
633        Self::Export,
634        Self::ModuleLocal,
635        Self::ModuleGlobal,
636    ];
637}
638
639#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
640pub enum ScopeKind {
641    File,
642    LocalBlock,
643    GlobalBlock,
644    SelectorBlock,
645    MixinBody,
646    FunctionBody,
647    AtRuleScope,
648    NestedRule,
649    ScopeAtRule,
650    MediaQuery,
651    SupportsQuery,
652    ContainerQuery,
653    CascadeLayer,
654    ModuleNamespace,
655    LessMixin,
656    SassControlFlow,
657    CssModuleExport,
658    CssModuleImport,
659}
660
661impl ScopeKind {
662    pub const ALL: &'static [Self] = &[
663        Self::File,
664        Self::LocalBlock,
665        Self::GlobalBlock,
666        Self::SelectorBlock,
667        Self::MixinBody,
668        Self::FunctionBody,
669        Self::AtRuleScope,
670        Self::NestedRule,
671        Self::ScopeAtRule,
672        Self::MediaQuery,
673        Self::SupportsQuery,
674        Self::ContainerQuery,
675        Self::CascadeLayer,
676        Self::ModuleNamespace,
677        Self::LessMixin,
678        Self::SassControlFlow,
679        Self::CssModuleExport,
680        Self::CssModuleImport,
681    ];
682}
683
684#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
685pub enum ReferenceKind {
686    Class,
687    Id,
688    TypeSelector,
689    PlaceholderSelector,
690    Keyframes,
691    ComposesTarget,
692    ComposesFrom,
693    CustomPropertyRead,
694    VarRead,
695    ValueRead,
696    Import,
697    Export,
698    MixinInclude,
699    FunctionCall,
700    NamespaceMember,
701    Layer,
702    Container,
703    SelectorExtends,
704    CssModuleAccess,
705    CssModuleToken,
706}
707
708impl ReferenceKind {
709    pub const ALL: &'static [Self] = &[
710        Self::Class,
711        Self::Id,
712        Self::TypeSelector,
713        Self::PlaceholderSelector,
714        Self::Keyframes,
715        Self::ComposesTarget,
716        Self::ComposesFrom,
717        Self::CustomPropertyRead,
718        Self::VarRead,
719        Self::ValueRead,
720        Self::Import,
721        Self::Export,
722        Self::MixinInclude,
723        Self::FunctionCall,
724        Self::NamespaceMember,
725        Self::Layer,
726        Self::Container,
727        Self::SelectorExtends,
728        Self::CssModuleAccess,
729        Self::CssModuleToken,
730    ];
731}
732
733#[derive(Debug, Clone, PartialEq, Eq)]
734pub struct OmenaSyntaxBoundarySummaryV0 {
735    pub schema_version: &'static str,
736    pub product: &'static str,
737    pub phase: &'static str,
738    pub syntax_kind_owner_crate: &'static str,
739    pub parser_consumer_policy: &'static str,
740    pub syntax_kind_count: usize,
741    pub token_kind_count: usize,
742    pub node_kind_count: usize,
743    pub bogus_kind_count: usize,
744    pub marker_kind_count: usize,
745    pub dialect_kind_count: usize,
746    pub style_dialect_count: usize,
747    pub module_mode_count: usize,
748    pub symbol_kind_count: usize,
749    pub scope_kind_count: usize,
750    pub reference_kind_count: usize,
751    pub cstree_integration_ready: bool,
752    pub ready_surfaces: Vec<&'static str>,
753    pub next_surfaces: Vec<&'static str>,
754}
755
756pub fn summarize_omena_syntax_boundary() -> OmenaSyntaxBoundarySummaryV0 {
757    OmenaSyntaxBoundarySummaryV0 {
758        schema_version: "0",
759        product: "omena-syntax.boundary",
760        phase: "h1-alpha-syntax-substrate",
761        syntax_kind_owner_crate: "omena-syntax",
762        parser_consumer_policy: "parserConsumesOmenaSyntaxKindNoLocalTaxonomy",
763        syntax_kind_count: SyntaxKind::ALL.len(),
764        token_kind_count: SyntaxKind::ALL
765            .iter()
766            .filter(|kind| kind.is_token())
767            .count(),
768        node_kind_count: SyntaxKind::ALL.iter().filter(|kind| kind.is_node()).count(),
769        bogus_kind_count: SyntaxKind::ALL
770            .iter()
771            .filter(|kind| kind.is_bogus())
772            .count(),
773        marker_kind_count: SyntaxKind::ALL
774            .iter()
775            .filter(|kind| kind.is_marker())
776            .count(),
777        dialect_kind_count: SyntaxKind::ALL
778            .iter()
779            .filter(|kind| kind.is_dialect())
780            .count(),
781        style_dialect_count: StyleDialect::ALL.len(),
782        module_mode_count: ModuleMode::ALL.len(),
783        symbol_kind_count: SymbolKind::ALL.len(),
784        scope_kind_count: ScopeKind::ALL.len(),
785        reference_kind_count: ReferenceKind::ALL.len(),
786        cstree_integration_ready: SyntaxKind::Ident.into_raw()
787            == RawSyntaxKind(SyntaxKind::Ident.as_u32())
788            && SyntaxKind::from_raw(RawSyntaxKind(SyntaxKind::Ident.as_u32())) == SyntaxKind::Ident,
789        ready_surfaces: vec![
790            "rangeDividedSyntaxKind",
791            "symbolScopeReferenceVocabulary",
792            "styleDialectAndModuleMode",
793            "cstreeRawKindBridge",
794            "bogusRecoveryKindSuperset",
795            "semanticSoaTables",
796            "parserCstEquivalence",
797        ],
798        next_surfaces: Vec::new(),
799    }
800}
801
802#[cfg(test)]
803mod tests {
804    use super::*;
805
806    #[test]
807    fn syntax_kind_ranges_are_disjoint() {
808        let mut raws: Vec<u32> = SyntaxKind::ALL.iter().map(|kind| kind.as_u32()).collect();
809        raws.sort_unstable();
810
811        for pair in raws.windows(2) {
812            assert_ne!(pair[0], pair[1]);
813        }
814    }
815
816    #[test]
817    fn classifies_token_node_bogus_marker_ranges() {
818        assert!(SyntaxKind::Ident.is_token());
819        assert!(SyntaxKind::ScssVariable.is_token());
820        assert!(SyntaxKind::Selector.is_node());
821        assert!(SyntaxKind::LessMixinCall.is_node());
822        assert!(SyntaxKind::BogusSelector.is_bogus());
823        assert!(SyntaxKind::Root.is_marker());
824        assert!(SyntaxKind::Whitespace.is_trivia());
825        assert!(SyntaxKind::ScssUseRule.is_dialect_specific());
826    }
827
828    #[test]
829    fn declares_four_style_dialects_and_module_modes() {
830        assert_eq!(StyleDialect::ALL.len(), 4);
831        assert_eq!(ModuleMode::ALL.len(), 2);
832    }
833
834    #[test]
835    fn cstree_round_trip_preserves_known_kinds() {
836        for kind in [
837            SyntaxKind::Ident,
838            SyntaxKind::Selector,
839            SyntaxKind::ScssUseRule,
840            SyntaxKind::BogusLessGuard,
841            SyntaxKind::Root,
842        ] {
843            let raw = kind.into_raw();
844            assert_eq!(SyntaxKind::from_raw(raw), kind);
845        }
846    }
847
848    #[test]
849    fn syntax_kind_raw_decode_round_trips_every_declared_kind() {
850        for kind in SyntaxKind::ALL {
851            assert_eq!(SyntaxKind::from_raw_kind(kind.as_u32()), Some(*kind));
852            assert_eq!(SyntaxKind::from_raw(kind.into_raw()), *kind);
853        }
854    }
855
856    #[test]
857    fn syntax_kind_raw_decode_rejects_unassigned_raw_values() {
858        let assigned: std::collections::BTreeSet<u32> =
859            SyntaxKind::ALL.iter().map(|kind| kind.as_u32()).collect();
860        let rejected: Vec<u32> = (TOKEN_START..=MARKER_END)
861            .filter(|raw| !assigned.contains(raw))
862            .take(8)
863            .collect();
864
865        assert_eq!(rejected.len(), 8);
866        for raw in rejected {
867            assert_eq!(SyntaxKind::from_raw_kind(raw), None);
868            assert_eq!(
869                SyntaxKind::from_raw(RawSyntaxKind(raw)),
870                SyntaxKind::Unknown
871            );
872        }
873    }
874
875    #[test]
876    fn declares_bogus_superset_contract() {
877        let bogus_count = SyntaxKind::ALL
878            .iter()
879            .filter(|kind| kind.is_bogus())
880            .count();
881
882        assert!(bogus_count >= 33);
883    }
884
885    #[test]
886    fn syntax_kind_count_tracks_phase_alpha_contract() {
887        let token_count = SyntaxKind::ALL
888            .iter()
889            .filter(|kind| kind.is_token())
890            .count();
891        let node_count = SyntaxKind::ALL.iter().filter(|kind| kind.is_node()).count();
892
893        assert!(SyntaxKind::ALL.len() >= 160);
894        assert!(token_count >= 80);
895        assert!(node_count >= 80);
896    }
897
898    #[test]
899    fn summarizes_phase_alpha_boundary_contract() {
900        let summary = summarize_omena_syntax_boundary();
901
902        assert_eq!(summary.product, "omena-syntax.boundary");
903        assert_eq!(summary.phase, "h1-alpha-syntax-substrate");
904        assert_eq!(summary.syntax_kind_owner_crate, "omena-syntax");
905        assert_eq!(
906            summary.parser_consumer_policy,
907            "parserConsumesOmenaSyntaxKindNoLocalTaxonomy"
908        );
909        assert!(summary.syntax_kind_count >= 160);
910        assert!(summary.bogus_kind_count >= 33);
911        assert_eq!(summary.style_dialect_count, 4);
912        assert_eq!(summary.module_mode_count, 2);
913        assert_eq!(summary.symbol_kind_count, SymbolKind::ALL.len());
914        assert_eq!(summary.scope_kind_count, ScopeKind::ALL.len());
915        assert_eq!(summary.reference_kind_count, ReferenceKind::ALL.len());
916        assert!(summary.cstree_integration_ready);
917        assert!(SyntaxKind::ScssUseRule.is_dialect());
918        assert!(
919            summary
920                .ready_surfaces
921                .contains(&"symbolScopeReferenceVocabulary")
922        );
923        assert!(summary.ready_surfaces.contains(&"semanticSoaTables"));
924        assert!(summary.ready_surfaces.contains(&"parserCstEquivalence"));
925        assert!(!summary.next_surfaces.contains(&"semanticSoaTables"));
926        assert!(!summary.next_surfaces.contains(&"parserCstEquivalence"));
927    }
928}