mirror of
https://github.com/less/less.js.git
synced 2026-05-01 03:00:22 -04:00
Merge branch 'mutually-exclusive-guards' of https://github.com/seven-phases-max/less.js
This commit is contained in:
@@ -232,6 +232,12 @@ tree.functions = {
|
||||
str = str.replace(/%%/g, '%');
|
||||
return new(tree.Quoted)('"' + str + '"', str);
|
||||
},
|
||||
default: function () {
|
||||
if (this.default.enabled) {
|
||||
return this.default.value
|
||||
? tree.True : tree.False;
|
||||
}
|
||||
},
|
||||
unit: function (val, unit) {
|
||||
if(!(val instanceof tree.Dimension)) {
|
||||
throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") };
|
||||
|
||||
@@ -20,6 +20,7 @@ tree.mixin.Call.prototype = {
|
||||
},
|
||||
eval: function (env) {
|
||||
var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule;
|
||||
var candidates = [], candidate, conditionResult = [], defaultFunc = tree.functions.default, defaultUsed = false;
|
||||
|
||||
args = this.arguments && this.arguments.map(function (a) {
|
||||
return { name: a.name, value: a.value.eval(env) };
|
||||
@@ -28,6 +29,13 @@ tree.mixin.Call.prototype = {
|
||||
for (i = 0; i < env.frames.length; i++) {
|
||||
if ((mixins = env.frames[i].find(this.selector)).length > 0) {
|
||||
isOneFound = true;
|
||||
defaultFunc.enabled = true;
|
||||
|
||||
// To make `default()` function independent of definition order we have two "subpasses" here.
|
||||
// At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
|
||||
// and build candidate list with corresponding flags. Then, when we know all possible matches,
|
||||
// we make a final decision.
|
||||
|
||||
for (m = 0; m < mixins.length; m++) {
|
||||
mixin = mixins[m];
|
||||
isRecursive = false;
|
||||
@@ -40,29 +48,70 @@ tree.mixin.Call.prototype = {
|
||||
if (isRecursive) {
|
||||
continue;
|
||||
}
|
||||
if (mixin.matchArgs(args, env)) {
|
||||
if (!mixin.matchCondition || mixin.matchCondition(args, env)) {
|
||||
try {
|
||||
if (!(mixin instanceof tree.mixin.Definition)) {
|
||||
mixin = new tree.mixin.Definition("", [], mixin.rules, null, false);
|
||||
mixin.originalRuleset = mixins[m].originalRuleset || mixins[m];
|
||||
}
|
||||
//if (this.important) {
|
||||
// isImportant = env.isImportant;
|
||||
// env.isImportant = true;
|
||||
//}
|
||||
Array.prototype.push.apply(
|
||||
rules, mixin.eval(env, args, this.important).rules);
|
||||
//if (this.important) {
|
||||
// env.isImportant = isImportant;
|
||||
//}
|
||||
} catch (e) {
|
||||
throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
|
||||
|
||||
if (mixin.matchArgs(args, env)) {
|
||||
candidate = {mixin: mixin};
|
||||
|
||||
if (mixin.matchCondition) {
|
||||
for (f = 0; f < 2; f++) {
|
||||
defaultFunc.value = f;
|
||||
conditionResult[f] = mixin.matchCondition(args, env);
|
||||
}
|
||||
if (conditionResult[0] || conditionResult[1]) {
|
||||
if (conditionResult[0] != conditionResult[1]) {
|
||||
if (defaultUsed) {
|
||||
// todo: ideally, it would make sense to also print the candidate
|
||||
// mixin definitions that cause the conflict (current one and the
|
||||
// mixin that set defaultUsed flag). But is there any easy method
|
||||
// to get their filename/line/index info here?
|
||||
throw { type: 'Runtime',
|
||||
message: 'Ambiguous use of `default()` found when matching for `'
|
||||
+ this.format(args) + '`',
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
}
|
||||
|
||||
defaultUsed = true;
|
||||
candidate.matchIfDefault = true;
|
||||
candidate.matchIfDefaultValue = conditionResult[1];
|
||||
}
|
||||
|
||||
candidates.push(candidate);
|
||||
}
|
||||
}
|
||||
else {
|
||||
candidates.push(candidate);
|
||||
}
|
||||
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
|
||||
defaultFunc.enabled = false;
|
||||
|
||||
for (m in candidates) {
|
||||
candidate = candidates[m];
|
||||
if (!candidate.matchIfDefault || (candidate.matchIfDefaultValue == (candidates.length == 1))) {
|
||||
try {
|
||||
mixin = candidate.mixin;
|
||||
if (!(mixin instanceof tree.mixin.Definition)) {
|
||||
mixin = new tree.mixin.Definition("", [], mixin.rules, null, false);
|
||||
mixin.originalRuleset = mixins[m].originalRuleset || mixins[m];
|
||||
}
|
||||
//if (this.important) {
|
||||
// isImportant = env.isImportant;
|
||||
// env.isImportant = true;
|
||||
//}
|
||||
Array.prototype.push.apply(
|
||||
rules, mixin.eval(env, args, this.important).rules);
|
||||
//if (this.important) {
|
||||
// env.isImportant = isImportant;
|
||||
//}
|
||||
} catch (e) {
|
||||
throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
if (!this.currentFileInfo || !this.currentFileInfo.reference) {
|
||||
for (i = 0; i < rules.length; i++) {
|
||||
@@ -78,26 +127,28 @@ tree.mixin.Call.prototype = {
|
||||
}
|
||||
if (isOneFound) {
|
||||
throw { type: 'Runtime',
|
||||
message: 'No matching definition was found for `' +
|
||||
this.selector.toCSS().trim() + '(' +
|
||||
(args ? args.map(function (a) {
|
||||
var argValue = "";
|
||||
if (a.name) {
|
||||
argValue += a.name + ":";
|
||||
}
|
||||
if (a.value.toCSS) {
|
||||
argValue += a.value.toCSS();
|
||||
} else {
|
||||
argValue += "???";
|
||||
}
|
||||
return argValue;
|
||||
}).join(', ') : "") + ")`",
|
||||
message: 'No matching definition was found for `' + this.format(args) + '`',
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
} else {
|
||||
throw { type: 'Name',
|
||||
message: this.selector.toCSS().trim() + " is undefined",
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
throw { type: 'Name',
|
||||
message: this.selector.toCSS().trim() + " is undefined",
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
}
|
||||
},
|
||||
format: function (args) {
|
||||
return this.selector.toCSS().trim() + '(' +
|
||||
(args ? args.map(function (a) {
|
||||
var argValue = "";
|
||||
if (a.name) {
|
||||
argValue += a.name + ":";
|
||||
}
|
||||
if (a.value.toCSS) {
|
||||
argValue += a.value.toCSS();
|
||||
} else {
|
||||
argValue += "???";
|
||||
}
|
||||
return argValue;
|
||||
}).join(', ') : "") + ")";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
114
test/css/mixins-guards-default-func.css
Normal file
114
test/css/mixins-guards-default-func.css
Normal file
@@ -0,0 +1,114 @@
|
||||
guard-default-basic-1-1 {
|
||||
case: 1;
|
||||
}
|
||||
guard-default-basic-1-2 {
|
||||
default: 2;
|
||||
}
|
||||
guard-default-basic-2-0 {
|
||||
default: 0;
|
||||
}
|
||||
guard-default-basic-2-2 {
|
||||
case: 2;
|
||||
}
|
||||
guard-default-basic-3-0 {
|
||||
default: 0;
|
||||
}
|
||||
guard-default-basic-3-2 {
|
||||
case: 2;
|
||||
}
|
||||
guard-default-basic-3-3 {
|
||||
case: 3;
|
||||
}
|
||||
guard-default-definition-order-0 {
|
||||
default: 0;
|
||||
}
|
||||
guard-default-definition-order-2 {
|
||||
case: 2;
|
||||
}
|
||||
guard-default-definition-order-2 {
|
||||
case: 3;
|
||||
}
|
||||
guard-default-out-of-guard-0 {
|
||||
case-0: default();
|
||||
case-1: 1;
|
||||
default: 2;
|
||||
case-2: default();
|
||||
}
|
||||
guard-default-out-of-guard-1 {
|
||||
default: default();
|
||||
}
|
||||
guard-default-out-of-guard-2 {
|
||||
default: default();
|
||||
}
|
||||
guard-default-expr-not-1 {
|
||||
case: 1;
|
||||
default: 1;
|
||||
}
|
||||
guard-default-expr-eq-true {
|
||||
case: true;
|
||||
}
|
||||
guard-default-expr-eq-false {
|
||||
case: false;
|
||||
default: false;
|
||||
}
|
||||
guard-default-expr-or-1 {
|
||||
case: 1;
|
||||
}
|
||||
guard-default-expr-or-2 {
|
||||
case: 2;
|
||||
default: 2;
|
||||
}
|
||||
guard-default-expr-or-3 {
|
||||
default: 3;
|
||||
}
|
||||
guard-default-expr-and-1 {
|
||||
case: 1;
|
||||
}
|
||||
guard-default-expr-and-2 {
|
||||
case: 2;
|
||||
}
|
||||
guard-default-expr-and-3 {
|
||||
default: 3;
|
||||
}
|
||||
guard-default-expr-always-1 {
|
||||
case: 1;
|
||||
default: 1;
|
||||
}
|
||||
guard-default-expr-always-2 {
|
||||
default: 2;
|
||||
}
|
||||
guard-default-expr-never-1 {
|
||||
case: 1;
|
||||
}
|
||||
guard-default-multi-1-0 {
|
||||
case: 0;
|
||||
}
|
||||
guard-default-multi-1-1 {
|
||||
default-1: 1;
|
||||
}
|
||||
guard-default-multi-2-1 {
|
||||
default-1: no;
|
||||
}
|
||||
guard-default-multi-2-2 {
|
||||
default-2: no;
|
||||
}
|
||||
guard-default-multi-2-3 {
|
||||
default-3: 3;
|
||||
}
|
||||
guard-default-multi-3-blue {
|
||||
case-2: #00008b;
|
||||
}
|
||||
guard-default-multi-3-green {
|
||||
default-color: #008000;
|
||||
}
|
||||
guard-default-multi-3-foo {
|
||||
case-1: I am 'foo';
|
||||
}
|
||||
guard-default-multi-3-baz {
|
||||
default-string: I am 'baz';
|
||||
}
|
||||
guard-default-multi-4 {
|
||||
always: 1;
|
||||
always: 2;
|
||||
case: 2;
|
||||
}
|
||||
9
test/less/errors/mixins-guards-default-func-1.less
Normal file
9
test/less/errors/mixins-guards-default-func-1.less
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
guard-default-func-conflict {
|
||||
.m(@x, @y) {}
|
||||
.m(@x, 2) when (default()) {}
|
||||
.m(@x, 2) when (default()) {}
|
||||
|
||||
.m(1, 1);
|
||||
.m(1, 2);
|
||||
}
|
||||
4
test/less/errors/mixins-guards-default-func-1.txt
Normal file
4
test/less/errors/mixins-guards-default-func-1.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
RuntimeError: Ambiguous use of `default()` found when matching for `.m(1, 2)` in {path}mixins-guards-default-func-1.less on line 8, column 5:
|
||||
7 .m(1, 1);
|
||||
8 .m(1, 2);
|
||||
9 }
|
||||
9
test/less/errors/mixins-guards-default-func-2.less
Normal file
9
test/less/errors/mixins-guards-default-func-2.less
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
guard-default-func-conflict {
|
||||
.m(1) {}
|
||||
.m(@x) when not(default()) {}
|
||||
.m(@x) when (@x = 3) and (default()) {}
|
||||
|
||||
.m(2);
|
||||
.m(3);
|
||||
}
|
||||
4
test/less/errors/mixins-guards-default-func-2.txt
Normal file
4
test/less/errors/mixins-guards-default-func-2.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
RuntimeError: Ambiguous use of `default()` found when matching for `.m(3)` in {path}mixins-guards-default-func-2.less on line 8, column 5:
|
||||
7 .m(2);
|
||||
8 .m(3);
|
||||
9 }
|
||||
159
test/less/mixins-guards-default-func.less
Normal file
159
test/less/mixins-guards-default-func.less
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
// basics:
|
||||
|
||||
guard-default-basic-1 {
|
||||
.m(1) {case: 1}
|
||||
.m(@x) when (default()) {default: @x}
|
||||
|
||||
&-1 {.m(1)}
|
||||
&-2 {.m(2)}
|
||||
}
|
||||
|
||||
guard-default-basic-2 {
|
||||
.m(1) {case: 1}
|
||||
.m(2) {case: 2}
|
||||
.m(3) {case: 3}
|
||||
.m(@x) when (default()) {default: @x}
|
||||
|
||||
&-0 {.m(0)}
|
||||
&-2 {.m(2)}
|
||||
}
|
||||
|
||||
guard-default-basic-3 {
|
||||
.m(@x) when (@x = 1) {case: 1}
|
||||
.m(2) {case: 2}
|
||||
.m(@x) when (@x = 3) {case: 3}
|
||||
.m(@x) when (default()) {default: @x}
|
||||
|
||||
&-0 {.m(0)}
|
||||
&-2 {.m(2)}
|
||||
&-3 {.m(3)}
|
||||
}
|
||||
|
||||
guard-default-definition-order {
|
||||
.m(@x) when (default()) {default: @x}
|
||||
.m(@x) when (@x = 1) {case: 1}
|
||||
.m(2) {case: 2}
|
||||
.m(@x) when (@x = 3) {case: 3}
|
||||
|
||||
&-0 {.m(0)}
|
||||
&-2 {.m(2)}
|
||||
&-2 {.m(3)}
|
||||
}
|
||||
|
||||
// out of guard:
|
||||
|
||||
guard-default-out-of-guard {
|
||||
.m(1) {case-1: 1}
|
||||
.m(@x: default()) when (default()) {default: @x}
|
||||
|
||||
&-0 {
|
||||
case-0: default();
|
||||
.m(1);
|
||||
.m(2);
|
||||
case-2: default();
|
||||
}
|
||||
&-1 {.m(default())}
|
||||
&-2 {.m()}
|
||||
}
|
||||
|
||||
// expressions:
|
||||
|
||||
guard-default-expr-not {
|
||||
.m(1) {case: 1}
|
||||
.m(@x) when not(default()) {default: @x}
|
||||
|
||||
&-1 {.m(1)}
|
||||
&-2 {.m(2)}
|
||||
}
|
||||
|
||||
guard-default-expr-eq {
|
||||
.m(@x) when (@x = true) {case: @x}
|
||||
.m(@x) when (@x = false) {case: @x}
|
||||
.m(@x) when (@x = default()) {default: @x}
|
||||
|
||||
&-true {.m(true)}
|
||||
&-false {.m(false)}
|
||||
}
|
||||
|
||||
guard-default-expr-or {
|
||||
.m(1) {case: 1}
|
||||
.m(2) {case: 2}
|
||||
.m(@x) when (default()), (@x = 2) {default: @x}
|
||||
|
||||
&-1 {.m(1)}
|
||||
&-2 {.m(2)}
|
||||
&-3 {.m(3)}
|
||||
}
|
||||
|
||||
guard-default-expr-and {
|
||||
.m(1) {case: 1}
|
||||
.m(2) {case: 2}
|
||||
.m(@x) when (default()) and (@x = 3) {default: @x}
|
||||
|
||||
&-1 {.m(1)}
|
||||
&-2 {.m(2)}
|
||||
&-3 {.m(3)}
|
||||
&-4 {.m(4)}
|
||||
}
|
||||
|
||||
guard-default-expr-always {
|
||||
.m(1) {case: 1}
|
||||
.m(@x) when (default()), not(default()) {default: @x} // always match
|
||||
|
||||
&-1 {.m(1)}
|
||||
&-2 {.m(2)}
|
||||
}
|
||||
|
||||
guard-default-expr-never {
|
||||
.m(1) {case: 1}
|
||||
.m(@x) when (default()) and not(default()) {default: @x} // never match
|
||||
|
||||
&-1 {.m(1)}
|
||||
&-2 {.m(2)}
|
||||
}
|
||||
|
||||
|
||||
// not conflicting multiple default() uses:
|
||||
|
||||
guard-default-multi-1 {
|
||||
.m(0) {case: 0}
|
||||
.m(@x) when (default()) {default-1: @x}
|
||||
.m(2) when (default()) {default-2: @x}
|
||||
|
||||
&-0 {.m(0)}
|
||||
&-1 {.m(1)}
|
||||
}
|
||||
|
||||
guard-default-multi-2 {
|
||||
.m(1, @x) when (default()) {default-1: @x}
|
||||
.m(2, @x) when (default()) {default-2: @x}
|
||||
.m(@x, yes) when (default()) {default-3: @x}
|
||||
|
||||
&-1 {.m(1, no)}
|
||||
&-2 {.m(2, no)}
|
||||
&-3 {.m(3, yes)}
|
||||
}
|
||||
|
||||
guard-default-multi-3 {
|
||||
.m(red) {case-1: darkred}
|
||||
.m(blue) {case-2: darkblue}
|
||||
.m(@x) when (iscolor(@x)) and (default()) {default-color: @x}
|
||||
.m('foo') {case-1: I am 'foo'}
|
||||
.m('bar') {case-2: I am 'bar'}
|
||||
.m(@x) when (isstring(@x)) and (default()) {default-string: I am @x}
|
||||
|
||||
&-blue {.m(blue)}
|
||||
&-green {.m(green)}
|
||||
&-foo {.m('foo')}
|
||||
&-baz {.m('baz')}
|
||||
}
|
||||
|
||||
guard-default-multi-4 {
|
||||
.m(@x) when (default()), not(default()) {always: @x}
|
||||
.m(@x) when (default()) and not(default()) {never: @x}
|
||||
.m(2) {case: 2}
|
||||
|
||||
.m(1);
|
||||
.m(2);
|
||||
}
|
||||
Reference in New Issue
Block a user