Skip to content

Commit abe822b

Browse files
authored
fix: correct value line/column instead of values always having line 1 (#69)
1 parent 0076944 commit abe822b

File tree

3 files changed

+119
-12
lines changed

3 files changed

+119
-12
lines changed

src/parse-value.test.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ describe('Value Node Types', () => {
1818
expect(value?.start).toBe(13)
1919
expect(value?.length).toBe(3)
2020
expect(value?.end).toBe(16)
21+
expect(value?.line).toBe(1)
22+
expect(value?.column).toBe(14)
23+
})
24+
25+
it('should have correct line and column on line 2', () => {
26+
const value = getValue('div {\n color: red;\n}')
27+
expect(value?.start).toBe(15)
28+
expect(value?.length).toBe(3)
29+
expect(value?.end).toBe(18)
30+
expect(value?.line).toBe(2)
31+
expect(value?.column).toBe(10)
2132
})
2233
})
2334

@@ -27,6 +38,17 @@ describe('Value Node Types', () => {
2738
expect(value?.start).toBe(15)
2839
expect(value?.length).toBe(3)
2940
expect(value?.end).toBe(18)
41+
expect(value?.line).toBe(1)
42+
expect(value?.column).toBe(16)
43+
})
44+
45+
it('should have correct line and column on line 3', () => {
46+
const value = getValue('div {\n /* comment */\n opacity: 0.5;\n}')
47+
expect(value?.start).toBe(33)
48+
expect(value?.length).toBe(3)
49+
expect(value?.end).toBe(36)
50+
expect(value?.line).toBe(3)
51+
expect(value?.column).toBe(12)
3052
})
3153
})
3254

@@ -36,6 +58,17 @@ describe('Value Node Types', () => {
3658
expect(value?.start).toBe(13)
3759
expect(value?.length).toBe(5)
3860
expect(value?.end).toBe(18)
61+
expect(value?.line).toBe(1)
62+
expect(value?.column).toBe(14)
63+
})
64+
65+
it('should have correct line and column on line 2', () => {
66+
const value = getValue('div {\n width: 100px;\n}')
67+
expect(value?.start).toBe(15)
68+
expect(value?.length).toBe(5)
69+
expect(value?.end).toBe(20)
70+
expect(value?.line).toBe(2)
71+
expect(value?.column).toBe(10)
3972
})
4073
})
4174

@@ -45,6 +78,17 @@ describe('Value Node Types', () => {
4578
expect(value?.start).toBe(15)
4679
expect(value?.length).toBe(7)
4780
expect(value?.end).toBe(22)
81+
expect(value?.line).toBe(1)
82+
expect(value?.column).toBe(16)
83+
})
84+
85+
it('should have correct line and column on line 4', () => {
86+
const value = getValue('div {\n /* line 2 */\n /* line 3 */\n content: "hello";\n}')
87+
expect(value?.start).toBe(47)
88+
expect(value?.length).toBe(7)
89+
expect(value?.end).toBe(54)
90+
expect(value?.line).toBe(4)
91+
expect(value?.column).toBe(12)
4892
})
4993
})
5094

@@ -54,6 +98,17 @@ describe('Value Node Types', () => {
5498
expect(value?.start).toBe(13)
5599
expect(value?.length).toBe(7)
56100
expect(value?.end).toBe(20)
101+
expect(value?.line).toBe(1)
102+
expect(value?.column).toBe(14)
103+
})
104+
105+
it('should have correct line and column on line 2', () => {
106+
const value = getValue('div {\n color: #ff0000;\n}')
107+
expect(value?.start).toBe(15)
108+
expect(value?.length).toBe(7)
109+
expect(value?.end).toBe(22)
110+
expect(value?.line).toBe(2)
111+
expect(value?.column).toBe(10)
57112
})
58113
})
59114

@@ -63,6 +118,17 @@ describe('Value Node Types', () => {
63118
expect(value?.start).toBe(13)
64119
expect(value?.length).toBe(14)
65120
expect(value?.end).toBe(27)
121+
expect(value?.line).toBe(1)
122+
expect(value?.column).toBe(14)
123+
})
124+
125+
it('should have correct line and column on line 3', () => {
126+
const value = getValue('div {\n /* comment */\n color: rgb(255, 0, 0);\n}')
127+
expect(value?.start).toBe(31)
128+
expect(value?.length).toBe(14)
129+
expect(value?.end).toBe(45)
130+
expect(value?.line).toBe(3)
131+
expect(value?.column).toBe(10)
66132
})
67133
})
68134

@@ -74,6 +140,19 @@ describe('Value Node Types', () => {
74140
expect(comma?.start).toBe(24)
75141
expect(comma?.length).toBe(1)
76142
expect(comma?.end).toBe(25)
143+
expect(comma?.line).toBe(1)
144+
expect(comma?.column).toBe(25)
145+
})
146+
147+
it('should have correct line and column on line 2', () => {
148+
const root = parse('div {\n font-family: Arial, sans-serif;\n}')
149+
const decl = root.first_child?.first_child?.next_sibling?.first_child
150+
const comma = decl?.values[1]
151+
expect(comma?.start).toBe(26)
152+
expect(comma?.length).toBe(1)
153+
expect(comma?.end).toBe(27)
154+
expect(comma?.line).toBe(2)
155+
expect(comma?.column).toBe(21)
77156
})
78157
})
79158

@@ -85,6 +164,19 @@ describe('Value Node Types', () => {
85164
expect(paren?.start).toBe(18)
86165
expect(paren?.length).toBe(13)
87166
expect(paren?.end).toBe(31)
167+
expect(paren?.line).toBe(1)
168+
expect(paren?.column).toBe(19)
169+
})
170+
171+
it('should have correct line and column on line 2', () => {
172+
const root = parse('div {\n width: calc((100% - 50px) / 2);\n}')
173+
const func = root.first_child?.first_child?.next_sibling?.first_child?.values[0]
174+
const paren = func?.children[0]
175+
expect(paren?.start).toBe(20)
176+
expect(paren?.length).toBe(13)
177+
expect(paren?.end).toBe(33)
178+
expect(paren?.line).toBe(2)
179+
expect(paren?.column).toBe(15)
88180
})
89181
})
90182

@@ -94,6 +186,17 @@ describe('Value Node Types', () => {
94186
expect(value?.start).toBe(18)
95187
expect(value?.length).toBe(16)
96188
expect(value?.end).toBe(34)
189+
expect(value?.line).toBe(1)
190+
expect(value?.column).toBe(19)
191+
})
192+
193+
it('should have correct line and column on line 2', () => {
194+
const value = getValue('div {\n background: url("image.png");\n}')
195+
expect(value?.start).toBe(20)
196+
expect(value?.length).toBe(16)
197+
expect(value?.end).toBe(36)
198+
expect(value?.line).toBe(2)
199+
expect(value?.column).toBe(15)
97200
})
98201
})
99202
})

src/parse-value.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ export class ValueParser {
3535

3636
// Parse a declaration value range into value nodes
3737
// Returns array of value node indices
38-
parse_value(start: number, end: number): number[] {
38+
parse_value(start: number, end: number, start_line: number, start_column: number): number[] {
3939
this.value_end = end
4040

41-
// Position lexer at value start
41+
// Position lexer at value start with provided line/column
4242
this.lexer.pos = start
43-
this.lexer.line = 1
43+
this.lexer.line = start_line
44+
this.lexer.column = start_column
4445

4546
let nodes: number[] = []
4647

@@ -126,8 +127,8 @@ export class ValueParser {
126127
node_type,
127128
start,
128129
end - start,
129-
this.lexer.line,
130-
this.lexer.column
130+
this.lexer.token_line,
131+
this.lexer.token_column
131132
)
132133
// Skip set_content_start_delta since delta = start - start = 0 (already zero-initialized)
133134
this.arena.set_content_length(node, end - start)
@@ -161,8 +162,8 @@ export class ValueParser {
161162
str_equals('url', func_name_substr) ? URL : FUNCTION,
162163
start,
163164
0, // length unknown yet
164-
this.lexer.line,
165-
this.lexer.column
165+
this.lexer.token_line,
166+
this.lexer.token_column
166167
)
167168
this.arena.set_content_start_delta(node, 0)
168169
this.arena.set_content_length(node, name_end - start)
@@ -287,8 +288,8 @@ export class ValueParser {
287288
PARENTHESIS,
288289
start,
289290
0, // length unknown yet
290-
this.lexer.line,
291-
this.lexer.column
291+
this.lexer.token_line,
292+
this.lexer.token_column
292293
)
293294

294295
// Parse parenthesized content (everything until matching ')')
@@ -348,8 +349,8 @@ export function parse_value(value_string: string): CSSNode[] {
348349
// Create value parser
349350
const value_parser = new ValueParser(arena, value_string)
350351

351-
// Parse the entire source as a value
352-
const node_indices = value_parser.parse_value(0, value_string.length)
352+
// Parse the entire source as a value (starting at line 1, column 1)
353+
const node_indices = value_parser.parse_value(0, value_string.length, 1, 1)
353354

354355
// Wrap each node index in a CSSNode
355356
return node_indices.map((index) => new CSSNode(arena, value_string, index))

src/parse.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ export class Parser {
322322

323323
// Track value start (after colon, skipping whitespace)
324324
let value_start = this.lexer.token_start
325+
let value_start_line = this.lexer.token_line
326+
let value_start_column = this.lexer.token_column
325327
let value_end = value_start
326328

327329
// Parse value (everything until ';' or '}')
@@ -367,7 +369,8 @@ export class Parser {
367369

368370
// Parse value into structured nodes (only if enabled)
369371
if (this.parse_values_enabled && this.value_parser) {
370-
let valueNodes = this.value_parser.parse_value(trimmed[0], trimmed[1])
372+
// Let the lexer track line/column as it advances through whitespace
373+
let valueNodes = this.value_parser.parse_value(value_start, trimmed[1], value_start_line, value_start_column)
371374

372375
// Link value nodes as children of the declaration
373376
this.arena.append_children(declaration, valueNodes)

0 commit comments

Comments
 (0)