From 881562b82e6326315c386f53becd785e1950f877 Mon Sep 17 00:00:00 2001 From: Mark Beznos Date: Fri, 4 Sep 2026 16:12:37 -0400 Subject: [PATCH 1/2] fixed parser overshoot because the scan marker set's the current location, instead of the preparsed one --- src/parser/cssParser.ts | 6 ++++++ src/test/css/parser.test.ts | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/parser/cssParser.ts b/src/parser/cssParser.ts index 13fdabd5..43eabba0 100644 --- a/src/parser/cssParser.ts +++ b/src/parser/cssParser.ts @@ -2012,6 +2012,12 @@ export class Parser { public _parseBinaryExpr(preparsedLeft?: nodes.BinaryExpression, preparsedOper?: nodes.Node): nodes.BinaryExpression | null { let node = this.create(nodes.BinaryExpression); + // fixes a overshoot later, where the operand token starts at current spot + // instead of backtracking to the beginning of the previous + if (preparsedLeft) { + node.offset = preparsedLeft.offset; + } + if (!node.setLeft((preparsedLeft || this._parseTerm()))) { return null; } diff --git a/src/test/css/parser.test.ts b/src/test/css/parser.test.ts index 0d6154e8..3e9e717b 100644 --- a/src/test/css/parser.test.ts +++ b/src/test/css/parser.test.ts @@ -44,6 +44,13 @@ export function assertError(text: string, parser: Parser, f: () => nodes.Node | } +export function assertType(text: string, parser: Parser, nodeType: nodes.NodeType, f: () => nodes.Node | null) { + const node = parser.internalParse(text, f)!; + assert.ok(node !== null, 'no node returned'); + const targetNode = node.findChildAtOffset(text.indexOf("--x") + 1, true)!; + assert.equal(nodes.NodeType[targetNode.type], nodes.NodeType[nodes.NodeType.Identifier]); +} + suite('CSS - Parser', () => { test('stylesheet', function () { @@ -701,4 +708,18 @@ suite('CSS - Parser', () => { assertError('url(http://msft.com\')', parser, parser._parseURILiteral.bind(parser), ParseError.RightParenthesisExpected); }); + test('nested identifier', function() { + const parser = new Parser(); + assertType("calc(100% - var(--x))", parser, nodes.NodeType.Identifier, parser._parseExpr.bind(parser));; + assertType("calc(100% - 2 * var(--x))", parser, nodes.NodeType.Identifier, parser._parseExpr.bind(parser));; + assertType("calc(1 + 2 + var(--x))", parser, nodes.NodeType.Identifier, parser._parseExpr.bind(parser));; + assertType("calc((100% - var(--x)) * 2)", parser, nodes.NodeType.Identifier, parser._parseExpr.bind(parser));; + // no longer fail + assertType("calc(2 * var(--x) + 1)", parser, nodes.NodeType.Identifier, parser._parseExpr.bind(parser));; + assertType("calc(var(--x) * 2 / 3)", parser, nodes.NodeType.Identifier, parser._parseExpr.bind(parser));; + assertType("calc(-1 * var(--x) + 1 * 2)", parser, nodes.NodeType.Identifier, parser._parseExpr.bind(parser));; + assertType("calc((100% - var(--x)) * 2 / 3)", parser, nodes.NodeType.Identifier, parser._parseExpr.bind(parser));; + + }); + }); From 25b4b49b9996c4addf1fc262df750f3a83490ef4 Mon Sep 17 00:00:00 2001 From: Mark Beznos Date: Fri, 4 Sep 2026 16:16:07 -0400 Subject: [PATCH 2/2] - cleaned up newline --- src/test/css/parser.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/css/parser.test.ts b/src/test/css/parser.test.ts index 3e9e717b..b17e28d9 100644 --- a/src/test/css/parser.test.ts +++ b/src/test/css/parser.test.ts @@ -719,7 +719,6 @@ suite('CSS - Parser', () => { assertType("calc(var(--x) * 2 / 3)", parser, nodes.NodeType.Identifier, parser._parseExpr.bind(parser));; assertType("calc(-1 * var(--x) + 1 * 2)", parser, nodes.NodeType.Identifier, parser._parseExpr.bind(parser));; assertType("calc((100% - var(--x)) * 2 / 3)", parser, nodes.NodeType.Identifier, parser._parseExpr.bind(parser));; - }); });