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