diff --git a/lib/less/parser.js b/lib/less/parser.js index 94669292a..db650baa8 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -1122,7 +1122,7 @@ less.Parser = function Parser(env) { var parsers = parser.parsers, entities = parsers.entities, returner = { args:null, variadic: false }, expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; + isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg, expands; while (true) { if (isCall) { @@ -1171,14 +1171,20 @@ less.Parser = function Parser(env) { } value = expect(parsers.expression); nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; + } else if ($re(/^\.{3}/)) { + if (isCall) { + name = nameLoop = val.name; + expands = true; + } else { + returner.variadic = true; + if ($char(";") && !isSemiColonSeperated) { + isSemiColonSeperated = true; + } + (isSemiColonSeperated ? argsSemiColon : argsComma) + .push({ name: arg.name, variadic: true }); + break; } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; + } else if (!isCall) { name = nameLoop = val.name; value = null; @@ -1189,7 +1195,7 @@ less.Parser = function Parser(env) { expressions.push(value); } - argsComma.push({ name:nameLoop, value:value }); + argsComma.push({ name:nameLoop, value:value, expands:expands }); if ($char(',')) { continue; @@ -1206,9 +1212,10 @@ less.Parser = function Parser(env) { if (expressions.length > 1) { value = new(tree.Value)(expressions); } - argsSemiColon.push({ name:name, value:value }); + argsSemiColon.push({ name:name, value:value, expands:expands }); name = null; + expands = null; expressions = []; expressionContainsNamed = false; } diff --git a/lib/less/tree/mixin.js b/lib/less/tree/mixin.js index 3e9ff5c69..630f0a4bf 100644 --- a/lib/less/tree/mixin.js +++ b/lib/less/tree/mixin.js @@ -19,13 +19,30 @@ tree.mixin.Call.prototype = { } }, eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, + var mixins, mixin, args = [], rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; + defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, arg, argValue; - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); + this.arguments = this.arguments || []; + + // To call a mixin and "expand" a variable containing a list into multiple arguments we loop + // over `expanded` variables and add each entry in the list. + // Example: `.mixin(@rest...);` + for (i = 0; i < this.arguments.length; i++) { + arg = this.arguments[i]; + argValue = arg.value.eval(env); + if (arg.expands) { + if (argValue.value instanceof Array) { + for (m = 0; m < argValue.value.length; m++) { + args.push({value: argValue.value[m]}); + } + } else { + args.push({ value: argValue}); + } + } else { + args.push({ name: arg.name, value: argValue }); + } + } for (i = 0; i < env.frames.length; i++) { if ((mixins = env.frames[i].find(this.selector)).length > 0) { diff --git a/test/less/mixins-args.less b/test/less/mixins-args.less index 8cdc67df1..5a79f0686 100644 --- a/test/less/mixins-args.less +++ b/test/less/mixins-args.less @@ -212,4 +212,19 @@ body { div { .test-calling-one-arg-mixin(1); -} \ No newline at end of file +} + +.test-calling-with-ellipsis-variable { + .mixin-multi-arg(1; 2; 3; 4) { } + + @all: 1, 2, 3, 4; + .mixin-multi-arg(@all...); + + @first: 1, 2; + @last: 3, 4; + .mixin-multi-arg(@first...; @last...); + + @single: 1; + .mixin-multi-arg(@single...; 2; 3; 4); + .mixin-multi-arg(@single...; 2; @last...); +}