Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions src/arena.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,4 +373,92 @@ export class CSSDataArena {
has_flag(node_index: number, flag: number): boolean {
return (this.get_flags(node_index) & flag) !== 0
}

// --- Node Creation Helpers (Used by parsers) ---

/**
* Create a node with standard properties and line/column tracking
* Used by parsers that don't need separate content tracking
*
* @param type - Node type constant
* @param start - Start offset in source
* @param end - End offset in source
* @param line - Start line number (from lexer)
* @param column - Start column number (from lexer)
* @returns The created node ID
*/
create_node_with_line_column(type: number, start: number, end: number, line: number, column: number): number {
let node = this.create_node()
this.set_type(node, type)
this.set_start_offset(node, start)
this.set_length(node, end - start)
this.set_start_line(node, line)
this.set_start_column(node, column)
return node
}

/**
* Create a node with content tracking (content and value have same range)
* Used by selectors and nodes where content range equals the full node range
*
* @param type - Node type constant
* @param start - Start offset in source
* @param end - End offset in source
* @param line - Start line number (from lexer)
* @param column - Start column number (from lexer)
* @returns The created node ID
*/
create_node_with_content(type: number, start: number, end: number, line: number, column: number): number {
let node = this.create_node_with_line_column(type, start, end, line, column)
this.set_content_start(node, start)
this.set_content_length(node, end - start)
return node
}

/**
* Link an array of nodes as siblings and attach to parent
* Sets both first_child and last_child on parent
* Reduces code duplication in node tree construction
*
* @param parent - Parent node ID
* @param nodes - Array of node IDs to link as siblings
*/
link_nodes_as_children(parent: number, nodes: number[]): void {
if (nodes.length === 0) return

this.set_first_child(parent, nodes[0])
if (nodes.length > 1) {
this.set_last_child(parent, nodes[nodes.length - 1])
for (let i = 0; i < nodes.length - 1; i++) {
this.set_next_sibling(nodes[i], nodes[i + 1])
}
}
}

/**
* Set content range (start offset and length) on a node
* Convenience method that avoids calculating length separately
*
* @param node - Node ID
* @param start - Content start offset
* @param end - Content end offset (exclusive)
*/
set_content_range(node: number, start: number, end: number): void {
this.set_content_start(node, start)
this.set_content_length(node, end - start)
}

/**
* Set value range (start offset and length) on a node
* Convenience method that avoids calculating length separately
*
* @param node - Node ID
* @param start - Value start offset
* @param end - Value end offset (exclusive)
*/
set_value_range(node: number, start: number, end: number): void {
this.set_value_start(node, start)
this.set_value_length(node, end - start)
}

}
17 changes: 4 additions & 13 deletions src/parse-atrule-prelude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,7 @@ export class AtRulePreludeParser {
}

private create_node(type: number, start: number, end: number): number {
let node = this.arena.create_node()
this.arena.set_type(node, type)
this.arena.set_start_offset(node, start)
this.arena.set_length(node, end - start)
this.arena.set_start_line(node, this.lexer.token_line)
this.arena.set_start_column(node, this.lexer.token_column)
return node
return this.arena.create_node_with_line_column(type, start, end, this.lexer.token_line, this.lexer.token_column)
}

private is_and_or_not(str: string): boolean {
Expand Down Expand Up @@ -218,8 +212,7 @@ export class AtRulePreludeParser {
// Store feature content (without parentheses) in value fields, trimmed
let trimmed = trim_boundaries(this.source, content_start, content_end)
if (trimmed) {
this.arena.set_value_start(feature, trimmed[0])
this.arena.set_value_length(feature, trimmed[1] - trimmed[0])
this.arena.set_value_range(feature, trimmed[0], trimmed[1])
}

return feature
Expand Down Expand Up @@ -316,8 +309,7 @@ export class AtRulePreludeParser {
// Store query content in value fields, trimmed
let trimmed = trim_boundaries(this.source, content_start, content_end)
if (trimmed) {
this.arena.set_value_start(query, trimmed[0])
this.arena.set_value_length(query, trimmed[1] - trimmed[0])
this.arena.set_value_range(query, trimmed[0], trimmed[1])
}

nodes.push(query)
Expand Down Expand Up @@ -509,8 +501,7 @@ export class AtRulePreludeParser {
if (content_length > 0) {
let trimmed = trim_boundaries(this.source, content_start, content_start + content_length)
if (trimmed) {
this.arena.set_content_start(layer_node, trimmed[0])
this.arena.set_content_length(layer_node, trimmed[1] - trimmed[0])
this.arena.set_content_range(layer_node, trimmed[0], trimmed[1])
}
}

Expand Down
18 changes: 2 additions & 16 deletions src/parse-selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,7 @@ export class SelectorParser {
this.arena.set_start_column(list_node, list_column)

// Link selector wrapper nodes as children
this.arena.set_first_child(list_node, selectors[0])
this.arena.set_last_child(list_node, selectors[selectors.length - 1])

// Chain selector wrappers as siblings (simple since they're already wrapped)
for (let i = 0; i < selectors.length - 1; i++) {
this.arena.set_next_sibling(selectors[i], selectors[i + 1])
}
this.arena.link_nodes_as_children(list_node, selectors)

return list_node
}
Expand Down Expand Up @@ -941,15 +935,7 @@ export class SelectorParser {
}

private create_node(type: number, start: number, end: number): number {
let node = this.arena.create_node()
this.arena.set_type(node, type)
this.arena.set_start_offset(node, start)
this.arena.set_length(node, end - start)
this.arena.set_start_line(node, this.lexer.line)
this.arena.set_start_column(node, this.lexer.column)
this.arena.set_content_start(node, start)
this.arena.set_content_length(node, end - start)
return node
return this.arena.create_node_with_content(type, start, end, this.lexer.line, this.lexer.column)
}

// Helper to skip whitespace
Expand Down
22 changes: 3 additions & 19 deletions src/parse-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,8 @@ export class ValueParser {
}

private create_node(node_type: number, start: number, end: number): number {
let node = this.arena.create_node()
this.arena.set_type(node, node_type)
this.arena.set_start_offset(node, start)
let node = this.arena.create_node_with_line_column(node_type, start, end, this.lexer.line, this.lexer.column)
let length = end - start
this.arena.set_length(node, length)
// Skip set_content_start since it would compute delta = start - start = 0 (already zero-initialized)
this.arena.set_content_length(node, length)
return node
}
Expand Down Expand Up @@ -270,13 +266,7 @@ export class ValueParser {

// Link arguments as children
if (args.length > 0) {
this.arena.set_first_child(node, args[0])
this.arena.set_last_child(node, args[args.length - 1])

// Chain arguments as siblings
for (let i = 0; i < args.length - 1; i++) {
this.arena.set_next_sibling(args[i], args[i + 1])
}
this.arena.link_nodes_as_children(node, args)
}

return node
Expand Down Expand Up @@ -328,13 +318,7 @@ export class ValueParser {

// Link children as siblings
if (children.length > 0) {
this.arena.set_first_child(node, children[0])
this.arena.set_last_child(node, children[children.length - 1])

// Chain children as siblings
for (let i = 0; i < children.length - 1; i++) {
this.arena.set_next_sibling(children[i], children[i + 1])
}
this.arena.link_nodes_as_children(node, children)
}

return node
Expand Down
12 changes: 4 additions & 8 deletions src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,7 @@ export class Parser {
this.arena.set_start_offset(declaration, prop_start)

// Store property name position
this.arena.set_content_start(declaration, prop_start)
this.arena.set_content_length(declaration, prop_end - prop_start)
this.arena.set_content_range(declaration, prop_start, prop_end)

// Check for vendor prefix and set flag if detected
if (is_vendor_prefixed(this.source, prop_start, prop_end)) {
Expand Down Expand Up @@ -352,8 +351,7 @@ export class Parser {
let trimmed = trim_boundaries(this.source, value_start, value_end)
if (trimmed) {
// Store raw value string offsets (for fast string access)
this.arena.set_value_start(declaration, trimmed[0])
this.arena.set_value_length(declaration, trimmed[1] - trimmed[0])
this.arena.set_value_range(declaration, trimmed[0], trimmed[1])

// Parse value into structured nodes (only if enabled)
if (this.parse_values_enabled && this.value_parser) {
Expand Down Expand Up @@ -414,8 +412,7 @@ export class Parser {
this.arena.set_start_offset(at_rule, at_rule_start)

// Store at-rule name in contentStart/contentLength
this.arena.set_content_start(at_rule, name_start)
this.arena.set_content_length(at_rule, name_length)
this.arena.set_content_range(at_rule, name_start, name_start + name_length)

// Track prelude start and end
let prelude_start = this.lexer.token_start
Expand All @@ -432,8 +429,7 @@ export class Parser {
// Store prelude position (trimmed)
let trimmed = trim_boundaries(this.source, prelude_start, prelude_end)
if (trimmed) {
this.arena.set_value_start(at_rule, trimmed[0])
this.arena.set_value_length(at_rule, trimmed[1] - trimmed[0])
this.arena.set_value_range(at_rule, trimmed[0], trimmed[1])

// Parse prelude if enabled
if (this.prelude_parser) {
Expand Down
Loading