mirror of
https://github.com/danog/dart-sass.git
synced 2024-12-02 09:37:49 +01:00
Store numbers' original values.
This commit is contained in:
parent
d5aa9e7f82
commit
8969fc75f2
@ -15,9 +15,13 @@ class NumberExpression implements Expression {
|
||||
/// The number's unit, or `null`.
|
||||
final String unit;
|
||||
|
||||
/// Whether the number produced should retain its original representation.
|
||||
final bool hasOriginal;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
NumberExpression(this.value, this.span, {this.unit});
|
||||
NumberExpression(this.value, this.span, {this.unit, bool original: false})
|
||||
: hasOriginal = original;
|
||||
|
||||
/*=T*/ accept/*<T>*/(ExpressionVisitor/*<T>*/ visitor) =>
|
||||
visitor.visitNumberExpression(this);
|
||||
|
@ -45,6 +45,9 @@ abstract class StylesheetParser extends Parser {
|
||||
/// or `@each`.
|
||||
var _inControlDirective = false;
|
||||
|
||||
/// Whether the parser is currently within a parenthesized expression.
|
||||
var _inParentheses = false;
|
||||
|
||||
StylesheetParser(String contents, {url}) : super(contents, url: url);
|
||||
|
||||
// ## Statements
|
||||
@ -1229,38 +1232,44 @@ abstract class StylesheetParser extends Parser {
|
||||
|
||||
/// Consumes a parenthesized expression.
|
||||
Expression _parentheses() {
|
||||
var start = scanner.state;
|
||||
scanner.expectChar($lparen);
|
||||
whitespace();
|
||||
if (!_lookingAtExpression()) {
|
||||
try {
|
||||
_inParentheses = true;
|
||||
var start = scanner.state;
|
||||
scanner.expectChar($lparen);
|
||||
whitespace();
|
||||
if (!_lookingAtExpression()) {
|
||||
scanner.expectChar($rparen);
|
||||
return new ListExpression([], ListSeparator.undecided,
|
||||
span: scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
var first = _expressionUntilComma();
|
||||
if (scanner.scanChar($colon)) {
|
||||
whitespace();
|
||||
_inParentheses = false;
|
||||
return _map(first, start);
|
||||
}
|
||||
|
||||
if (!scanner.scanChar($comma)) {
|
||||
scanner.expectChar($rparen);
|
||||
return first;
|
||||
}
|
||||
whitespace();
|
||||
|
||||
var expressions = [first];
|
||||
while (true) {
|
||||
if (!_lookingAtExpression()) break;
|
||||
expressions.add(_expressionUntilComma());
|
||||
if (!scanner.scanChar($comma)) break;
|
||||
whitespace();
|
||||
}
|
||||
|
||||
scanner.expectChar($rparen);
|
||||
return new ListExpression([], ListSeparator.undecided,
|
||||
return new ListExpression(expressions, ListSeparator.comma,
|
||||
span: scanner.spanFrom(start));
|
||||
} finally {
|
||||
_inParentheses = false;
|
||||
}
|
||||
|
||||
var first = _expressionUntilComma();
|
||||
if (scanner.scanChar($colon)) {
|
||||
whitespace();
|
||||
return _map(first, start);
|
||||
}
|
||||
|
||||
if (!scanner.scanChar($comma)) {
|
||||
scanner.expectChar($rparen);
|
||||
return first;
|
||||
}
|
||||
whitespace();
|
||||
|
||||
var expressions = [first];
|
||||
while (true) {
|
||||
if (!_lookingAtExpression()) break;
|
||||
expressions.add(_expressionUntilComma());
|
||||
if (!scanner.scanChar($comma)) break;
|
||||
whitespace();
|
||||
}
|
||||
|
||||
scanner.expectChar($rparen);
|
||||
return new ListExpression(expressions, ListSeparator.comma,
|
||||
span: scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
/// Consumes a map expression.
|
||||
@ -1444,7 +1453,7 @@ abstract class StylesheetParser extends Parser {
|
||||
}
|
||||
|
||||
return new NumberExpression(sign * number, scanner.spanFrom(start),
|
||||
unit: unit);
|
||||
unit: unit, original: !_inParentheses);
|
||||
}
|
||||
|
||||
/// Consumes a variable expression.
|
||||
|
@ -281,6 +281,11 @@ abstract class Value {
|
||||
/// The SassScript unary `not` operation.
|
||||
Value unaryNot() => sassFalse;
|
||||
|
||||
/// Returns a copy of [this] without [SassNumber.original] set.
|
||||
///
|
||||
/// If this isn't a [SassNumber], returns it as-is.
|
||||
Value withoutOriginal() => this;
|
||||
|
||||
/// Returns a valid CSS representation of [this].
|
||||
///
|
||||
/// Throws a [SassScriptException] if [this] can't be represented in plain
|
||||
|
@ -161,6 +161,28 @@ class SassNumber extends Value {
|
||||
/// This number's denominator units.
|
||||
final List<String> denominatorUnits;
|
||||
|
||||
/// Whether this should be represented as a slash-separated series of numbers.
|
||||
///
|
||||
/// This is `true` if and only if [original] contains a slash.
|
||||
bool get isSlashSeparated => _hasOriginal && _original != null;
|
||||
|
||||
/// The original representation of the number.
|
||||
///
|
||||
/// This is used to preserve slash-separated numbers in some contexts.
|
||||
String get original {
|
||||
if (_original != null) return _original;
|
||||
if (_hasOriginal) return toCssString();
|
||||
return _original;
|
||||
}
|
||||
|
||||
final String _original;
|
||||
|
||||
/// Whether this number still has its original representation.
|
||||
///
|
||||
/// This is separate from [_original] so that we can avoid eagerly converting
|
||||
/// all number literals to strings.
|
||||
final bool _hasOriginal;
|
||||
|
||||
/// Whether [this] has any units.
|
||||
bool get hasUnits => numeratorUnits.isNotEmpty || denominatorUnits.isNotEmpty;
|
||||
|
||||
@ -181,14 +203,27 @@ class SassNumber extends Value {
|
||||
String get unitString =>
|
||||
hasUnits ? _unitString(numeratorUnits, denominatorUnits) : '';
|
||||
|
||||
/// Returns a number, optionally with a single numerator unit.
|
||||
/// Creates a number, optionally with a single numerator unit.
|
||||
///
|
||||
/// This matches the numbers that can be written as literals.
|
||||
/// [SassNumber.withUnits] can be used to construct more complex units.
|
||||
SassNumber(num value, [String unit])
|
||||
: this.withUnits(value, numeratorUnits: unit == null ? null : [unit]);
|
||||
SassNumber(this.value, [String unit])
|
||||
: numeratorUnits =
|
||||
unit == null ? const [] : new List.unmodifiable([unit]),
|
||||
denominatorUnits = const [],
|
||||
_original = null,
|
||||
_hasOriginal = false;
|
||||
|
||||
/// Returns a number with full [numeratorUnits] and [denominatorUnits].
|
||||
/// Like [new SassNumber], but sets [original] based on the string
|
||||
/// representation of the number.
|
||||
SassNumber.withOriginal(this.value, [String unit])
|
||||
: numeratorUnits =
|
||||
unit == null ? const [] : new List.unmodifiable([unit]),
|
||||
denominatorUnits = const [],
|
||||
_original = null,
|
||||
_hasOriginal = true;
|
||||
|
||||
/// Creates a number with full [numeratorUnits] and [denominatorUnits].
|
||||
SassNumber.withUnits(this.value,
|
||||
{Iterable<String> numeratorUnits, Iterable<String> denominatorUnits})
|
||||
: numeratorUnits = numeratorUnits == null
|
||||
@ -196,11 +231,28 @@ class SassNumber extends Value {
|
||||
: new List.unmodifiable(numeratorUnits),
|
||||
denominatorUnits = denominatorUnits == null
|
||||
? const []
|
||||
: new List.unmodifiable(denominatorUnits);
|
||||
: new List.unmodifiable(denominatorUnits),
|
||||
_original = null,
|
||||
_hasOriginal = false;
|
||||
|
||||
SassNumber._(this.value, this.numeratorUnits, this.denominatorUnits,
|
||||
[String original])
|
||||
: _original = original,
|
||||
_hasOriginal = original != null;
|
||||
|
||||
/*=T*/ accept/*<T>*/(ValueVisitor/*<T>*/ visitor) =>
|
||||
visitor.visitNumber(this);
|
||||
|
||||
/// Returns a copy of [this] without [original] set.
|
||||
SassNumber withoutOriginal() {
|
||||
if (!_hasOriginal) return this;
|
||||
return new SassNumber._(value, numeratorUnits, denominatorUnits);
|
||||
}
|
||||
|
||||
/// Returns a copy of [this] with [this.original] set to [original].
|
||||
SassNumber _withOriginal(String original) =>
|
||||
new SassNumber._(value, numeratorUnits, denominatorUnits, original);
|
||||
|
||||
SassNumber assertNumber([String name]) => this;
|
||||
|
||||
/// Returns [value] as an [int], if it's an integer value according to
|
||||
@ -403,8 +455,10 @@ class SassNumber extends Value {
|
||||
|
||||
Value dividedBy(Value other) {
|
||||
if (other is SassNumber) {
|
||||
return _multiplyUnits(this.value / other.value, this.numeratorUnits,
|
||||
var result = _multiplyUnits(this.value / other.value, this.numeratorUnits,
|
||||
this.denominatorUnits, other.denominatorUnits, other.numeratorUnits);
|
||||
if (!this._hasOriginal || !other._hasOriginal) return result;
|
||||
return result._withOriginal("${this.original}/${other.original}");
|
||||
}
|
||||
if (other is! SassColor) super.dividedBy(other);
|
||||
throw new SassScriptException('Undefined operation "$this / $other".');
|
||||
|
@ -622,7 +622,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}
|
||||
|
||||
void visitVariableDeclaration(VariableDeclaration node) {
|
||||
_environment.setVariable(node.name, node.expression.accept(this),
|
||||
_environment.setVariable(
|
||||
node.name, node.expression.accept(this).withoutOriginal(),
|
||||
global: node.isGlobal);
|
||||
}
|
||||
|
||||
@ -732,8 +733,9 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
|
||||
SassNull visitNullExpression(NullExpression node) => sassNull;
|
||||
|
||||
SassNumber visitNumberExpression(NumberExpression node) =>
|
||||
new SassNumber(node.value, node.unit);
|
||||
SassNumber visitNumberExpression(NumberExpression node) => node.hasOriginal
|
||||
? new SassNumber.withOriginal(node.value, node.unit)
|
||||
: new SassNumber(node.value, node.unit);
|
||||
|
||||
SassColor visitColorExpression(ColorExpression node) => node.value;
|
||||
|
||||
@ -761,7 +763,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
var function = _environment.getFunction(plainName);
|
||||
if (function != null) {
|
||||
if (function is BuiltInCallable) {
|
||||
return _runBuiltInCallable(node, function);
|
||||
return _runBuiltInCallable(node, function).withoutOriginal();
|
||||
} else if (function is UserDefinedCallable) {
|
||||
return _runUserDefinedCallable(node, function, () {
|
||||
for (var statement in function.declaration.children) {
|
||||
@ -771,7 +773,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
|
||||
throw _exception("Function finished without @return.",
|
||||
function.declaration.span);
|
||||
});
|
||||
}).withoutOriginal();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -353,6 +353,11 @@ class _SerializeCssVisitor
|
||||
}
|
||||
|
||||
void visitNumber(SassNumber value) {
|
||||
if (value.isSlashSeparated) {
|
||||
_buffer.write(value.original);
|
||||
return;
|
||||
}
|
||||
|
||||
_writeNumber(value.value);
|
||||
|
||||
if (!_inspect) {
|
||||
|
Loading…
Reference in New Issue
Block a user