diff --git a/src/parser/scssParser.ts b/src/parser/scssParser.ts index 43edb74f..1c427ac9 100644 --- a/src/parser/scssParser.ts +++ b/src/parser/scssParser.ts @@ -198,6 +198,22 @@ export class SCSSParser extends cssParser.Parser { super._parseTermExpression(); } + public _parseNamedLine(): nodes.Node | null { + // Sass bracketed lists are a superset of the CSS named-line syntax: the brackets may hold any + // space or comma separated list of values, not just a sequence of identifiers. + // https://sass-lang.com/documentation/values/lists/#bracketed-lists + if (!this.peek(TokenType.BracketL)) { + return null; + } + const node = this.createNode(nodes.NodeType.GridLine); + this.consumeToken(); + node.addChild(this._parseExpr()); // the list may be empty + if (!this.accept(TokenType.BracketR)) { + return this.finish(node, ParseError.RightSquareBracketExpected); + } + return this.finish(node); + } + public _parseInterpolation(): nodes.Node | null { if (this.peek(scssScanner.InterpolationFunction)) { const node = this.create(nodes.Interpolation); diff --git a/src/test/scss/parser.test.ts b/src/test/scss/parser.test.ts index e23052d2..78c5361f 100644 --- a/src/test/scss/parser.test.ts +++ b/src/test/scss/parser.test.ts @@ -51,6 +51,8 @@ suite('SCSS - Parser', () => { assertNode('$footer-height: 40px !default !global', parser, parser._parseVariableDeclaration.bind(parser)); assertNode('$primary-font: "wf_SegoeUI","Segoe UI","Segoe","Segoe WP"', parser, parser._parseVariableDeclaration.bind(parser)); assertNode('$color: red !important', parser, parser._parseVariableDeclaration.bind(parser)); + assertNode('$colors: ["#69ffae", "#1ae5b5"]', parser, parser._parseVariableDeclaration.bind(parser)); // #399 + assertNode('$sizes: [1px, 2px] !default', parser, parser._parseVariableDeclaration.bind(parser)); // #231 assertError('$color: red !def', parser, parser._parseVariableDeclaration.bind(parser), ParseError.UnknownKeyword); assertError('$color : !default', parser, parser._parseVariableDeclaration.bind(parser), ParseError.VariableValueExpected); @@ -133,6 +135,35 @@ suite('SCSS - Parser', () => { assertError('(20 + 20', parser, parser._parseExpr.bind(parser), ParseError.RightParenthesisExpected); }); + test('Bracketed list in a stylesheet', function () { + const parser = new SCSSParser(); + assertNode('.a { $x: [1px, 2px]; }', parser, parser._parseStylesheet.bind(parser)); // #231 + assertNode('@use "sass:list"; .a { color: list.nth([a, b], 2); }', parser, parser._parseStylesheet.bind(parser)); // #399 + assertNode('$m: (k: [a, b], j: [c]);', parser, parser._parseStylesheet.bind(parser)); + assertNode('@mixin m($x: [a, b]) { color: red; }', parser, parser._parseStylesheet.bind(parser)); + assertNode('@each $i in [a, b] { .#{$i} { color: red; } }', parser, parser._parseStylesheet.bind(parser)); + }); + + test('Bracketed list', function () { + const parser = new SCSSParser(); + // https://sass-lang.com/documentation/values/lists/#bracketed-lists + assertNode('[]', parser, parser._parseExpr.bind(parser)); + assertNode('[a]', parser, parser._parseExpr.bind(parser)); + assertNode('[a b c]', parser, parser._parseExpr.bind(parser)); + assertNode('[a, b, c]', parser, parser._parseExpr.bind(parser)); + assertNode('[a, b,]', parser, parser._parseExpr.bind(parser)); + assertNode('[1px, 2em, 3%]', parser, parser._parseExpr.bind(parser)); + assertNode('[$a, $b]', parser, parser._parseExpr.bind(parser)); + assertNode('[#{$a}, b]', parser, parser._parseExpr.bind(parser)); + assertNode('[[a, b], [c d]]', parser, parser._parseExpr.bind(parser)); + assertNode('[math.div(6, 2), 2]', parser, parser._parseExpr.bind(parser)); + + // the CSS named-line syntax keeps working + assertNode('[full-start] 1fr [full-end]', parser, parser._parseExpr.bind(parser)); + + assertError('[a, b', parser, parser._parseExpr.bind(parser), ParseError.RightSquareBracketExpected); + }); + test('SCSSOperator', function () { const parser = new SCSSParser(); assertNode('>=', parser, parser._parseOperator.bind(parser));