The automatic @charset adding is useful in general, but there are
consistently cases where it trips up naïve downstream tools. This
option makes it easier for users to control when it occurs.
The value of an `AtRule` can be null, so it should not be visited in that case.
Ran across this issue when I attempted to run the module migrator on a stylesheet containing `@font-face` (which has children, but no value).
This adds a --no-unicode option to disable Unicode span rendering,
decouples repl highlighting from SourceSpan.highlight, and updates
tests to work with the new error highlighting.
It also tightly scopes source spans for statements with children.
Previously, source spans for these nodes extended all the way through
any whitespace that followed the node. This led to messy-looking
multiline span highlights with dart-lang/source_span#25.
Now, StylesheetParser.children doesn't consume trailing whitespace.
Instead, we add a helper method StylesheetParser._withChildren that
parses children, creates the appropriate span, and then consumes the
trailing whitespace.
This codifies in types the constraint that only the evaluator should
modify CSS nodes. It also makes it possible to create non-mutable
classes that don't need to care about stuff like tracking parent nodes.
Previously, evaluator called BinaryOperationExpression.span for each
binary operation it evaluated, which in turn called spanForList() to
create a span covering both child expressions. spanForList() then
called .span for both the left and right child operations *twice*,
leading to exponential behavior.
This is now avoided in three complementary ways:
1. The evaluator avoids eagerly calling AstNode.span, instead keeping
the original AstNode until the span itself needs to be accessed.
This means that a span will only be accessed when an error actually
occurs, and then only one operation's span will be accessed.
2. BinaryOperationExpression.span now iterates through any child
operations before calling their .span methods, so it only performs
O(1) allocations.
3. spanForList() now only calls each AstNode.span once.
Getting all the tests update and the output looking nice is proving
more difficult than expected, and I want to unblock other pull
requests for Dart Sass in the meantime.