-
Notifications
You must be signed in to change notification settings - Fork 358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Indented syntax improvements] Dart implementation #2467
base: main
Are you sure you want to change the base?
Conversation
…d-syntax-improvements
…d-syntax-improvements
@nex3 This is ready for review. Here are some questions I wanted to flag as you review. Thanks!
|
…d-syntax-improvements
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nex3 This is ready for review. Here are some questions I wanted to flag as you review. Thanks!
allowNewlines
is set totrue
in some cases, like in parser* functions or inscss.dart
, where the value is non-impactful. Should this usage be differentiated somehow for clarity?
For classes where it's universally non-impactful, I'd define a local _whitespace()
function that takes no arguments with a comment explaining that the value isn't relevant.
- There are some places, like in
_atRootQuery
, whereallowNewlines
is true because it is inside parentheses. I opted to not use the_inParentheses
variable except for one place where the consumption did rely on it, in_supportsCondition
. Would it be more idiomatic to use_inParentheses
, even if the consumption is in the same function?
I think how it is now is better.
- In the
+
include syntax, I am not supporting whitespace after the+
as it can be part of a selector. This differs from the=
mixin syntax- should they be the same?
There's already an odd inconsistency here where whitespace is allowed after =
but not after +
, so I think it's fine to expand that to allowing newlines as well. It's possible that we should deprecate that, but it's not in-scope for this change.
- I added paren/bracket context to
almostAnyValue
, with the only change being that they must be matched. That means the error fora:has(d[)]
will be changed from "Expected identifer" to "Expected]
". SincealmostAnyValue
is used for more than selectors, should I make this functionality opt-in, dependent on a parameter? Based on the places where this is used, I don't foresee issues with bracket matching, but want to verify.
It's fine to change this error. Arguably the bracket error is clearer anyway.
allowNewlines: allowNewlines || | ||
bracketList || | ||
_inParentheses || | ||
wasInExpression); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this trigger on wasInExpression
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In reducing it to a minimal reproduction, this covers newlines after a value in a comma separated list that has a space separated list as a value. (This is a common syntax in grid and font declarations, for instance.)
For instance, this would be covered by wasInParentheses
.
$a: (b, e
)
However, this would not be covered by wasInParentheses
, but is covered by wasInExpression
.
$a: (b c, e
)
Is there a cleaner way of doing this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you pass consumeNewlines: true
into expressionUntilComma()
in parentheses()
, I think that's a much more explicit way of representing the logic that parenthesized lists should allow newlines everywhere. That should allow you to remove the _inParentheses
check as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nex3 This is ready for another round of review, along with the spec draft 1.2. Thanks!
allowNewlines: allowNewlines || | ||
bracketList || | ||
_inParentheses || | ||
wasInExpression); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In reducing it to a minimal reproduction, this covers newlines after a value in a comma separated list that has a space separated list as a value. (This is a common syntax in grid and font declarations, for instance.)
For instance, this would be covered by wasInParentheses
.
$a: (b, e
)
However, this would not be covered by wasInParentheses
, but is covered by wasInExpression
.
$a: (b c, e
)
Is there a cleaner way of doing this?
lib/src/parse/stylesheet.dart
Outdated
@@ -1064,7 +1074,7 @@ abstract class StylesheetParser extends Parser { | |||
ImportRule _importRule(LineScannerState start) { | |||
var imports = <Import>[]; | |||
do { | |||
whitespace(); | |||
whitespace(allowNewlines: false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain more what needs to be changed? As far as I can tell, allowNewlines is false throughout the import rule.
lib/src/parse/sass.dart
Outdated
switch (scanner.peekChar()) { | ||
case $semicolon | ||
when canEndInSemicolon && [$lf, $ff].contains(scanner.peekChar(1)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed my approach in f093d6a. I'd appreciate feedback on this approach.
Now, in the indented format, a call to expectStatementSeparator()
consumes ; // foo
before checking if atEndOfStatement()
. This deviates from SCSS, which consumes only the semicolon, but we need to move the scanner position to immediately before the newline in order to peekIndentation()
.
This required some minor tweaks to @debug
, for example, so that the scanner range matches SCSS, and doesn't include ; // foo
.
## 1.84.0-dev | ||
|
||
* Allow newlines in whitespace in the indented syntax. | ||
* **Potentially breaking bug fix**: Selectors with interpolations that render |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the term "render" is confusing here, because the issue is that the brackets are unmatched at the source level.
return scanner.scanChar($colon); | ||
} | ||
|
||
// ## Tokens | ||
|
||
/// 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is ambiguous between "consumeNewlines: true
should only be passed in positions where a statement can't end" and "this function will automatically ignore consumeNewlines: true
in positions where a statement can end". Probably worth rephrasing it to be explicit.
// This overrides whitespace consumption so that it doesn't consume | ||
// newlines. | ||
// newlines where that would cause a statement to end. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly to above, I think it makes more sense to phrase this in terms of the function's interface, so something like "when consumeNewlines
is false."
@@ -404,6 +394,7 @@ class SassParser extends StylesheetParser { | |||
} | |||
|
|||
var start = scanner.state; | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: churn
/// Consumes a semicolon and trailing whitespace, including comments. | ||
/// | ||
/// Returns whether a semicolon was consumed. | ||
bool _trailingSemicolon() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally if a production may or may not consume something and returns whether it did, we use a name that starts with "try".
allowNewlines: allowNewlines || | ||
bracketList || | ||
_inParentheses || | ||
wasInExpression); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you pass consumeNewlines: true
into expressionUntilComma()
in parentheses()
, I think that's a much more explicit way of representing the logic that parenthesized lists should allow newlines everywhere. That should allow you to remove the _inParentheses
check as well.
case $rparen || $rbracket: | ||
if (brackets.isEmpty) { | ||
scanner.error( | ||
'Unexpected "${String.fromCharCode(scanner.peekChar()!)}".'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, you can write case ($paren || $rbracket) && var char
and it'll bind it to char
.
Blocked until proposal is accepted.
[skip sass-embedded]