mirror of
https://github.com/danog/dart-sass.git
synced 2024-12-03 10:08:01 +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`.
|
/// The number's unit, or `null`.
|
||||||
final String unit;
|
final String unit;
|
||||||
|
|
||||||
|
/// Whether the number produced should retain its original representation.
|
||||||
|
final bool hasOriginal;
|
||||||
|
|
||||||
final FileSpan span;
|
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) =>
|
/*=T*/ accept/*<T>*/(ExpressionVisitor/*<T>*/ visitor) =>
|
||||||
visitor.visitNumberExpression(this);
|
visitor.visitNumberExpression(this);
|
||||||
|
@ -45,6 +45,9 @@ abstract class StylesheetParser extends Parser {
|
|||||||
/// or `@each`.
|
/// or `@each`.
|
||||||
var _inControlDirective = false;
|
var _inControlDirective = false;
|
||||||
|
|
||||||
|
/// Whether the parser is currently within a parenthesized expression.
|
||||||
|
var _inParentheses = false;
|
||||||
|
|
||||||
StylesheetParser(String contents, {url}) : super(contents, url: url);
|
StylesheetParser(String contents, {url}) : super(contents, url: url);
|
||||||
|
|
||||||
// ## Statements
|
// ## Statements
|
||||||
@ -1229,38 +1232,44 @@ abstract class StylesheetParser extends Parser {
|
|||||||
|
|
||||||
/// Consumes a parenthesized expression.
|
/// Consumes a parenthesized expression.
|
||||||
Expression _parentheses() {
|
Expression _parentheses() {
|
||||||
var start = scanner.state;
|
try {
|
||||||
scanner.expectChar($lparen);
|
_inParentheses = true;
|
||||||
whitespace();
|
var start = scanner.state;
|
||||||
if (!_lookingAtExpression()) {
|
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);
|
scanner.expectChar($rparen);
|
||||||
return new ListExpression([], ListSeparator.undecided,
|
return new ListExpression(expressions, ListSeparator.comma,
|
||||||
span: scanner.spanFrom(start));
|
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.
|
/// Consumes a map expression.
|
||||||
@ -1444,7 +1453,7 @@ abstract class StylesheetParser extends Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new NumberExpression(sign * number, scanner.spanFrom(start),
|
return new NumberExpression(sign * number, scanner.spanFrom(start),
|
||||||
unit: unit);
|
unit: unit, original: !_inParentheses);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes a variable expression.
|
/// Consumes a variable expression.
|
||||||
|
@ -281,6 +281,11 @@ abstract class Value {
|
|||||||
/// The SassScript unary `not` operation.
|
/// The SassScript unary `not` operation.
|
||||||
Value unaryNot() => sassFalse;
|
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].
|
/// Returns a valid CSS representation of [this].
|
||||||
///
|
///
|
||||||
/// Throws a [SassScriptException] if [this] can't be represented in plain
|
/// Throws a [SassScriptException] if [this] can't be represented in plain
|
||||||
|
@ -161,6 +161,28 @@ class SassNumber extends Value {
|
|||||||
/// This number's denominator units.
|
/// This number's denominator units.
|
||||||
final List<String> denominatorUnits;
|
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.
|
/// Whether [this] has any units.
|
||||||
bool get hasUnits => numeratorUnits.isNotEmpty || denominatorUnits.isNotEmpty;
|
bool get hasUnits => numeratorUnits.isNotEmpty || denominatorUnits.isNotEmpty;
|
||||||
|
|
||||||
@ -181,14 +203,27 @@ class SassNumber extends Value {
|
|||||||
String get unitString =>
|
String get unitString =>
|
||||||
hasUnits ? _unitString(numeratorUnits, denominatorUnits) : '';
|
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.
|
/// This matches the numbers that can be written as literals.
|
||||||
/// [SassNumber.withUnits] can be used to construct more complex units.
|
/// [SassNumber.withUnits] can be used to construct more complex units.
|
||||||
SassNumber(num value, [String unit])
|
SassNumber(this.value, [String unit])
|
||||||
: this.withUnits(value, numeratorUnits: unit == null ? null : [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,
|
SassNumber.withUnits(this.value,
|
||||||
{Iterable<String> numeratorUnits, Iterable<String> denominatorUnits})
|
{Iterable<String> numeratorUnits, Iterable<String> denominatorUnits})
|
||||||
: numeratorUnits = numeratorUnits == null
|
: numeratorUnits = numeratorUnits == null
|
||||||
@ -196,11 +231,28 @@ class SassNumber extends Value {
|
|||||||
: new List.unmodifiable(numeratorUnits),
|
: new List.unmodifiable(numeratorUnits),
|
||||||
denominatorUnits = denominatorUnits == null
|
denominatorUnits = denominatorUnits == null
|
||||||
? const []
|
? 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) =>
|
/*=T*/ accept/*<T>*/(ValueVisitor/*<T>*/ visitor) =>
|
||||||
visitor.visitNumber(this);
|
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;
|
SassNumber assertNumber([String name]) => this;
|
||||||
|
|
||||||
/// Returns [value] as an [int], if it's an integer value according to
|
/// 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) {
|
Value dividedBy(Value other) {
|
||||||
if (other is SassNumber) {
|
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);
|
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);
|
if (other is! SassColor) super.dividedBy(other);
|
||||||
throw new SassScriptException('Undefined operation "$this / $other".');
|
throw new SassScriptException('Undefined operation "$this / $other".');
|
||||||
|
@ -622,7 +622,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void visitVariableDeclaration(VariableDeclaration node) {
|
void visitVariableDeclaration(VariableDeclaration node) {
|
||||||
_environment.setVariable(node.name, node.expression.accept(this),
|
_environment.setVariable(
|
||||||
|
node.name, node.expression.accept(this).withoutOriginal(),
|
||||||
global: node.isGlobal);
|
global: node.isGlobal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -732,8 +733,9 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
|
|
||||||
SassNull visitNullExpression(NullExpression node) => sassNull;
|
SassNull visitNullExpression(NullExpression node) => sassNull;
|
||||||
|
|
||||||
SassNumber visitNumberExpression(NumberExpression node) =>
|
SassNumber visitNumberExpression(NumberExpression node) => node.hasOriginal
|
||||||
new SassNumber(node.value, node.unit);
|
? new SassNumber.withOriginal(node.value, node.unit)
|
||||||
|
: new SassNumber(node.value, node.unit);
|
||||||
|
|
||||||
SassColor visitColorExpression(ColorExpression node) => node.value;
|
SassColor visitColorExpression(ColorExpression node) => node.value;
|
||||||
|
|
||||||
@ -761,7 +763,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
var function = _environment.getFunction(plainName);
|
var function = _environment.getFunction(plainName);
|
||||||
if (function != null) {
|
if (function != null) {
|
||||||
if (function is BuiltInCallable) {
|
if (function is BuiltInCallable) {
|
||||||
return _runBuiltInCallable(node, function);
|
return _runBuiltInCallable(node, function).withoutOriginal();
|
||||||
} else if (function is UserDefinedCallable) {
|
} else if (function is UserDefinedCallable) {
|
||||||
return _runUserDefinedCallable(node, function, () {
|
return _runUserDefinedCallable(node, function, () {
|
||||||
for (var statement in function.declaration.children) {
|
for (var statement in function.declaration.children) {
|
||||||
@ -771,7 +773,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
|
|
||||||
throw _exception("Function finished without @return.",
|
throw _exception("Function finished without @return.",
|
||||||
function.declaration.span);
|
function.declaration.span);
|
||||||
});
|
}).withoutOriginal();
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -353,6 +353,11 @@ class _SerializeCssVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
void visitNumber(SassNumber value) {
|
void visitNumber(SassNumber value) {
|
||||||
|
if (value.isSlashSeparated) {
|
||||||
|
_buffer.write(value.original);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_writeNumber(value.value);
|
_writeNumber(value.value);
|
||||||
|
|
||||||
if (!_inspect) {
|
if (!_inspect) {
|
||||||
|
Loading…
Reference in New Issue
Block a user