From 2c4a054e8539e9b371a43d7ac1eaca1281855a1b Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 1 Nov 2024 14:59:11 -0400 Subject: [PATCH 01/58] Support optional semicolons at the end of indented lines --- lib/src/parse/sass.dart | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index 55b2086c9..01bb637c1 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -54,7 +54,7 @@ class SassParser extends StylesheetParser { } void expectStatementSeparator([String? name]) { - if (!atEndOfStatement()) _expectNewline(); + if (!atEndOfStatement()) _expectNewline(canEndInSemicolon: true); if (_peekIndentation() <= currentIndentation) return; scanner.error( "Nothing may be indented ${name == null ? 'here' : 'beneath a $name'}.", @@ -335,9 +335,21 @@ class SassParser extends StylesheetParser { } } - /// Expect and consume a single newline character. - void _expectNewline() { + /// Expect and consume a single newline. + /// + /// + void _expectNewline({bool canEndInSemicolon = false}) { switch (scanner.peekChar()) { + case $semicolon + when canEndInSemicolon && [$lf, $ff].contains(scanner.peekChar(1)): + scanner.readChar(); + scanner.readChar(); + return; + case $semicolon when canEndInSemicolon && scanner.peekChar(1) == $cr: + scanner.readChar(); + scanner.readChar(); + if (scanner.peekChar() == $lf) scanner.readChar(); + return; case $semicolon: scanner.error("semicolons aren't allowed in the indented syntax."); case $cr: From 4d0e43cb42d89de46e5d8006e8d616208838a526 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Wed, 27 Nov 2024 15:44:09 -0500 Subject: [PATCH 02/58] Add comment, update error message --- lib/src/parse/sass.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index 01bb637c1..db9eab01f 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -337,7 +337,8 @@ class SassParser extends StylesheetParser { /// Expect and consume a single newline. /// - /// + /// If [canEndInSemicolon] is true, this will also consume a `;` before the + /// newline if present. void _expectNewline({bool canEndInSemicolon = false}) { switch (scanner.peekChar()) { case $semicolon @@ -351,7 +352,8 @@ class SassParser extends StylesheetParser { if (scanner.peekChar() == $lf) scanner.readChar(); return; case $semicolon: - scanner.error("semicolons aren't allowed in the indented syntax."); + scanner.error( + "multiple statements on one line are not supported in the indented syntax."); case $cr: scanner.readChar(); if (scanner.peekChar() == $lf) scanner.readChar(); From dc12326e566f8e7faff9606f0d29acea50d00052 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Wed, 27 Nov 2024 16:43:49 -0500 Subject: [PATCH 03/58] Handle each --- lib/src/parse/parser.dart | 11 ++++++++--- lib/src/parse/sass.dart | 8 ++++++-- lib/src/parse/stylesheet.dart | 6 +++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index 19ef3971c..7d2d14331 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -74,16 +74,21 @@ class Parser { // ## Tokens /// Consumes whitespace, including any comments. + /// + /// Set `consumeNewlines` to true when a statement could not end, so newlines + /// can be consumed. Only used in the indented syntax. + /// + /// TODO: Bikeshed the variable name @protected - void whitespace() { + void whitespace({bool consumeNewlines = false}) { do { - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: consumeNewlines); } while (scanComment()); } /// Consumes whitespace, but not comments. @protected - void whitespaceWithoutComments() { + void whitespaceWithoutComments({bool consumeNewlines = true}) { while (!scanner.isDone && scanner.peekChar().isWhitespace) { scanner.readChar(); } diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index db9eab01f..2cda843ee 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -309,12 +309,16 @@ class SassParser extends StylesheetParser { return LoudComment(buffer.interpolation(scanner.spanFrom(start))); } - void whitespaceWithoutComments() { + void whitespaceWithoutComments({consumeNewlines = false}) { // This overrides whitespace consumption so that it doesn't consume // newlines. while (!scanner.isDone) { var next = scanner.peekChar(); - if (next != $tab && next != $space) break; + if (!consumeNewlines && !next.isSpaceOrTab) { + break; + } else if (consumeNewlines && !next.isWhitespace) { + break; + } scanner.readChar(); } } diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 94f551d2d..3c38e56e6 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -609,7 +609,7 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; scanner.expectChar($at, name: "@-rule"); var name = interpolatedIdentifier(); - whitespace(); + whitespace(consumeNewlines: true); // We want to set [_isUseAllowed] to `false` *unless* we're parsing // `@charset`, `@forward`, or `@use`. To avoid double-comparing the rule @@ -826,9 +826,9 @@ abstract class StylesheetParser extends Parser { variables.add(variableName()); whitespace(); } - + whitespace(consumeNewlines: true); expectIdentifier("in"); - whitespace(); + whitespace(consumeNewlines: true); var list = _expression(); From 332ca09d23d5d293528e6bd9ae184d8118f5101f Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 2 Dec 2024 09:51:48 -0500 Subject: [PATCH 04/58] Allow whitespace in @for, handle initial whitespace for at-rules individually --- lib/src/parse/stylesheet.dart | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 3c38e56e6..e502b6f2c 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -609,7 +609,6 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; scanner.expectChar($at, name: "@-rule"); var name = interpolatedIdentifier(); - whitespace(consumeNewlines: true); // We want to set [_isUseAllowed] to `false` *unless* we're parsing // `@charset`, `@forward`, or `@use`. To avoid double-comparing the rule @@ -620,52 +619,74 @@ abstract class StylesheetParser extends Parser { switch (name.asPlain) { case "at-root": + whitespace(); return _atRootRule(start); case "content": + whitespace(); return _contentRule(start); case "debug": + whitespace(consumeNewlines: false); return _debugRule(start); case "each": + whitespace(consumeNewlines: true); return _eachRule(start, child); case "else": + whitespace(); return _disallowedAtRule(start); case "error": + whitespace(consumeNewlines: false); return _errorRule(start); case "extend": + whitespace(); return _extendRule(start); case "for": + whitespace(consumeNewlines: true); return _forRule(start, child); case "forward": + whitespace(); _isUseAllowed = wasUseAllowed; if (!root) _disallowedAtRule(start); return _forwardRule(start); case "function": + whitespace(); return _functionRule(start); case "if": + whitespace(); return _ifRule(start, child); case "import": + whitespace(); return _importRule(start); case "include": + whitespace(); return _includeRule(start); case "media": + whitespace(); return mediaRule(start); case "mixin": + whitespace(); return _mixinRule(start); case "-moz-document": + whitespace(); return mozDocumentRule(start, name); case "return": + whitespace(); return _disallowedAtRule(start); case "supports": + whitespace(); return supportsRule(start); case "use": + whitespace(); _isUseAllowed = wasUseAllowed; if (!root) _disallowedAtRule(start); return _useRule(start); case "warn": + whitespace(consumeNewlines: false); return _warnRule(start); case "while": + whitespace(); return _whileRule(start, child); default: + whitespace(); return unknownAtRule(start, name); } } @@ -926,10 +947,10 @@ abstract class StylesheetParser extends Parser { var wasInControlDirective = _inControlDirective; _inControlDirective = true; var variable = variableName(); - whitespace(); + whitespace(consumeNewlines: true); expectIdentifier("from"); - whitespace(); + whitespace(consumeNewlines: true); bool? exclusive; var from = _expression(until: () { @@ -946,7 +967,7 @@ abstract class StylesheetParser extends Parser { }); if (exclusive == null) scanner.error('Expected "to" or "through".'); - whitespace(); + whitespace(consumeNewlines: true); var to = _expression(); return _withChildren(child, start, (children, span) { From 3715ee48440718283041fee139fcdc2ffdb211b0 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 2 Dec 2024 10:55:06 -0500 Subject: [PATCH 05/58] @content, @extend newlines --- lib/src/parse/stylesheet.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index e502b6f2c..22a83b32f 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -619,10 +619,11 @@ abstract class StylesheetParser extends Parser { switch (name.asPlain) { case "at-root": - whitespace(); + whitespace(consumeNewlines: false); return _atRootRule(start); case "content": - whitespace(); + // TODO: _contentRule consumes whitespace- is this needed? + whitespace(consumeNewlines: false); return _contentRule(start); case "debug": whitespace(consumeNewlines: false); @@ -637,7 +638,7 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: false); return _errorRule(start); case "extend": - whitespace(); + whitespace(consumeNewlines: true); return _extendRule(start); case "for": whitespace(consumeNewlines: true); @@ -810,11 +811,11 @@ abstract class StylesheetParser extends Parser { } var beforeWhitespace = scanner.location; - whitespace(); + whitespace(consumeNewlines: false); ArgumentInvocation arguments; if (scanner.peekChar() == $lparen) { arguments = _argumentInvocation(mixin: true); - whitespace(); + whitespace(consumeNewlines: false); } else { arguments = ArgumentInvocation.empty(beforeWhitespace.pointSpan()); } @@ -881,7 +882,7 @@ abstract class StylesheetParser extends Parser { var optional = scanner.scanChar($exclamation); if (optional) { expectIdentifier("optional"); - whitespace(); + whitespace(consumeNewlines: false); } expectStatementSeparator("@extend rule"); return ExtendRule(value, scanner.spanFrom(start), optional: optional); From 8594fe554a8bc28dce46258696331e0a81b98b82 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 2 Dec 2024 11:24:15 -0500 Subject: [PATCH 06/58] @input newlines --- lib/src/parse/stylesheet.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 22a83b32f..d5608309d 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -655,7 +655,7 @@ abstract class StylesheetParser extends Parser { whitespace(); return _ifRule(start, child); case "import": - whitespace(); + whitespace(consumeNewlines: false); return _importRule(start); case "include": whitespace(); @@ -1087,7 +1087,7 @@ abstract class StylesheetParser extends Parser { ImportRule _importRule(LineScannerState start) { var imports = []; do { - whitespace(); + whitespace(consumeNewlines: false); var argument = importArgument(); if (argument is DynamicImport) { warnings.add(( @@ -1105,7 +1105,7 @@ abstract class StylesheetParser extends Parser { } imports.add(argument); - whitespace(); + whitespace(consumeNewlines: false); } while (scanner.scanChar($comma)); expectStatementSeparator("@import rule"); @@ -1120,7 +1120,7 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; if (scanner.peekChar() case $u || $U) { var url = dynamicUrl(); - whitespace(); + whitespace(consumeNewlines: false); var modifiers = tryImportModifiers(); return StaticImport( url is StringExpression @@ -1132,7 +1132,7 @@ abstract class StylesheetParser extends Parser { var url = string(); var urlSpan = scanner.spanFrom(start); - whitespace(); + whitespace(consumeNewlines: false); var modifiers = tryImportModifiers(); if (isPlainImportUrl(url) || modifiers != null) { return StaticImport( From 512304b765d104d48488615dd7a6bc6bf5377d25 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 2 Dec 2024 13:55:37 -0500 Subject: [PATCH 07/58] Whitespace for @mixin, @include --- lib/src/parse/stylesheet.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index d5608309d..d1e933965 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -658,13 +658,13 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: false); return _importRule(start); case "include": - whitespace(); + whitespace(consumeNewlines: true); return _includeRule(start); case "media": whitespace(); return mediaRule(start); case "mixin": - whitespace(); + whitespace(consumeNewlines: true); return _mixinRule(start); case "-moz-document": whitespace(); @@ -1281,17 +1281,17 @@ abstract class StylesheetParser extends Parser { name = _publicIdentifier(); } - whitespace(); + whitespace(consumeNewlines: false); var arguments = scanner.peekChar() == $lparen ? _argumentInvocation(mixin: true) : ArgumentInvocation.empty(scanner.emptySpan); - whitespace(); + whitespace(consumeNewlines: false); ArgumentDeclaration? contentArguments; if (scanIdentifier("using")) { - whitespace(); + whitespace(consumeNewlines: true); contentArguments = _argumentDeclaration(); - whitespace(); + whitespace(consumeNewlines: false); } ContentBlock? content; @@ -1344,7 +1344,7 @@ abstract class StylesheetParser extends Parser { )); } - whitespace(); + whitespace(consumeNewlines: false); var arguments = scanner.peekChar() == $lparen ? _argumentDeclaration() : ArgumentDeclaration.empty(scanner.emptySpan); @@ -1357,7 +1357,7 @@ abstract class StylesheetParser extends Parser { scanner.spanFrom(start)); } - whitespace(); + whitespace(consumeNewlines: false); _inMixin = true; return _withChildren(_statement, start, (children, span) { From c09e506a840ac1c6b75534af2eddef77df4417ba Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 2 Dec 2024 14:24:27 -0500 Subject: [PATCH 08/58] Whitespace in @media, @-moz-document, @supports --- lib/src/parse/parser.dart | 4 ++-- lib/src/parse/stylesheet.dart | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index 7d2d14331..a5cb3bc5d 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -126,8 +126,8 @@ class Parser { if (scanner.isDone || !(scanner.peekChar().isWhitespace || scanComment())) { scanner.error("Expected whitespace."); } - - whitespace(); + // TODO: Does this need to by dynamic by context? + whitespace(consumeNewlines: false); } /// Consumes and ignores a single silent (Sass-style) comment, not including diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index d1e933965..26e8e8ca5 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -1394,7 +1394,7 @@ abstract class StylesheetParser extends Parser { buffer.addInterpolation(contents); } else { scanner.expectChar($lparen); - whitespace(); + whitespace(consumeNewlines: false); var argument = interpolatedString(); scanner.expectChar($rparen); @@ -1427,7 +1427,7 @@ abstract class StylesheetParser extends Parser { } } - whitespace(); + whitespace(consumeNewlines: false); if (!scanner.scanChar($comma)) break; buffer.writeCharCode($comma); @@ -3118,9 +3118,9 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; var buffer = InterpolationBuffer(); while (true) { - whitespace(); + whitespace(consumeNewlines: false); _mediaQuery(buffer); - whitespace(); + whitespace(consumeNewlines: false); if (!scanner.scanChar($comma)) break; buffer.writeCharCode($comma); buffer.writeCharCode($space); @@ -3131,9 +3131,10 @@ abstract class StylesheetParser extends Parser { /// Consumes a single media query. void _mediaQuery(InterpolationBuffer buffer) { // This is somewhat duplicated in MediaQueryParser._mediaQuery. + // TODO: Do we need to duplicate the whitespace handling there? if (scanner.peekChar() == $lparen) { _mediaInParens(buffer); - whitespace(); + whitespace(consumeNewlines: false); if (scanIdentifier("and")) { buffer.write(" and "); expectWhitespace(); @@ -3159,7 +3160,7 @@ abstract class StylesheetParser extends Parser { } } - whitespace(); + whitespace(consumeNewlines: false); buffer.addInterpolation(identifier1); if (!_lookingAtInterpolatedIdentifier()) { // For example, "@media screen {". @@ -3174,7 +3175,7 @@ abstract class StylesheetParser extends Parser { // For example, "@media screen and ..." buffer.write(" and "); } else { - whitespace(); + whitespace(consumeNewlines: false); buffer.addInterpolation(identifier2); if (scanIdentifier("and")) { // For example, "@media only screen and ..." From 1fa6ccf1f33e58e3f27b5cfec629987a0a2db186 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 2 Dec 2024 15:31:26 -0500 Subject: [PATCH 09/58] Whitespace in @use and @forward --- lib/src/parse/stylesheet.dart | 38 ++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 26e8e8ca5..a9381042f 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -644,7 +644,7 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: true); return _forRule(start, child); case "forward": - whitespace(); + whitespace(consumeNewlines: true); _isUseAllowed = wasUseAllowed; if (!root) _disallowedAtRule(start); return _forwardRule(start); @@ -661,22 +661,22 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: true); return _includeRule(start); case "media": - whitespace(); + whitespace(consumeNewlines: false); return mediaRule(start); case "mixin": whitespace(consumeNewlines: true); return _mixinRule(start); case "-moz-document": - whitespace(); + whitespace(consumeNewlines: false); return mozDocumentRule(start, name); case "return": whitespace(); return _disallowedAtRule(start); case "supports": - whitespace(); + whitespace(consumeNewlines: false); return supportsRule(start); case "use": - whitespace(); + whitespace(consumeNewlines: true); _isUseAllowed = wasUseAllowed; if (!root) _disallowedAtRule(start); return _useRule(start); @@ -987,10 +987,10 @@ abstract class StylesheetParser extends Parser { String? prefix; if (scanIdentifier("as")) { - whitespace(); + whitespace(consumeNewlines: true); prefix = identifier(normalize: true); scanner.expectChar($asterisk); - whitespace(); + whitespace(consumeNewlines: false); } Set? shownMixinsAndFunctions; @@ -1004,7 +1004,7 @@ abstract class StylesheetParser extends Parser { } var configuration = _configuration(allowGuarded: true); - whitespace(); + whitespace(consumeNewlines: false); expectStatementSeparator("@forward rule"); var span = scanner.spanFrom(start); @@ -1477,12 +1477,12 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. UseRule _useRule(LineScannerState start) { var url = _urlString(); - whitespace(); + whitespace(consumeNewlines: false); var namespace = _useNamespace(url, start); - whitespace(); + whitespace(consumeNewlines: false); var configuration = _configuration(); - whitespace(); + whitespace(consumeNewlines: false); var span = scanner.spanFrom(start); if (!_isUseAllowed) { @@ -1499,7 +1499,7 @@ abstract class StylesheetParser extends Parser { /// Returns `null` to indicate a `@use` rule without a URL. String? _useNamespace(Uri url, LineScannerState start) { if (scanIdentifier("as")) { - whitespace(); + whitespace(consumeNewlines: true); return scanner.scanChar($asterisk) ? null : identifier(); } @@ -1530,17 +1530,19 @@ abstract class StylesheetParser extends Parser { var variableNames = {}; var configuration = []; - whitespace(); + whitespace(consumeNewlines: true); scanner.expectChar($lparen); while (true) { - whitespace(); + whitespace(consumeNewlines: true); var variableStart = scanner.state; var name = variableName(); - whitespace(); + whitespace(consumeNewlines: true); scanner.expectChar($colon); - whitespace(); + whitespace(consumeNewlines: true); + // TODO: This doesn't handle newlines, prevents tests in + // spec/directives/use|forward/whitespace/ from passing var expression = expressionUntilComma(); var guarded = false; @@ -1548,7 +1550,7 @@ abstract class StylesheetParser extends Parser { if (allowGuarded && scanner.scanChar($exclamation)) { if (identifier() == 'default') { guarded = true; - whitespace(); + whitespace(consumeNewlines: true); } else { error("Invalid flag name.", scanner.spanFrom(flagStart)); } @@ -1563,7 +1565,7 @@ abstract class StylesheetParser extends Parser { .add(ConfiguredVariable(name, expression, span, guarded: guarded)); if (!scanner.scanChar($comma)) break; - whitespace(); + whitespace(consumeNewlines: true); if (!_lookingAtExpression()) break; } From 9e49c3b05241b9d17f3a6b502fbf92b3409ba179 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 2 Dec 2024 16:10:38 -0500 Subject: [PATCH 10/58] Whitespace in @function and @return --- lib/src/parse/stylesheet.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index a9381042f..317c763c6 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -649,7 +649,7 @@ abstract class StylesheetParser extends Parser { if (!root) _disallowedAtRule(start); return _forwardRule(start); case "function": - whitespace(); + whitespace(consumeNewlines: true); return _functionRule(start); case "if": whitespace(); @@ -670,7 +670,7 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: false); return mozDocumentRule(start, name); case "return": - whitespace(); + whitespace(consumeNewlines: false); return _disallowedAtRule(start); case "supports": whitespace(consumeNewlines: false); @@ -909,7 +909,7 @@ abstract class StylesheetParser extends Parser { )); } - whitespace(); + whitespace(consumeNewlines: false); var arguments = _argumentDeclaration(); if (_inMixin || _inContentBlock) { @@ -1456,6 +1456,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ReturnRule _returnRule(LineScannerState start) { + whitespace(consumeNewlines: true); var value = _expression(); expectStatementSeparator("@return rule"); return ReturnRule(value, scanner.spanFrom(start)); @@ -1467,7 +1468,7 @@ abstract class StylesheetParser extends Parser { @protected SupportsRule supportsRule(LineScannerState start) { var condition = _supportsCondition(); - whitespace(); + whitespace(consumeNewlines: false); return _withChildren(_statement, start, (children, span) => SupportsRule(condition, children, span)); } From 939d2faf6e9e69f89d059a59c3815b9fa19b3c7d Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Tue, 3 Dec 2024 14:29:30 -0500 Subject: [PATCH 11/58] Whitespace in @if --- lib/src/parse/stylesheet.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 317c763c6..6cfb06d0f 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -632,7 +632,7 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: true); return _eachRule(start, child); case "else": - whitespace(); + whitespace(consumeNewlines: false); return _disallowedAtRule(start); case "error": whitespace(consumeNewlines: false); @@ -652,7 +652,7 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: true); return _functionRule(start); case "if": - whitespace(); + whitespace(consumeNewlines: true); return _ifRule(start, child); case "import": whitespace(consumeNewlines: false); @@ -1065,9 +1065,9 @@ abstract class StylesheetParser extends Parser { ElseClause? lastClause; while (scanElse(ifIndentation)) { - whitespace(); + whitespace(consumeNewlines: true); if (scanIdentifier("if")) { - whitespace(); + whitespace(consumeNewlines: true); clauses.add(IfClause(_expression(), this.children(child))); } else { lastClause = ElseClause(this.children(child)); @@ -1960,7 +1960,7 @@ abstract class StylesheetParser extends Parser { loop: while (true) { - whitespace(); + whitespace(consumeNewlines: false); if (until != null && until()) break; switch (scanner.peekChar()) { From d050c0182c8efce64bef751bd533fb89f7bac29a Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Tue, 3 Dec 2024 15:02:19 -0500 Subject: [PATCH 12/58] Whitespace in @while --- lib/src/parse/stylesheet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 6cfb06d0f..0b02cf03c 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -684,7 +684,7 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: false); return _warnRule(start); case "while": - whitespace(); + whitespace(consumeNewlines: true); return _whileRule(start, child); default: whitespace(); From e8cf0fba7ee9df3c27ab124cf6f1d792fd403fd2 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Tue, 3 Dec 2024 15:58:05 -0500 Subject: [PATCH 13/58] Whitespace in @at-root --- lib/src/parse/stylesheet.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 0b02cf03c..7d307cfe3 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -687,7 +687,7 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: true); return _whileRule(start, child); default: - whitespace(); + whitespace(consumeNewlines: false); return unknownAtRule(start, name); } } @@ -784,18 +784,20 @@ abstract class StylesheetParser extends Parser { var buffer = InterpolationBuffer(); scanner.expectChar($lparen); buffer.writeCharCode($lparen); - whitespace(); + whitespace(consumeNewlines: true); _addOrInject(buffer, _expression()); + // TODO: _expression here needs to be able to consume newlines. if (scanner.scanChar($colon)) { - whitespace(); + whitespace(consumeNewlines: true); buffer.writeCharCode($colon); buffer.writeCharCode($space); + // TODO: _expression here needs to be able to consume newlines. _addOrInject(buffer, _expression()); } scanner.expectChar($rparen); - whitespace(); + whitespace(consumeNewlines: false); buffer.writeCharCode($rparen); return buffer.interpolation(scanner.spanFrom(start)); From 0aaa63a18b36c4a78821e76bad6c50407d09cf50 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Wed, 4 Dec 2024 10:39:20 -0500 Subject: [PATCH 14/58] Whitespace in arguments and @charset --- lib/src/parse/stylesheet.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 7d307cfe3..15896ff19 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -91,7 +91,7 @@ abstract class StylesheetParser extends Parser { // Handle this specially so that [atRule] always returns a non-nullable // Statement. if (scanner.scan('@charset')) { - whitespace(); + whitespace(consumeNewlines: true); string(); return null; } @@ -1642,23 +1642,23 @@ abstract class StylesheetParser extends Parser { ArgumentDeclaration _argumentDeclaration() { var start = scanner.state; scanner.expectChar($lparen); - whitespace(); + whitespace(consumeNewlines: true); var arguments = []; var named = {}; String? restArgument; while (scanner.peekChar() == $dollar) { var variableStart = scanner.state; var name = variableName(); - whitespace(); + whitespace(consumeNewlines: true); Expression? defaultValue; if (scanner.scanChar($colon)) { - whitespace(); + whitespace(consumeNewlines: true); defaultValue = expressionUntilComma(); } else if (scanner.scanChar($dot)) { scanner.expectChar($dot); scanner.expectChar($dot); - whitespace(); + whitespace(consumeNewlines: true); restArgument = name; break; } @@ -1670,7 +1670,7 @@ abstract class StylesheetParser extends Parser { } if (!scanner.scanChar($comma)) break; - whitespace(); + whitespace(consumeNewlines: true); } scanner.expectChar($rparen); return ArgumentDeclaration(arguments, scanner.spanFrom(start), From cfa30ee159c26c414d840271a03958a5e9941a80 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Wed, 4 Dec 2024 11:56:55 -0500 Subject: [PATCH 15/58] + and = syntax whitespace --- lib/src/parse/stylesheet.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 15896ff19..f648cb503 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -178,6 +178,8 @@ abstract class StylesheetParser extends Parser { return atRule(() => _statement(), root: root); case $plus: + // TODO: newlines not supported because of `lookingAtIdentifier`. + // This means it doesn't behave the same as = mixin syntax below. if (!indented || !lookingAtIdentifier(1)) return _styleRule(); _isUseAllowed = false; var start = scanner.state; @@ -189,7 +191,7 @@ abstract class StylesheetParser extends Parser { _isUseAllowed = false; var start = scanner.state; scanner.readChar(); - whitespace(); + whitespace(consumeNewlines: true); return _mixinRule(start); case $rbrace: From e6442d47adda85ec751bed2dfaad34915cd947b4 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Wed, 4 Dec 2024 12:15:03 -0500 Subject: [PATCH 16/58] Whitespace in variable declarations --- lib/src/parse/stylesheet.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index f648cb503..ce8bade63 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -231,9 +231,9 @@ abstract class StylesheetParser extends Parser { scanner.spanFrom(start)); } - whitespace(); + whitespace(consumeNewlines: true); scanner.expectChar($colon); - whitespace(); + whitespace(consumeNewlines: true); var value = _expression(); @@ -273,7 +273,7 @@ abstract class StylesheetParser extends Parser { error("Invalid flag name.", scanner.spanFrom(flagStart)); } - whitespace(); + whitespace(consumeNewlines: false); flagStart = scanner.state; } From 0d818501e65cf7df52de869ca2c8af138217eeb1 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Wed, 4 Dec 2024 14:46:31 -0500 Subject: [PATCH 17/58] Move initial whitespace inside each at rule --- lib/src/parse/stylesheet.dart | 44 ++++++++++++++++------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index ce8bade63..67f88b2c6 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -621,75 +621,52 @@ abstract class StylesheetParser extends Parser { switch (name.asPlain) { case "at-root": - whitespace(consumeNewlines: false); return _atRootRule(start); case "content": - // TODO: _contentRule consumes whitespace- is this needed? - whitespace(consumeNewlines: false); return _contentRule(start); case "debug": - whitespace(consumeNewlines: false); return _debugRule(start); case "each": - whitespace(consumeNewlines: true); return _eachRule(start, child); case "else": - whitespace(consumeNewlines: false); return _disallowedAtRule(start); case "error": - whitespace(consumeNewlines: false); return _errorRule(start); case "extend": - whitespace(consumeNewlines: true); return _extendRule(start); case "for": - whitespace(consumeNewlines: true); return _forRule(start, child); case "forward": - whitespace(consumeNewlines: true); _isUseAllowed = wasUseAllowed; if (!root) _disallowedAtRule(start); return _forwardRule(start); case "function": - whitespace(consumeNewlines: true); return _functionRule(start); case "if": - whitespace(consumeNewlines: true); return _ifRule(start, child); case "import": - whitespace(consumeNewlines: false); return _importRule(start); case "include": - whitespace(consumeNewlines: true); return _includeRule(start); case "media": - whitespace(consumeNewlines: false); return mediaRule(start); case "mixin": - whitespace(consumeNewlines: true); return _mixinRule(start); case "-moz-document": - whitespace(consumeNewlines: false); return mozDocumentRule(start, name); case "return": - whitespace(consumeNewlines: false); return _disallowedAtRule(start); case "supports": - whitespace(consumeNewlines: false); return supportsRule(start); case "use": - whitespace(consumeNewlines: true); _isUseAllowed = wasUseAllowed; if (!root) _disallowedAtRule(start); return _useRule(start); case "warn": - whitespace(consumeNewlines: false); return _warnRule(start); case "while": - whitespace(consumeNewlines: true); return _whileRule(start, child); default: - whitespace(consumeNewlines: false); return unknownAtRule(start, name); } } @@ -758,7 +735,6 @@ abstract class StylesheetParser extends Parser { String _plainAtRuleName() { scanner.expectChar($at, name: "@-rule"); var name = identifier(); - whitespace(); return name; } @@ -766,6 +742,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. AtRootRule _atRootRule(LineScannerState start) { + whitespace(consumeNewlines: false); if (scanner.peekChar() == $lparen) { var query = _atRootQuery(); whitespace(); @@ -832,6 +809,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. DebugRule _debugRule(LineScannerState start) { + whitespace(consumeNewlines: false); var value = _expression(); expectStatementSeparator("@debug rule"); return DebugRule(value, scanner.spanFrom(start)); @@ -842,6 +820,7 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. [child] is called to consume any /// children that are specifically allowed in the caller's context. EachRule _eachRule(LineScannerState start, Statement child()) { + whitespace(consumeNewlines: true); var wasInControlDirective = _inControlDirective; _inControlDirective = true; @@ -868,6 +847,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ErrorRule _errorRule(LineScannerState start) { + whitespace(consumeNewlines: false); var value = _expression(); expectStatementSeparator("@error rule"); return ErrorRule(value, scanner.spanFrom(start)); @@ -877,6 +857,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ExtendRule _extendRule(LineScannerState start) { + whitespace(consumeNewlines: true); if (!_inStyleRule && !_inMixin && !_inContentBlock) { error("@extend may only be used within style rules.", scanner.spanFrom(start)); @@ -896,6 +877,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. FunctionRule _functionRule(LineScannerState start) { + whitespace(consumeNewlines: true); var precedingComment = lastSilentComment; lastSilentComment = null; var beforeName = scanner.state; @@ -949,6 +931,7 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. [child] is called to consume any /// children that are specifically allowed in the caller's context. ForRule _forRule(LineScannerState start, Statement child()) { + whitespace(consumeNewlines: true); var wasInControlDirective = _inControlDirective; _inControlDirective = true; var variable = variableName(); @@ -986,6 +969,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ForwardRule _forwardRule(LineScannerState start) { + whitespace(consumeNewlines: true); var url = _urlString(); whitespace(); @@ -1058,6 +1042,7 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. [child] is called to consume any /// children that are specifically allowed in the caller's context. IfRule _ifRule(LineScannerState start, Statement child()) { + whitespace(consumeNewlines: true); var ifIndentation = currentIndentation; var wasInControlDirective = _inControlDirective; _inControlDirective = true; @@ -1278,6 +1263,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. IncludeRule _includeRule(LineScannerState start) { + whitespace(consumeNewlines: true); String? namespace; var name = identifier(); if (scanner.scanChar($dot)) { @@ -1322,6 +1308,7 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. @protected MediaRule mediaRule(LineScannerState start) { + whitespace(consumeNewlines: false); var query = _mediaQueryList(); return _withChildren(_statement, start, (children, span) => MediaRule(query, children, span)); @@ -1331,6 +1318,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. MixinRule _mixinRule(LineScannerState start) { + whitespace(consumeNewlines: true); var precedingComment = lastSilentComment; lastSilentComment = null; var beforeName = scanner.state; @@ -1380,6 +1368,7 @@ abstract class StylesheetParser extends Parser { /// [the specification]: http://www.w3.org/TR/css3-conditional/ @protected AtRule mozDocumentRule(LineScannerState start, Interpolation name) { + whitespace(consumeNewlines: false); var valueStart = scanner.state; var buffer = InterpolationBuffer(); var needsDeprecationWarning = false; @@ -1471,6 +1460,7 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. @protected SupportsRule supportsRule(LineScannerState start) { + whitespace(consumeNewlines: false); var condition = _supportsCondition(); whitespace(consumeNewlines: false); return _withChildren(_statement, start, @@ -1481,6 +1471,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. UseRule _useRule(LineScannerState start) { + whitespace(consumeNewlines: true); var url = _urlString(); whitespace(consumeNewlines: false); @@ -1582,6 +1573,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. WarnRule _warnRule(LineScannerState start) { + whitespace(consumeNewlines: false); var value = _expression(); expectStatementSeparator("@warn rule"); return WarnRule(value, scanner.spanFrom(start)); @@ -1592,6 +1584,7 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. [child] is called to consume any /// children that are specifically allowed in the caller's context. WhileRule _whileRule(LineScannerState start, Statement child()) { + whitespace(consumeNewlines: true); var wasInControlDirective = _inControlDirective; _inControlDirective = true; var condition = _expression(); @@ -1609,6 +1602,8 @@ abstract class StylesheetParser extends Parser { var wasInUnknownAtRule = _inUnknownAtRule; _inUnknownAtRule = true; + whitespace(consumeNewlines: false); + Interpolation? value; if (scanner.peekChar() != $exclamation && !atEndOfStatement()) { value = _interpolatedDeclarationValue(allowOpenBrace: false); @@ -1636,6 +1631,7 @@ abstract class StylesheetParser extends Parser { /// This declares a return type of [Statement] so that it can be returned /// within case statements. Statement _disallowedAtRule(LineScannerState start) { + whitespace(consumeNewlines: false); _interpolatedDeclarationValue(allowEmpty: true, allowOpenBrace: false); error("This at-rule is not allowed here.", scanner.spanFrom(start)); } From 2a3c1606e6e3931e27ccf24a5e487e2c03722848 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Thu, 5 Dec 2024 10:57:23 -0500 Subject: [PATCH 18/58] Whitespace in lists and maps --- lib/src/parse/stylesheet.dart | 41 ++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 67f88b2c6..2bee2fbbd 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -745,7 +745,8 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: false); if (scanner.peekChar() == $lparen) { var query = _atRootQuery(); - whitespace(); + // TODO- This can be removed, as it is redundant with one in _atRootQuery + // whitespace(); return _withChildren(_statement, start, (children, span) => AtRootRule(children, span, query: query)); } else if (lookingAtChildren() || (indented && atEndOfStatement())) { @@ -825,11 +826,11 @@ abstract class StylesheetParser extends Parser { _inControlDirective = true; var variables = [variableName()]; - whitespace(); + whitespace(consumeNewlines: false); while (scanner.scanChar($comma)) { - whitespace(); + whitespace(consumeNewlines: false); variables.add(variableName()); - whitespace(); + whitespace(consumeNewlines: false); } whitespace(consumeNewlines: true); expectIdentifier("in"); @@ -918,7 +919,7 @@ abstract class StylesheetParser extends Parser { error("Invalid function name.", scanner.spanFrom(start)); } - whitespace(); + whitespace(consumeNewlines: false); return _withChildren( _functionChild, start, @@ -971,7 +972,7 @@ abstract class StylesheetParser extends Parser { ForwardRule _forwardRule(LineScannerState start) { whitespace(consumeNewlines: true); var url = _urlString(); - whitespace(); + whitespace(consumeNewlines: false); String? prefix; if (scanIdentifier("as")) { @@ -986,8 +987,10 @@ abstract class StylesheetParser extends Parser { Set? hiddenMixinsAndFunctions; Set? hiddenVariables; if (scanIdentifier("show")) { + whitespace(consumeNewlines: true); (shownMixinsAndFunctions, shownVariables) = _memberList(); } else if (scanIdentifier("hide")) { + whitespace(consumeNewlines: true); (hiddenMixinsAndFunctions, hiddenVariables) = _memberList(); } @@ -1023,7 +1026,7 @@ abstract class StylesheetParser extends Parser { var identifiers = {}; var variables = {}; do { - whitespace(); + whitespace(consumeNewlines: false); withErrorMessage("Expected variable, mixin, or function name", () { if (scanner.peekChar() == $dollar) { variables.add(variableName()); @@ -1031,7 +1034,7 @@ abstract class StylesheetParser extends Parser { identifiers.add(identifier(normalize: true)); } }); - whitespace(); + whitespace(consumeNewlines: false); } while (scanner.scanChar($comma)); return (identifiers, variables); @@ -1762,7 +1765,7 @@ abstract class StylesheetParser extends Parser { if (bracketList) { beforeBracket = scanner.state; scanner.expectChar($lbracket); - whitespace(); + whitespace(consumeNewlines: true); if (scanner.scanChar($rbracket)) { return ListExpression( @@ -1938,8 +1941,8 @@ abstract class StylesheetParser extends Parser { length: operator.operator.length); } operands.add(singleExpression); - - whitespace(); + // TODO: This needs more test coverage + whitespace(consumeNewlines: true); singleExpression_ = _singleExpression(); } @@ -1958,9 +1961,11 @@ abstract class StylesheetParser extends Parser { spaceExpressions_ = null; } + var consumeNewlines = bracketList || _inParentheses; + loop: while (true) { - whitespace(consumeNewlines: false); + whitespace(consumeNewlines: consumeNewlines); if (until != null && until()) break; switch (scanner.peekChar()) { @@ -2204,7 +2209,7 @@ abstract class StylesheetParser extends Parser { try { var start = scanner.state; scanner.expectChar($lparen); - whitespace(); + whitespace(consumeNewlines: true); if (!_lookingAtExpression()) { scanner.expectChar($rparen); return ListExpression( @@ -2213,7 +2218,7 @@ abstract class StylesheetParser extends Parser { var first = expressionUntilComma(); if (scanner.scanChar($colon)) { - whitespace(); + whitespace(consumeNewlines: true); return _map(first, start); } @@ -2221,14 +2226,14 @@ abstract class StylesheetParser extends Parser { scanner.expectChar($rparen); return ParenthesizedExpression(first, scanner.spanFrom(start)); } - whitespace(); + whitespace(consumeNewlines: true); var expressions = [first]; while (true) { if (!_lookingAtExpression()) break; expressions.add(expressionUntilComma()); if (!scanner.scanChar($comma)) break; - whitespace(); + whitespace(consumeNewlines: true); } scanner.expectChar($rparen); @@ -2248,12 +2253,12 @@ abstract class StylesheetParser extends Parser { var pairs = [(first, expressionUntilComma())]; while (scanner.scanChar($comma)) { - whitespace(); + whitespace(consumeNewlines: true); if (!_lookingAtExpression()) break; var key = expressionUntilComma(); scanner.expectChar($colon); - whitespace(); + whitespace(consumeNewlines: true); var value = expressionUntilComma(); pairs.add((key, value)); } From 65ec4598296176ba10b0f333fafc6f21876bc060 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Thu, 5 Dec 2024 13:31:45 -0500 Subject: [PATCH 19/58] Handle whitespace in important and unaries --- lib/src/parse/stylesheet.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 2bee2fbbd..2753ce7fe 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -2373,7 +2373,7 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; scanner.readChar(); - whitespace(); + whitespace(consumeNewlines: true); expectIdentifier("important"); return StringExpression.plain("!important", scanner.spanFrom(start)); } @@ -2389,7 +2389,7 @@ abstract class StylesheetParser extends Parser { position: scanner.position - 1, length: 1); } - whitespace(); + whitespace(consumeNewlines: true); var operand = _singleExpression(); return UnaryOperationExpression(operator, operand, scanner.spanFrom(start)); } From 7de5da3b073d4cd0f0976ccac6692695819d28bf Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Thu, 5 Dec 2024 15:32:46 -0500 Subject: [PATCH 20/58] Whitespace in interpolation, unblock expressionUntilComma issues --- lib/src/parse/stylesheet.dart | 68 +++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 2753ce7fe..4b893a83d 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -766,14 +766,12 @@ abstract class StylesheetParser extends Parser { buffer.writeCharCode($lparen); whitespace(consumeNewlines: true); - _addOrInject(buffer, _expression()); - // TODO: _expression here needs to be able to consume newlines. + _addOrInject(buffer, _expression(consumeNewlines: true)); if (scanner.scanChar($colon)) { whitespace(consumeNewlines: true); buffer.writeCharCode($colon); buffer.writeCharCode($space); - // TODO: _expression here needs to be able to consume newlines. - _addOrInject(buffer, _expression()); + _addOrInject(buffer, _expression(consumeNewlines: true)); } scanner.expectChar($rparen); @@ -942,18 +940,20 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: true); bool? exclusive; - var from = _expression(until: () { - if (!lookingAtIdentifier()) return false; - if (scanIdentifier("to")) { - exclusive = true; - return true; - } else if (scanIdentifier("through")) { - exclusive = false; - return true; - } else { - return false; - } - }); + var from = _expression( + consumeNewlines: true, + until: () { + if (!lookingAtIdentifier()) return false; + if (scanIdentifier("to")) { + exclusive = true; + return true; + } else if (scanIdentifier("through")) { + exclusive = false; + return true; + } else { + return false; + } + }); if (exclusive == null) scanner.error('Expected "to" or "through".'); whitespace(consumeNewlines: true); @@ -1201,9 +1201,9 @@ abstract class StylesheetParser extends Parser { } scanner.expectChar($rparen); - whitespace(); + whitespace(consumeNewlines: false); } else { - whitespace(); + whitespace(consumeNewlines: true); if (scanner.scanChar($comma)) { buffer.write(", "); buffer.addInterpolation(_mediaQueryList()); @@ -1224,7 +1224,7 @@ abstract class StylesheetParser extends Parser { /// (but not the function name or parentheses). SupportsCondition _importSupportsQuery() { if (scanIdentifier("not")) { - whitespace(); + whitespace(consumeNewlines: true); var start = scanner.state; return SupportsNegation( _supportsConditionInParens(), scanner.spanFrom(start)); @@ -1540,9 +1540,8 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: true); scanner.expectChar($colon); whitespace(consumeNewlines: true); - // TODO: This doesn't handle newlines, prevents tests in - // spec/directives/use|forward/whitespace/ from passing - var expression = expressionUntilComma(); + + var expression = expressionUntilComma(consumeNewlines: true); var guarded = false; var flagStart = scanner.state; @@ -1758,7 +1757,10 @@ abstract class StylesheetParser extends Parser { /// expression. @protected Expression _expression( - {bool bracketList = false, bool singleEquals = false, bool until()?}) { + {bool bracketList = false, + bool singleEquals = false, + bool until()?, + bool consumeNewlines = false}) { if (until != null && until()) scanner.error("Expected expression."); LineScannerState? beforeBracket; @@ -1961,11 +1963,13 @@ abstract class StylesheetParser extends Parser { spaceExpressions_ = null; } - var consumeNewlines = bracketList || _inParentheses; - loop: while (true) { - whitespace(consumeNewlines: consumeNewlines); + whitespace( + consumeNewlines: consumeNewlines || + bracketList || + _inParentheses || + wasInExpression); if (until != null && until()) break; switch (scanner.peekChar()) { @@ -2159,8 +2163,12 @@ abstract class StylesheetParser extends Parser { /// /// If [singleEquals] is true, this will allow the Microsoft-style `=` /// operator at the top level. - Expression expressionUntilComma({bool singleEquals = false}) => _expression( - singleEquals: singleEquals, until: () => scanner.peekChar() == $comma); + Expression expressionUntilComma( + {bool singleEquals = false, bool consumeNewlines = false}) => + _expression( + singleEquals: singleEquals, + consumeNewlines: consumeNewlines, + until: () => scanner.peekChar() == $comma); /// Whether [expression] is allowed as an operand of a `/` expression that /// produces a potentially slash-separated number. @@ -3107,8 +3115,8 @@ abstract class StylesheetParser extends Parser { (Expression, FileSpan span) singleInterpolation() { var start = scanner.state; scanner.expect('#{'); - whitespace(); - var contents = _expression(); + whitespace(consumeNewlines: true); + var contents = _expression(consumeNewlines: true); scanner.expectChar($rbrace); var span = scanner.spanFrom(start); From 85d14a9f92cd94cd51f2f5406b7b0702f4f330ba Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 6 Dec 2024 10:19:36 -0500 Subject: [PATCH 21/58] Consume whitespace in sub-stylesheet parsers --- lib/src/parse/stylesheet.dart | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 4b893a83d..307589e69 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -108,10 +108,12 @@ abstract class StylesheetParser extends Parser { ArgumentDeclaration parseArgumentDeclaration() => _parseSingleProduction(() { scanner.expectChar($at, name: "@-rule"); identifier(); - whitespace(); + // TODO: No tests + whitespace(consumeNewlines: true); identifier(); var arguments = _argumentDeclaration(); - whitespace(); + // TODO: No tests + whitespace(consumeNewlines: true); scanner.expectChar($lbrace); return arguments; }); @@ -136,7 +138,8 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; scanner.expectChar($at, name: "@-rule"); expectIdentifier("use"); - whitespace(); + // TODO: No tests + whitespace(consumeNewlines: true); return _useRule(start); }), warnings @@ -1755,6 +1758,9 @@ abstract class StylesheetParser extends Parser { /// If [until] is passed, it's called each time the expression could end and /// still be a valid expression. When it returns `true`, this returns the /// expression. + /// + /// If [consumeNewlines] is true, consuming whitespace in the indented syntax + /// will include newlines. @protected Expression _expression( {bool bracketList = false, From 61304e67ad25e04ce8ab22556b3f6b44a47cd260 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 6 Dec 2024 10:48:30 -0500 Subject: [PATCH 22/58] Consume whitespace in parse/css. Meaningless, as it extends scss --- lib/src/parse/css.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/parse/css.dart b/lib/src/parse/css.dart index 4fd9ae2df..f6f546cd4 100644 --- a/lib/src/parse/css.dart +++ b/lib/src/parse/css.dart @@ -49,7 +49,7 @@ class CssParser extends ScssParser { var start = scanner.state; scanner.expectChar($at); var name = interpolatedIdentifier(); - whitespace(); + whitespace(consumeNewlines: true); return switch (name.asPlain) { "at-root" || @@ -113,7 +113,7 @@ class CssParser extends ScssParser { .text }; - whitespace(); + whitespace(consumeNewlines: true); var modifiers = tryImportModifiers(); expectStatementSeparator("@import rule"); return ImportRule( @@ -126,7 +126,7 @@ class CssParser extends ScssParser { // evaluation time. var start = scanner.state; scanner.expectChar($lparen); - whitespace(); + whitespace(consumeNewlines: true); var expression = expressionUntilComma(); scanner.expectChar($rparen); return ParenthesizedExpression(expression, scanner.spanFrom(start)); @@ -151,7 +151,7 @@ class CssParser extends ScssParser { var arguments = []; if (!scanner.scanChar($rparen)) { do { - whitespace(); + whitespace(consumeNewlines: true); if (allowEmptySecondArg && arguments.length == 1 && scanner.peekChar() == $rparen) { @@ -160,7 +160,7 @@ class CssParser extends ScssParser { } arguments.add(expressionUntilComma(singleEquals: true)); - whitespace(); + whitespace(consumeNewlines: true); } while (scanner.scanChar($comma)); scanner.expectChar($rparen); } From d7ee188fd05753ec828c7b2befef0ba59df8dac4 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 6 Dec 2024 11:54:12 -0500 Subject: [PATCH 23/58] Whitespace in @supports --- lib/src/parse/stylesheet.dart | 36 +++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 307589e69..c3d34f414 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -2936,7 +2936,8 @@ abstract class StylesheetParser extends Parser { bool allowSemicolon = false, bool allowColon = true, bool allowOpenBrace = true, - bool silentComments = true}) { + bool silentComments = true, + bool consumeNewlines = false}) { // NOTE: this logic is largely duplicated in Parser.declarationValue. Most // changes here should be mirrored there. @@ -2986,7 +2987,7 @@ abstract class StylesheetParser extends Parser { case $space || $tab: buffer.writeCharCode(scanner.readChar()); - case $lf || $cr || $ff when indented: + case $lf || $cr || $ff when indented && !consumeNewlines: break loop; case $lf || $cr || $ff: @@ -3330,13 +3331,13 @@ abstract class StylesheetParser extends Parser { SupportsCondition _supportsCondition() { var start = scanner.state; if (scanIdentifier("not")) { - whitespace(); + whitespace(consumeNewlines: true); return SupportsNegation( _supportsConditionInParens(), scanner.spanFrom(start)); } var condition = _supportsConditionInParens(); - whitespace(); + whitespace(consumeNewlines: false); String? operator; while (lookingAtIdentifier()) { if (operator != null) { @@ -3348,11 +3349,11 @@ abstract class StylesheetParser extends Parser { operator = "and"; } - whitespace(); + whitespace(consumeNewlines: true); var right = _supportsConditionInParens(); condition = SupportsOperation( condition, right, operator, scanner.spanFrom(start)); - whitespace(); + whitespace(consumeNewlines: false); } return condition; } @@ -3369,7 +3370,7 @@ abstract class StylesheetParser extends Parser { if (scanner.scanChar($lparen)) { var arguments = _interpolatedDeclarationValue( - allowEmpty: true, allowSemicolon: true); + allowEmpty: true, allowSemicolon: true, consumeNewlines: true); scanner.expectChar($rparen); return SupportsFunction(identifier, arguments, scanner.spanFrom(start)); } else if (identifier.contents case [Expression expression]) { @@ -3380,9 +3381,9 @@ abstract class StylesheetParser extends Parser { } scanner.expectChar($lparen); - whitespace(); + whitespace(consumeNewlines: true); if (scanIdentifier("not")) { - whitespace(); + whitespace(consumeNewlines: true); var condition = _supportsConditionInParens(); scanner.expectChar($rparen); return SupportsNegation(condition, scanner.spanFrom(start)); @@ -3410,7 +3411,7 @@ abstract class StylesheetParser extends Parser { var nameStart = scanner.state; var wasInParentheses = _inParentheses; try { - name = _expression(); + name = _expression(consumeNewlines: true); scanner.expectChar($colon); } on FormatException catch (_) { scanner.state = nameStart; @@ -3429,7 +3430,10 @@ abstract class StylesheetParser extends Parser { var contents = (InterpolationBuffer() ..addInterpolation(identifier) ..addInterpolation(_interpolatedDeclarationValue( - allowEmpty: true, allowSemicolon: true, allowColon: false))) + allowEmpty: true, + allowSemicolon: true, + allowColon: false, + consumeNewlines: true))) .interpolation(scanner.spanFrom(nameStart)); if (scanner.peekChar() == $colon) rethrow; @@ -3449,8 +3453,8 @@ abstract class StylesheetParser extends Parser { when text.initialPlain.startsWith("--")) { return StringExpression(_interpolatedDeclarationValue()); } else { - whitespace(); - return _expression(); + whitespace(consumeNewlines: true); + return _expression(consumeNewlines: true); } } @@ -3464,7 +3468,7 @@ abstract class StylesheetParser extends Parser { if (expression is! Expression) return null; var beforeWhitespace = scanner.state; - whitespace(); + whitespace(consumeNewlines: true); SupportsOperation? operation; String? operator; @@ -3480,14 +3484,14 @@ abstract class StylesheetParser extends Parser { return null; } - whitespace(); + whitespace(consumeNewlines: true); var right = _supportsConditionInParens(); operation = SupportsOperation( operation ?? SupportsInterpolation(expression, interpolation.span), right, operator, scanner.spanFrom(start)); - whitespace(); + whitespace(consumeNewlines: true); } return operation; From 976e38a5e4682eb62728ff755f92c7d6f0d194b0 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 6 Dec 2024 12:26:55 -0500 Subject: [PATCH 24/58] Whitespace in @media --- lib/src/parse/parser.dart | 10 ++++++---- lib/src/parse/stylesheet.dart | 21 +++++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index a5cb3bc5d..ad3d27639 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -75,7 +75,7 @@ class Parser { /// Consumes whitespace, including any comments. /// - /// Set `consumeNewlines` to true when a statement could not end, so newlines + /// Set [consumeNewlines] to true when a statement could not end, so newlines /// can be consumed. Only used in the indented syntax. /// /// TODO: Bikeshed the variable name @@ -121,13 +121,15 @@ class Parser { } /// Like [whitespace], but throws an error if no whitespace is consumed. + /// + /// If [consumeNewlines] is true, consuming whitespace in the indented syntax + /// will include newlines. @protected - void expectWhitespace() { + void expectWhitespace({bool consumeNewlines = false}) { if (scanner.isDone || !(scanner.peekChar().isWhitespace || scanComment())) { scanner.error("Expected whitespace."); } - // TODO: Does this need to by dynamic by context? - whitespace(consumeNewlines: false); + whitespace(consumeNewlines: consumeNewlines); } /// Consumes and ignores a single silent (Sass-style) comment, not including diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index c3d34f414..59d61a986 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -3255,32 +3255,32 @@ abstract class StylesheetParser extends Parser { void _mediaInParens(InterpolationBuffer buffer) { scanner.expectChar($lparen, name: "media condition in parentheses"); buffer.writeCharCode($lparen); - whitespace(); + whitespace(consumeNewlines: true); if (scanner.peekChar() == $lparen) { _mediaInParens(buffer); - whitespace(); + whitespace(consumeNewlines: true); if (scanIdentifier("and")) { buffer.write(" and "); - expectWhitespace(); + expectWhitespace(consumeNewlines: true); _mediaLogicSequence(buffer, "and"); } else if (scanIdentifier("or")) { buffer.write(" or "); - expectWhitespace(); + expectWhitespace(consumeNewlines: true); _mediaLogicSequence(buffer, "or"); } } else if (scanIdentifier("not")) { buffer.write("not "); - expectWhitespace(); + expectWhitespace(consumeNewlines: true); _mediaOrInterp(buffer); } else { var expressionBefore = _expressionUntilComparison(); buffer.add(expressionBefore, expressionBefore.span); if (scanner.scanChar($colon)) { - whitespace(); + whitespace(consumeNewlines: true); buffer.writeCharCode($colon); buffer.writeCharCode($space); - var expressionAfter = _expression(); + var expressionAfter = _expression(consumeNewlines: true); buffer.add(expressionAfter, expressionAfter.span); } else { var next = scanner.peekChar(); @@ -3292,7 +3292,7 @@ abstract class StylesheetParser extends Parser { } buffer.writeCharCode($space); - whitespace(); + whitespace(consumeNewlines: true); var expressionMiddle = _expressionUntilComparison(); buffer.add(expressionMiddle, expressionMiddle.span); @@ -3303,7 +3303,7 @@ abstract class StylesheetParser extends Parser { if (scanner.scanChar($equal)) buffer.writeCharCode($equal); buffer.writeCharCode($space); - whitespace(); + whitespace(consumeNewlines: true); var expressionAfter = _expressionUntilComparison(); buffer.add(expressionAfter, expressionAfter.span); } @@ -3312,13 +3312,14 @@ abstract class StylesheetParser extends Parser { } scanner.expectChar($rparen); - whitespace(); + whitespace(consumeNewlines: false); buffer.writeCharCode($rparen); } /// Consumes an expression until it reaches a top-level `<`, `>`, or a `=` /// that's not `==`. Expression _expressionUntilComparison() => _expression( + consumeNewlines: true, until: () => switch (scanner.peekChar()) { $equal => scanner.peekChar(1) != $equal, $langle || $rangle => true, From a7f439c6c0ca3bb7b4621d465c607600f241f0f8 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 6 Dec 2024 15:30:43 -0500 Subject: [PATCH 25/58] Handle whitespace in arg invocations --- lib/src/parse/stylesheet.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 59d61a986..52e14178d 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -550,7 +550,7 @@ abstract class StylesheetParser extends Parser { name = interpolatedIdentifier(); } - whitespace(); + whitespace(consumeNewlines: false); scanner.expectChar($colon); if (parseCustomProperties && name.initialPlain.startsWith('--')) { @@ -560,7 +560,7 @@ abstract class StylesheetParser extends Parser { return Declaration(name, value, scanner.spanFrom(start)); } - whitespace(); + whitespace(consumeNewlines: false); if (_tryDeclarationChildren(name, start) case var nested?) return nested; var value = _expression(); @@ -1695,7 +1695,7 @@ abstract class StylesheetParser extends Parser { {bool mixin = false, bool allowEmptySecondArg = false}) { var start = scanner.state; scanner.expectChar($lparen); - whitespace(); + whitespace(consumeNewlines: true); var positional = []; var named = {}; @@ -1703,10 +1703,10 @@ abstract class StylesheetParser extends Parser { Expression? keywordRest; while (_lookingAtExpression()) { var expression = expressionUntilComma(singleEquals: !mixin); - whitespace(); + whitespace(consumeNewlines: true); if (expression is VariableExpression && scanner.scanChar($colon)) { - whitespace(); + whitespace(consumeNewlines: true); if (named.containsKey(expression.name)) { error("Duplicate argument.", expression.span); } @@ -1718,7 +1718,7 @@ abstract class StylesheetParser extends Parser { rest = expression; } else { keywordRest = expression; - whitespace(); + whitespace(consumeNewlines: true); break; } } else if (named.isNotEmpty) { @@ -1728,9 +1728,9 @@ abstract class StylesheetParser extends Parser { positional.add(expression); } - whitespace(); + whitespace(consumeNewlines: true); if (!scanner.scanChar($comma)) break; - whitespace(); + whitespace(consumeNewlines: true); if (allowEmptySecondArg && positional.length == 1 && From 14dbd8d9297c745e17b7e77690099f27640e5d12 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 6 Dec 2024 16:03:57 -0500 Subject: [PATCH 26/58] Whitespace in parsers --- lib/src/parse/at_root_query.dart | 9 +++++---- lib/src/parse/keyframe_selector.dart | 4 ++-- lib/src/parse/media_query.dart | 12 ++++++------ lib/src/parse/parser.dart | 6 +++--- lib/src/parse/stylesheet.dart | 8 +++----- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/lib/src/parse/at_root_query.dart b/lib/src/parse/at_root_query.dart index f69501ea6..f8ec84849 100644 --- a/lib/src/parse/at_root_query.dart +++ b/lib/src/parse/at_root_query.dart @@ -14,17 +14,18 @@ class AtRootQueryParser extends Parser { AtRootQuery parse() { return wrapSpanFormatException(() { scanner.expectChar($lparen); - whitespace(); + // TODO: No tests + whitespace(consumeNewlines: true); var include = scanIdentifier("with"); if (!include) expectIdentifier("without", name: '"with" or "without"'); - whitespace(); + whitespace(consumeNewlines: true); scanner.expectChar($colon); - whitespace(); + whitespace(consumeNewlines: true); var atRules = {}; do { atRules.add(identifier().toLowerCase()); - whitespace(); + whitespace(consumeNewlines: true); } while (lookingAtIdentifier()); scanner.expectChar($rparen); scanner.expectDone(); diff --git a/lib/src/parse/keyframe_selector.dart b/lib/src/parse/keyframe_selector.dart index fb69ee638..506e93e02 100644 --- a/lib/src/parse/keyframe_selector.dart +++ b/lib/src/parse/keyframe_selector.dart @@ -15,7 +15,7 @@ class KeyframeSelectorParser extends Parser { return wrapSpanFormatException(() { var selectors = []; do { - whitespace(); + whitespace(consumeNewlines: false); if (lookingAtIdentifier()) { if (scanIdentifier("from")) { selectors.add("from"); @@ -26,7 +26,7 @@ class KeyframeSelectorParser extends Parser { } else { selectors.add(_percentage()); } - whitespace(); + whitespace(consumeNewlines: false); } while (scanner.scanChar($comma)); scanner.expectDone(); diff --git a/lib/src/parse/media_query.dart b/lib/src/parse/media_query.dart index 89d854161..24ebbb1d3 100644 --- a/lib/src/parse/media_query.dart +++ b/lib/src/parse/media_query.dart @@ -16,9 +16,9 @@ class MediaQueryParser extends Parser { return wrapSpanFormatException(() { var queries = []; do { - whitespace(); + whitespace(consumeNewlines: true); queries.add(_mediaQuery()); - whitespace(); + whitespace(consumeNewlines: true); } while (scanner.scanChar($comma)); scanner.expectDone(); return queries; @@ -30,7 +30,7 @@ class MediaQueryParser extends Parser { // This is somewhat duplicated in StylesheetParser._mediaQuery. if (scanner.peekChar() == $lparen) { var conditions = [_mediaInParens()]; - whitespace(); + whitespace(consumeNewlines: true); var conjunction = true; if (scanIdentifier("and")) { @@ -57,7 +57,7 @@ class MediaQueryParser extends Parser { } } - whitespace(); + whitespace(consumeNewlines: true); if (!lookingAtIdentifier()) { // For example, "@media screen {" return CssMediaQuery.type(identifier1); @@ -70,7 +70,7 @@ class MediaQueryParser extends Parser { // For example, "@media screen and ..." type = identifier1; } else { - whitespace(); + whitespace(consumeNewlines: true); modifier = identifier1; type = identifier2; if (scanIdentifier("and")) { @@ -102,7 +102,7 @@ class MediaQueryParser extends Parser { var result = []; while (true) { result.add(_mediaInParens()); - whitespace(); + whitespace(consumeNewlines: true); if (!scanIdentifier(operator)) return result; expectWhitespace(); diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index ad3d27639..badd817f4 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -67,7 +67,7 @@ class Parser { if (!scanner.scanChar($dollar)) return false; if (!lookingAtIdentifier()) return false; identifier(); - whitespace(); + whitespace(consumeNewlines: false); return scanner.scanChar($colon); } @@ -393,7 +393,7 @@ class Parser { return null; } - whitespace(); + whitespace(consumeNewlines: true); // Match Ruby Sass's behavior: parse a raw URL() if possible, and if not // backtrack and re-parse as a function expression. @@ -414,7 +414,7 @@ class Parser { >= 0x0080: buffer.writeCharCode(scanner.readChar()); case int(isWhitespace: true): - whitespace(); + whitespace(consumeNewlines: true); if (scanner.peekChar() != $rparen) break loop; case $rparen: buffer.writeCharCode(scanner.readChar()); diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 52e14178d..783e9842b 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -748,8 +748,6 @@ abstract class StylesheetParser extends Parser { whitespace(consumeNewlines: false); if (scanner.peekChar() == $lparen) { var query = _atRootQuery(); - // TODO- This can be removed, as it is redundant with one in _atRootQuery - // whitespace(); return _withChildren(_statement, start, (children, span) => AtRootRule(children, span, query: query)); } else if (lookingAtChildren() || (indented && atEndOfStatement())) { @@ -2638,7 +2636,7 @@ abstract class StylesheetParser extends Parser { return IfExpression( invocation, identifier.span.expand(invocation.span)); } else if (plain == "not") { - whitespace(); + whitespace(consumeNewlines: true); var expression = _singleExpression(); return UnaryOperationExpression(UnaryOperator.not, expression, identifier.span.expand(expression.span)); @@ -3230,10 +3228,10 @@ abstract class StylesheetParser extends Parser { void _mediaLogicSequence(InterpolationBuffer buffer, String operator) { while (true) { _mediaOrInterp(buffer); - whitespace(); + whitespace(consumeNewlines: false); if (!scanIdentifier(operator)) return; - expectWhitespace(); + expectWhitespace(consumeNewlines: false); buffer.writeCharCode($space); buffer.write(operator); From c552cddc445ec1b4938f7d6d0248c295241b846b Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Thu, 12 Dec 2024 16:01:53 -0500 Subject: [PATCH 27/58] Allow newlines in comments --- lib/src/parse/sass.dart | 20 ++------------------ lib/src/parse/scss.dart | 2 +- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index 2cda843ee..3608990de 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -259,7 +259,7 @@ class SassParser extends StylesheetParser { buffer.writeCharCode(scanner.readChar()); buffer.writeCharCode(scanner.readChar()); var span = scanner.spanFrom(start); - whitespace(); + whitespace(consumeNewlines: false); // For backwards compatibility, allow additional comments after // the initial comment is closed. @@ -269,7 +269,7 @@ class SassParser extends StylesheetParser { _expectNewline(); } _readIndentation(); - whitespace(); + whitespace(consumeNewlines: false); } if (!scanner.isDone && !scanner.peekChar().isNewline) { @@ -323,22 +323,6 @@ class SassParser extends StylesheetParser { } } - void loudComment() { - // This overrides loud comment consumption so that it doesn't consume - // multi-line comments. - scanner.expect("/*"); - while (true) { - var next = scanner.readChar(); - if (next.isNewline) scanner.error("expected */."); - if (next != $asterisk) continue; - - do { - next = scanner.readChar(); - } while (next == $asterisk); - if (next == $slash) break; - } - } - /// Expect and consume a single newline. /// /// If [canEndInSemicolon] is true, this will also consume a `;` before the diff --git a/lib/src/parse/scss.dart b/lib/src/parse/scss.dart index d8b923104..0dd553cdf 100644 --- a/lib/src/parse/scss.dart +++ b/lib/src/parse/scss.dart @@ -38,7 +38,7 @@ class ScssParser extends StylesheetParser { bool scanElse(int ifIndentation) { var start = scanner.state; - whitespace(); + whitespace(consumeNewlines: true); var beforeAt = scanner.state; if (scanner.scanChar($at)) { if (scanIdentifier('else', caseSensitive: true)) return true; From adc94db89f5a3b72fdb2234a4ea10c1b6f33527b Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 13 Dec 2024 10:42:46 -0500 Subject: [PATCH 28/58] Newlines in selector parse --- lib/src/parse/selector.dart | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/src/parse/selector.dart b/lib/src/parse/selector.dart index 595c0bba7..b0c73c375 100644 --- a/lib/src/parse/selector.dart +++ b/lib/src/parse/selector.dart @@ -87,9 +87,9 @@ class SelectorParser extends Parser { var previousLine = scanner.line; var components = [_complexSelector()]; - whitespace(); + whitespace(consumeNewlines: false); while (scanner.scanChar($comma)) { - whitespace(); + whitespace(consumeNewlines: false); if (scanner.peekChar() == $comma) continue; if (scanner.isDone) break; @@ -117,7 +117,7 @@ class SelectorParser extends Parser { loop: while (true) { - whitespace(); + whitespace(consumeNewlines: false); switch (scanner.peekChar()) { case $plus: @@ -239,22 +239,23 @@ class SelectorParser extends Parser { AttributeSelector _attributeSelector() { var start = scanner.state; scanner.expectChar($lbracket); - whitespace(); + whitespace(consumeNewlines: true); var name = _attributeName(); - whitespace(); + + whitespace(consumeNewlines: true); if (scanner.scanChar($rbracket)) { return AttributeSelector(name, spanFrom(start)); } var operator = _attributeOperator(); - whitespace(); + whitespace(consumeNewlines: true); var next = scanner.peekChar(); var value = next == $single_quote || next == $double_quote ? string() : identifier(); - whitespace(); + whitespace(consumeNewlines: true); next = scanner.peekChar(); var modifier = next != null && next.isAlphabetic @@ -366,7 +367,7 @@ class SelectorParser extends Parser { if (!scanner.scanChar($lparen)) { return PseudoSelector(name, spanFrom(start), element: element); } - whitespace(); + whitespace(consumeNewlines: true); var unvendored = unvendor(name); String? argument; @@ -381,11 +382,11 @@ class SelectorParser extends Parser { selector = _selectorList(); } else if (unvendored == "nth-child" || unvendored == "nth-last-child") { argument = _aNPlusB(); - whitespace(); + whitespace(consumeNewlines: true); if (scanner.peekChar(-1).isWhitespace && scanner.peekChar() != $rparen) { expectIdentifier("of"); argument += " of"; - whitespace(); + whitespace(consumeNewlines: true); selector = _selectorList(); } @@ -421,18 +422,18 @@ class SelectorParser extends Parser { do { buffer.writeCharCode(scanner.readChar()); } while (scanner.peekChar().isDigit); - whitespace(); + whitespace(consumeNewlines: true); if (!scanIdentChar($n)) return buffer.toString(); } else { expectIdentChar($n); } buffer.writeCharCode($n); - whitespace(); + whitespace(consumeNewlines: true); var next = scanner.peekChar(); if (next != $plus && next != $minus) return buffer.toString(); buffer.writeCharCode(scanner.readChar()); - whitespace(); + whitespace(consumeNewlines: true); if (!scanner.peekChar().isDigit) scanner.error("Expected a number."); do { From 114cfa66586a76859821176e74e10737c8c3708e Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 13 Dec 2024 10:51:49 -0500 Subject: [PATCH 29/58] Require whitespace argument --- lib/src/parse/parser.dart | 4 ++-- lib/src/parse/sass.dart | 2 +- lib/src/parse/scss.dart | 18 +++++++++--------- lib/src/parse/stylesheet.dart | 20 ++++++++++---------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index badd817f4..a13e96357 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -80,7 +80,7 @@ class Parser { /// /// TODO: Bikeshed the variable name @protected - void whitespace({bool consumeNewlines = false}) { + void whitespace({required bool consumeNewlines}) { do { whitespaceWithoutComments(consumeNewlines: consumeNewlines); } while (scanComment()); @@ -88,7 +88,7 @@ class Parser { /// Consumes whitespace, but not comments. @protected - void whitespaceWithoutComments({bool consumeNewlines = true}) { + void whitespaceWithoutComments({required bool consumeNewlines}) { while (!scanner.isDone && scanner.peekChar().isWhitespace) { scanner.readChar(); } diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index 3608990de..511f28416 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -309,7 +309,7 @@ class SassParser extends StylesheetParser { return LoudComment(buffer.interpolation(scanner.spanFrom(start))); } - void whitespaceWithoutComments({consumeNewlines = false}) { + void whitespaceWithoutComments({required bool consumeNewlines}) { // This overrides whitespace consumption so that it doesn't consume // newlines. while (!scanner.isDone) { diff --git a/lib/src/parse/scss.dart b/lib/src/parse/scss.dart index 0dd553cdf..6cf75da3b 100644 --- a/lib/src/parse/scss.dart +++ b/lib/src/parse/scss.dart @@ -20,7 +20,7 @@ class ScssParser extends StylesheetParser { Interpolation styleRuleSelector() => almostAnyValue(); void expectStatementSeparator([String? name]) { - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: true); if (scanner.isDone) return; if (scanner.peekChar() case $semicolon || $rbrace) return; scanner.expectChar($semicolon); @@ -62,7 +62,7 @@ class ScssParser extends StylesheetParser { List children(Statement child()) { scanner.expectChar($lbrace); - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: true); var children = []; while (true) { switch (scanner.peekChar()) { @@ -73,17 +73,17 @@ class ScssParser extends StylesheetParser { switch (scanner.peekChar(1)) { case $slash: children.add(_silentComment()); - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: true); case $asterisk: children.add(_loudComment()); - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: true); default: children.add(child()); } case $semicolon: scanner.readChar(); - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: true); case $rbrace: scanner.expectChar($rbrace); @@ -97,7 +97,7 @@ class ScssParser extends StylesheetParser { List statements(Statement? statement()) { var statements = []; - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: true); while (!scanner.isDone) { switch (scanner.peekChar()) { case $dollar: @@ -107,17 +107,17 @@ class ScssParser extends StylesheetParser { switch (scanner.peekChar(1)) { case $slash: statements.add(_silentComment()); - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: true); case $asterisk: statements.add(_loudComment()); - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: true); default: if (statement() case var child?) statements.add(child); } case $semicolon: scanner.readChar(); - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: true); default: if (statement() case var child?) statements.add(child); diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 783e9842b..d5fd5ca9a 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -368,7 +368,7 @@ abstract class StylesheetParser extends Parser { if (_lookingAtPotentialPropertyHack()) { startsWithPunctuation = true; nameBuffer.writeCharCode(scanner.readChar()); - nameBuffer.write(rawText(whitespace)); + nameBuffer.write(rawText(() => whitespace(consumeNewlines: false))); } if (!_lookingAtInterpolatedIdentifier()) return nameBuffer; @@ -386,7 +386,7 @@ abstract class StylesheetParser extends Parser { if (scanner.matches("/*")) nameBuffer.write(rawText(loudComment)); var midBuffer = StringBuffer(); - midBuffer.write(rawText(whitespace)); + midBuffer.write(rawText(() => whitespace(consumeNewlines: false))); var beforeColon = scanner.state; if (!scanner.scanChar($colon)) { if (midBuffer.isNotEmpty) nameBuffer.writeCharCode($space); @@ -413,7 +413,7 @@ abstract class StylesheetParser extends Parser { return nameBuffer..write(midBuffer); } - var postColonWhitespace = rawText(whitespace); + var postColonWhitespace = rawText(() => whitespace(consumeNewlines: false)); if (_tryDeclarationChildren(name, start) case var nested?) return nested; midBuffer.write(postColonWhitespace); @@ -536,7 +536,7 @@ abstract class StylesheetParser extends Parser { if (_lookingAtPotentialPropertyHack()) { var nameBuffer = InterpolationBuffer(); nameBuffer.writeCharCode(scanner.readChar()); - nameBuffer.write(rawText(whitespace)); + nameBuffer.write(rawText(() => whitespace(consumeNewlines: false))); nameBuffer.addInterpolation(interpolatedIdentifier()); name = nameBuffer.interpolation(scanner.spanFrom(start)); } else if (!plainCss) { @@ -1052,7 +1052,7 @@ abstract class StylesheetParser extends Parser { _inControlDirective = true; var condition = _expression(); var children = this.children(child); - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: false); var clauses = [IfClause(condition, children)]; ElseClause? lastClause; @@ -1070,7 +1070,7 @@ abstract class StylesheetParser extends Parser { _inControlDirective = wasInControlDirective; var span = scanner.spanFrom(start); - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: false); return IfRule(clauses, span, lastClause: lastClause); } @@ -1428,7 +1428,7 @@ abstract class StylesheetParser extends Parser { if (!scanner.scanChar($comma)) break; buffer.writeCharCode($comma); - buffer.write(rawText(whitespace)); + buffer.write(rawText(() => whitespace(consumeNewlines: false))); } var value = buffer.interpolation(scanner.spanFrom(valueStart)); @@ -2761,7 +2761,7 @@ abstract class StylesheetParser extends Parser { var beginningOfContents = scanner.state; if (!scanner.scanChar($lparen)) return null; - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: false); // Match Ruby Sass's behavior: parse a raw URL() if possible, and if not // backtrack and re-parse as a function expression. @@ -2788,7 +2788,7 @@ abstract class StylesheetParser extends Parser { >= 0x80: buffer.writeCharCode(scanner.readChar()); case int(isWhitespace: true): - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: false); if (scanner.peekChar() != $rparen) break loop; case $rparen: buffer.writeCharCode(scanner.readChar()); @@ -3572,7 +3572,7 @@ abstract class StylesheetParser extends Parser { T _withChildren(Statement child(), LineScannerState start, T create(List children, FileSpan span)) { var result = create(children(child), scanner.spanFrom(start)); - whitespaceWithoutComments(); + whitespaceWithoutComments(consumeNewlines: false); return result; } From 7e58b2dcbdf646d394cab82a232e122ac1b9fd10 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 13 Dec 2024 10:58:55 -0500 Subject: [PATCH 30/58] Allow whitespace inside url parens --- lib/src/parse/stylesheet.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index d5fd5ca9a..9b7ee55a8 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -2761,7 +2761,7 @@ abstract class StylesheetParser extends Parser { var beginningOfContents = scanner.state; if (!scanner.scanChar($lparen)) return null; - whitespaceWithoutComments(consumeNewlines: false); + whitespaceWithoutComments(consumeNewlines: true); // Match Ruby Sass's behavior: parse a raw URL() if possible, and if not // backtrack and re-parse as a function expression. @@ -2788,7 +2788,7 @@ abstract class StylesheetParser extends Parser { >= 0x80: buffer.writeCharCode(scanner.readChar()); case int(isWhitespace: true): - whitespaceWithoutComments(consumeNewlines: false); + whitespaceWithoutComments(consumeNewlines: true); if (scanner.peekChar() != $rparen) break loop; case $rparen: buffer.writeCharCode(scanner.readChar()); From 78bd20faf215f4a40dbe36c466665d0de3f6b917 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 13 Dec 2024 15:03:49 -0500 Subject: [PATCH 31/58] Cleanup --- lib/src/parse/at_root_query.dart | 1 - lib/src/parse/stylesheet.dart | 14 ++++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/src/parse/at_root_query.dart b/lib/src/parse/at_root_query.dart index f8ec84849..6eac7680d 100644 --- a/lib/src/parse/at_root_query.dart +++ b/lib/src/parse/at_root_query.dart @@ -14,7 +14,6 @@ class AtRootQueryParser extends Parser { AtRootQuery parse() { return wrapSpanFormatException(() { scanner.expectChar($lparen); - // TODO: No tests whitespace(consumeNewlines: true); var include = scanIdentifier("with"); if (!include) expectIdentifier("without", name: '"with" or "without"'); diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 9b7ee55a8..e01186310 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -108,11 +108,9 @@ abstract class StylesheetParser extends Parser { ArgumentDeclaration parseArgumentDeclaration() => _parseSingleProduction(() { scanner.expectChar($at, name: "@-rule"); identifier(); - // TODO: No tests whitespace(consumeNewlines: true); identifier(); var arguments = _argumentDeclaration(); - // TODO: No tests whitespace(consumeNewlines: true); scanner.expectChar($lbrace); return arguments; @@ -138,7 +136,6 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; scanner.expectChar($at, name: "@-rule"); expectIdentifier("use"); - // TODO: No tests whitespace(consumeNewlines: true); return _useRule(start); }), @@ -1197,10 +1194,9 @@ abstract class StylesheetParser extends Parser { } else { buffer.writeCharCode($lparen); buffer.addInterpolation(_interpolatedDeclarationValue( - allowEmpty: true, allowSemicolon: true)); + allowEmpty: true, allowSemicolon: true, consumeNewlines: true)); buffer.writeCharCode($rparen); } - scanner.expectChar($rparen); whitespace(consumeNewlines: false); } else { @@ -1235,7 +1231,7 @@ abstract class StylesheetParser extends Parser { if (_tryImportSupportsFunction() case var function?) return function; var start = scanner.state; - var name = _expression(); + var name = _expression(consumeNewlines: true); scanner.expectChar($colon); return SupportsDeclaration( name, _supportsDeclarationValue(name), scanner.spanFrom(start)); @@ -1256,8 +1252,8 @@ abstract class StylesheetParser extends Parser { return null; } - var value = - _interpolatedDeclarationValue(allowEmpty: true, allowSemicolon: true); + var value = _interpolatedDeclarationValue( + allowEmpty: true, allowSemicolon: true, consumeNewlines: true); scanner.expectChar($rparen); return SupportsFunction(name, value, scanner.spanFrom(start)); @@ -1947,7 +1943,6 @@ abstract class StylesheetParser extends Parser { length: operator.operator.length); } operands.add(singleExpression); - // TODO: This needs more test coverage whitespace(consumeNewlines: true); singleExpression_ = _singleExpression(); } @@ -3152,7 +3147,6 @@ abstract class StylesheetParser extends Parser { /// Consumes a single media query. void _mediaQuery(InterpolationBuffer buffer) { // This is somewhat duplicated in MediaQueryParser._mediaQuery. - // TODO: Do we need to duplicate the whitespace handling there? if (scanner.peekChar() == $lparen) { _mediaInParens(buffer); whitespace(consumeNewlines: false); From 13c42b2ba2e0288d582c6066709c34c4cef4c545 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Sun, 15 Dec 2024 21:24:55 -0500 Subject: [PATCH 32/58] Handle newlines in parentheses in supports conditions --- lib/src/parse/stylesheet.dart | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index d7f90082b..c1146fd4f 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -1219,13 +1219,14 @@ abstract class StylesheetParser extends Parser { /// Consumes the contents of a `supports()` function after an `@import` rule /// (but not the function name or parentheses). SupportsCondition _importSupportsQuery() { + whitespace(consumeNewlines: true); if (scanIdentifier("not")) { whitespace(consumeNewlines: true); var start = scanner.state; return SupportsNegation( _supportsConditionInParens(), scanner.spanFrom(start)); } else if (scanner.peekChar() == $lparen) { - return _supportsCondition(); + return _supportsCondition(inParentheses: true); } else { if (_tryImportSupportsFunction() case var function?) return function; @@ -1459,7 +1460,7 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. @protected SupportsRule supportsRule(LineScannerState start) { - whitespace(consumeNewlines: false); + whitespace(consumeNewlines: true); var condition = _supportsCondition(); whitespace(consumeNewlines: false); return _withChildren(_statement, start, @@ -3322,7 +3323,11 @@ abstract class StylesheetParser extends Parser { // ## Supports Conditions /// Consumes a `@supports` condition. - SupportsCondition _supportsCondition() { + /// + /// Set [inParentheses] to true if the condition is inside parentheses, to + /// allow the indented syntax to consume newlines where a statement otherwise + /// would end. + SupportsCondition _supportsCondition({bool inParentheses = false}) { var start = scanner.state; if (scanIdentifier("not")) { whitespace(consumeNewlines: true); @@ -3331,7 +3336,7 @@ abstract class StylesheetParser extends Parser { } var condition = _supportsConditionInParens(); - whitespace(consumeNewlines: false); + whitespace(consumeNewlines: inParentheses); String? operator; while (lookingAtIdentifier()) { if (operator != null) { @@ -3382,7 +3387,7 @@ abstract class StylesheetParser extends Parser { scanner.expectChar($rparen); return SupportsNegation(condition, scanner.spanFrom(start)); } else if (scanner.peekChar() == $lparen) { - var condition = _supportsCondition(); + var condition = _supportsCondition(inParentheses: true); scanner.expectChar($rparen); return condition.withSpan(scanner.spanFrom(start)); } From 61b4137e68465fffa7ea559611f0c57405407ed4 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 16 Dec 2024 11:20:08 -0500 Subject: [PATCH 33/58] Allow newlines in brackets and parens in selectors --- lib/src/parse/stylesheet.dart | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index c1146fd4f..4b7e6b210 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -177,8 +177,6 @@ abstract class StylesheetParser extends Parser { return atRule(() => _statement(), root: root); case $plus: - // TODO: newlines not supported because of `lookingAtIdentifier`. - // This means it doesn't behave the same as = mixin syntax below. if (!indented || !lookingAtIdentifier(1)) return _styleRule(); _isUseAllowed = false; var start = scanner.state; @@ -2835,6 +2833,8 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; var buffer = InterpolationBuffer(); + var brackets = []; + loop: while (true) { switch (scanner.peekChar()) { @@ -2870,7 +2870,7 @@ abstract class StylesheetParser extends Parser { buffer.addInterpolation(interpolatedIdentifier()); case $cr || $lf || $ff: - if (indented) break loop; + if (indented && brackets.isEmpty) break loop; buffer.writeCharCode(scanner.readChar()); case $exclamation || $semicolon || $lbrace || $rbrace: @@ -2895,6 +2895,17 @@ abstract class StylesheetParser extends Parser { buffer.writeCharCode(scanner.readChar()); } + case $lparen || $lbracket: + var bracket = scanner.readChar(); + buffer.writeCharCode(bracket); + brackets.add(opposite(bracket)); + + case $rparen || $rbracket: + if (brackets.isEmpty) break loop; + var bracket = brackets.removeLast(); + scanner.expectChar(bracket); + buffer.writeCharCode(bracket); + case null: break loop; From 0843edd8d7319534314b3e0b00d0d79e39dc8da6 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 16 Dec 2024 12:45:57 -0500 Subject: [PATCH 34/58] Documentation, review --- lib/src/parse/parser.dart | 13 +++++++------ lib/src/parse/sass.dart | 2 +- lib/src/parse/selector.dart | 4 ++-- lib/src/parse/stylesheet.dart | 16 +++++++++------- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index a13e96357..519c862a7 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -75,10 +75,8 @@ class Parser { /// Consumes whitespace, including any comments. /// - /// Set [consumeNewlines] to true when a statement could not end, so newlines - /// can be consumed. Only used in the indented syntax. - /// - /// TODO: Bikeshed the variable name + /// If [consumeNewlines] is true, the indented syntax will consume newlines as + /// whitespace, in positions when a statement can not end. @protected void whitespace({required bool consumeNewlines}) { do { @@ -87,6 +85,9 @@ class Parser { } /// Consumes whitespace, but not comments. + /// + /// If [consumeNewlines] is true, the indented syntax will consume newlines as + /// whitespace, in positions when a statement can not end. @protected void whitespaceWithoutComments({required bool consumeNewlines}) { while (!scanner.isDone && scanner.peekChar().isWhitespace) { @@ -122,8 +123,8 @@ class Parser { /// Like [whitespace], but throws an error if no whitespace is consumed. /// - /// If [consumeNewlines] is true, consuming whitespace in the indented syntax - /// will include newlines. + /// If [consumeNewlines] is true, the indented syntax will consume newlines as + /// whitespace, in positions when a statement can not end. @protected void expectWhitespace({bool consumeNewlines = false}) { if (scanner.isDone || !(scanner.peekChar().isWhitespace || scanComment())) { diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index 511f28416..518e38ea5 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -311,7 +311,7 @@ class SassParser extends StylesheetParser { void whitespaceWithoutComments({required bool consumeNewlines}) { // This overrides whitespace consumption so that it doesn't consume - // newlines. + // newlines where that would cause a statement to end. while (!scanner.isDone) { var next = scanner.peekChar(); if (!consumeNewlines && !next.isSpaceOrTab) { diff --git a/lib/src/parse/selector.dart b/lib/src/parse/selector.dart index b0c73c375..49f886196 100644 --- a/lib/src/parse/selector.dart +++ b/lib/src/parse/selector.dart @@ -87,9 +87,9 @@ class SelectorParser extends Parser { var previousLine = scanner.line; var components = [_complexSelector()]; - whitespace(consumeNewlines: false); + whitespace(consumeNewlines: true); while (scanner.scanChar($comma)) { - whitespace(consumeNewlines: false); + whitespace(consumeNewlines: true); if (scanner.peekChar() == $comma) continue; if (scanner.isDone) break; diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 4b7e6b210..b7d54b27f 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -1753,14 +1753,14 @@ abstract class StylesheetParser extends Parser { /// still be a valid expression. When it returns `true`, this returns the /// expression. /// - /// If [consumeNewlines] is true, consuming whitespace in the indented syntax - /// will include newlines. + /// If [consumeNewlines] is true, the indented syntax will consume newlines as + /// whitespace, in positions when a statement can not end. @protected Expression _expression( {bool bracketList = false, bool singleEquals = false, - bool until()?, - bool consumeNewlines = false}) { + bool consumeNewlines = false, + bool until()?}) { if (until != null && until()) scanner.error("Expected expression."); LineScannerState? beforeBracket; @@ -2162,6 +2162,9 @@ abstract class StylesheetParser extends Parser { /// /// If [singleEquals] is true, this will allow the Microsoft-style `=` /// operator at the top level. + /// + /// If [consumeNewlines] is true, the indented syntax will consume newlines as + /// whitespace, in positions when a statement can not end. Expression expressionUntilComma( {bool singleEquals = false, bool consumeNewlines = false}) => _expression( @@ -3335,9 +3338,8 @@ abstract class StylesheetParser extends Parser { /// Consumes a `@supports` condition. /// - /// Set [inParentheses] to true if the condition is inside parentheses, to - /// allow the indented syntax to consume newlines where a statement otherwise - /// would end. + /// If [inParentheses] is true, the indented syntax will consume newlines + /// where a statement otherwise would end. SupportsCondition _supportsCondition({bool inParentheses = false}) { var start = scanner.state; if (scanIdentifier("not")) { From 5e880e3fe3fd522df7178f2f44943a3c56f89320 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 16 Dec 2024 12:48:49 -0500 Subject: [PATCH 35/58] More docs --- lib/src/parse/stylesheet.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index b7d54b27f..41ddd67d3 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -2939,6 +2939,9 @@ abstract class StylesheetParser extends Parser { /// comments. Otherwise, it will preserve two adjacent slashes and emit them /// to CSS. /// + /// If [consumeNewlines] is true, the indented syntax will consume newlines as + /// whitespace, in positions when a statement can not end. + /// /// Unlike [declarationValue], this allows interpolation. Interpolation _interpolatedDeclarationValue( {bool allowEmpty = false, From 1ec13e506b7c4fd0a05a12d656b9d37f72fc448d Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 16 Dec 2024 13:24:52 -0500 Subject: [PATCH 36/58] Rename consumeNewlines to allowNewlines --- lib/src/parse/at_root_query.dart | 8 +- lib/src/parse/css.dart | 10 +- lib/src/parse/keyframe_selector.dart | 4 +- lib/src/parse/media_query.dart | 12 +- lib/src/parse/parser.dart | 22 +- lib/src/parse/sass.dart | 10 +- lib/src/parse/scss.dart | 20 +- lib/src/parse/selector.dart | 26 +-- lib/src/parse/stylesheet.dart | 334 +++++++++++++-------------- 9 files changed, 223 insertions(+), 223 deletions(-) diff --git a/lib/src/parse/at_root_query.dart b/lib/src/parse/at_root_query.dart index 6eac7680d..205994b55 100644 --- a/lib/src/parse/at_root_query.dart +++ b/lib/src/parse/at_root_query.dart @@ -14,17 +14,17 @@ class AtRootQueryParser extends Parser { AtRootQuery parse() { return wrapSpanFormatException(() { scanner.expectChar($lparen); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var include = scanIdentifier("with"); if (!include) expectIdentifier("without", name: '"with" or "without"'); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); scanner.expectChar($colon); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var atRules = {}; do { atRules.add(identifier().toLowerCase()); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); } while (lookingAtIdentifier()); scanner.expectChar($rparen); scanner.expectDone(); diff --git a/lib/src/parse/css.dart b/lib/src/parse/css.dart index 21af455c1..6a44dc024 100644 --- a/lib/src/parse/css.dart +++ b/lib/src/parse/css.dart @@ -49,7 +49,7 @@ class CssParser extends ScssParser { var start = scanner.state; scanner.expectChar($at); var name = interpolatedIdentifier(); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); return switch (name.asPlain) { "at-root" || @@ -113,7 +113,7 @@ class CssParser extends ScssParser { .text }; - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var modifiers = tryImportModifiers(); expectStatementSeparator("@import rule"); return ImportRule( @@ -126,7 +126,7 @@ class CssParser extends ScssParser { // evaluation time. var start = scanner.state; scanner.expectChar($lparen); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var expression = expressionUntilComma(); scanner.expectChar($rparen); return ParenthesizedExpression(expression, scanner.spanFrom(start)); @@ -151,7 +151,7 @@ class CssParser extends ScssParser { var arguments = []; if (!scanner.scanChar($rparen)) { do { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (allowEmptySecondArg && arguments.length == 1 && scanner.peekChar() == $rparen) { @@ -160,7 +160,7 @@ class CssParser extends ScssParser { } arguments.add(expressionUntilComma(singleEquals: true)); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); } while (scanner.scanChar($comma)); scanner.expectChar($rparen); } diff --git a/lib/src/parse/keyframe_selector.dart b/lib/src/parse/keyframe_selector.dart index 506e93e02..62870e9f7 100644 --- a/lib/src/parse/keyframe_selector.dart +++ b/lib/src/parse/keyframe_selector.dart @@ -15,7 +15,7 @@ class KeyframeSelectorParser extends Parser { return wrapSpanFormatException(() { var selectors = []; do { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); if (lookingAtIdentifier()) { if (scanIdentifier("from")) { selectors.add("from"); @@ -26,7 +26,7 @@ class KeyframeSelectorParser extends Parser { } else { selectors.add(_percentage()); } - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); } while (scanner.scanChar($comma)); scanner.expectDone(); diff --git a/lib/src/parse/media_query.dart b/lib/src/parse/media_query.dart index 24ebbb1d3..f4aedfbd0 100644 --- a/lib/src/parse/media_query.dart +++ b/lib/src/parse/media_query.dart @@ -16,9 +16,9 @@ class MediaQueryParser extends Parser { return wrapSpanFormatException(() { var queries = []; do { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); queries.add(_mediaQuery()); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); } while (scanner.scanChar($comma)); scanner.expectDone(); return queries; @@ -30,7 +30,7 @@ class MediaQueryParser extends Parser { // This is somewhat duplicated in StylesheetParser._mediaQuery. if (scanner.peekChar() == $lparen) { var conditions = [_mediaInParens()]; - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var conjunction = true; if (scanIdentifier("and")) { @@ -57,7 +57,7 @@ class MediaQueryParser extends Parser { } } - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (!lookingAtIdentifier()) { // For example, "@media screen {" return CssMediaQuery.type(identifier1); @@ -70,7 +70,7 @@ class MediaQueryParser extends Parser { // For example, "@media screen and ..." type = identifier1; } else { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); modifier = identifier1; type = identifier2; if (scanIdentifier("and")) { @@ -102,7 +102,7 @@ class MediaQueryParser extends Parser { var result = []; while (true) { result.add(_mediaInParens()); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (!scanIdentifier(operator)) return result; expectWhitespace(); diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index 519c862a7..e7fb6212e 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -67,7 +67,7 @@ class Parser { if (!scanner.scanChar($dollar)) return false; if (!lookingAtIdentifier()) return false; identifier(); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); return scanner.scanChar($colon); } @@ -75,21 +75,21 @@ class Parser { /// Consumes whitespace, including any comments. /// - /// If [consumeNewlines] is true, the indented syntax will consume newlines as + /// If [allowNewlines] is true, the indented syntax will consume newlines as /// whitespace, in positions when a statement can not end. @protected - void whitespace({required bool consumeNewlines}) { + void whitespace({required bool allowNewlines}) { do { - whitespaceWithoutComments(consumeNewlines: consumeNewlines); + whitespaceWithoutComments(allowNewlines: allowNewlines); } while (scanComment()); } /// Consumes whitespace, but not comments. /// - /// If [consumeNewlines] is true, the indented syntax will consume newlines as + /// If [allowNewlines] is true, the indented syntax will consume newlines as /// whitespace, in positions when a statement can not end. @protected - void whitespaceWithoutComments({required bool consumeNewlines}) { + void whitespaceWithoutComments({required bool allowNewlines}) { while (!scanner.isDone && scanner.peekChar().isWhitespace) { scanner.readChar(); } @@ -123,14 +123,14 @@ class Parser { /// Like [whitespace], but throws an error if no whitespace is consumed. /// - /// If [consumeNewlines] is true, the indented syntax will consume newlines as + /// If [allowNewlines] is true, the indented syntax will consume newlines as /// whitespace, in positions when a statement can not end. @protected - void expectWhitespace({bool consumeNewlines = false}) { + void expectWhitespace({bool allowNewlines = false}) { if (scanner.isDone || !(scanner.peekChar().isWhitespace || scanComment())) { scanner.error("Expected whitespace."); } - whitespace(consumeNewlines: consumeNewlines); + whitespace(allowNewlines: allowNewlines); } /// Consumes and ignores a single silent (Sass-style) comment, not including @@ -394,7 +394,7 @@ class Parser { return null; } - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); // Match Ruby Sass's behavior: parse a raw URL() if possible, and if not // backtrack and re-parse as a function expression. @@ -415,7 +415,7 @@ class Parser { >= 0x0080: buffer.writeCharCode(scanner.readChar()); case int(isWhitespace: true): - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (scanner.peekChar() != $rparen) break loop; case $rparen: buffer.writeCharCode(scanner.readChar()); diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index 518e38ea5..6fa554234 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -259,7 +259,7 @@ class SassParser extends StylesheetParser { buffer.writeCharCode(scanner.readChar()); buffer.writeCharCode(scanner.readChar()); var span = scanner.spanFrom(start); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); // For backwards compatibility, allow additional comments after // the initial comment is closed. @@ -269,7 +269,7 @@ class SassParser extends StylesheetParser { _expectNewline(); } _readIndentation(); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); } if (!scanner.isDone && !scanner.peekChar().isNewline) { @@ -309,14 +309,14 @@ class SassParser extends StylesheetParser { return LoudComment(buffer.interpolation(scanner.spanFrom(start))); } - void whitespaceWithoutComments({required bool consumeNewlines}) { + void whitespaceWithoutComments({required bool allowNewlines}) { // This overrides whitespace consumption so that it doesn't consume // newlines where that would cause a statement to end. while (!scanner.isDone) { var next = scanner.peekChar(); - if (!consumeNewlines && !next.isSpaceOrTab) { + if (!allowNewlines && !next.isSpaceOrTab) { break; - } else if (consumeNewlines && !next.isWhitespace) { + } else if (allowNewlines && !next.isWhitespace) { break; } scanner.readChar(); diff --git a/lib/src/parse/scss.dart b/lib/src/parse/scss.dart index 6cf75da3b..d55d45266 100644 --- a/lib/src/parse/scss.dart +++ b/lib/src/parse/scss.dart @@ -20,7 +20,7 @@ class ScssParser extends StylesheetParser { Interpolation styleRuleSelector() => almostAnyValue(); void expectStatementSeparator([String? name]) { - whitespaceWithoutComments(consumeNewlines: true); + whitespaceWithoutComments(allowNewlines: true); if (scanner.isDone) return; if (scanner.peekChar() case $semicolon || $rbrace) return; scanner.expectChar($semicolon); @@ -38,7 +38,7 @@ class ScssParser extends StylesheetParser { bool scanElse(int ifIndentation) { var start = scanner.state; - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var beforeAt = scanner.state; if (scanner.scanChar($at)) { if (scanIdentifier('else', caseSensitive: true)) return true; @@ -62,7 +62,7 @@ class ScssParser extends StylesheetParser { List children(Statement child()) { scanner.expectChar($lbrace); - whitespaceWithoutComments(consumeNewlines: true); + whitespaceWithoutComments(allowNewlines: true); var children = []; while (true) { switch (scanner.peekChar()) { @@ -73,17 +73,17 @@ class ScssParser extends StylesheetParser { switch (scanner.peekChar(1)) { case $slash: children.add(_silentComment()); - whitespaceWithoutComments(consumeNewlines: true); + whitespaceWithoutComments(allowNewlines: true); case $asterisk: children.add(_loudComment()); - whitespaceWithoutComments(consumeNewlines: true); + whitespaceWithoutComments(allowNewlines: true); default: children.add(child()); } case $semicolon: scanner.readChar(); - whitespaceWithoutComments(consumeNewlines: true); + whitespaceWithoutComments(allowNewlines: true); case $rbrace: scanner.expectChar($rbrace); @@ -97,7 +97,7 @@ class ScssParser extends StylesheetParser { List statements(Statement? statement()) { var statements = []; - whitespaceWithoutComments(consumeNewlines: true); + whitespaceWithoutComments(allowNewlines: true); while (!scanner.isDone) { switch (scanner.peekChar()) { case $dollar: @@ -107,17 +107,17 @@ class ScssParser extends StylesheetParser { switch (scanner.peekChar(1)) { case $slash: statements.add(_silentComment()); - whitespaceWithoutComments(consumeNewlines: true); + whitespaceWithoutComments(allowNewlines: true); case $asterisk: statements.add(_loudComment()); - whitespaceWithoutComments(consumeNewlines: true); + whitespaceWithoutComments(allowNewlines: true); default: if (statement() case var child?) statements.add(child); } case $semicolon: scanner.readChar(); - whitespaceWithoutComments(consumeNewlines: true); + whitespaceWithoutComments(allowNewlines: true); default: if (statement() case var child?) statements.add(child); diff --git a/lib/src/parse/selector.dart b/lib/src/parse/selector.dart index 49f886196..5ad2d5917 100644 --- a/lib/src/parse/selector.dart +++ b/lib/src/parse/selector.dart @@ -87,9 +87,9 @@ class SelectorParser extends Parser { var previousLine = scanner.line; var components = [_complexSelector()]; - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); while (scanner.scanChar($comma)) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (scanner.peekChar() == $comma) continue; if (scanner.isDone) break; @@ -117,7 +117,7 @@ class SelectorParser extends Parser { loop: while (true) { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); switch (scanner.peekChar()) { case $plus: @@ -239,23 +239,23 @@ class SelectorParser extends Parser { AttributeSelector _attributeSelector() { var start = scanner.state; scanner.expectChar($lbracket); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var name = _attributeName(); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (scanner.scanChar($rbracket)) { return AttributeSelector(name, spanFrom(start)); } var operator = _attributeOperator(); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var next = scanner.peekChar(); var value = next == $single_quote || next == $double_quote ? string() : identifier(); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); next = scanner.peekChar(); var modifier = next != null && next.isAlphabetic @@ -367,7 +367,7 @@ class SelectorParser extends Parser { if (!scanner.scanChar($lparen)) { return PseudoSelector(name, spanFrom(start), element: element); } - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var unvendored = unvendor(name); String? argument; @@ -382,11 +382,11 @@ class SelectorParser extends Parser { selector = _selectorList(); } else if (unvendored == "nth-child" || unvendored == "nth-last-child") { argument = _aNPlusB(); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (scanner.peekChar(-1).isWhitespace && scanner.peekChar() != $rparen) { expectIdentifier("of"); argument += " of"; - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); selector = _selectorList(); } @@ -422,18 +422,18 @@ class SelectorParser extends Parser { do { buffer.writeCharCode(scanner.readChar()); } while (scanner.peekChar().isDigit); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (!scanIdentChar($n)) return buffer.toString(); } else { expectIdentChar($n); } buffer.writeCharCode($n); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var next = scanner.peekChar(); if (next != $plus && next != $minus) return buffer.toString(); buffer.writeCharCode(scanner.readChar()); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (!scanner.peekChar().isDigit) scanner.error("Expected a number."); do { diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 41ddd67d3..eebf674fd 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -91,7 +91,7 @@ abstract class StylesheetParser extends Parser { // Handle this specially so that [atRule] always returns a non-nullable // Statement. if (scanner.scan('@charset')) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); string(); return null; } @@ -108,10 +108,10 @@ abstract class StylesheetParser extends Parser { ParameterList parseParameterList() => _parseSingleProduction(() { scanner.expectChar($at, name: "@-rule"); identifier(); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); identifier(); var parameters = _parameterList(); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); scanner.expectChar($lbrace); return parameters; }); @@ -136,7 +136,7 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; scanner.expectChar($at, name: "@-rule"); expectIdentifier("use"); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); return _useRule(start); }), warnings @@ -188,7 +188,7 @@ abstract class StylesheetParser extends Parser { _isUseAllowed = false; var start = scanner.state; scanner.readChar(); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); return _mixinRule(start); case $rbrace: @@ -228,9 +228,9 @@ abstract class StylesheetParser extends Parser { scanner.spanFrom(start)); } - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); scanner.expectChar($colon); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var value = _expression(); @@ -270,7 +270,7 @@ abstract class StylesheetParser extends Parser { error("Invalid flag name.", scanner.spanFrom(flagStart)); } - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); flagStart = scanner.state; } @@ -362,7 +362,7 @@ abstract class StylesheetParser extends Parser { if (_lookingAtPotentialPropertyHack()) { startsWithPunctuation = true; nameBuffer.writeCharCode(scanner.readChar()); - nameBuffer.write(rawText(() => whitespace(consumeNewlines: false))); + nameBuffer.write(rawText(() => whitespace(allowNewlines: false))); } if (!_lookingAtInterpolatedIdentifier()) return nameBuffer; @@ -380,7 +380,7 @@ abstract class StylesheetParser extends Parser { if (scanner.matches("/*")) nameBuffer.write(rawText(loudComment)); var midBuffer = StringBuffer(); - midBuffer.write(rawText(() => whitespace(consumeNewlines: false))); + midBuffer.write(rawText(() => whitespace(allowNewlines: false))); var beforeColon = scanner.state; if (!scanner.scanChar($colon)) { if (midBuffer.isNotEmpty) nameBuffer.writeCharCode($space); @@ -407,7 +407,7 @@ abstract class StylesheetParser extends Parser { return nameBuffer..write(midBuffer); } - var postColonWhitespace = rawText(() => whitespace(consumeNewlines: false)); + var postColonWhitespace = rawText(() => whitespace(allowNewlines: false)); if (_tryDeclarationChildren(name, start) case var nested?) return nested; midBuffer.write(postColonWhitespace); @@ -530,7 +530,7 @@ abstract class StylesheetParser extends Parser { if (_lookingAtPotentialPropertyHack()) { var nameBuffer = InterpolationBuffer(); nameBuffer.writeCharCode(scanner.readChar()); - nameBuffer.write(rawText(() => whitespace(consumeNewlines: false))); + nameBuffer.write(rawText(() => whitespace(allowNewlines: false))); nameBuffer.addInterpolation(interpolatedIdentifier()); name = nameBuffer.interpolation(scanner.spanFrom(start)); } else if (!plainCss) { @@ -544,7 +544,7 @@ abstract class StylesheetParser extends Parser { name = interpolatedIdentifier(); } - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); scanner.expectChar($colon); if (parseCustomProperties && name.initialPlain.startsWith('--')) { @@ -554,7 +554,7 @@ abstract class StylesheetParser extends Parser { return Declaration(name, value, scanner.spanFrom(start)); } - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); if (_tryDeclarationChildren(name, start) case var nested?) return nested; var value = _expression(); @@ -739,7 +739,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. AtRootRule _atRootRule(LineScannerState start) { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); if (scanner.peekChar() == $lparen) { var query = _atRootQuery(); return _withChildren(_statement, start, @@ -759,18 +759,18 @@ abstract class StylesheetParser extends Parser { var buffer = InterpolationBuffer(); scanner.expectChar($lparen); buffer.writeCharCode($lparen); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); - _addOrInject(buffer, _expression(consumeNewlines: true)); + _addOrInject(buffer, _expression(allowNewlines: true)); if (scanner.scanChar($colon)) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); buffer.writeCharCode($colon); buffer.writeCharCode($space); - _addOrInject(buffer, _expression(consumeNewlines: true)); + _addOrInject(buffer, _expression(allowNewlines: true)); } scanner.expectChar($rparen); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); buffer.writeCharCode($rparen); return buffer.interpolation(scanner.spanFrom(start)); @@ -786,11 +786,11 @@ abstract class StylesheetParser extends Parser { } var beforeWhitespace = scanner.location; - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); ArgumentList arguments; if (scanner.peekChar() == $lparen) { arguments = _argumentInvocation(mixin: true); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); } else { arguments = ArgumentList.empty(beforeWhitespace.pointSpan()); } @@ -803,7 +803,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. DebugRule _debugRule(LineScannerState start) { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var value = _expression(); expectStatementSeparator("@debug rule"); return DebugRule(value, scanner.spanFrom(start)); @@ -814,20 +814,20 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. [child] is called to consume any /// children that are specifically allowed in the caller's context. EachRule _eachRule(LineScannerState start, Statement child()) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var wasInControlDirective = _inControlDirective; _inControlDirective = true; var variables = [variableName()]; - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); while (scanner.scanChar($comma)) { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); variables.add(variableName()); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); } - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); expectIdentifier("in"); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var list = _expression(); @@ -841,7 +841,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ErrorRule _errorRule(LineScannerState start) { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var value = _expression(); expectStatementSeparator("@error rule"); return ErrorRule(value, scanner.spanFrom(start)); @@ -851,7 +851,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ExtendRule _extendRule(LineScannerState start) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (!_inStyleRule && !_inMixin && !_inContentBlock) { error("@extend may only be used within style rules.", scanner.spanFrom(start)); @@ -861,7 +861,7 @@ abstract class StylesheetParser extends Parser { var optional = scanner.scanChar($exclamation); if (optional) { expectIdentifier("optional"); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); } expectStatementSeparator("@extend rule"); return ExtendRule(value, scanner.spanFrom(start), optional: optional); @@ -871,7 +871,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. FunctionRule _functionRule(LineScannerState start) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var precedingComment = lastSilentComment; lastSilentComment = null; var beforeName = scanner.state; @@ -889,7 +889,7 @@ abstract class StylesheetParser extends Parser { )); } - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var parameters = _parameterList(); if (_inMixin || _inContentBlock) { @@ -912,7 +912,7 @@ abstract class StylesheetParser extends Parser { error("Invalid function name.", scanner.spanFrom(start)); } - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); return _withChildren( _functionChild, start, @@ -925,18 +925,18 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. [child] is called to consume any /// children that are specifically allowed in the caller's context. ForRule _forRule(LineScannerState start, Statement child()) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var wasInControlDirective = _inControlDirective; _inControlDirective = true; var variable = variableName(); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); expectIdentifier("from"); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); bool? exclusive; var from = _expression( - consumeNewlines: true, + allowNewlines: true, until: () { if (!lookingAtIdentifier()) return false; if (scanIdentifier("to")) { @@ -951,7 +951,7 @@ abstract class StylesheetParser extends Parser { }); if (exclusive == null) scanner.error('Expected "to" or "through".'); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var to = _expression(); return _withChildren(child, start, (children, span) { @@ -965,16 +965,16 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ForwardRule _forwardRule(LineScannerState start) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var url = _urlString(); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); String? prefix; if (scanIdentifier("as")) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); prefix = identifier(normalize: true); scanner.expectChar($asterisk); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); } Set? shownMixinsAndFunctions; @@ -982,15 +982,15 @@ abstract class StylesheetParser extends Parser { Set? hiddenMixinsAndFunctions; Set? hiddenVariables; if (scanIdentifier("show")) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); (shownMixinsAndFunctions, shownVariables) = _memberList(); } else if (scanIdentifier("hide")) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); (hiddenMixinsAndFunctions, hiddenVariables) = _memberList(); } var configuration = _configuration(allowGuarded: true); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); expectStatementSeparator("@forward rule"); var span = scanner.spanFrom(start); @@ -1021,7 +1021,7 @@ abstract class StylesheetParser extends Parser { var identifiers = {}; var variables = {}; do { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); withErrorMessage("Expected variable, mixin, or function name", () { if (scanner.peekChar() == $dollar) { variables.add(variableName()); @@ -1029,7 +1029,7 @@ abstract class StylesheetParser extends Parser { identifiers.add(identifier(normalize: true)); } }); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); } while (scanner.scanChar($comma)); return (identifiers, variables); @@ -1040,21 +1040,21 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. [child] is called to consume any /// children that are specifically allowed in the caller's context. IfRule _ifRule(LineScannerState start, Statement child()) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var ifIndentation = currentIndentation; var wasInControlDirective = _inControlDirective; _inControlDirective = true; var condition = _expression(); var children = this.children(child); - whitespaceWithoutComments(consumeNewlines: false); + whitespaceWithoutComments(allowNewlines: false); var clauses = [IfClause(condition, children)]; ElseClause? lastClause; while (scanElse(ifIndentation)) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (scanIdentifier("if")) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); clauses.add(IfClause(_expression(), this.children(child))); } else { lastClause = ElseClause(this.children(child)); @@ -1064,7 +1064,7 @@ abstract class StylesheetParser extends Parser { _inControlDirective = wasInControlDirective; var span = scanner.spanFrom(start); - whitespaceWithoutComments(consumeNewlines: false); + whitespaceWithoutComments(allowNewlines: false); return IfRule(clauses, span, lastClause: lastClause); } @@ -1074,7 +1074,7 @@ abstract class StylesheetParser extends Parser { ImportRule _importRule(LineScannerState start) { var imports = []; do { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var argument = importArgument(); if (argument is DynamicImport) { warnings.add(( @@ -1092,7 +1092,7 @@ abstract class StylesheetParser extends Parser { } imports.add(argument); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); } while (scanner.scanChar($comma)); expectStatementSeparator("@import rule"); @@ -1107,7 +1107,7 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; if (scanner.peekChar() case $u || $U) { var url = dynamicUrl(); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var modifiers = tryImportModifiers(); return StaticImport( url is StringExpression @@ -1119,7 +1119,7 @@ abstract class StylesheetParser extends Parser { var url = string(); var urlSpan = scanner.spanFrom(start); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var modifiers = tryImportModifiers(); if (isPlainImportUrl(url) || modifiers != null) { return StaticImport( @@ -1191,13 +1191,13 @@ abstract class StylesheetParser extends Parser { } else { buffer.writeCharCode($lparen); buffer.addInterpolation(_interpolatedDeclarationValue( - allowEmpty: true, allowSemicolon: true, consumeNewlines: true)); + allowEmpty: true, allowSemicolon: true, allowNewlines: true)); buffer.writeCharCode($rparen); } scanner.expectChar($rparen); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); } else { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (scanner.scanChar($comma)) { buffer.write(", "); buffer.addInterpolation(_mediaQueryList()); @@ -1217,9 +1217,9 @@ abstract class StylesheetParser extends Parser { /// Consumes the contents of a `supports()` function after an `@import` rule /// (but not the function name or parentheses). SupportsCondition _importSupportsQuery() { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (scanIdentifier("not")) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var start = scanner.state; return SupportsNegation( _supportsConditionInParens(), scanner.spanFrom(start)); @@ -1229,7 +1229,7 @@ abstract class StylesheetParser extends Parser { if (_tryImportSupportsFunction() case var function?) return function; var start = scanner.state; - var name = _expression(consumeNewlines: true); + var name = _expression(allowNewlines: true); scanner.expectChar($colon); return SupportsDeclaration( name, _supportsDeclarationValue(name), scanner.spanFrom(start)); @@ -1251,7 +1251,7 @@ abstract class StylesheetParser extends Parser { } var value = _interpolatedDeclarationValue( - allowEmpty: true, allowSemicolon: true, consumeNewlines: true); + allowEmpty: true, allowSemicolon: true, allowNewlines: true); scanner.expectChar($rparen); return SupportsFunction(name, value, scanner.spanFrom(start)); @@ -1261,7 +1261,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. IncludeRule _includeRule(LineScannerState start) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); String? namespace; var name = identifier(); if (scanner.scanChar($dot)) { @@ -1269,17 +1269,17 @@ abstract class StylesheetParser extends Parser { name = _publicIdentifier(); } - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var arguments = scanner.peekChar() == $lparen ? _argumentInvocation(mixin: true) : ArgumentList.empty(scanner.emptySpan); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); ParameterList? contentParameters; if (scanIdentifier("using")) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); contentParameters = _parameterList(); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); } ContentBlock? content; @@ -1306,7 +1306,7 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. @protected MediaRule mediaRule(LineScannerState start) { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var query = _mediaQueryList(); return _withChildren(_statement, start, (children, span) => MediaRule(query, children, span)); @@ -1316,7 +1316,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. MixinRule _mixinRule(LineScannerState start) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var precedingComment = lastSilentComment; lastSilentComment = null; var beforeName = scanner.state; @@ -1334,7 +1334,7 @@ abstract class StylesheetParser extends Parser { )); } - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var parameters = scanner.peekChar() == $lparen ? _parameterList() : ParameterList.empty(scanner.emptySpan); @@ -1347,7 +1347,7 @@ abstract class StylesheetParser extends Parser { scanner.spanFrom(start)); } - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); _inMixin = true; return _withChildren(_statement, start, (children, span) { @@ -1366,7 +1366,7 @@ abstract class StylesheetParser extends Parser { /// [the specification]: http://www.w3.org/TR/css3-conditional/ @protected AtRule mozDocumentRule(LineScannerState start, Interpolation name) { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var valueStart = scanner.state; var buffer = InterpolationBuffer(); var needsDeprecationWarning = false; @@ -1385,7 +1385,7 @@ abstract class StylesheetParser extends Parser { buffer.addInterpolation(contents); } else { scanner.expectChar($lparen); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var argument = interpolatedString(); scanner.expectChar($rparen); @@ -1418,11 +1418,11 @@ abstract class StylesheetParser extends Parser { } } - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); if (!scanner.scanChar($comma)) break; buffer.writeCharCode($comma); - buffer.write(rawText(() => whitespace(consumeNewlines: false))); + buffer.write(rawText(() => whitespace(allowNewlines: false))); } var value = buffer.interpolation(scanner.spanFrom(valueStart)); @@ -1447,7 +1447,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ReturnRule _returnRule(LineScannerState start) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var value = _expression(); expectStatementSeparator("@return rule"); return ReturnRule(value, scanner.spanFrom(start)); @@ -1458,9 +1458,9 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. @protected SupportsRule supportsRule(LineScannerState start) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var condition = _supportsCondition(); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); return _withChildren(_statement, start, (children, span) => SupportsRule(condition, children, span)); } @@ -1469,14 +1469,14 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. UseRule _useRule(LineScannerState start) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var url = _urlString(); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var namespace = _useNamespace(url, start); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var configuration = _configuration(); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var span = scanner.spanFrom(start); if (!_isUseAllowed) { @@ -1493,7 +1493,7 @@ abstract class StylesheetParser extends Parser { /// Returns `null` to indicate a `@use` rule without a URL. String? _useNamespace(Uri url, LineScannerState start) { if (scanIdentifier("as")) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); return scanner.scanChar($asterisk) ? null : identifier(); } @@ -1524,26 +1524,26 @@ abstract class StylesheetParser extends Parser { var variableNames = {}; var configuration = []; - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); scanner.expectChar($lparen); while (true) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var variableStart = scanner.state; var name = variableName(); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); scanner.expectChar($colon); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); - var expression = expressionUntilComma(consumeNewlines: true); + var expression = expressionUntilComma(allowNewlines: true); var guarded = false; var flagStart = scanner.state; if (allowGuarded && scanner.scanChar($exclamation)) { if (identifier() == 'default') { guarded = true; - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); } else { error("Invalid flag name.", scanner.spanFrom(flagStart)); } @@ -1558,7 +1558,7 @@ abstract class StylesheetParser extends Parser { .add(ConfiguredVariable(name, expression, span, guarded: guarded)); if (!scanner.scanChar($comma)) break; - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (!_lookingAtExpression()) break; } @@ -1570,7 +1570,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. WarnRule _warnRule(LineScannerState start) { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); var value = _expression(); expectStatementSeparator("@warn rule"); return WarnRule(value, scanner.spanFrom(start)); @@ -1581,7 +1581,7 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. [child] is called to consume any /// children that are specifically allowed in the caller's context. WhileRule _whileRule(LineScannerState start, Statement child()) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var wasInControlDirective = _inControlDirective; _inControlDirective = true; var condition = _expression(); @@ -1599,7 +1599,7 @@ abstract class StylesheetParser extends Parser { var wasInUnknownAtRule = _inUnknownAtRule; _inUnknownAtRule = true; - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); Interpolation? value; if (scanner.peekChar() != $exclamation && !atEndOfStatement()) { @@ -1628,7 +1628,7 @@ abstract class StylesheetParser extends Parser { /// This declares a return type of [Statement] so that it can be returned /// within case statements. Statement _disallowedAtRule(LineScannerState start) { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); _interpolatedDeclarationValue(allowEmpty: true, allowOpenBrace: false); error("This at-rule is not allowed here.", scanner.spanFrom(start)); } @@ -1637,24 +1637,24 @@ abstract class StylesheetParser extends Parser { ParameterList _parameterList() { var start = scanner.state; scanner.expectChar($lparen); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var parameters = []; var named = {}; String? restParameter; while (scanner.peekChar() == $dollar) { var variableStart = scanner.state; var name = variableName(); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); Expression? defaultValue; if (scanner.scanChar($colon)) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); defaultValue = expressionUntilComma(); } else if (scanner.scanChar($dot)) { scanner.expectChar($dot); scanner.expectChar($dot); - whitespace(consumeNewlines: true); - if (scanner.scanChar($comma)) whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); + if (scanner.scanChar($comma)) whitespace(allowNewlines: true); restParameter = name; break; } @@ -1666,7 +1666,7 @@ abstract class StylesheetParser extends Parser { } if (!scanner.scanChar($comma)) break; - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); } scanner.expectChar($rparen); return ParameterList(parameters, scanner.spanFrom(start), @@ -1688,7 +1688,7 @@ abstract class StylesheetParser extends Parser { {bool mixin = false, bool allowEmptySecondArg = false}) { var start = scanner.state; scanner.expectChar($lparen); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var positional = []; var named = {}; @@ -1696,10 +1696,10 @@ abstract class StylesheetParser extends Parser { Expression? keywordRest; while (_lookingAtExpression()) { var expression = expressionUntilComma(singleEquals: !mixin); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (expression is VariableExpression && scanner.scanChar($colon)) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (named.containsKey(expression.name)) { error("Duplicate argument.", expression.span); } @@ -1711,8 +1711,8 @@ abstract class StylesheetParser extends Parser { rest = expression; } else { keywordRest = expression; - whitespace(consumeNewlines: true); - if (scanner.scanChar($comma)) whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); + if (scanner.scanChar($comma)) whitespace(allowNewlines: true); break; } } else if (named.isNotEmpty) { @@ -1722,9 +1722,9 @@ abstract class StylesheetParser extends Parser { positional.add(expression); } - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (!scanner.scanChar($comma)) break; - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (allowEmptySecondArg && positional.length == 1 && @@ -1753,13 +1753,13 @@ abstract class StylesheetParser extends Parser { /// still be a valid expression. When it returns `true`, this returns the /// expression. /// - /// If [consumeNewlines] is true, the indented syntax will consume newlines as + /// If [allowNewlines] is true, the indented syntax will consume newlines as /// whitespace, in positions when a statement can not end. @protected Expression _expression( {bool bracketList = false, bool singleEquals = false, - bool consumeNewlines = false, + bool allowNewlines = false, bool until()?}) { if (until != null && until()) scanner.error("Expected expression."); @@ -1767,7 +1767,7 @@ abstract class StylesheetParser extends Parser { if (bracketList) { beforeBracket = scanner.state; scanner.expectChar($lbracket); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (scanner.scanChar($rbracket)) { return ListExpression( @@ -1943,7 +1943,7 @@ abstract class StylesheetParser extends Parser { length: operator.operator.length); } operands.add(singleExpression); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); singleExpression_ = _singleExpression(); } @@ -1965,7 +1965,7 @@ abstract class StylesheetParser extends Parser { loop: while (true) { whitespace( - consumeNewlines: consumeNewlines || + allowNewlines: allowNewlines || bracketList || _inParentheses || wasInExpression); @@ -2163,13 +2163,13 @@ abstract class StylesheetParser extends Parser { /// If [singleEquals] is true, this will allow the Microsoft-style `=` /// operator at the top level. /// - /// If [consumeNewlines] is true, the indented syntax will consume newlines as + /// If [allowNewlines] is true, the indented syntax will consume newlines as /// whitespace, in positions when a statement can not end. Expression expressionUntilComma( - {bool singleEquals = false, bool consumeNewlines = false}) => + {bool singleEquals = false, bool allowNewlines = false}) => _expression( singleEquals: singleEquals, - consumeNewlines: consumeNewlines, + allowNewlines: allowNewlines, until: () => scanner.peekChar() == $comma); /// Whether [expression] is allowed as an operand of a `/` expression that @@ -2219,7 +2219,7 @@ abstract class StylesheetParser extends Parser { try { var start = scanner.state; scanner.expectChar($lparen); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (!_lookingAtExpression()) { scanner.expectChar($rparen); return ListExpression( @@ -2228,7 +2228,7 @@ abstract class StylesheetParser extends Parser { var first = expressionUntilComma(); if (scanner.scanChar($colon)) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); return _map(first, start); } @@ -2236,14 +2236,14 @@ abstract class StylesheetParser extends Parser { scanner.expectChar($rparen); return ParenthesizedExpression(first, scanner.spanFrom(start)); } - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var expressions = [first]; while (true) { if (!_lookingAtExpression()) break; expressions.add(expressionUntilComma()); if (!scanner.scanChar($comma)) break; - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); } scanner.expectChar($rparen); @@ -2263,12 +2263,12 @@ abstract class StylesheetParser extends Parser { var pairs = [(first, expressionUntilComma())]; while (scanner.scanChar($comma)) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (!_lookingAtExpression()) break; var key = expressionUntilComma(); scanner.expectChar($colon); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var value = expressionUntilComma(); pairs.add((key, value)); } @@ -2383,7 +2383,7 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; scanner.readChar(); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); expectIdentifier("important"); return StringExpression.plain("!important", scanner.spanFrom(start)); } @@ -2399,7 +2399,7 @@ abstract class StylesheetParser extends Parser { position: scanner.position - 1, length: 1); } - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var operand = _singleExpression(); return UnaryOperationExpression(operator, operand, scanner.spanFrom(start)); } @@ -2634,7 +2634,7 @@ abstract class StylesheetParser extends Parser { return IfExpression( invocation, identifier.span.expand(invocation.span)); } else if (plain == "not") { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var expression = _singleExpression(); return UnaryOperationExpression(UnaryOperator.not, expression, identifier.span.expand(expression.span)); @@ -2759,7 +2759,7 @@ abstract class StylesheetParser extends Parser { var beginningOfContents = scanner.state; if (!scanner.scanChar($lparen)) return null; - whitespaceWithoutComments(consumeNewlines: true); + whitespaceWithoutComments(allowNewlines: true); // Match Ruby Sass's behavior: parse a raw URL() if possible, and if not // backtrack and re-parse as a function expression. @@ -2786,7 +2786,7 @@ abstract class StylesheetParser extends Parser { >= 0x80: buffer.writeCharCode(scanner.readChar()); case int(isWhitespace: true): - whitespaceWithoutComments(consumeNewlines: true); + whitespaceWithoutComments(allowNewlines: true); if (scanner.peekChar() != $rparen) break loop; case $rparen: buffer.writeCharCode(scanner.readChar()); @@ -2939,7 +2939,7 @@ abstract class StylesheetParser extends Parser { /// comments. Otherwise, it will preserve two adjacent slashes and emit them /// to CSS. /// - /// If [consumeNewlines] is true, the indented syntax will consume newlines as + /// If [allowNewlines] is true, the indented syntax will consume newlines as /// whitespace, in positions when a statement can not end. /// /// Unlike [declarationValue], this allows interpolation. @@ -2949,7 +2949,7 @@ abstract class StylesheetParser extends Parser { bool allowColon = true, bool allowOpenBrace = true, bool silentComments = true, - bool consumeNewlines = false}) { + bool allowNewlines = false}) { // NOTE: this logic is largely duplicated in Parser.declarationValue. Most // changes here should be mirrored there. @@ -2999,7 +2999,7 @@ abstract class StylesheetParser extends Parser { case $space || $tab: buffer.writeCharCode(scanner.readChar()); - case $lf || $cr || $ff when indented && !consumeNewlines: + case $lf || $cr || $ff when indented && !allowNewlines: break loop; case $lf || $cr || $ff: @@ -3134,8 +3134,8 @@ abstract class StylesheetParser extends Parser { (Expression, FileSpan span) singleInterpolation() { var start = scanner.state; scanner.expect('#{'); - whitespace(consumeNewlines: true); - var contents = _expression(consumeNewlines: true); + whitespace(allowNewlines: true); + var contents = _expression(allowNewlines: true); scanner.expectChar($rbrace); var span = scanner.spanFrom(start); @@ -3153,9 +3153,9 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; var buffer = InterpolationBuffer(); while (true) { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); _mediaQuery(buffer); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); if (!scanner.scanChar($comma)) break; buffer.writeCharCode($comma); buffer.writeCharCode($space); @@ -3168,7 +3168,7 @@ abstract class StylesheetParser extends Parser { // This is somewhat duplicated in MediaQueryParser._mediaQuery. if (scanner.peekChar() == $lparen) { _mediaInParens(buffer); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); if (scanIdentifier("and")) { buffer.write(" and "); expectWhitespace(); @@ -3194,7 +3194,7 @@ abstract class StylesheetParser extends Parser { } } - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); buffer.addInterpolation(identifier1); if (!_lookingAtInterpolatedIdentifier()) { // For example, "@media screen {". @@ -3209,7 +3209,7 @@ abstract class StylesheetParser extends Parser { // For example, "@media screen and ..." buffer.write(" and "); } else { - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); buffer.addInterpolation(identifier2); if (scanIdentifier("and")) { // For example, "@media only screen and ..." @@ -3241,10 +3241,10 @@ abstract class StylesheetParser extends Parser { void _mediaLogicSequence(InterpolationBuffer buffer, String operator) { while (true) { _mediaOrInterp(buffer); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); if (!scanIdentifier(operator)) return; - expectWhitespace(consumeNewlines: false); + expectWhitespace(allowNewlines: false); buffer.writeCharCode($space); buffer.write(operator); @@ -3266,32 +3266,32 @@ abstract class StylesheetParser extends Parser { void _mediaInParens(InterpolationBuffer buffer) { scanner.expectChar($lparen, name: "media condition in parentheses"); buffer.writeCharCode($lparen); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (scanner.peekChar() == $lparen) { _mediaInParens(buffer); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (scanIdentifier("and")) { buffer.write(" and "); - expectWhitespace(consumeNewlines: true); + expectWhitespace(allowNewlines: true); _mediaLogicSequence(buffer, "and"); } else if (scanIdentifier("or")) { buffer.write(" or "); - expectWhitespace(consumeNewlines: true); + expectWhitespace(allowNewlines: true); _mediaLogicSequence(buffer, "or"); } } else if (scanIdentifier("not")) { buffer.write("not "); - expectWhitespace(consumeNewlines: true); + expectWhitespace(allowNewlines: true); _mediaOrInterp(buffer); } else { var expressionBefore = _expressionUntilComparison(); buffer.add(expressionBefore, expressionBefore.span); if (scanner.scanChar($colon)) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); buffer.writeCharCode($colon); buffer.writeCharCode($space); - var expressionAfter = _expression(consumeNewlines: true); + var expressionAfter = _expression(allowNewlines: true); buffer.add(expressionAfter, expressionAfter.span); } else { var next = scanner.peekChar(); @@ -3303,7 +3303,7 @@ abstract class StylesheetParser extends Parser { } buffer.writeCharCode($space); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var expressionMiddle = _expressionUntilComparison(); buffer.add(expressionMiddle, expressionMiddle.span); @@ -3314,7 +3314,7 @@ abstract class StylesheetParser extends Parser { if (scanner.scanChar($equal)) buffer.writeCharCode($equal); buffer.writeCharCode($space); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var expressionAfter = _expressionUntilComparison(); buffer.add(expressionAfter, expressionAfter.span); } @@ -3323,14 +3323,14 @@ abstract class StylesheetParser extends Parser { } scanner.expectChar($rparen); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); buffer.writeCharCode($rparen); } /// Consumes an expression until it reaches a top-level `<`, `>`, or a `=` /// that's not `==`. Expression _expressionUntilComparison() => _expression( - consumeNewlines: true, + allowNewlines: true, until: () => switch (scanner.peekChar()) { $equal => scanner.peekChar(1) != $equal, $langle || $rangle => true, @@ -3346,13 +3346,13 @@ abstract class StylesheetParser extends Parser { SupportsCondition _supportsCondition({bool inParentheses = false}) { var start = scanner.state; if (scanIdentifier("not")) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); return SupportsNegation( _supportsConditionInParens(), scanner.spanFrom(start)); } var condition = _supportsConditionInParens(); - whitespace(consumeNewlines: inParentheses); + whitespace(allowNewlines: inParentheses); String? operator; while (lookingAtIdentifier()) { if (operator != null) { @@ -3364,11 +3364,11 @@ abstract class StylesheetParser extends Parser { operator = "and"; } - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var right = _supportsConditionInParens(); condition = SupportsOperation( condition, right, operator, scanner.spanFrom(start)); - whitespace(consumeNewlines: false); + whitespace(allowNewlines: false); } return condition; } @@ -3385,7 +3385,7 @@ abstract class StylesheetParser extends Parser { if (scanner.scanChar($lparen)) { var arguments = _interpolatedDeclarationValue( - allowEmpty: true, allowSemicolon: true, consumeNewlines: true); + allowEmpty: true, allowSemicolon: true, allowNewlines: true); scanner.expectChar($rparen); return SupportsFunction(identifier, arguments, scanner.spanFrom(start)); } else if (identifier.contents case [Expression expression]) { @@ -3396,9 +3396,9 @@ abstract class StylesheetParser extends Parser { } scanner.expectChar($lparen); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); if (scanIdentifier("not")) { - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var condition = _supportsConditionInParens(); scanner.expectChar($rparen); return SupportsNegation(condition, scanner.spanFrom(start)); @@ -3426,7 +3426,7 @@ abstract class StylesheetParser extends Parser { var nameStart = scanner.state; var wasInParentheses = _inParentheses; try { - name = _expression(consumeNewlines: true); + name = _expression(allowNewlines: true); scanner.expectChar($colon); } on FormatException catch (_) { scanner.state = nameStart; @@ -3448,7 +3448,7 @@ abstract class StylesheetParser extends Parser { allowEmpty: true, allowSemicolon: true, allowColon: false, - consumeNewlines: true))) + allowNewlines: true))) .interpolation(scanner.spanFrom(nameStart)); if (scanner.peekChar() == $colon) rethrow; @@ -3468,8 +3468,8 @@ abstract class StylesheetParser extends Parser { when text.initialPlain.startsWith("--")) { return StringExpression(_interpolatedDeclarationValue()); } else { - whitespace(consumeNewlines: true); - return _expression(consumeNewlines: true); + whitespace(allowNewlines: true); + return _expression(allowNewlines: true); } } @@ -3483,7 +3483,7 @@ abstract class StylesheetParser extends Parser { if (expression is! Expression) return null; var beforeWhitespace = scanner.state; - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); SupportsOperation? operation; String? operator; @@ -3499,14 +3499,14 @@ abstract class StylesheetParser extends Parser { return null; } - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); var right = _supportsConditionInParens(); operation = SupportsOperation( operation ?? SupportsInterpolation(expression, interpolation.span), right, operator, scanner.spanFrom(start)); - whitespace(consumeNewlines: true); + whitespace(allowNewlines: true); } return operation; @@ -3588,7 +3588,7 @@ abstract class StylesheetParser extends Parser { T _withChildren(Statement child(), LineScannerState start, T create(List children, FileSpan span)) { var result = create(children(child), scanner.spanFrom(start)); - whitespaceWithoutComments(consumeNewlines: false); + whitespaceWithoutComments(allowNewlines: false); return result; } From 5e3db806e20ed45c5df60418fdf08aea1224b85f Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Tue, 17 Dec 2024 12:11:40 -0500 Subject: [PATCH 37/58] Fix bug in semicolons in sass --- lib/src/parse/sass.dart | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index 6fa554234..72bc0a53e 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -61,7 +61,12 @@ class SassParser extends StylesheetParser { position: _nextIndentationEnd!.position); } - bool atEndOfStatement() => scanner.peekChar()?.isNewline ?? true; + bool atEndOfStatement() { + var next = scanner.peekChar(); + return next.isNewline || + next == null || + (next == $semicolon && scanner.peekChar(1).isNewline); + } bool lookingAtChildren() => atEndOfStatement() && _peekIndentation() > currentIndentation; @@ -406,7 +411,14 @@ class SassParser extends StylesheetParser { } var start = scanner.state; - if (!scanCharIf((char) => char.isNewline)) { + + if (scanner.peekChar().isNewline) { + scanner.readChar(); + } else if (scanner.peekChar() == $semicolon && + scanner.peekChar(1).isNewline) { + scanner.readChar(); + scanner.readChar(); + } else { scanner.error("Expected newline.", position: scanner.position); } From ba62f15ce3121c261136ca179185d6bdc5ecbd65 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 30 Dec 2024 09:13:12 -0500 Subject: [PATCH 38/58] Use local whitespace functions where value is irrelevant --- lib/src/parse/at_root_query.dart | 13 ++++++++---- lib/src/parse/css.dart | 15 +++++++++----- lib/src/parse/keyframe_selector.dart | 9 ++++++-- lib/src/parse/media_query.dart | 17 +++++++++------ lib/src/parse/parser.dart | 8 +++---- lib/src/parse/scss.dart | 30 ++++++++++++++++++--------- lib/src/parse/selector.dart | 31 ++++++++++++++++------------ lib/src/parse/stylesheet.dart | 6 +++--- 8 files changed, 82 insertions(+), 47 deletions(-) diff --git a/lib/src/parse/at_root_query.dart b/lib/src/parse/at_root_query.dart index 205994b55..be662064e 100644 --- a/lib/src/parse/at_root_query.dart +++ b/lib/src/parse/at_root_query.dart @@ -14,17 +14,17 @@ class AtRootQueryParser extends Parser { AtRootQuery parse() { return wrapSpanFormatException(() { scanner.expectChar($lparen); - whitespace(allowNewlines: true); + _whitespace(); var include = scanIdentifier("with"); if (!include) expectIdentifier("without", name: '"with" or "without"'); - whitespace(allowNewlines: true); + _whitespace(); scanner.expectChar($colon); - whitespace(allowNewlines: true); + _whitespace(); var atRules = {}; do { atRules.add(identifier().toLowerCase()); - whitespace(allowNewlines: true); + _whitespace(); } while (lookingAtIdentifier()); scanner.expectChar($rparen); scanner.expectDone(); @@ -32,4 +32,9 @@ class AtRootQueryParser extends Parser { return AtRootQuery(atRules, include: include); }); } + + /// The value of `allowNewlines` is not relevant for this class. + void _whitespace() { + whitespace(allowNewlines: true); + } } diff --git a/lib/src/parse/css.dart b/lib/src/parse/css.dart index 6a44dc024..814da9656 100644 --- a/lib/src/parse/css.dart +++ b/lib/src/parse/css.dart @@ -49,7 +49,7 @@ class CssParser extends ScssParser { var start = scanner.state; scanner.expectChar($at); var name = interpolatedIdentifier(); - whitespace(allowNewlines: true); + _whitespace(); return switch (name.asPlain) { "at-root" || @@ -113,7 +113,7 @@ class CssParser extends ScssParser { .text }; - whitespace(allowNewlines: true); + _whitespace(); var modifiers = tryImportModifiers(); expectStatementSeparator("@import rule"); return ImportRule( @@ -126,7 +126,7 @@ class CssParser extends ScssParser { // evaluation time. var start = scanner.state; scanner.expectChar($lparen); - whitespace(allowNewlines: true); + _whitespace(); var expression = expressionUntilComma(); scanner.expectChar($rparen); return ParenthesizedExpression(expression, scanner.spanFrom(start)); @@ -151,7 +151,7 @@ class CssParser extends ScssParser { var arguments = []; if (!scanner.scanChar($rparen)) { do { - whitespace(allowNewlines: true); + _whitespace(); if (allowEmptySecondArg && arguments.length == 1 && scanner.peekChar() == $rparen) { @@ -160,7 +160,7 @@ class CssParser extends ScssParser { } arguments.add(expressionUntilComma(singleEquals: true)); - whitespace(allowNewlines: true); + _whitespace(); } while (scanner.scanChar($comma)); scanner.expectChar($rparen); } @@ -180,4 +180,9 @@ class CssParser extends ScssParser { var expression = super.namespacedExpression(namespace, start); error("Module namespaces aren't allowed in plain CSS.", expression.span); } + + /// The value of `allowNewlines` is not relevant for this class. + void _whitespace() { + whitespace(allowNewlines: true); + } } diff --git a/lib/src/parse/keyframe_selector.dart b/lib/src/parse/keyframe_selector.dart index 62870e9f7..68e92c4bd 100644 --- a/lib/src/parse/keyframe_selector.dart +++ b/lib/src/parse/keyframe_selector.dart @@ -15,7 +15,7 @@ class KeyframeSelectorParser extends Parser { return wrapSpanFormatException(() { var selectors = []; do { - whitespace(allowNewlines: false); + _whitespace(); if (lookingAtIdentifier()) { if (scanIdentifier("from")) { selectors.add("from"); @@ -26,7 +26,7 @@ class KeyframeSelectorParser extends Parser { } else { selectors.add(_percentage()); } - whitespace(allowNewlines: false); + _whitespace(); } while (scanner.scanChar($comma)); scanner.expectDone(); @@ -71,4 +71,9 @@ class KeyframeSelectorParser extends Parser { buffer.writeCharCode($percent); return buffer.toString(); } + + /// The value of `allowNewlines` is not relevant for this class. + void _whitespace() { + whitespace(allowNewlines: true); + } } diff --git a/lib/src/parse/media_query.dart b/lib/src/parse/media_query.dart index f4aedfbd0..d117c01c0 100644 --- a/lib/src/parse/media_query.dart +++ b/lib/src/parse/media_query.dart @@ -16,9 +16,9 @@ class MediaQueryParser extends Parser { return wrapSpanFormatException(() { var queries = []; do { - whitespace(allowNewlines: true); + _whitespace(); queries.add(_mediaQuery()); - whitespace(allowNewlines: true); + _whitespace(); } while (scanner.scanChar($comma)); scanner.expectDone(); return queries; @@ -30,7 +30,7 @@ class MediaQueryParser extends Parser { // This is somewhat duplicated in StylesheetParser._mediaQuery. if (scanner.peekChar() == $lparen) { var conditions = [_mediaInParens()]; - whitespace(allowNewlines: true); + _whitespace(); var conjunction = true; if (scanIdentifier("and")) { @@ -57,7 +57,7 @@ class MediaQueryParser extends Parser { } } - whitespace(allowNewlines: true); + _whitespace(); if (!lookingAtIdentifier()) { // For example, "@media screen {" return CssMediaQuery.type(identifier1); @@ -70,7 +70,7 @@ class MediaQueryParser extends Parser { // For example, "@media screen and ..." type = identifier1; } else { - whitespace(allowNewlines: true); + _whitespace(); modifier = identifier1; type = identifier2; if (scanIdentifier("and")) { @@ -102,7 +102,7 @@ class MediaQueryParser extends Parser { var result = []; while (true) { result.add(_mediaInParens()); - whitespace(allowNewlines: true); + _whitespace(); if (!scanIdentifier(operator)) return result; expectWhitespace(); @@ -117,4 +117,9 @@ class MediaQueryParser extends Parser { scanner.expectChar($rparen); return result; } + + /// The value of `allowNewlines` is not relevant for this class. + void _whitespace() { + whitespace(allowNewlines: true); + } } diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index e7fb6212e..000829002 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -67,7 +67,7 @@ class Parser { if (!scanner.scanChar($dollar)) return false; if (!lookingAtIdentifier()) return false; identifier(); - whitespace(allowNewlines: false); + whitespace(allowNewlines: true); return scanner.scanChar($colon); } @@ -76,7 +76,7 @@ class Parser { /// Consumes whitespace, including any comments. /// /// If [allowNewlines] is true, the indented syntax will consume newlines as - /// whitespace, in positions when a statement can not end. + /// whitespace in positions when a statement can't end. @protected void whitespace({required bool allowNewlines}) { do { @@ -87,7 +87,7 @@ class Parser { /// Consumes whitespace, but not comments. /// /// If [allowNewlines] is true, the indented syntax will consume newlines as - /// whitespace, in positions when a statement can not end. + /// whitespace in positions when a statement can't end. @protected void whitespaceWithoutComments({required bool allowNewlines}) { while (!scanner.isDone && scanner.peekChar().isWhitespace) { @@ -124,7 +124,7 @@ class Parser { /// Like [whitespace], but throws an error if no whitespace is consumed. /// /// If [allowNewlines] is true, the indented syntax will consume newlines as - /// whitespace, in positions when a statement can not end. + /// whitespace in positions when a statement can't end. @protected void expectWhitespace({bool allowNewlines = false}) { if (scanner.isDone || !(scanner.peekChar().isWhitespace || scanComment())) { diff --git a/lib/src/parse/scss.dart b/lib/src/parse/scss.dart index d55d45266..45ec5fb08 100644 --- a/lib/src/parse/scss.dart +++ b/lib/src/parse/scss.dart @@ -20,7 +20,7 @@ class ScssParser extends StylesheetParser { Interpolation styleRuleSelector() => almostAnyValue(); void expectStatementSeparator([String? name]) { - whitespaceWithoutComments(allowNewlines: true); + _whitespaceWithoutComments(); if (scanner.isDone) return; if (scanner.peekChar() case $semicolon || $rbrace) return; scanner.expectChar($semicolon); @@ -38,7 +38,7 @@ class ScssParser extends StylesheetParser { bool scanElse(int ifIndentation) { var start = scanner.state; - whitespace(allowNewlines: true); + _whitespace(); var beforeAt = scanner.state; if (scanner.scanChar($at)) { if (scanIdentifier('else', caseSensitive: true)) return true; @@ -62,7 +62,7 @@ class ScssParser extends StylesheetParser { List children(Statement child()) { scanner.expectChar($lbrace); - whitespaceWithoutComments(allowNewlines: true); + _whitespaceWithoutComments(); var children = []; while (true) { switch (scanner.peekChar()) { @@ -73,17 +73,17 @@ class ScssParser extends StylesheetParser { switch (scanner.peekChar(1)) { case $slash: children.add(_silentComment()); - whitespaceWithoutComments(allowNewlines: true); + _whitespaceWithoutComments(); case $asterisk: children.add(_loudComment()); - whitespaceWithoutComments(allowNewlines: true); + _whitespaceWithoutComments(); default: children.add(child()); } case $semicolon: scanner.readChar(); - whitespaceWithoutComments(allowNewlines: true); + _whitespaceWithoutComments(); case $rbrace: scanner.expectChar($rbrace); @@ -97,7 +97,7 @@ class ScssParser extends StylesheetParser { List statements(Statement? statement()) { var statements = []; - whitespaceWithoutComments(allowNewlines: true); + _whitespaceWithoutComments(); while (!scanner.isDone) { switch (scanner.peekChar()) { case $dollar: @@ -107,17 +107,17 @@ class ScssParser extends StylesheetParser { switch (scanner.peekChar(1)) { case $slash: statements.add(_silentComment()); - whitespaceWithoutComments(allowNewlines: true); + _whitespaceWithoutComments(); case $asterisk: statements.add(_loudComment()); - whitespaceWithoutComments(allowNewlines: true); + _whitespaceWithoutComments(); default: if (statement() case var child?) statements.add(child); } case $semicolon: scanner.readChar(); - whitespaceWithoutComments(allowNewlines: true); + _whitespaceWithoutComments(); default: if (statement() case var child?) statements.add(child); @@ -183,4 +183,14 @@ class ScssParser extends StylesheetParser { } } } + + /// The value of `allowNewlines` is not relevant for this class. + void _whitespace() { + whitespace(allowNewlines: true); + } + + /// The value of `allowNewlines` is not relevant for this class. + void _whitespaceWithoutComments() { + whitespaceWithoutComments(allowNewlines: true); + } } diff --git a/lib/src/parse/selector.dart b/lib/src/parse/selector.dart index 5ad2d5917..609961964 100644 --- a/lib/src/parse/selector.dart +++ b/lib/src/parse/selector.dart @@ -87,9 +87,9 @@ class SelectorParser extends Parser { var previousLine = scanner.line; var components = [_complexSelector()]; - whitespace(allowNewlines: true); + _whitespace(); while (scanner.scanChar($comma)) { - whitespace(allowNewlines: true); + _whitespace(); if (scanner.peekChar() == $comma) continue; if (scanner.isDone) break; @@ -117,7 +117,7 @@ class SelectorParser extends Parser { loop: while (true) { - whitespace(allowNewlines: false); + _whitespace(); switch (scanner.peekChar()) { case $plus: @@ -239,23 +239,23 @@ class SelectorParser extends Parser { AttributeSelector _attributeSelector() { var start = scanner.state; scanner.expectChar($lbracket); - whitespace(allowNewlines: true); + _whitespace(); var name = _attributeName(); - whitespace(allowNewlines: true); + _whitespace(); if (scanner.scanChar($rbracket)) { return AttributeSelector(name, spanFrom(start)); } var operator = _attributeOperator(); - whitespace(allowNewlines: true); + _whitespace(); var next = scanner.peekChar(); var value = next == $single_quote || next == $double_quote ? string() : identifier(); - whitespace(allowNewlines: true); + _whitespace(); next = scanner.peekChar(); var modifier = next != null && next.isAlphabetic @@ -367,7 +367,7 @@ class SelectorParser extends Parser { if (!scanner.scanChar($lparen)) { return PseudoSelector(name, spanFrom(start), element: element); } - whitespace(allowNewlines: true); + _whitespace(); var unvendored = unvendor(name); String? argument; @@ -382,11 +382,11 @@ class SelectorParser extends Parser { selector = _selectorList(); } else if (unvendored == "nth-child" || unvendored == "nth-last-child") { argument = _aNPlusB(); - whitespace(allowNewlines: true); + _whitespace(); if (scanner.peekChar(-1).isWhitespace && scanner.peekChar() != $rparen) { expectIdentifier("of"); argument += " of"; - whitespace(allowNewlines: true); + _whitespace(); selector = _selectorList(); } @@ -422,18 +422,18 @@ class SelectorParser extends Parser { do { buffer.writeCharCode(scanner.readChar()); } while (scanner.peekChar().isDigit); - whitespace(allowNewlines: true); + _whitespace(); if (!scanIdentChar($n)) return buffer.toString(); } else { expectIdentChar($n); } buffer.writeCharCode($n); - whitespace(allowNewlines: true); + _whitespace(); var next = scanner.peekChar(); if (next != $plus && next != $minus) return buffer.toString(); buffer.writeCharCode(scanner.readChar()); - whitespace(allowNewlines: true); + _whitespace(); if (!scanner.peekChar().isDigit) scanner.error("Expected a number."); do { @@ -479,4 +479,9 @@ class SelectorParser extends Parser { $ampersand => _plainCss, _ => false }; + + /// The value of `allowNewlines` is not relevant for this class. + void _whitespace() { + whitespace(allowNewlines: true); + } } diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index eebf674fd..ba6dc1f43 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -1754,7 +1754,7 @@ abstract class StylesheetParser extends Parser { /// expression. /// /// If [allowNewlines] is true, the indented syntax will consume newlines as - /// whitespace, in positions when a statement can not end. + /// whitespace in positions when a statement can't end. @protected Expression _expression( {bool bracketList = false, @@ -2164,7 +2164,7 @@ abstract class StylesheetParser extends Parser { /// operator at the top level. /// /// If [allowNewlines] is true, the indented syntax will consume newlines as - /// whitespace, in positions when a statement can not end. + /// whitespace in positions when a statement can't end. Expression expressionUntilComma( {bool singleEquals = false, bool allowNewlines = false}) => _expression( @@ -2940,7 +2940,7 @@ abstract class StylesheetParser extends Parser { /// to CSS. /// /// If [allowNewlines] is true, the indented syntax will consume newlines as - /// whitespace, in positions when a statement can not end. + /// whitespace in positions when a statement can't end. /// /// Unlike [declarationValue], this allows interpolation. Interpolation _interpolatedDeclarationValue( From 38ea36b59d21aad015bfac052e8a96bf6db006ed Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 30 Dec 2024 09:25:15 -0500 Subject: [PATCH 39/58] Apply code suggestions to sass.dart --- lib/src/parse/sass.dart | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index 72bc0a53e..9c633e1eb 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -63,8 +63,8 @@ class SassParser extends StylesheetParser { bool atEndOfStatement() { var next = scanner.peekChar(); - return next.isNewline || - next == null || + return next == null || + next.isNewline || (next == $semicolon && scanner.peekChar(1).isNewline); } @@ -319,11 +319,7 @@ class SassParser extends StylesheetParser { // newlines where that would cause a statement to end. while (!scanner.isDone) { var next = scanner.peekChar(); - if (!allowNewlines && !next.isSpaceOrTab) { - break; - } else if (allowNewlines && !next.isWhitespace) { - break; - } + if (allowNewlines ? !next.isWhitespace : !next.isSpaceOrTab) break; scanner.readChar(); } } From 6d6993851d56468ab56697248aa799d088f4878f Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 30 Dec 2024 09:29:17 -0500 Subject: [PATCH 40/58] Allow whitespace after debug, error, warn --- lib/src/parse/stylesheet.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index ba6dc1f43..69e5fbe41 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -803,7 +803,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. DebugRule _debugRule(LineScannerState start) { - whitespace(allowNewlines: false); + whitespace(allowNewlines: true); var value = _expression(); expectStatementSeparator("@debug rule"); return DebugRule(value, scanner.spanFrom(start)); @@ -841,7 +841,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ErrorRule _errorRule(LineScannerState start) { - whitespace(allowNewlines: false); + whitespace(allowNewlines: true); var value = _expression(); expectStatementSeparator("@error rule"); return ErrorRule(value, scanner.spanFrom(start)); @@ -1570,7 +1570,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. WarnRule _warnRule(LineScannerState start) { - whitespace(allowNewlines: false); + whitespace(allowNewlines: true); var value = _expression(); expectStatementSeparator("@warn rule"); return WarnRule(value, scanner.spanFrom(start)); From b99d3acc8e454df47dea53ae702b32ff1550628c Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 30 Dec 2024 10:27:55 -0500 Subject: [PATCH 41/58] Revise whitespace in if, function and supports conditions --- lib/src/parse/stylesheet.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 69e5fbe41..277880a0b 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -889,7 +889,7 @@ abstract class StylesheetParser extends Parser { )); } - whitespace(allowNewlines: false); + whitespace(allowNewlines: true); var parameters = _parameterList(); if (_inMixin || _inContentBlock) { @@ -1052,7 +1052,7 @@ abstract class StylesheetParser extends Parser { ElseClause? lastClause; while (scanElse(ifIndentation)) { - whitespace(allowNewlines: true); + whitespace(allowNewlines: false); if (scanIdentifier("if")) { whitespace(allowNewlines: true); clauses.add(IfClause(_expression(), this.children(child))); @@ -1197,7 +1197,7 @@ abstract class StylesheetParser extends Parser { scanner.expectChar($rparen); whitespace(allowNewlines: false); } else { - whitespace(allowNewlines: true); + whitespace(allowNewlines: false); if (scanner.scanChar($comma)) { buffer.write(", "); buffer.addInterpolation(_mediaQueryList()); @@ -1458,7 +1458,7 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. @protected SupportsRule supportsRule(LineScannerState start) { - whitespace(allowNewlines: true); + whitespace(allowNewlines: false); var condition = _supportsCondition(); whitespace(allowNewlines: false); return _withChildren(_statement, start, @@ -3346,7 +3346,7 @@ abstract class StylesheetParser extends Parser { SupportsCondition _supportsCondition({bool inParentheses = false}) { var start = scanner.state; if (scanIdentifier("not")) { - whitespace(allowNewlines: true); + whitespace(allowNewlines: inParentheses); return SupportsNegation( _supportsConditionInParens(), scanner.spanFrom(start)); } @@ -3364,11 +3364,11 @@ abstract class StylesheetParser extends Parser { operator = "and"; } - whitespace(allowNewlines: true); + whitespace(allowNewlines: inParentheses); var right = _supportsConditionInParens(); condition = SupportsOperation( condition, right, operator, scanner.spanFrom(start)); - whitespace(allowNewlines: false); + whitespace(allowNewlines: inParentheses); } return condition; } From 8ad5805d8ad0c50c23b3e16205df8352a8acfd53 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 30 Dec 2024 10:52:00 -0500 Subject: [PATCH 42/58] Don't break inside brackets in declarations --- lib/src/parse/stylesheet.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 277880a0b..fe7767ad7 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -2999,7 +2999,8 @@ abstract class StylesheetParser extends Parser { case $space || $tab: buffer.writeCharCode(scanner.readChar()); - case $lf || $cr || $ff when indented && !allowNewlines: + case $lf || $cr || $ff + when indented && !allowNewlines && brackets.isEmpty: break loop; case $lf || $cr || $ff: From ce9522b2f0225ea94a083c6a26d7f3617c5e929b Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 30 Dec 2024 16:10:44 -0500 Subject: [PATCH 43/58] Whitespace in destructuring each --- lib/src/parse/stylesheet.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index fe7767ad7..89e653b76 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -819,11 +819,11 @@ abstract class StylesheetParser extends Parser { _inControlDirective = true; var variables = [variableName()]; - whitespace(allowNewlines: false); + whitespace(allowNewlines: true); while (scanner.scanChar($comma)) { - whitespace(allowNewlines: false); + whitespace(allowNewlines: true); variables.add(variableName()); - whitespace(allowNewlines: false); + whitespace(allowNewlines: true); } whitespace(allowNewlines: true); expectIdentifier("in"); From f0f8c7a4f8bbe819234c6e0739653df3db81ef7f Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 30 Dec 2024 16:43:38 -0500 Subject: [PATCH 44/58] Disallow whitespace after @charset, document _expression arg --- lib/src/parse/stylesheet.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 89e653b76..19d2d6929 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -91,7 +91,7 @@ abstract class StylesheetParser extends Parser { // Handle this specially so that [atRule] always returns a non-nullable // Statement. if (scanner.scan('@charset')) { - whitespace(allowNewlines: true); + whitespace(allowNewlines: false); string(); return null; } @@ -1754,7 +1754,8 @@ abstract class StylesheetParser extends Parser { /// expression. /// /// If [allowNewlines] is true, the indented syntax will consume newlines as - /// whitespace in positions when a statement can't end. + /// whitespace at the top level of the expression, outside of bracketed + /// subexpressions, in positions when a statement can't end. @protected Expression _expression( {bool bracketList = false, From 233d7743ef025b52bdefbde4c81e8e54e0a09225 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 30 Dec 2024 21:08:08 -0500 Subject: [PATCH 45/58] Add changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index acecb7469..ca5ce69a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.84.0-dev + +* Allow newlines in whitespace in the indented syntax. +* **Potentially breaking bug fix**: Selectors with interpolations that render + unmatched brackets previously parse, but now error. For example, + `[foo#{"]:is(bar"}) {a: b}` will now throw an error. + ## 1.83.0 * Allow trailing commas in *all* argument and parameter lists. From aad4b5c22e5e122941d32dbfff7b6e4eb6d24ef8 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Mon, 30 Dec 2024 21:40:47 -0500 Subject: [PATCH 46/58] Errors on unmatched brackets --- lib/src/parse/stylesheet.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 19d2d6929..165b3bec6 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -2905,7 +2905,10 @@ abstract class StylesheetParser extends Parser { brackets.add(opposite(bracket)); case $rparen || $rbracket: - if (brackets.isEmpty) break loop; + if (brackets.isEmpty) { + scanner.error( + 'Unexpected "${String.fromCharCode(scanner.peekChar()!)}".'); + } var bracket = brackets.removeLast(); scanner.expectChar(bracket); buffer.writeCharCode(bracket); From cf711de5502d47b30550eece46dd6607408f7696 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Tue, 31 Dec 2024 10:23:14 -0500 Subject: [PATCH 47/58] Allow whitespace after comma in memberlist --- lib/src/parse/stylesheet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 165b3bec6..92ab150c0 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -1021,7 +1021,7 @@ abstract class StylesheetParser extends Parser { var identifiers = {}; var variables = {}; do { - whitespace(allowNewlines: false); + whitespace(allowNewlines: true); withErrorMessage("Expected variable, mixin, or function name", () { if (scanner.peekChar() == $dollar) { variables.add(variableName()); From f093d6a72ab65142e1a4ee276f3f3b8908633750 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Thu, 2 Jan 2025 10:06:33 -0500 Subject: [PATCH 48/58] Support whitespace and comments after semicolon --- lib/src/parse/sass.dart | 55 +++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index 9c633e1eb..1f7f39da4 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -54,19 +54,17 @@ class SassParser extends StylesheetParser { } void expectStatementSeparator([String? name]) { - if (!atEndOfStatement()) _expectNewline(canEndInSemicolon: true); + var trailingSemicolon = _trailingSemicolon(); + if (!atEndOfStatement()) { + _expectNewline(trailingSemicolon: trailingSemicolon); + } if (_peekIndentation() <= currentIndentation) return; scanner.error( "Nothing may be indented ${name == null ? 'here' : 'beneath a $name'}.", position: _nextIndentationEnd!.position); } - bool atEndOfStatement() { - var next = scanner.peekChar(); - return next == null || - next.isNewline || - (next == $semicolon && scanner.peekChar(1).isNewline); - } + bool atEndOfStatement() => scanner.peekChar()?.isNewline ?? true; bool lookingAtChildren() => atEndOfStatement() && _peekIndentation() > currentIndentation; @@ -324,25 +322,12 @@ class SassParser extends StylesheetParser { } } - /// Expect and consume a single newline. - /// - /// If [canEndInSemicolon] is true, this will also consume a `;` before the - /// newline if present. - void _expectNewline({bool canEndInSemicolon = false}) { + /// Expect and consume a single newline character. + void _expectNewline({bool trailingSemicolon = false}) { switch (scanner.peekChar()) { - case $semicolon - when canEndInSemicolon && [$lf, $ff].contains(scanner.peekChar(1)): - scanner.readChar(); - scanner.readChar(); - return; - case $semicolon when canEndInSemicolon && scanner.peekChar(1) == $cr: - scanner.readChar(); - scanner.readChar(); - if (scanner.peekChar() == $lf) scanner.readChar(); - return; case $semicolon: - scanner.error( - "multiple statements on one line are not supported in the indented syntax."); + // TODO: jamesnw change + scanner.error("semicolons aren't allowed in the indented syntax."); case $cr: scanner.readChar(); if (scanner.peekChar() == $lf) scanner.readChar(); @@ -351,7 +336,9 @@ class SassParser extends StylesheetParser { scanner.readChar(); return; default: - scanner.error("expected newline."); + scanner.error(trailingSemicolon + ? "multiple statements on one line are not supported in the indented syntax." + : "expected newline."); } } @@ -408,13 +395,7 @@ class SassParser extends StylesheetParser { var start = scanner.state; - if (scanner.peekChar().isNewline) { - scanner.readChar(); - } else if (scanner.peekChar() == $semicolon && - scanner.peekChar(1).isNewline) { - scanner.readChar(); - scanner.readChar(); - } else { + if (!scanCharIf((char) => char.isNewline)) { scanner.error("Expected newline.", position: scanner.position); } @@ -477,4 +458,14 @@ class SassParser extends StylesheetParser { position: scanner.position - scanner.column, length: scanner.column); } } + + //`[whitespace no newlines, no comments] ';'? [whitespace with newlines, comments] [newline]` + + bool _trailingSemicolon() { + if (scanCharIf((char) => char == $semicolon)) { + whitespace(allowNewlines: false); + return true; + } + return false; + } } From efb0d4d409d571944582e3bf2d21b64df637151d Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Thu, 2 Jan 2025 10:08:53 -0500 Subject: [PATCH 49/58] Exclude semicolons and comments from spans --- lib/src/parse/stylesheet.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 92ab150c0..fb6b8e617 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -805,8 +805,9 @@ abstract class StylesheetParser extends Parser { DebugRule _debugRule(LineScannerState start) { whitespace(allowNewlines: true); var value = _expression(); + var expressionEnd = scanner.state; expectStatementSeparator("@debug rule"); - return DebugRule(value, scanner.spanFrom(start)); + return DebugRule(value, scanner.spanFrom(start, expressionEnd)); } /// Consumes an `@each` rule. @@ -843,8 +844,9 @@ abstract class StylesheetParser extends Parser { ErrorRule _errorRule(LineScannerState start) { whitespace(allowNewlines: true); var value = _expression(); + var expressionEnd = scanner.state; expectStatementSeparator("@error rule"); - return ErrorRule(value, scanner.spanFrom(start)); + return ErrorRule(value, scanner.spanFrom(start, expressionEnd)); } /// Consumes an `@extend` rule. @@ -1572,8 +1574,9 @@ abstract class StylesheetParser extends Parser { WarnRule _warnRule(LineScannerState start) { whitespace(allowNewlines: true); var value = _expression(); + var expressionEnd = scanner.state; expectStatementSeparator("@warn rule"); - return WarnRule(value, scanner.spanFrom(start)); + return WarnRule(value, scanner.spanFrom(start, expressionEnd)); } /// Consumes a `@while` rule. From 66838ca0725b16b410bc49eb4e2ecb513e1ec716 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Thu, 2 Jan 2025 10:18:42 -0500 Subject: [PATCH 50/58] Docs --- lib/src/parse/sass.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index 1f7f39da4..5c7ceadcd 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -323,11 +323,11 @@ class SassParser extends StylesheetParser { } /// Expect and consume a single newline character. + /// + /// If [trailingSemicolon] is true, this follows a semicolon, which is used + /// for error reporting. void _expectNewline({bool trailingSemicolon = false}) { switch (scanner.peekChar()) { - case $semicolon: - // TODO: jamesnw change - scanner.error("semicolons aren't allowed in the indented syntax."); case $cr: scanner.readChar(); if (scanner.peekChar() == $lf) scanner.readChar(); @@ -459,8 +459,9 @@ class SassParser extends StylesheetParser { } } - //`[whitespace no newlines, no comments] ';'? [whitespace with newlines, comments] [newline]` - + /// Consumes a semicolon and trailing whitespace, including comments. + /// + /// Returns whether a semicolon was consumed. bool _trailingSemicolon() { if (scanCharIf((char) => char == $semicolon)) { whitespace(allowNewlines: false); From 1ff011ad67bab3c7bea359adfd87b51fbace7bb6 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Thu, 2 Jan 2025 10:30:01 -0500 Subject: [PATCH 51/58] allowNewlines > consumeNewlines --- lib/src/parse/at_root_query.dart | 4 +- lib/src/parse/css.dart | 4 +- lib/src/parse/keyframe_selector.dart | 4 +- lib/src/parse/media_query.dart | 4 +- lib/src/parse/parser.dart | 22 +- lib/src/parse/sass.dart | 10 +- lib/src/parse/scss.dart | 8 +- lib/src/parse/selector.dart | 4 +- lib/src/parse/stylesheet.dart | 334 +++++++++++++-------------- 9 files changed, 197 insertions(+), 197 deletions(-) diff --git a/lib/src/parse/at_root_query.dart b/lib/src/parse/at_root_query.dart index be662064e..008ac1b26 100644 --- a/lib/src/parse/at_root_query.dart +++ b/lib/src/parse/at_root_query.dart @@ -33,8 +33,8 @@ class AtRootQueryParser extends Parser { }); } - /// The value of `allowNewlines` is not relevant for this class. + /// The value of `consumeNewlines` is not relevant for this class. void _whitespace() { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); } } diff --git a/lib/src/parse/css.dart b/lib/src/parse/css.dart index 814da9656..93baaa19e 100644 --- a/lib/src/parse/css.dart +++ b/lib/src/parse/css.dart @@ -181,8 +181,8 @@ class CssParser extends ScssParser { error("Module namespaces aren't allowed in plain CSS.", expression.span); } - /// The value of `allowNewlines` is not relevant for this class. + /// The value of `consumeNewlines` is not relevant for this class. void _whitespace() { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); } } diff --git a/lib/src/parse/keyframe_selector.dart b/lib/src/parse/keyframe_selector.dart index 68e92c4bd..227d3a4fd 100644 --- a/lib/src/parse/keyframe_selector.dart +++ b/lib/src/parse/keyframe_selector.dart @@ -72,8 +72,8 @@ class KeyframeSelectorParser extends Parser { return buffer.toString(); } - /// The value of `allowNewlines` is not relevant for this class. + /// The value of `consumeNewlines` is not relevant for this class. void _whitespace() { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); } } diff --git a/lib/src/parse/media_query.dart b/lib/src/parse/media_query.dart index d117c01c0..5101d4393 100644 --- a/lib/src/parse/media_query.dart +++ b/lib/src/parse/media_query.dart @@ -118,8 +118,8 @@ class MediaQueryParser extends Parser { return result; } - /// The value of `allowNewlines` is not relevant for this class. + /// The value of `consumeNewlines` is not relevant for this class. void _whitespace() { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); } } diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index 000829002..137027870 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -67,7 +67,7 @@ class Parser { if (!scanner.scanChar($dollar)) return false; if (!lookingAtIdentifier()) return false; identifier(); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); return scanner.scanChar($colon); } @@ -75,21 +75,21 @@ class Parser { /// Consumes whitespace, including any comments. /// - /// If [allowNewlines] is true, the indented syntax will consume newlines as + /// If [consumeNewlines] is true, the indented syntax will consume newlines as /// whitespace in positions when a statement can't end. @protected - void whitespace({required bool allowNewlines}) { + void whitespace({required bool consumeNewlines}) { do { - whitespaceWithoutComments(allowNewlines: allowNewlines); + whitespaceWithoutComments(consumeNewlines: consumeNewlines); } while (scanComment()); } /// Consumes whitespace, but not comments. /// - /// If [allowNewlines] is true, the indented syntax will consume newlines as + /// If [consumeNewlines] is true, the indented syntax will consume newlines as /// whitespace in positions when a statement can't end. @protected - void whitespaceWithoutComments({required bool allowNewlines}) { + void whitespaceWithoutComments({required bool consumeNewlines}) { while (!scanner.isDone && scanner.peekChar().isWhitespace) { scanner.readChar(); } @@ -123,14 +123,14 @@ class Parser { /// Like [whitespace], but throws an error if no whitespace is consumed. /// - /// If [allowNewlines] is true, the indented syntax will consume newlines as + /// If [consumeNewlines] is true, the indented syntax will consume newlines as /// whitespace in positions when a statement can't end. @protected - void expectWhitespace({bool allowNewlines = false}) { + void expectWhitespace({bool consumeNewlines = false}) { if (scanner.isDone || !(scanner.peekChar().isWhitespace || scanComment())) { scanner.error("Expected whitespace."); } - whitespace(allowNewlines: allowNewlines); + whitespace(consumeNewlines: consumeNewlines); } /// Consumes and ignores a single silent (Sass-style) comment, not including @@ -394,7 +394,7 @@ class Parser { return null; } - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); // Match Ruby Sass's behavior: parse a raw URL() if possible, and if not // backtrack and re-parse as a function expression. @@ -415,7 +415,7 @@ class Parser { >= 0x0080: buffer.writeCharCode(scanner.readChar()); case int(isWhitespace: true): - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (scanner.peekChar() != $rparen) break loop; case $rparen: buffer.writeCharCode(scanner.readChar()); diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index 5c7ceadcd..e03d4b9d5 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -262,7 +262,7 @@ class SassParser extends StylesheetParser { buffer.writeCharCode(scanner.readChar()); buffer.writeCharCode(scanner.readChar()); var span = scanner.spanFrom(start); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); // For backwards compatibility, allow additional comments after // the initial comment is closed. @@ -272,7 +272,7 @@ class SassParser extends StylesheetParser { _expectNewline(); } _readIndentation(); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); } if (!scanner.isDone && !scanner.peekChar().isNewline) { @@ -312,12 +312,12 @@ class SassParser extends StylesheetParser { return LoudComment(buffer.interpolation(scanner.spanFrom(start))); } - void whitespaceWithoutComments({required bool allowNewlines}) { + void whitespaceWithoutComments({required bool consumeNewlines}) { // This overrides whitespace consumption so that it doesn't consume // newlines where that would cause a statement to end. while (!scanner.isDone) { var next = scanner.peekChar(); - if (allowNewlines ? !next.isWhitespace : !next.isSpaceOrTab) break; + if (consumeNewlines ? !next.isWhitespace : !next.isSpaceOrTab) break; scanner.readChar(); } } @@ -464,7 +464,7 @@ class SassParser extends StylesheetParser { /// Returns whether a semicolon was consumed. bool _trailingSemicolon() { if (scanCharIf((char) => char == $semicolon)) { - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); return true; } return false; diff --git a/lib/src/parse/scss.dart b/lib/src/parse/scss.dart index 45ec5fb08..0c238d292 100644 --- a/lib/src/parse/scss.dart +++ b/lib/src/parse/scss.dart @@ -184,13 +184,13 @@ class ScssParser extends StylesheetParser { } } - /// The value of `allowNewlines` is not relevant for this class. + /// The value of `consumeNewlines` is not relevant for this class. void _whitespace() { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); } - /// The value of `allowNewlines` is not relevant for this class. + /// The value of `consumeNewlines` is not relevant for this class. void _whitespaceWithoutComments() { - whitespaceWithoutComments(allowNewlines: true); + whitespaceWithoutComments(consumeNewlines: true); } } diff --git a/lib/src/parse/selector.dart b/lib/src/parse/selector.dart index 609961964..3d1e5e671 100644 --- a/lib/src/parse/selector.dart +++ b/lib/src/parse/selector.dart @@ -480,8 +480,8 @@ class SelectorParser extends Parser { _ => false }; - /// The value of `allowNewlines` is not relevant for this class. + /// The value of `consumeNewlines` is not relevant for this class. void _whitespace() { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); } } diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index fb6b8e617..e10db8612 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -91,7 +91,7 @@ abstract class StylesheetParser extends Parser { // Handle this specially so that [atRule] always returns a non-nullable // Statement. if (scanner.scan('@charset')) { - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); string(); return null; } @@ -108,10 +108,10 @@ abstract class StylesheetParser extends Parser { ParameterList parseParameterList() => _parseSingleProduction(() { scanner.expectChar($at, name: "@-rule"); identifier(); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); identifier(); var parameters = _parameterList(); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); scanner.expectChar($lbrace); return parameters; }); @@ -136,7 +136,7 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; scanner.expectChar($at, name: "@-rule"); expectIdentifier("use"); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); return _useRule(start); }), warnings @@ -188,7 +188,7 @@ abstract class StylesheetParser extends Parser { _isUseAllowed = false; var start = scanner.state; scanner.readChar(); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); return _mixinRule(start); case $rbrace: @@ -228,9 +228,9 @@ abstract class StylesheetParser extends Parser { scanner.spanFrom(start)); } - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); scanner.expectChar($colon); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var value = _expression(); @@ -270,7 +270,7 @@ abstract class StylesheetParser extends Parser { error("Invalid flag name.", scanner.spanFrom(flagStart)); } - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); flagStart = scanner.state; } @@ -362,7 +362,7 @@ abstract class StylesheetParser extends Parser { if (_lookingAtPotentialPropertyHack()) { startsWithPunctuation = true; nameBuffer.writeCharCode(scanner.readChar()); - nameBuffer.write(rawText(() => whitespace(allowNewlines: false))); + nameBuffer.write(rawText(() => whitespace(consumeNewlines: false))); } if (!_lookingAtInterpolatedIdentifier()) return nameBuffer; @@ -380,7 +380,7 @@ abstract class StylesheetParser extends Parser { if (scanner.matches("/*")) nameBuffer.write(rawText(loudComment)); var midBuffer = StringBuffer(); - midBuffer.write(rawText(() => whitespace(allowNewlines: false))); + midBuffer.write(rawText(() => whitespace(consumeNewlines: false))); var beforeColon = scanner.state; if (!scanner.scanChar($colon)) { if (midBuffer.isNotEmpty) nameBuffer.writeCharCode($space); @@ -407,7 +407,7 @@ abstract class StylesheetParser extends Parser { return nameBuffer..write(midBuffer); } - var postColonWhitespace = rawText(() => whitespace(allowNewlines: false)); + var postColonWhitespace = rawText(() => whitespace(consumeNewlines: false)); if (_tryDeclarationChildren(name, start) case var nested?) return nested; midBuffer.write(postColonWhitespace); @@ -530,7 +530,7 @@ abstract class StylesheetParser extends Parser { if (_lookingAtPotentialPropertyHack()) { var nameBuffer = InterpolationBuffer(); nameBuffer.writeCharCode(scanner.readChar()); - nameBuffer.write(rawText(() => whitespace(allowNewlines: false))); + nameBuffer.write(rawText(() => whitespace(consumeNewlines: false))); nameBuffer.addInterpolation(interpolatedIdentifier()); name = nameBuffer.interpolation(scanner.spanFrom(start)); } else if (!plainCss) { @@ -544,7 +544,7 @@ abstract class StylesheetParser extends Parser { name = interpolatedIdentifier(); } - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); scanner.expectChar($colon); if (parseCustomProperties && name.initialPlain.startsWith('--')) { @@ -554,7 +554,7 @@ abstract class StylesheetParser extends Parser { return Declaration(name, value, scanner.spanFrom(start)); } - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); if (_tryDeclarationChildren(name, start) case var nested?) return nested; var value = _expression(); @@ -739,7 +739,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. AtRootRule _atRootRule(LineScannerState start) { - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); if (scanner.peekChar() == $lparen) { var query = _atRootQuery(); return _withChildren(_statement, start, @@ -759,18 +759,18 @@ abstract class StylesheetParser extends Parser { var buffer = InterpolationBuffer(); scanner.expectChar($lparen); buffer.writeCharCode($lparen); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); - _addOrInject(buffer, _expression(allowNewlines: true)); + _addOrInject(buffer, _expression(consumeNewlines: true)); if (scanner.scanChar($colon)) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); buffer.writeCharCode($colon); buffer.writeCharCode($space); - _addOrInject(buffer, _expression(allowNewlines: true)); + _addOrInject(buffer, _expression(consumeNewlines: true)); } scanner.expectChar($rparen); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); buffer.writeCharCode($rparen); return buffer.interpolation(scanner.spanFrom(start)); @@ -786,11 +786,11 @@ abstract class StylesheetParser extends Parser { } var beforeWhitespace = scanner.location; - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); ArgumentList arguments; if (scanner.peekChar() == $lparen) { arguments = _argumentInvocation(mixin: true); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); } else { arguments = ArgumentList.empty(beforeWhitespace.pointSpan()); } @@ -803,7 +803,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. DebugRule _debugRule(LineScannerState start) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var value = _expression(); var expressionEnd = scanner.state; expectStatementSeparator("@debug rule"); @@ -815,20 +815,20 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. [child] is called to consume any /// children that are specifically allowed in the caller's context. EachRule _eachRule(LineScannerState start, Statement child()) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var wasInControlDirective = _inControlDirective; _inControlDirective = true; var variables = [variableName()]; - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); while (scanner.scanChar($comma)) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); variables.add(variableName()); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); } - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); expectIdentifier("in"); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var list = _expression(); @@ -842,7 +842,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ErrorRule _errorRule(LineScannerState start) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var value = _expression(); var expressionEnd = scanner.state; expectStatementSeparator("@error rule"); @@ -853,7 +853,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ExtendRule _extendRule(LineScannerState start) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (!_inStyleRule && !_inMixin && !_inContentBlock) { error("@extend may only be used within style rules.", scanner.spanFrom(start)); @@ -863,7 +863,7 @@ abstract class StylesheetParser extends Parser { var optional = scanner.scanChar($exclamation); if (optional) { expectIdentifier("optional"); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); } expectStatementSeparator("@extend rule"); return ExtendRule(value, scanner.spanFrom(start), optional: optional); @@ -873,7 +873,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. FunctionRule _functionRule(LineScannerState start) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var precedingComment = lastSilentComment; lastSilentComment = null; var beforeName = scanner.state; @@ -891,7 +891,7 @@ abstract class StylesheetParser extends Parser { )); } - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var parameters = _parameterList(); if (_inMixin || _inContentBlock) { @@ -914,7 +914,7 @@ abstract class StylesheetParser extends Parser { error("Invalid function name.", scanner.spanFrom(start)); } - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); return _withChildren( _functionChild, start, @@ -927,18 +927,18 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. [child] is called to consume any /// children that are specifically allowed in the caller's context. ForRule _forRule(LineScannerState start, Statement child()) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var wasInControlDirective = _inControlDirective; _inControlDirective = true; var variable = variableName(); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); expectIdentifier("from"); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); bool? exclusive; var from = _expression( - allowNewlines: true, + consumeNewlines: true, until: () { if (!lookingAtIdentifier()) return false; if (scanIdentifier("to")) { @@ -953,7 +953,7 @@ abstract class StylesheetParser extends Parser { }); if (exclusive == null) scanner.error('Expected "to" or "through".'); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var to = _expression(); return _withChildren(child, start, (children, span) { @@ -967,16 +967,16 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ForwardRule _forwardRule(LineScannerState start) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var url = _urlString(); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); String? prefix; if (scanIdentifier("as")) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); prefix = identifier(normalize: true); scanner.expectChar($asterisk); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); } Set? shownMixinsAndFunctions; @@ -984,15 +984,15 @@ abstract class StylesheetParser extends Parser { Set? hiddenMixinsAndFunctions; Set? hiddenVariables; if (scanIdentifier("show")) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); (shownMixinsAndFunctions, shownVariables) = _memberList(); } else if (scanIdentifier("hide")) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); (hiddenMixinsAndFunctions, hiddenVariables) = _memberList(); } var configuration = _configuration(allowGuarded: true); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); expectStatementSeparator("@forward rule"); var span = scanner.spanFrom(start); @@ -1023,7 +1023,7 @@ abstract class StylesheetParser extends Parser { var identifiers = {}; var variables = {}; do { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); withErrorMessage("Expected variable, mixin, or function name", () { if (scanner.peekChar() == $dollar) { variables.add(variableName()); @@ -1031,7 +1031,7 @@ abstract class StylesheetParser extends Parser { identifiers.add(identifier(normalize: true)); } }); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); } while (scanner.scanChar($comma)); return (identifiers, variables); @@ -1042,21 +1042,21 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. [child] is called to consume any /// children that are specifically allowed in the caller's context. IfRule _ifRule(LineScannerState start, Statement child()) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var ifIndentation = currentIndentation; var wasInControlDirective = _inControlDirective; _inControlDirective = true; var condition = _expression(); var children = this.children(child); - whitespaceWithoutComments(allowNewlines: false); + whitespaceWithoutComments(consumeNewlines: false); var clauses = [IfClause(condition, children)]; ElseClause? lastClause; while (scanElse(ifIndentation)) { - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); if (scanIdentifier("if")) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); clauses.add(IfClause(_expression(), this.children(child))); } else { lastClause = ElseClause(this.children(child)); @@ -1066,7 +1066,7 @@ abstract class StylesheetParser extends Parser { _inControlDirective = wasInControlDirective; var span = scanner.spanFrom(start); - whitespaceWithoutComments(allowNewlines: false); + whitespaceWithoutComments(consumeNewlines: false); return IfRule(clauses, span, lastClause: lastClause); } @@ -1076,7 +1076,7 @@ abstract class StylesheetParser extends Parser { ImportRule _importRule(LineScannerState start) { var imports = []; do { - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); var argument = importArgument(); if (argument is DynamicImport) { warnings.add(( @@ -1094,7 +1094,7 @@ abstract class StylesheetParser extends Parser { } imports.add(argument); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); } while (scanner.scanChar($comma)); expectStatementSeparator("@import rule"); @@ -1109,7 +1109,7 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; if (scanner.peekChar() case $u || $U) { var url = dynamicUrl(); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); var modifiers = tryImportModifiers(); return StaticImport( url is StringExpression @@ -1121,7 +1121,7 @@ abstract class StylesheetParser extends Parser { var url = string(); var urlSpan = scanner.spanFrom(start); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); var modifiers = tryImportModifiers(); if (isPlainImportUrl(url) || modifiers != null) { return StaticImport( @@ -1193,13 +1193,13 @@ abstract class StylesheetParser extends Parser { } else { buffer.writeCharCode($lparen); buffer.addInterpolation(_interpolatedDeclarationValue( - allowEmpty: true, allowSemicolon: true, allowNewlines: true)); + allowEmpty: true, allowSemicolon: true, consumeNewlines: true)); buffer.writeCharCode($rparen); } scanner.expectChar($rparen); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); } else { - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); if (scanner.scanChar($comma)) { buffer.write(", "); buffer.addInterpolation(_mediaQueryList()); @@ -1219,9 +1219,9 @@ abstract class StylesheetParser extends Parser { /// Consumes the contents of a `supports()` function after an `@import` rule /// (but not the function name or parentheses). SupportsCondition _importSupportsQuery() { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (scanIdentifier("not")) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var start = scanner.state; return SupportsNegation( _supportsConditionInParens(), scanner.spanFrom(start)); @@ -1231,7 +1231,7 @@ abstract class StylesheetParser extends Parser { if (_tryImportSupportsFunction() case var function?) return function; var start = scanner.state; - var name = _expression(allowNewlines: true); + var name = _expression(consumeNewlines: true); scanner.expectChar($colon); return SupportsDeclaration( name, _supportsDeclarationValue(name), scanner.spanFrom(start)); @@ -1253,7 +1253,7 @@ abstract class StylesheetParser extends Parser { } var value = _interpolatedDeclarationValue( - allowEmpty: true, allowSemicolon: true, allowNewlines: true); + allowEmpty: true, allowSemicolon: true, consumeNewlines: true); scanner.expectChar($rparen); return SupportsFunction(name, value, scanner.spanFrom(start)); @@ -1263,7 +1263,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. IncludeRule _includeRule(LineScannerState start) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); String? namespace; var name = identifier(); if (scanner.scanChar($dot)) { @@ -1271,17 +1271,17 @@ abstract class StylesheetParser extends Parser { name = _publicIdentifier(); } - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); var arguments = scanner.peekChar() == $lparen ? _argumentInvocation(mixin: true) : ArgumentList.empty(scanner.emptySpan); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); ParameterList? contentParameters; if (scanIdentifier("using")) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); contentParameters = _parameterList(); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); } ContentBlock? content; @@ -1308,7 +1308,7 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. @protected MediaRule mediaRule(LineScannerState start) { - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); var query = _mediaQueryList(); return _withChildren(_statement, start, (children, span) => MediaRule(query, children, span)); @@ -1318,7 +1318,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. MixinRule _mixinRule(LineScannerState start) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var precedingComment = lastSilentComment; lastSilentComment = null; var beforeName = scanner.state; @@ -1336,7 +1336,7 @@ abstract class StylesheetParser extends Parser { )); } - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); var parameters = scanner.peekChar() == $lparen ? _parameterList() : ParameterList.empty(scanner.emptySpan); @@ -1349,7 +1349,7 @@ abstract class StylesheetParser extends Parser { scanner.spanFrom(start)); } - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); _inMixin = true; return _withChildren(_statement, start, (children, span) { @@ -1368,7 +1368,7 @@ abstract class StylesheetParser extends Parser { /// [the specification]: http://www.w3.org/TR/css3-conditional/ @protected AtRule mozDocumentRule(LineScannerState start, Interpolation name) { - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); var valueStart = scanner.state; var buffer = InterpolationBuffer(); var needsDeprecationWarning = false; @@ -1387,7 +1387,7 @@ abstract class StylesheetParser extends Parser { buffer.addInterpolation(contents); } else { scanner.expectChar($lparen); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); var argument = interpolatedString(); scanner.expectChar($rparen); @@ -1420,11 +1420,11 @@ abstract class StylesheetParser extends Parser { } } - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); if (!scanner.scanChar($comma)) break; buffer.writeCharCode($comma); - buffer.write(rawText(() => whitespace(allowNewlines: false))); + buffer.write(rawText(() => whitespace(consumeNewlines: false))); } var value = buffer.interpolation(scanner.spanFrom(valueStart)); @@ -1449,7 +1449,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. ReturnRule _returnRule(LineScannerState start) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var value = _expression(); expectStatementSeparator("@return rule"); return ReturnRule(value, scanner.spanFrom(start)); @@ -1460,9 +1460,9 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. @protected SupportsRule supportsRule(LineScannerState start) { - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); var condition = _supportsCondition(); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); return _withChildren(_statement, start, (children, span) => SupportsRule(condition, children, span)); } @@ -1471,14 +1471,14 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. UseRule _useRule(LineScannerState start) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var url = _urlString(); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); var namespace = _useNamespace(url, start); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); var configuration = _configuration(); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); var span = scanner.spanFrom(start); if (!_isUseAllowed) { @@ -1495,7 +1495,7 @@ abstract class StylesheetParser extends Parser { /// Returns `null` to indicate a `@use` rule without a URL. String? _useNamespace(Uri url, LineScannerState start) { if (scanIdentifier("as")) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); return scanner.scanChar($asterisk) ? null : identifier(); } @@ -1526,26 +1526,26 @@ abstract class StylesheetParser extends Parser { var variableNames = {}; var configuration = []; - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); scanner.expectChar($lparen); while (true) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var variableStart = scanner.state; var name = variableName(); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); scanner.expectChar($colon); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); - var expression = expressionUntilComma(allowNewlines: true); + var expression = expressionUntilComma(consumeNewlines: true); var guarded = false; var flagStart = scanner.state; if (allowGuarded && scanner.scanChar($exclamation)) { if (identifier() == 'default') { guarded = true; - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); } else { error("Invalid flag name.", scanner.spanFrom(flagStart)); } @@ -1560,7 +1560,7 @@ abstract class StylesheetParser extends Parser { .add(ConfiguredVariable(name, expression, span, guarded: guarded)); if (!scanner.scanChar($comma)) break; - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (!_lookingAtExpression()) break; } @@ -1572,7 +1572,7 @@ abstract class StylesheetParser extends Parser { /// /// [start] should point before the `@`. WarnRule _warnRule(LineScannerState start) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var value = _expression(); var expressionEnd = scanner.state; expectStatementSeparator("@warn rule"); @@ -1584,7 +1584,7 @@ abstract class StylesheetParser extends Parser { /// [start] should point before the `@`. [child] is called to consume any /// children that are specifically allowed in the caller's context. WhileRule _whileRule(LineScannerState start, Statement child()) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var wasInControlDirective = _inControlDirective; _inControlDirective = true; var condition = _expression(); @@ -1602,7 +1602,7 @@ abstract class StylesheetParser extends Parser { var wasInUnknownAtRule = _inUnknownAtRule; _inUnknownAtRule = true; - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); Interpolation? value; if (scanner.peekChar() != $exclamation && !atEndOfStatement()) { @@ -1631,7 +1631,7 @@ abstract class StylesheetParser extends Parser { /// This declares a return type of [Statement] so that it can be returned /// within case statements. Statement _disallowedAtRule(LineScannerState start) { - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); _interpolatedDeclarationValue(allowEmpty: true, allowOpenBrace: false); error("This at-rule is not allowed here.", scanner.spanFrom(start)); } @@ -1640,24 +1640,24 @@ abstract class StylesheetParser extends Parser { ParameterList _parameterList() { var start = scanner.state; scanner.expectChar($lparen); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var parameters = []; var named = {}; String? restParameter; while (scanner.peekChar() == $dollar) { var variableStart = scanner.state; var name = variableName(); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); Expression? defaultValue; if (scanner.scanChar($colon)) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); defaultValue = expressionUntilComma(); } else if (scanner.scanChar($dot)) { scanner.expectChar($dot); scanner.expectChar($dot); - whitespace(allowNewlines: true); - if (scanner.scanChar($comma)) whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); + if (scanner.scanChar($comma)) whitespace(consumeNewlines: true); restParameter = name; break; } @@ -1669,7 +1669,7 @@ abstract class StylesheetParser extends Parser { } if (!scanner.scanChar($comma)) break; - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); } scanner.expectChar($rparen); return ParameterList(parameters, scanner.spanFrom(start), @@ -1691,7 +1691,7 @@ abstract class StylesheetParser extends Parser { {bool mixin = false, bool allowEmptySecondArg = false}) { var start = scanner.state; scanner.expectChar($lparen); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var positional = []; var named = {}; @@ -1699,10 +1699,10 @@ abstract class StylesheetParser extends Parser { Expression? keywordRest; while (_lookingAtExpression()) { var expression = expressionUntilComma(singleEquals: !mixin); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (expression is VariableExpression && scanner.scanChar($colon)) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (named.containsKey(expression.name)) { error("Duplicate argument.", expression.span); } @@ -1714,8 +1714,8 @@ abstract class StylesheetParser extends Parser { rest = expression; } else { keywordRest = expression; - whitespace(allowNewlines: true); - if (scanner.scanChar($comma)) whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); + if (scanner.scanChar($comma)) whitespace(consumeNewlines: true); break; } } else if (named.isNotEmpty) { @@ -1725,9 +1725,9 @@ abstract class StylesheetParser extends Parser { positional.add(expression); } - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (!scanner.scanChar($comma)) break; - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (allowEmptySecondArg && positional.length == 1 && @@ -1756,14 +1756,14 @@ abstract class StylesheetParser extends Parser { /// still be a valid expression. When it returns `true`, this returns the /// expression. /// - /// If [allowNewlines] is true, the indented syntax will consume newlines as + /// If [consumeNewlines] is true, the indented syntax will consume newlines as /// whitespace at the top level of the expression, outside of bracketed /// subexpressions, in positions when a statement can't end. @protected Expression _expression( {bool bracketList = false, bool singleEquals = false, - bool allowNewlines = false, + bool consumeNewlines = false, bool until()?}) { if (until != null && until()) scanner.error("Expected expression."); @@ -1771,7 +1771,7 @@ abstract class StylesheetParser extends Parser { if (bracketList) { beforeBracket = scanner.state; scanner.expectChar($lbracket); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (scanner.scanChar($rbracket)) { return ListExpression( @@ -1947,7 +1947,7 @@ abstract class StylesheetParser extends Parser { length: operator.operator.length); } operands.add(singleExpression); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); singleExpression_ = _singleExpression(); } @@ -1969,7 +1969,7 @@ abstract class StylesheetParser extends Parser { loop: while (true) { whitespace( - allowNewlines: allowNewlines || + consumeNewlines: consumeNewlines || bracketList || _inParentheses || wasInExpression); @@ -2167,13 +2167,13 @@ abstract class StylesheetParser extends Parser { /// If [singleEquals] is true, this will allow the Microsoft-style `=` /// operator at the top level. /// - /// If [allowNewlines] is true, the indented syntax will consume newlines as + /// If [consumeNewlines] is true, the indented syntax will consume newlines as /// whitespace in positions when a statement can't end. Expression expressionUntilComma( - {bool singleEquals = false, bool allowNewlines = false}) => + {bool singleEquals = false, bool consumeNewlines = false}) => _expression( singleEquals: singleEquals, - allowNewlines: allowNewlines, + consumeNewlines: consumeNewlines, until: () => scanner.peekChar() == $comma); /// Whether [expression] is allowed as an operand of a `/` expression that @@ -2223,7 +2223,7 @@ abstract class StylesheetParser extends Parser { try { var start = scanner.state; scanner.expectChar($lparen); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (!_lookingAtExpression()) { scanner.expectChar($rparen); return ListExpression( @@ -2232,7 +2232,7 @@ abstract class StylesheetParser extends Parser { var first = expressionUntilComma(); if (scanner.scanChar($colon)) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); return _map(first, start); } @@ -2240,14 +2240,14 @@ abstract class StylesheetParser extends Parser { scanner.expectChar($rparen); return ParenthesizedExpression(first, scanner.spanFrom(start)); } - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var expressions = [first]; while (true) { if (!_lookingAtExpression()) break; expressions.add(expressionUntilComma()); if (!scanner.scanChar($comma)) break; - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); } scanner.expectChar($rparen); @@ -2267,12 +2267,12 @@ abstract class StylesheetParser extends Parser { var pairs = [(first, expressionUntilComma())]; while (scanner.scanChar($comma)) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (!_lookingAtExpression()) break; var key = expressionUntilComma(); scanner.expectChar($colon); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var value = expressionUntilComma(); pairs.add((key, value)); } @@ -2387,7 +2387,7 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; scanner.readChar(); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); expectIdentifier("important"); return StringExpression.plain("!important", scanner.spanFrom(start)); } @@ -2403,7 +2403,7 @@ abstract class StylesheetParser extends Parser { position: scanner.position - 1, length: 1); } - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var operand = _singleExpression(); return UnaryOperationExpression(operator, operand, scanner.spanFrom(start)); } @@ -2638,7 +2638,7 @@ abstract class StylesheetParser extends Parser { return IfExpression( invocation, identifier.span.expand(invocation.span)); } else if (plain == "not") { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var expression = _singleExpression(); return UnaryOperationExpression(UnaryOperator.not, expression, identifier.span.expand(expression.span)); @@ -2763,7 +2763,7 @@ abstract class StylesheetParser extends Parser { var beginningOfContents = scanner.state; if (!scanner.scanChar($lparen)) return null; - whitespaceWithoutComments(allowNewlines: true); + whitespaceWithoutComments(consumeNewlines: true); // Match Ruby Sass's behavior: parse a raw URL() if possible, and if not // backtrack and re-parse as a function expression. @@ -2790,7 +2790,7 @@ abstract class StylesheetParser extends Parser { >= 0x80: buffer.writeCharCode(scanner.readChar()); case int(isWhitespace: true): - whitespaceWithoutComments(allowNewlines: true); + whitespaceWithoutComments(consumeNewlines: true); if (scanner.peekChar() != $rparen) break loop; case $rparen: buffer.writeCharCode(scanner.readChar()); @@ -2946,7 +2946,7 @@ abstract class StylesheetParser extends Parser { /// comments. Otherwise, it will preserve two adjacent slashes and emit them /// to CSS. /// - /// If [allowNewlines] is true, the indented syntax will consume newlines as + /// If [consumeNewlines] is true, the indented syntax will consume newlines as /// whitespace in positions when a statement can't end. /// /// Unlike [declarationValue], this allows interpolation. @@ -2956,7 +2956,7 @@ abstract class StylesheetParser extends Parser { bool allowColon = true, bool allowOpenBrace = true, bool silentComments = true, - bool allowNewlines = false}) { + bool consumeNewlines = false}) { // NOTE: this logic is largely duplicated in Parser.declarationValue. Most // changes here should be mirrored there. @@ -3007,7 +3007,7 @@ abstract class StylesheetParser extends Parser { buffer.writeCharCode(scanner.readChar()); case $lf || $cr || $ff - when indented && !allowNewlines && brackets.isEmpty: + when indented && !consumeNewlines && brackets.isEmpty: break loop; case $lf || $cr || $ff: @@ -3142,8 +3142,8 @@ abstract class StylesheetParser extends Parser { (Expression, FileSpan span) singleInterpolation() { var start = scanner.state; scanner.expect('#{'); - whitespace(allowNewlines: true); - var contents = _expression(allowNewlines: true); + whitespace(consumeNewlines: true); + var contents = _expression(consumeNewlines: true); scanner.expectChar($rbrace); var span = scanner.spanFrom(start); @@ -3161,9 +3161,9 @@ abstract class StylesheetParser extends Parser { var start = scanner.state; var buffer = InterpolationBuffer(); while (true) { - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); _mediaQuery(buffer); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); if (!scanner.scanChar($comma)) break; buffer.writeCharCode($comma); buffer.writeCharCode($space); @@ -3176,7 +3176,7 @@ abstract class StylesheetParser extends Parser { // This is somewhat duplicated in MediaQueryParser._mediaQuery. if (scanner.peekChar() == $lparen) { _mediaInParens(buffer); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); if (scanIdentifier("and")) { buffer.write(" and "); expectWhitespace(); @@ -3202,7 +3202,7 @@ abstract class StylesheetParser extends Parser { } } - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); buffer.addInterpolation(identifier1); if (!_lookingAtInterpolatedIdentifier()) { // For example, "@media screen {". @@ -3217,7 +3217,7 @@ abstract class StylesheetParser extends Parser { // For example, "@media screen and ..." buffer.write(" and "); } else { - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); buffer.addInterpolation(identifier2); if (scanIdentifier("and")) { // For example, "@media only screen and ..." @@ -3249,10 +3249,10 @@ abstract class StylesheetParser extends Parser { void _mediaLogicSequence(InterpolationBuffer buffer, String operator) { while (true) { _mediaOrInterp(buffer); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); if (!scanIdentifier(operator)) return; - expectWhitespace(allowNewlines: false); + expectWhitespace(consumeNewlines: false); buffer.writeCharCode($space); buffer.write(operator); @@ -3274,32 +3274,32 @@ abstract class StylesheetParser extends Parser { void _mediaInParens(InterpolationBuffer buffer) { scanner.expectChar($lparen, name: "media condition in parentheses"); buffer.writeCharCode($lparen); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (scanner.peekChar() == $lparen) { _mediaInParens(buffer); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (scanIdentifier("and")) { buffer.write(" and "); - expectWhitespace(allowNewlines: true); + expectWhitespace(consumeNewlines: true); _mediaLogicSequence(buffer, "and"); } else if (scanIdentifier("or")) { buffer.write(" or "); - expectWhitespace(allowNewlines: true); + expectWhitespace(consumeNewlines: true); _mediaLogicSequence(buffer, "or"); } } else if (scanIdentifier("not")) { buffer.write("not "); - expectWhitespace(allowNewlines: true); + expectWhitespace(consumeNewlines: true); _mediaOrInterp(buffer); } else { var expressionBefore = _expressionUntilComparison(); buffer.add(expressionBefore, expressionBefore.span); if (scanner.scanChar($colon)) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); buffer.writeCharCode($colon); buffer.writeCharCode($space); - var expressionAfter = _expression(allowNewlines: true); + var expressionAfter = _expression(consumeNewlines: true); buffer.add(expressionAfter, expressionAfter.span); } else { var next = scanner.peekChar(); @@ -3311,7 +3311,7 @@ abstract class StylesheetParser extends Parser { } buffer.writeCharCode($space); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var expressionMiddle = _expressionUntilComparison(); buffer.add(expressionMiddle, expressionMiddle.span); @@ -3322,7 +3322,7 @@ abstract class StylesheetParser extends Parser { if (scanner.scanChar($equal)) buffer.writeCharCode($equal); buffer.writeCharCode($space); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var expressionAfter = _expressionUntilComparison(); buffer.add(expressionAfter, expressionAfter.span); } @@ -3331,14 +3331,14 @@ abstract class StylesheetParser extends Parser { } scanner.expectChar($rparen); - whitespace(allowNewlines: false); + whitespace(consumeNewlines: false); buffer.writeCharCode($rparen); } /// Consumes an expression until it reaches a top-level `<`, `>`, or a `=` /// that's not `==`. Expression _expressionUntilComparison() => _expression( - allowNewlines: true, + consumeNewlines: true, until: () => switch (scanner.peekChar()) { $equal => scanner.peekChar(1) != $equal, $langle || $rangle => true, @@ -3354,13 +3354,13 @@ abstract class StylesheetParser extends Parser { SupportsCondition _supportsCondition({bool inParentheses = false}) { var start = scanner.state; if (scanIdentifier("not")) { - whitespace(allowNewlines: inParentheses); + whitespace(consumeNewlines: inParentheses); return SupportsNegation( _supportsConditionInParens(), scanner.spanFrom(start)); } var condition = _supportsConditionInParens(); - whitespace(allowNewlines: inParentheses); + whitespace(consumeNewlines: inParentheses); String? operator; while (lookingAtIdentifier()) { if (operator != null) { @@ -3372,11 +3372,11 @@ abstract class StylesheetParser extends Parser { operator = "and"; } - whitespace(allowNewlines: inParentheses); + whitespace(consumeNewlines: inParentheses); var right = _supportsConditionInParens(); condition = SupportsOperation( condition, right, operator, scanner.spanFrom(start)); - whitespace(allowNewlines: inParentheses); + whitespace(consumeNewlines: inParentheses); } return condition; } @@ -3393,7 +3393,7 @@ abstract class StylesheetParser extends Parser { if (scanner.scanChar($lparen)) { var arguments = _interpolatedDeclarationValue( - allowEmpty: true, allowSemicolon: true, allowNewlines: true); + allowEmpty: true, allowSemicolon: true, consumeNewlines: true); scanner.expectChar($rparen); return SupportsFunction(identifier, arguments, scanner.spanFrom(start)); } else if (identifier.contents case [Expression expression]) { @@ -3404,9 +3404,9 @@ abstract class StylesheetParser extends Parser { } scanner.expectChar($lparen); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); if (scanIdentifier("not")) { - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var condition = _supportsConditionInParens(); scanner.expectChar($rparen); return SupportsNegation(condition, scanner.spanFrom(start)); @@ -3434,7 +3434,7 @@ abstract class StylesheetParser extends Parser { var nameStart = scanner.state; var wasInParentheses = _inParentheses; try { - name = _expression(allowNewlines: true); + name = _expression(consumeNewlines: true); scanner.expectChar($colon); } on FormatException catch (_) { scanner.state = nameStart; @@ -3456,7 +3456,7 @@ abstract class StylesheetParser extends Parser { allowEmpty: true, allowSemicolon: true, allowColon: false, - allowNewlines: true))) + consumeNewlines: true))) .interpolation(scanner.spanFrom(nameStart)); if (scanner.peekChar() == $colon) rethrow; @@ -3476,8 +3476,8 @@ abstract class StylesheetParser extends Parser { when text.initialPlain.startsWith("--")) { return StringExpression(_interpolatedDeclarationValue()); } else { - whitespace(allowNewlines: true); - return _expression(allowNewlines: true); + whitespace(consumeNewlines: true); + return _expression(consumeNewlines: true); } } @@ -3491,7 +3491,7 @@ abstract class StylesheetParser extends Parser { if (expression is! Expression) return null; var beforeWhitespace = scanner.state; - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); SupportsOperation? operation; String? operator; @@ -3507,14 +3507,14 @@ abstract class StylesheetParser extends Parser { return null; } - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); var right = _supportsConditionInParens(); operation = SupportsOperation( operation ?? SupportsInterpolation(expression, interpolation.span), right, operator, scanner.spanFrom(start)); - whitespace(allowNewlines: true); + whitespace(consumeNewlines: true); } return operation; @@ -3596,7 +3596,7 @@ abstract class StylesheetParser extends Parser { T _withChildren(Statement child(), LineScannerState start, T create(List children, FileSpan span)) { var result = create(children(child), scanner.spanFrom(start)); - whitespaceWithoutComments(allowNewlines: false); + whitespaceWithoutComments(consumeNewlines: false); return result; } From 99311ad91c18289ad1a63fb03ace5205337c7fbb Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Thu, 2 Jan 2025 10:37:04 -0500 Subject: [PATCH 52/58] Update pubspec to pass tests --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index ba310f719..e3bc53507 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.83.0 +version: 1.84.0-dev description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From 29f76df7b1a5d460ca2f9f90207a5b87e480d83a Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 3 Jan 2025 09:33:38 -0500 Subject: [PATCH 53/58] Revisions --- CHANGELOG.md | 2 +- lib/src/parse/sass.dart | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca5ce69a9..9dbb1cdb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## 1.84.0-dev * Allow newlines in whitespace in the indented syntax. -* **Potentially breaking bug fix**: Selectors with interpolations that render +* **Potentially breaking bug fix**: Selectors with interpolations containing unmatched brackets previously parse, but now error. For example, `[foo#{"]:is(bar"}) {a: b}` will now throw an error. diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index e03d4b9d5..fa3da5f67 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -54,7 +54,7 @@ class SassParser extends StylesheetParser { } void expectStatementSeparator([String? name]) { - var trailingSemicolon = _trailingSemicolon(); + var trailingSemicolon = _tryTrailingSemicolon(); if (!atEndOfStatement()) { _expectNewline(trailingSemicolon: trailingSemicolon); } @@ -394,7 +394,6 @@ class SassParser extends StylesheetParser { } var start = scanner.state; - if (!scanCharIf((char) => char.isNewline)) { scanner.error("Expected newline.", position: scanner.position); } @@ -462,7 +461,7 @@ class SassParser extends StylesheetParser { /// Consumes a semicolon and trailing whitespace, including comments. /// /// Returns whether a semicolon was consumed. - bool _trailingSemicolon() { + bool _tryTrailingSemicolon() { if (scanCharIf((char) => char == $semicolon)) { whitespace(consumeNewlines: false); return true; From 43ee92998a62f50d73015bfa28b126ed211f96b6 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 3 Jan 2025 09:56:46 -0500 Subject: [PATCH 54/58] Consume newlines in expressionUntilComma in parentheses and maps --- lib/src/parse/stylesheet.dart | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index e10db8612..9fcc6c391 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -1968,11 +1968,7 @@ abstract class StylesheetParser extends Parser { loop: while (true) { - whitespace( - consumeNewlines: consumeNewlines || - bracketList || - _inParentheses || - wasInExpression); + whitespace(consumeNewlines: consumeNewlines || bracketList); if (until != null && until()) break; switch (scanner.peekChar()) { @@ -2230,7 +2226,7 @@ abstract class StylesheetParser extends Parser { [], ListSeparator.undecided, scanner.spanFrom(start)); } - var first = expressionUntilComma(); + var first = expressionUntilComma(consumeNewlines: true); if (scanner.scanChar($colon)) { whitespace(consumeNewlines: true); return _map(first, start); @@ -2245,7 +2241,7 @@ abstract class StylesheetParser extends Parser { var expressions = [first]; while (true) { if (!_lookingAtExpression()) break; - expressions.add(expressionUntilComma()); + expressions.add(expressionUntilComma(consumeNewlines: true)); if (!scanner.scanChar($comma)) break; whitespace(consumeNewlines: true); } @@ -2264,16 +2260,16 @@ abstract class StylesheetParser extends Parser { /// as the expression before the colon and [start] the point before the /// opening parenthesis. MapExpression _map(Expression first, LineScannerState start) { - var pairs = [(first, expressionUntilComma())]; + var pairs = [(first, expressionUntilComma(consumeNewlines: true))]; while (scanner.scanChar($comma)) { whitespace(consumeNewlines: true); if (!_lookingAtExpression()) break; - var key = expressionUntilComma(); + var key = expressionUntilComma(consumeNewlines: true); scanner.expectChar($colon); whitespace(consumeNewlines: true); - var value = expressionUntilComma(); + var value = expressionUntilComma(consumeNewlines: true); pairs.add((key, value)); } @@ -2907,10 +2903,9 @@ abstract class StylesheetParser extends Parser { buffer.writeCharCode(bracket); brackets.add(opposite(bracket)); - case $rparen || $rbracket: + case ($rparen || $rbracket) && var char: if (brackets.isEmpty) { - scanner.error( - 'Unexpected "${String.fromCharCode(scanner.peekChar()!)}".'); + scanner.error('Unexpected "${String.fromCharCode(char!)}".'); } var bracket = brackets.removeLast(); scanner.expectChar(bracket); From ea8b098aedf395acd7c9c73f807813dc03de9f6d Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Fri, 3 Jan 2025 10:13:21 -0500 Subject: [PATCH 55/58] Update comments --- lib/src/parse/parser.dart | 15 +++++++++------ lib/src/parse/sass.dart | 4 ++-- lib/src/parse/stylesheet.dart | 10 ++++++---- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index 137027870..e573187f6 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -75,8 +75,9 @@ class Parser { /// Consumes whitespace, including any comments. /// - /// If [consumeNewlines] is true, the indented syntax will consume newlines as - /// whitespace in positions when a statement can't end. + /// If [consumeNewlines] is `true`, the indented syntax will consume newlines + /// as whitespace. It should only be set to `true` in positions when a + /// statement can't end. @protected void whitespace({required bool consumeNewlines}) { do { @@ -86,8 +87,9 @@ class Parser { /// Consumes whitespace, but not comments. /// - /// If [consumeNewlines] is true, the indented syntax will consume newlines as - /// whitespace in positions when a statement can't end. + /// If [consumeNewlines] is `true`, the indented syntax will consume newlines + /// as whitespace. It should only be set to `true` in positions when a + /// statement can't end. @protected void whitespaceWithoutComments({required bool consumeNewlines}) { while (!scanner.isDone && scanner.peekChar().isWhitespace) { @@ -123,8 +125,9 @@ class Parser { /// Like [whitespace], but throws an error if no whitespace is consumed. /// - /// If [consumeNewlines] is true, the indented syntax will consume newlines as - /// whitespace in positions when a statement can't end. + /// If [consumeNewlines] is `true`, the indented syntax will consume newlines + /// as whitespace. It should only be set to `true` in positions when a + /// statement can't end. @protected void expectWhitespace({bool consumeNewlines = false}) { if (scanner.isDone || !(scanner.peekChar().isWhitespace || scanComment())) { diff --git a/lib/src/parse/sass.dart b/lib/src/parse/sass.dart index fa3da5f67..406011445 100644 --- a/lib/src/parse/sass.dart +++ b/lib/src/parse/sass.dart @@ -313,8 +313,8 @@ class SassParser extends StylesheetParser { } void whitespaceWithoutComments({required bool consumeNewlines}) { - // This overrides whitespace consumption so that it doesn't consume - // newlines where that would cause a statement to end. + // This overrides whitespace consumption to only consume newlines when + // `consumeNewlines` is true. while (!scanner.isDone) { var next = scanner.peekChar(); if (consumeNewlines ? !next.isWhitespace : !next.isSpaceOrTab) break; diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 9fcc6c391..baa50496c 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -2163,8 +2163,9 @@ abstract class StylesheetParser extends Parser { /// If [singleEquals] is true, this will allow the Microsoft-style `=` /// operator at the top level. /// - /// If [consumeNewlines] is true, the indented syntax will consume newlines as - /// whitespace in positions when a statement can't end. + /// If [consumeNewlines] is `true`, the indented syntax will consume newlines + /// as whitespace. It should only be set to `true` in positions when a + /// statement can't end. Expression expressionUntilComma( {bool singleEquals = false, bool consumeNewlines = false}) => _expression( @@ -2941,8 +2942,9 @@ abstract class StylesheetParser extends Parser { /// comments. Otherwise, it will preserve two adjacent slashes and emit them /// to CSS. /// - /// If [consumeNewlines] is true, the indented syntax will consume newlines as - /// whitespace in positions when a statement can't end. + /// If [consumeNewlines] is `true`, the indented syntax will consume newlines + /// as whitespace. It should only be set to `true` in positions when a + /// statement can't end. /// /// Unlike [declarationValue], this allows interpolation. Interpolation _interpolatedDeclarationValue( From 523e38b10b441a465d393d3fba30aa9fea52379f Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 13 Jan 2025 16:47:22 -0800 Subject: [PATCH 56/58] Style changes --- CHANGELOG.md | 7 ++++--- lib/src/parse/stylesheet.dart | 10 +++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b8fe8189..6cf85eb40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ ## 1.84.0-dev * Allow newlines in whitespace in the indented syntax. -* **Potentially breaking bug fix**: Selectors with interpolations containing - unmatched brackets previously parse, but now error. For example, - `[foo#{"]:is(bar"}) {a: b}` will now throw an error. + +* **Potentially breaking bug fix**: Selectors with unmatched brackets now always + produce a parser error. Previously, some edge cases like `[foo#{"]:is(bar"}) {a: + b}` would compile without error, but this was an unintentional bug. ## 1.83.1 diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index baa50496c..8f3b6c96f 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -1756,9 +1756,9 @@ abstract class StylesheetParser extends Parser { /// still be a valid expression. When it returns `true`, this returns the /// expression. /// - /// If [consumeNewlines] is true, the indented syntax will consume newlines as - /// whitespace at the top level of the expression, outside of bracketed - /// subexpressions, in positions when a statement can't end. + /// If [consumeNewlines] is `true`, the indented syntax will consume newlines + /// as whitespace. It should only be set to `true` in positions when a + /// statement can't end. @protected Expression _expression( {bool bracketList = false, @@ -2904,9 +2904,9 @@ abstract class StylesheetParser extends Parser { buffer.writeCharCode(bracket); brackets.add(opposite(bracket)); - case ($rparen || $rbracket) && var char: + case ($rparen || $rbracket) && var char?: if (brackets.isEmpty) { - scanner.error('Unexpected "${String.fromCharCode(char!)}".'); + scanner.error('Unexpected "${String.fromCharCode(char)}".'); } var bracket = brackets.removeLast(); scanner.expectChar(bracket); From e1df2ad69f4f4c93cecc1bbe2f2a8e6c12721b0f Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Tue, 28 Jan 2025 13:19:59 -0500 Subject: [PATCH 57/58] expressionUntilComma always consumes whitespace --- lib/src/parse/stylesheet.dart | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 8f3b6c96f..c75a3df53 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -1538,7 +1538,7 @@ abstract class StylesheetParser extends Parser { scanner.expectChar($colon); whitespace(consumeNewlines: true); - var expression = expressionUntilComma(consumeNewlines: true); + var expression = expressionUntilComma(); var guarded = false; var flagStart = scanner.state; @@ -2158,20 +2158,17 @@ abstract class StylesheetParser extends Parser { } } - /// Consumes an expression until it reaches a top-level comma. + /// Consumes an expression, inlcuding newlines, until it reaches a top-level + /// comma. /// /// If [singleEquals] is true, this will allow the Microsoft-style `=` /// operator at the top level. - /// - /// If [consumeNewlines] is `true`, the indented syntax will consume newlines - /// as whitespace. It should only be set to `true` in positions when a - /// statement can't end. - Expression expressionUntilComma( - {bool singleEquals = false, bool consumeNewlines = false}) => - _expression( - singleEquals: singleEquals, - consumeNewlines: consumeNewlines, - until: () => scanner.peekChar() == $comma); + Expression expressionUntilComma({bool singleEquals = false}) { + return _expression( + singleEquals: singleEquals, + consumeNewlines: true, + until: () => scanner.peekChar() == $comma); + } /// Whether [expression] is allowed as an operand of a `/` expression that /// produces a potentially slash-separated number. @@ -2227,7 +2224,7 @@ abstract class StylesheetParser extends Parser { [], ListSeparator.undecided, scanner.spanFrom(start)); } - var first = expressionUntilComma(consumeNewlines: true); + var first = expressionUntilComma(); if (scanner.scanChar($colon)) { whitespace(consumeNewlines: true); return _map(first, start); @@ -2242,7 +2239,7 @@ abstract class StylesheetParser extends Parser { var expressions = [first]; while (true) { if (!_lookingAtExpression()) break; - expressions.add(expressionUntilComma(consumeNewlines: true)); + expressions.add(expressionUntilComma()); if (!scanner.scanChar($comma)) break; whitespace(consumeNewlines: true); } @@ -2261,16 +2258,16 @@ abstract class StylesheetParser extends Parser { /// as the expression before the colon and [start] the point before the /// opening parenthesis. MapExpression _map(Expression first, LineScannerState start) { - var pairs = [(first, expressionUntilComma(consumeNewlines: true))]; + var pairs = [(first, expressionUntilComma())]; while (scanner.scanChar($comma)) { whitespace(consumeNewlines: true); if (!_lookingAtExpression()) break; - var key = expressionUntilComma(consumeNewlines: true); + var key = expressionUntilComma(); scanner.expectChar($colon); whitespace(consumeNewlines: true); - var value = expressionUntilComma(consumeNewlines: true); + var value = expressionUntilComma(); pairs.add((key, value)); } From 7c6d741430ffcd60a0b6cbcd61acf6ff842b41e9 Mon Sep 17 00:00:00 2001 From: James Stuckey Weber Date: Wed, 29 Jan 2025 18:55:24 -0500 Subject: [PATCH 58/58] Fix changelog --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dd203db9..e7a78d68a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,9 @@ * **Potentially breaking bug fix**: Selectors with unmatched brackets now always produce a parser error. Previously, some edge cases like `[foo#{"]:is(bar"}) {a: b}` would compile without error, but this was an unintentional bug. -## 1.83.5-dev * Fix the error message for `@extend` without a selector and possibly other - parsing edge-cases in contexts that allow interpolation.. + parsing edge-cases in contexts that allow interpolation. ## 1.83.4