From 6485cafcd8c7d988ec8e2f4a4e192d737f4e8f46 Mon Sep 17 00:00:00 2001 From: Romain Menke <11521496+romainmenke@users.noreply.github.com> Date: Wed, 22 May 2024 15:59:59 +0200 Subject: [PATCH] feat: add `sourceIndex` to `Selector` nodes (#290) --- src/__tests__/node.mjs | 2 +- src/__tests__/sourceIndex.mjs | 67 +++++++++++++++++++++++++++++++++++ src/parser.js | 17 +++++++-- 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/src/__tests__/node.mjs b/src/__tests__/node.mjs index f248bd5..859bc2f 100644 --- a/src/__tests__/node.mjs +++ b/src/__tests__/node.mjs @@ -99,8 +99,8 @@ test('Node#isAtPosition', (t) => { let notSelector = pseudoNot.first; t.deepEqual(notSelector.isAtPosition(1, 1), false); t.deepEqual(notSelector.isAtPosition(1, 4), false); - t.deepEqual(notSelector.isAtPosition(1, 5), true); t.deepEqual(notSelector.isAtPosition(1, 6), true); + t.deepEqual(notSelector.isAtPosition(1, 7), true); t.deepEqual(notSelector.isAtPosition(1, 9), true); t.deepEqual(notSelector.isAtPosition(1, 10), true); t.deepEqual(notSelector.isAtPosition(1, 11), false); diff --git a/src/__tests__/sourceIndex.mjs b/src/__tests__/sourceIndex.mjs index f99003d..258cfca 100644 --- a/src/__tests__/sourceIndex.mjs +++ b/src/__tests__/sourceIndex.mjs @@ -1,12 +1,18 @@ import {test} from './util/helpers.mjs'; test('universal selector', '*', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 1); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[0].source.start.column, 1); t.deepEqual(tree.nodes[0].nodes[0].source.end.column, 1); t.deepEqual(tree.nodes[0].nodes[0].sourceIndex, 0); }); test('lobotomized owl selector', ' * + * ', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 6); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[0].source.start.column, 2); t.deepEqual(tree.nodes[0].nodes[0].source.end.column, 2); t.deepEqual(tree.nodes[0].nodes[0].sourceIndex, 1); @@ -19,12 +25,18 @@ test('lobotomized owl selector', ' * + * ', (t, tree) => { }); test('comment', '/**\n * Hello!\n */', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 3); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[0].source.start.column, 1); t.deepEqual(tree.nodes[0].nodes[0].source.end.column, 3); t.deepEqual(tree.nodes[0].nodes[0].sourceIndex, 0); }); test('comment & universal selectors', '*/*test*/*', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 10); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[0].source.start.column, 1); t.deepEqual(tree.nodes[0].nodes[0].source.end.column, 1); t.deepEqual(tree.nodes[0].nodes[0].sourceIndex, 0); @@ -37,27 +49,42 @@ test('comment & universal selectors', '*/*test*/*', (t, tree) => { }); test('tag selector', 'h1', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 2); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[0].source.start.column, 1); t.deepEqual(tree.nodes[0].nodes[0].source.end.column, 2); t.deepEqual(tree.nodes[0].nodes[0].sourceIndex, 0); }); test('id selector', '#id', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 3); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[0].source.start.column, 1); t.deepEqual(tree.nodes[0].nodes[0].source.end.column, 3); t.deepEqual(tree.nodes[0].nodes[0].sourceIndex, 0); }); test('tag selector followed by id selector', 'h1, #id', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 2); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[0].source.start.column, 1); t.deepEqual(tree.nodes[0].nodes[0].source.end.column, 2); t.deepEqual(tree.nodes[0].nodes[0].sourceIndex, 0); + t.deepEqual(tree.nodes[1].source.start.column, 4); + t.deepEqual(tree.nodes[1].source.end.column, 7); + t.deepEqual(tree.nodes[1].sourceIndex, 3); t.deepEqual(tree.nodes[1].nodes[0].source.start.column, 5); t.deepEqual(tree.nodes[1].nodes[0].source.end.column, 7); t.deepEqual(tree.nodes[1].nodes[0].sourceIndex, 4); }); test('multiple id selectors', '#one#two', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 8); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[0].source.start.column, 1); t.deepEqual(tree.nodes[0].nodes[0].source.end.column, 4); t.deepEqual(tree.nodes[0].nodes[0].sourceIndex, 0); @@ -67,6 +94,9 @@ test('multiple id selectors', '#one#two', (t, tree) => { }); test('multiple id selectors (2)', '#one#two#three#four', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 19); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[2].source.start.column, 9); t.deepEqual(tree.nodes[0].nodes[2].source.end.column, 14); t.deepEqual(tree.nodes[0].nodes[2].sourceIndex, 8); @@ -76,24 +106,39 @@ test('multiple id selectors (2)', '#one#two#three#four', (t, tree) => { }); test('multiple id selectors (3)', '#one#two,#three#four', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 8); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[1].source.start.column, 5); t.deepEqual(tree.nodes[0].nodes[1].source.end.column, 8); t.deepEqual(tree.nodes[0].nodes[1].sourceIndex, 4); + t.deepEqual(tree.nodes[1].source.start.column, 10); + t.deepEqual(tree.nodes[1].source.end.column, 20); + t.deepEqual(tree.nodes[1].sourceIndex, 9); t.deepEqual(tree.nodes[1].nodes[1].source.start.column, 16); t.deepEqual(tree.nodes[1].nodes[1].source.end.column, 20); t.deepEqual(tree.nodes[1].nodes[1].sourceIndex, 15); }); test('multiple class selectors', '.one.two,.three.four', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 8); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[1].source.start.column, 5); t.deepEqual(tree.nodes[0].nodes[1].source.end.column, 8); t.deepEqual(tree.nodes[0].nodes[1].sourceIndex, 4); + t.deepEqual(tree.nodes[1].source.start.column, 10); + t.deepEqual(tree.nodes[1].source.end.column, 20); + t.deepEqual(tree.nodes[1].sourceIndex, 9); t.deepEqual(tree.nodes[1].nodes[1].source.start.column, 16); t.deepEqual(tree.nodes[1].nodes[1].source.end.column, 20); t.deepEqual(tree.nodes[1].nodes[1].sourceIndex, 15); }); test('attribute selector', '[name="james"]', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 14); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[0].source.start.line, 1); t.deepEqual(tree.nodes[0].nodes[0].source.start.column, 1); t.deepEqual(tree.nodes[0].nodes[0].source.end.column, 14); @@ -130,11 +175,33 @@ test('pseudo-class', 'h1:first-child', (t, tree) => { t.deepEqual(tree.nodes[0].nodes[1].sourceIndex, 2); }); +test('pseudo-class without argument', ':not()', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 6); + t.deepEqual(tree.nodes[0].sourceIndex, 0); + t.deepEqual(tree.nodes[0].nodes[0].source.start.line, 1); + t.deepEqual(tree.nodes[0].nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].nodes[0].source.end.column, 6); + t.deepEqual(tree.nodes[0].nodes[0].sourceIndex, 0); + t.deepEqual(tree.nodes[0].nodes[0].nodes[0].source.start.column, 6); + t.deepEqual(tree.nodes[0].nodes[0].nodes[0].source.end.column, 6); + t.deepEqual(tree.nodes[0].nodes[0].nodes[0].sourceIndex, 5); +}); + test('pseudo-class with argument', 'h1:not(.strudel, .food)', (t, tree) => { + t.deepEqual(tree.nodes[0].source.start.column, 1); + t.deepEqual(tree.nodes[0].source.end.column, 23); + t.deepEqual(tree.nodes[0].sourceIndex, 0); t.deepEqual(tree.nodes[0].nodes[1].source.start.line, 1); t.deepEqual(tree.nodes[0].nodes[1].source.start.column, 3); t.deepEqual(tree.nodes[0].nodes[1].source.end.column, 23); t.deepEqual(tree.nodes[0].nodes[1].sourceIndex, 2); + t.deepEqual(tree.nodes[0].nodes[1].nodes[0].source.start.column, 8); + t.deepEqual(tree.nodes[0].nodes[1].nodes[0].source.end.column, 15); + t.deepEqual(tree.nodes[0].nodes[1].nodes[0].sourceIndex, 7); + t.deepEqual(tree.nodes[0].nodes[1].nodes[1].source.start.column, 17); + t.deepEqual(tree.nodes[0].nodes[1].nodes[1].source.end.column, 23); + t.deepEqual(tree.nodes[0].nodes[1].nodes[1].sourceIndex, 16); }); test('pseudo-element', 'h1::before', (t, tree) => { diff --git a/src/parser.js b/src/parser.js index 577b89c..4815c6e 100644 --- a/src/parser.js +++ b/src/parser.js @@ -131,7 +131,10 @@ export default class Parser { this.root.errorGenerator = this._errorGenerator(); - const selector = new Selector({source: {start: {line: 1, column: 1}}}); + const selector = new Selector({ + source: {start: {line: 1, column: 1}}, + sourceIndex: 0, + }); this.root.append(selector); this.current = selector; @@ -608,7 +611,12 @@ export default class Parser { return; } this.current._inferEndPosition(); - const selector = new Selector({source: {start: tokenStart(this.tokens[this.position + 1])}}); + const selector = new Selector({ + source: { + start: tokenStart(this.tokens[this.position + 1]), + }, + sourceIndex: this.tokens[this.position + 1][TOKEN.START_POS], + }); this.current.parent.append(selector); this.current = selector; this.position ++; @@ -685,7 +693,10 @@ export default class Parser { let unbalanced = 1; this.position ++; if (last && last.type === types.PSEUDO) { - const selector = new Selector({source: {start: tokenStart(this.tokens[this.position - 1])}}); + const selector = new Selector({ + source: {start: tokenStart(this.tokens[this.position])}, + sourceIndex: this.tokens[this.position][TOKEN.START_POS], + }); const cache = this.current; last.append(selector); this.current = selector;