Skip to content

Commit f09e296

Browse files
graberzzai
authored andcommitted
Add offset property to position
1 parent 1ee975a commit f09e296

5 files changed

Lines changed: 189 additions & 175 deletions

File tree

lib/node.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ export type ChildProps =
2121
| CommentProps
2222

2323
export interface Position {
24+
/**
25+
* Source offset in file.
26+
*/
27+
offset: number
28+
2429
/**
2530
* Source line in file.
2631
*/

lib/parser.js

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict'
22

3+
let lineColumn = require('line-column')
4+
35
let Declaration = require('./declaration')
46
let tokenizer = require('./tokenize')
57
let Comment = require('./comment')
@@ -10,6 +12,7 @@ let Rule = require('./rule')
1012
class Parser {
1113
constructor (input) {
1214
this.input = input
15+
this.finder = lineColumn(this.input.css)
1316

1417
this.root = new Root()
1518
this.current = this.root
@@ -18,7 +21,7 @@ class Parser {
1821
this.customProperty = false
1922

2023
this.createTokenizer()
21-
this.root.source = { input, start: { line: 1, column: 1 } }
24+
this.root.source = { input, start: { offset: 0, line: 1, column: 1 } }
2225
}
2326

2427
createTokenizer () {
@@ -65,8 +68,13 @@ class Parser {
6568

6669
comment (token) {
6770
let node = new Comment()
68-
this.init(node, token[2], token[3])
69-
node.source.end = { line: token[4], column: token[5] }
71+
this.init(node, token[2])
72+
let pos = this.finder.fromIndex(token[3])
73+
node.source.end = {
74+
offset: token[3],
75+
line: pos.line,
76+
column: pos.col
77+
}
7078

7179
let text = token[1].slice(2, -2)
7280
if (/^\s*$/.test(text)) {
@@ -83,7 +91,7 @@ class Parser {
8391

8492
emptyRule (token) {
8593
let node = new Rule()
86-
this.init(node, token[2], token[3])
94+
this.init(node, token[2])
8795
node.selector = ''
8896
node.raws.between = ''
8997
this.current = node
@@ -154,7 +162,7 @@ class Parser {
154162
tokens.pop()
155163

156164
let node = new Rule()
157-
this.init(node, tokens[0][2], tokens[0][3])
165+
this.init(node, tokens[0][2])
158166

159167
node.raws.between = this.spacesAndCommentsFromEnd(tokens)
160168
this.raw(node, 'selector', tokens)
@@ -170,17 +178,32 @@ class Parser {
170178
this.semicolon = true
171179
tokens.pop()
172180
}
173-
if (last[4]) {
174-
node.source.end = { line: last[4], column: last[5] }
181+
if (last[3]) {
182+
let pos = this.finder.fromIndex(last[3])
183+
node.source.end = {
184+
offset: last[3],
185+
line: pos.line,
186+
column: pos.col
187+
}
175188
} else {
176-
node.source.end = { line: last[2], column: last[3] }
189+
let pos = this.finder.fromIndex(last[2])
190+
node.source.end = {
191+
offset: last[2],
192+
line: pos.line,
193+
column: pos.col
194+
}
177195
}
178196

179197
while (tokens[0][0] !== 'word') {
180198
if (tokens.length === 1) this.unknownWord(tokens)
181199
node.raws.before += tokens.shift()[1]
182200
}
183-
node.source.start = { line: tokens[0][2], column: tokens[0][3] }
201+
let pos = this.finder.fromIndex(tokens[0][2])
202+
node.source.start = {
203+
offset: tokens[0][2],
204+
line: pos.line,
205+
column: pos.col
206+
}
184207

185208
node.prop = ''
186209
while (tokens.length) {
@@ -264,7 +287,7 @@ class Parser {
264287
if (node.name === '') {
265288
this.unnamedAtrule(node, token)
266289
}
267-
this.init(node, token[2], token[3])
290+
this.init(node, token[2])
268291

269292
let type
270293
let prev
@@ -288,7 +311,12 @@ class Parser {
288311

289312
if (brackets.length === 0) {
290313
if (type === ';') {
291-
node.source.end = { line: token[2], column: token[3] }
314+
let pos = this.finder.fromIndex(token[2])
315+
node.source.end = {
316+
offset: token[2],
317+
line: pos.line,
318+
column: pos.col
319+
}
292320
this.semicolon = true
293321
break
294322
} else if (type === '{') {
@@ -302,7 +330,12 @@ class Parser {
302330
prev = params[--shift]
303331
}
304332
if (prev) {
305-
node.source.end = { line: prev[4], column: prev[5] }
333+
let pos = this.finder.fromIndex(prev[4])
334+
node.source.end = {
335+
offset: prev[4],
336+
line: pos.line,
337+
column: pos.col
338+
}
306339
}
307340
}
308341
this.end(token)
@@ -326,7 +359,12 @@ class Parser {
326359
this.raw(node, 'params', params)
327360
if (last) {
328361
token = params[params.length - 1]
329-
node.source.end = { line: token[4], column: token[5] }
362+
let pos = this.finder.fromIndex(token[3])
363+
node.source.end = {
364+
offset: token[3],
365+
line: pos.line,
366+
column: pos.col
367+
}
330368
this.spaces = node.raws.between
331369
node.raws.between = ''
332370
}
@@ -351,7 +389,12 @@ class Parser {
351389
this.spaces = ''
352390

353391
if (this.current.parent) {
354-
this.current.source.end = { line: token[2], column: token[3] }
392+
let pos = this.finder.fromIndex(token[2])
393+
this.current.source.end = {
394+
offset: token[2],
395+
line: pos.line,
396+
column: pos.col
397+
}
355398
this.current = this.current.parent
356399
} else {
357400
this.unexpectedClose(token)
@@ -379,10 +422,18 @@ class Parser {
379422

380423
// Helpers
381424

382-
init (node, line, column) {
425+
init (node, offset) {
383426
this.current.push(node)
384427

385-
node.source = { start: { line, column }, input: this.input }
428+
let pos = this.finder.fromIndex(offset) || { line: null, column: null }
429+
node.source = {
430+
start: {
431+
offset,
432+
line: pos.line,
433+
column: pos.col
434+
},
435+
input: this.input
436+
}
386437
node.raws.before = this.spaces
387438
this.spaces = ''
388439
if (node.type !== 'comment') this.semicolon = false
@@ -504,15 +555,18 @@ class Parser {
504555
// Errors
505556

506557
unclosedBracket (bracket) {
507-
throw this.input.error('Unclosed bracket', bracket[2], bracket[3])
558+
let pos = this.finder.fromIndex(bracket[2])
559+
throw this.input.error('Unclosed bracket', pos.line, pos.col)
508560
}
509561

510562
unknownWord (tokens) {
511-
throw this.input.error('Unknown word', tokens[0][2], tokens[0][3])
563+
let pos = this.finder.fromIndex(tokens[0][2])
564+
throw this.input.error('Unknown word', pos.line, pos.col)
512565
}
513566

514567
unexpectedClose (token) {
515-
throw this.input.error('Unexpected }', token[2], token[3])
568+
let pos = this.finder.fromIndex(token[2])
569+
throw this.input.error('Unexpected }', pos.line, pos.col)
516570
}
517571

518572
unclosedBlock () {
@@ -521,11 +575,13 @@ class Parser {
521575
}
522576

523577
doubleColon (token) {
524-
throw this.input.error('Double colon', token[2], token[3])
578+
let pos = this.finder.fromIndex(token[2])
579+
throw this.input.error('Double colon', pos.line, pos.col)
525580
}
526581

527582
unnamedAtrule (node, token) {
528-
throw this.input.error('At-rule without name', token[2], token[3])
583+
let pos = this.finder.fromIndex(token[2])
584+
throw this.input.error('At-rule without name', pos.line, pos.col)
529585
}
530586

531587
precheckMissedSemicolon (/* tokens */) {
@@ -545,7 +601,8 @@ class Parser {
545601
if (founded === 2) break
546602
}
547603
}
548-
throw this.input.error('Missed semicolon', token[2], token[3])
604+
let pos = this.finder.fromIndex(token[2])
605+
throw this.input.error('Missed semicolon', pos.line, pos.col)
549606
}
550607
}
551608

lib/tokenize.js

Lines changed: 9 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ module.exports = function tokenizer (input, options = {}) {
102102
case SEMICOLON:
103103
case CLOSE_PARENTHESES: {
104104
let controlChar = String.fromCharCode(code)
105-
currentToken = [controlChar, controlChar, line, pos - offset]
105+
currentToken = [controlChar, controlChar, pos]
106106
break
107107
}
108108

@@ -138,31 +138,17 @@ module.exports = function tokenizer (input, options = {}) {
138138
}
139139
} while (escaped)
140140

141-
currentToken = [
142-
'brackets',
143-
css.slice(pos, next + 1),
144-
line,
145-
pos - offset,
146-
line,
147-
next - offset
148-
]
141+
currentToken = ['brackets', css.slice(pos, next + 1), pos, next]
149142

150143
pos = next
151144
} else {
152145
next = css.indexOf(')', pos + 1)
153146
content = css.slice(pos, next + 1)
154147

155148
if (next === -1 || RE_BAD_BRACKET.test(content)) {
156-
currentToken = ['(', '(', line, pos - offset]
149+
currentToken = ['(', '(', pos]
157150
} else {
158-
currentToken = [
159-
'brackets',
160-
content,
161-
line,
162-
pos - offset,
163-
line,
164-
next - offset
165-
]
151+
currentToken = ['brackets', content, pos, next]
166152
pos = next
167153
}
168154
}
@@ -204,14 +190,7 @@ module.exports = function tokenizer (input, options = {}) {
204190
nextOffset = offset
205191
}
206192

207-
currentToken = [
208-
'string',
209-
css.slice(pos, next + 1),
210-
line,
211-
pos - offset,
212-
nextLine,
213-
next - nextOffset
214-
]
193+
currentToken = ['string', css.slice(pos, next + 1), pos, next]
215194

216195
offset = nextOffset
217196
line = nextLine
@@ -228,14 +207,7 @@ module.exports = function tokenizer (input, options = {}) {
228207
next = RE_AT_END.lastIndex - 2
229208
}
230209

231-
currentToken = [
232-
'at-word',
233-
css.slice(pos, next + 1),
234-
line,
235-
pos - offset,
236-
line,
237-
next - offset
238-
]
210+
currentToken = ['at-word', css.slice(pos, next + 1), pos, next]
239211

240212
pos = next
241213
break
@@ -269,14 +241,7 @@ module.exports = function tokenizer (input, options = {}) {
269241
}
270242
}
271243

272-
currentToken = [
273-
'word',
274-
css.slice(pos, next + 1),
275-
line,
276-
pos - offset,
277-
line,
278-
next - offset
279-
]
244+
currentToken = ['word', css.slice(pos, next + 1), pos, next]
280245

281246
pos = next
282247
break
@@ -305,14 +270,7 @@ module.exports = function tokenizer (input, options = {}) {
305270
nextOffset = offset
306271
}
307272

308-
currentToken = [
309-
'comment',
310-
content,
311-
line,
312-
pos - offset,
313-
nextLine,
314-
next - nextOffset
315-
]
273+
currentToken = ['comment', content, pos, next]
316274

317275
offset = nextOffset
318276
line = nextLine
@@ -326,14 +284,7 @@ module.exports = function tokenizer (input, options = {}) {
326284
next = RE_WORD_END.lastIndex - 2
327285
}
328286

329-
currentToken = [
330-
'word',
331-
css.slice(pos, next + 1),
332-
line,
333-
pos - offset,
334-
line,
335-
next - offset
336-
]
287+
currentToken = ['word', css.slice(pos, next + 1), pos, next]
337288

338289
buffer.push(currentToken)
339290

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"repository": "postcss/postcss",
3939
"dependencies": {
4040
"colorette": "^1.2.1",
41+
"line-column": "^1.0.2",
4142
"nanoid": "^3.1.12",
4243
"source-map": "^0.6.1"
4344
},

0 commit comments

Comments
 (0)