tiny.css.scan
Defined in tiny.css.
Byte level CSS scanning shared by the sheet parser and the media reader.
API (16)
Actions
Public operations.
consumePrefixIgnoreCaseconsumeWordcontainsTopLevelLogicalendsWithIgnoreCasefindAtRuleBoundaryfindBlockEndfindDeclarationEndfindParenEndfindTopLevelByteidentBytereadIdentskipCssSpaceskipSpaceAndCommentsskipString
Types and contracts
Public types and contracts.
Source
Source: lib/css/src/root.zig:41
zig
pub const scan = @import("scan.zig");Source: lib/css/src/scan.zig
zig
//! Byte level CSS scanning shared by the sheet parser and the media reader.//!//! These helpers respect strings, comments, and bracket nesting, which is what//! keeps a declaration delimiter inside a data URL or a comma inside a media//! query from ending the construct that holds it.const std = @import("std");pub fn findTopLevelByte(source: []const u8, start: usize, needle: u8) ?usize { var index = start; var square_depth: usize = 0; var paren_depth: usize = 0; while (index < source.len) : (index += 1) { const byte = source[index]; if (byte == '"' or byte == '\'') { index = skipString(source, index); continue; } switch (byte) { '[' => square_depth += 1, ']' => { if (square_depth > 0) square_depth -= 1; }, '(' => paren_depth += 1, ')' => { if (paren_depth > 0) paren_depth -= 1; }, else => {}, } if (byte == needle and square_depth == 0 and paren_depth == 0) return index; } return null;}pub fn findBlockEnd(source: []const u8, open: usize) ?usize { var index = open + 1; var depth: usize = 1; while (index < source.len) : (index += 1) { const byte = source[index]; if (byte == '"' or byte == '\'') { index = skipString(source, index); continue; } if (byte == '/' and index + 1 < source.len and source[index + 1] == '*') { const end = std.mem.indexOf(u8, source[index + 2 ..], "*/") orelse return null; index += end + 3; continue; } if (byte == '{') { depth += 1; continue; } if (byte == '}') { depth -= 1; if (depth == 0) return index; } } return null;}pub fn skipString(source: []const u8, quote: usize) usize { var index = quote + 1; while (index < source.len) : (index += 1) { if (source[index] == '\\') { if (index + 1 < source.len) index += 1; continue; } if (source[index] == source[quote]) return index; } return source.len;}pub fn findDeclarationEnd(source: []const u8, start: usize) usize { return findTopLevelByte(source, start, ';') orelse source.len;}pub fn findParenEnd(source: []const u8, open: usize) ?usize { var index = open + 1; var depth: usize = 1; while (index < source.len) : (index += 1) { const byte = source[index]; if (byte == '"' or byte == '\'') { index = skipString(source, index); continue; } if (byte == '/' and index + 1 < source.len and source[index + 1] == '*') { const end = std.mem.indexOf(u8, source[index + 2 ..], "*/") orelse return null; index += end + 3; continue; } if (byte == '(') { depth += 1; continue; } if (byte == ')') { depth -= 1; if (depth == 0) return index; } } return null;}pub fn containsTopLevelLogical(source: []const u8) bool { var index: usize = 0; var paren_depth: usize = 0; while (index < source.len) : (index += 1) { const byte = source[index]; if (byte == '"' or byte == '\'') { index = skipString(source, index); continue; } switch (byte) { '(' => paren_depth += 1, ')' => { if (paren_depth > 0) paren_depth -= 1; }, else => {}, } if (paren_depth == 0 and identByte(byte)) { const end = readIdent(source, index); const word = source[index..end]; if (std.ascii.eqlIgnoreCase(word, "and") or std.ascii.eqlIgnoreCase(word, "or")) return true; index = end - 1; } } return false;}pub fn consumePrefixIgnoreCase(source: []const u8, prefix: []const u8) ?[]const u8 { if (source.len < prefix.len) return null; if (!std.ascii.eqlIgnoreCase(source[0..prefix.len], prefix)) return null; return source[prefix.len..];}pub fn endsWithIgnoreCase(source: []const u8, suffix: []const u8) bool { if (source.len < suffix.len) return false; return std.ascii.eqlIgnoreCase(source[source.len - suffix.len ..], suffix);}pub fn consumeWord(source: []const u8, index: *usize, word: []const u8) bool { index.* = skipCssSpace(source, index.*); const end = readIdent(source, index.*); if (end == index.* or !std.ascii.eqlIgnoreCase(source[index.*..end], word)) return false; index.* = end; return true;}pub fn skipCssSpace(source: []const u8, start: usize) usize { var index = start; while (index < source.len and std.ascii.isWhitespace(source[index])) index += 1; return index;}pub fn readIdent(source: []const u8, start: usize) usize { var index = start; while (index < source.len and identByte(source[index])) index += 1; return index;}pub fn identByte(byte: u8) bool { return std.ascii.isAlphanumeric(byte) or byte == '_' or byte == '-';}pub fn skipSpaceAndComments(source: []const u8, start: usize) usize { var index = start; while (index < source.len) { while (index < source.len and std.ascii.isWhitespace(source[index])) index += 1; if (index + 1 < source.len and source[index] == '/' and source[index + 1] == '*') { const end = std.mem.indexOf(u8, source[index + 2 ..], "*/") orelse return source.len; index += end + 4; continue; } return index; } return index;}pub const AtRuleBoundaryKind = enum { semicolon, block,};pub const AtRuleBoundary = struct { kind: AtRuleBoundaryKind, index: usize,};pub fn findAtRuleBoundary(source: []const u8, start: usize) ?AtRuleBoundary { var index = start; var square_depth: usize = 0; var paren_depth: usize = 0; while (index < source.len) : (index += 1) { const byte = source[index]; if (byte == '"' or byte == '\'') { index = skipString(source, index); continue; } switch (byte) { '[' => square_depth += 1, ']' => { if (square_depth > 0) square_depth -= 1; }, '(' => paren_depth += 1, ')' => { if (paren_depth > 0) paren_depth -= 1; }, ';' => if (square_depth == 0 and paren_depth == 0) return .{ .kind = .semicolon, .index = index }, '{' => if (square_depth == 0 and paren_depth == 0) return .{ .kind = .block, .index = index }, else => {}, } } return null;}Audit
| Definitions | 17 |
|---|---|
| Public names | 17 |
| Members | 4 |
| Version | 26.7.0 |
| Revision | daab053ee433 |