tiny.css.selector
Defined in tiny.css.
The selector grammar: compounds, combinators, specificity, and bucket keys.
API (18)
Actions
Public operations.
Builder.intern: Interns one identifier, folding it when the position is case insensitive.Builder.note: Records one diagnostic against the shared diagnostic cursor.bit: The bitclassoccupies in an element's pseudo-class word.parseList: Parses a comma separated selector list oversource[start..end]and returns the span of selectors it produced.parseOpaque: Publishes one selector that never matches an element.pseudoByName: Resolves a pseudo-class written with a colon, ASCII case insensitively.stateByName: Resolves one of the ten published node states, written[state~=name].
Types and contracts
Public types and contracts.
BucketKey: The strongest key of a selector's rightmost compound.Builder: Builds compiled selectors into placement arrays, or counts them when the arrays are absent.Combinator: How two compounds are joined.Compound: One compound selector.PseudoClass: The interaction and node state pseudo-classes, one bit each.Range: The span of selectors one selector list contributed.Selector: One compiled selector.Specificity: The four specificity fields, ordered from strongest to weakest.
Namespaces
Public namespaces.
Values and defaults
Public values and defaults.
states: The node states the published node record carries in itsstateword.unmatchable: Set on a compound whose grammar named something the node model cannot carry.
Source
Source: lib/css/src/selector/grammar.zig:127
/// The strongest key of a selector's rightmost compound. The rule bucket index/// groups selectors by this key so a node visits few candidates.pub const BucketKey = union(enum(u8)) { universal, id: u32, class: u32, role: u16, kind: u16,};Source: lib/css/src/selector/grammar.zig:159
/// Builds compiled selectors into placement arrays, or counts them when the/// arrays are absent. One builder serves both passes so the inspection and/// emission walks cannot drift.pub const Builder = struct { source: []const u8, atoms: ?*atom.Table = null, classes: ?[]u32 = null, compounds: ?[]Compound = null, combinators: ?[]Combinator = null, nths: ?[]nth.Nth = null, selectors: ?[]Selector = null, diagnostics: ?[][]const u8 = null, class_used: u32 = 0, compound_used: u32 = 0, combinator_used: u32 = 0, nth_used: u32 = 0, selector_used: u32 = 0, atom_used: u32 = 0, atom_bytes: u32 = 0, dependency_used: u32 = 0, diagnostic_used: u32 = 0, /// Interns one identifier, folding it when the position is case /// insensitive. Returns `atom.none` when the identifier cannot be held. pub fn intern(self: *Builder, text: []const u8, folded: bool) error{CapacityOverflow}!u32 { if (text.len == 0 or text.len > atom.max_text_bytes) return atom.none; var buffer: [atom.max_text_bytes]u8 = undefined; var body = text; var copy = false; if (token.hasEscape(body)) { body = token.unescape(body, &buffer) orelse return atom.none; copy = true; } if (folded and needsFold(body)) { if (!copy) @memcpy(buffer[0..body.len], body); for (buffer[0..body.len]) |*byte| byte.* = std.ascii.toLower(byte.*); body = buffer[0..body.len]; copy = true; } if (body.len == 0) return atom.none; if (self.atoms) |table| return table.intern(body, copy); self.atom_used = try grow(self.atom_used, 1); if (copy) self.atom_bytes = try grow(self.atom_bytes, @intCast(body.len)); return self.atom_used; } fn pushClass(self: *Builder, id: u32) error{CapacityOverflow}!void { if (self.classes) |slice| { std.debug.assert(self.class_used < slice.len); slice[self.class_used] = id; } self.class_used = try grow(self.class_used, 1); } fn pushCompound(self: *Builder, compound: Compound) error{CapacityOverflow}!void { if (self.compounds) |slice| { std.debug.assert(self.compound_used < slice.len); slice[self.compound_used] = compound; } self.compound_used = try grow(self.compound_used, 1); } fn pushCombinator(self: *Builder, joint: Combinator) error{CapacityOverflow}!void { if (self.combinators) |slice| { std.debug.assert(self.combinator_used < slice.len); slice[self.combinator_used] = joint; } self.combinator_used = try grow(self.combinator_used, 1); } fn pushNth(self: *Builder, out: *Compound, record: nth.Nth) error{CapacityOverflow}!void { const id = try grow(self.nth_used, 1); if (self.nths) |slice| { std.debug.assert(self.nth_used < slice.len); slice[self.nth_used] = record; } self.nth_used = id; if (out.nth == 0) { out.nth = id; return; } const slice = self.nths orelse return; var walk = out.nth; var guard: usize = 0; while (slice[walk - 1].next != 0 and guard <= slice.len) : (guard += 1) { walk = slice[walk - 1].next; } slice[walk - 1].next = id; } /// Records one diagnostic against the shared diagnostic cursor. pub fn note(self: *Builder, text: []const u8) error{CapacityOverflow}!void { if (self.diagnostics) |slice| { std.debug.assert(self.diagnostic_used < slice.len); slice[self.diagnostic_used] = text; } self.diagnostic_used = try grow(self.diagnostic_used, 1); } fn flagLast(self: *Builder) void { const slice = self.compounds orelse return; if (self.compound_used == 0) return; slice[self.compound_used - 1].flags |= unmatchable; }};Source: lib/css/src/selector/grammar.zig:9
/// How two compounds are joined. The list runs left to right, so/// `combinators[i]` joins `compounds[i]` to `compounds[i + 1]`.pub const Combinator = enum(u8) { descendant, child, next_sibling, subsequent_sibling,};Source: lib/css/src/selector/grammar.zig:114
/// One compound selector. Every identifier is an interned atom, so matching a/// compound is a run of integer compares.pub const Compound = extern struct { kind: u16 = 0, role: u16 = 0, id: u32 = 0, class_first: u32 = 0, class_count: u16 = 0, flags: u16 = 0, pseudo: u32 = 0, nth: u32 = 0,};Source: lib/css/src/selector/grammar.zig:19
/// The interaction and node state pseudo-classes, one bit each. An element/// interface reports them as one word, so `:hover` and `[state~=selected]` are/// the same test on the same bit.pub const PseudoClass = enum(u5) { hover = 0, focus = 1, focus_visible = 2, focus_within = 3, active = 4, target = 5, root = 6, empty = 7, enabled = 8, disabled = 9, checked = 10, indeterminate = 11, selected = 12, expanded = 13, pressed = 14, busy = 15, invalid = 16, required = 17, current = 18, mixed = 19, link = 20, visited = 21, read_only = 22, read_write = 23, placeholder_shown = 24, optional = 25, default = 26, open = 27, modal = 28, defined = 29,};Source: lib/css/src/selector/grammar.zig:151
/// The span of selectors one selector list contributed.pub const Range = struct { first: u32, count: u32,};Source: lib/css/src/selector/grammar.zig:53
/// The bit `class` occupies in an element's pseudo-class word.pub fn bit(class: PseudoClass) u32 { return @as(u32, 1) << @backingInt(class);}Source: lib/css/src/selector/grammar.zig:276
/// Parses a comma separated selector list over `source[start..end]` and/// returns the span of selectors it produced.pub fn parseList(builder: *Builder, start: u32, end: u32) error{CapacityOverflow}!Range { std.debug.assert(start <= end); std.debug.assert(end <= builder.source.len); const first = builder.selector_used; var cursor = start; var guard: usize = 0; while (cursor <= end and guard <= builder.source.len + 1) : (guard += 1) { const comma = topLevelComma(builder.source, cursor, end) orelse end; try parseSelector(builder, cursor, comma); if (comma >= end) break; cursor = comma + 1; } return .{ .first = first, .count = builder.selector_used - first };}Source: lib/css/src/selector/grammar.zig:293
/// Publishes one selector that never matches an element. An at-rule/// descriptor block keeps its prelude text this way without joining matching.pub fn parseOpaque(builder: *Builder, start: u32, end: u32) error{CapacityOverflow}!Range { std.debug.assert(start <= end); std.debug.assert(end <= builder.source.len); const first = builder.selector_used; const compound_first = builder.compound_used; const combinator_first = builder.combinator_used; try builder.pushCompound(.{ .flags = unmatchable }); try publish(builder, .{ .compound_first = compound_first, .combinator_first = combinator_first, .count = 1, .start = start, .end = end, .specificity = .{}, }); return .{ .first = first, .count = builder.selector_used - first };}Source: lib/css/src/selector/grammar.zig:58
/// Resolves a pseudo-class written with a colon, ASCII case insensitively.pub fn pseudoByName(name: []const u8) ?PseudoClass { return byName(name);}Source: lib/css/src/selector/grammar.zig:63
/// Resolves one of the ten published node states, written `[state~=name]`.pub fn stateByName(name: []const u8) ?PseudoClass { const found = byName(name) orelse return null; for (states) |candidate| { if (candidate == found) return found; } return null;}Source: lib/css/src/selector/grammar.zig:72
/// The node states the published node record carries in its `state` word.pub const states = [_]PseudoClass{ .selected, .disabled, .expanded, .checked, .mixed, .pressed, .busy, .invalid, .required, .current,};Source: lib/css/src/selector/grammar.zig:110
/// Set on a compound whose grammar named something the node model cannot/// carry. Such a compound never matches, which is what CSS requires of a/// selector over an attribute no element has.pub const unmatchable: u16 = 1;Source: lib/css/src/root.zig:42
pub const selector = @import("selector/root.zig");Source: lib/css/src/selector/root.zig
//! The selector grammar: compounds, combinators, specificity, and bucket keys.//!//! A compound holds interned atoms rather than byte slices, so matching is a//! run of integer compares and a rule bucket is an integer key. Interaction//! pseudo-classes and the published node states share one bit word, which is//! why `:disabled` and `[state~=disabled]` are the same test.//!//! Attribute selectors cover the four keys the node model carries: `id`,//! `class`, `role`, and `state`. A selector over any other attribute parses,//! reports a diagnostic, and is marked unmatchable, which is the CSS result for//! an attribute no element in the model can hold.//!//! `Builder` runs both passes. With its placement arrays absent it counts, with//! them present it fills, so the inspection and emission walks cannot drift.const grammar = @import("grammar.zig");const nth_expression = @import("nth.zig");pub const nth = nth_expression;pub const BucketKey = grammar.BucketKey;pub const Builder = grammar.Builder;pub const Combinator = grammar.Combinator;pub const Compound = grammar.Compound;pub const PseudoClass = grammar.PseudoClass;pub const Range = grammar.Range;pub const Selector = grammar.Selector;pub const Specificity = grammar.Specificity;pub const bit = grammar.bit;pub const parseList = grammar.parseList;pub const parseOpaque = grammar.parseOpaque;pub const pseudoByName = grammar.pseudoByName;pub const stateByName = grammar.stateByName;pub const states = grammar.states;pub const unmatchable = grammar.unmatchable;Audit
| Definitions | 16 |
|---|---|
| Public names | 16 |
| Members | 66 |
| Version | 26.7.0 |
| Revision | daab053ee433 |