From d56feee6f0230b92f9909398ce7bbeb01554b91a Mon Sep 17 00:00:00 2001 From: cloudhead Date: Sun, 25 Apr 2010 00:09:40 -0400 Subject: [PATCH] Support for pattern-matching on mixin calls. --- lib/less/parser.js | 18 ++++++--- lib/less/tree/mixin.js | 37 +++++++++++++++--- lib/less/tree/rule.js | 3 +- lib/less/tree/ruleset.js | 5 ++- test/css/mixins-pattern.css | 33 ++++++++++++++++ test/less/mixins-pattern.less | 73 +++++++++++++++++++++++++++++++++++ 6 files changed, 155 insertions(+), 14 deletions(-) create mode 100644 test/css/mixins-pattern.css create mode 100644 test/less/mixins-pattern.less diff --git a/lib/less/parser.js b/lib/less/parser.js index 5ec25f3d..a135f059 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -533,15 +533,21 @@ less.Parser = function Parser(env) { if (match = $(/([#.][a-zA-Z0-9_-]+)\s*\(/g)) { name = match[1]; - while (param = $(/@[\w-]+/g)) { - if ($(':')) { - if (value = $(this.expression)) { - params.push({ name: param, value: value }); + while (param = $(/@[\w-]+/g) || $(this.entities.literal) + || $(this.entities.keyword)) { + // Variable + if (param[0] === '@') { + if ($(':')) { + if (value = $(this.expression)) { + params.push({ name: param, value: value }); + } else { + throw new(Error)("Expected value"); + } } else { - throw new(Error)("Expected value"); + params.push({ name: param }); } } else { - params.push({ name: param }); + params.push({ value: param }); } if (! $(',')) { break } } diff --git a/lib/less/tree/mixin.js b/lib/less/tree/mixin.js index 2431042a..30fb842e 100644 --- a/lib/less/tree/mixin.js +++ b/lib/less/tree/mixin.js @@ -12,8 +12,10 @@ tree.mixin.Call.prototype = { for (var i = 0; i < env.frames.length; i++) { if ((mixins = env.frames[i].find(this.selector)).length > 0) { for (var m = 0; m < mixins.length; m++) { - Array.prototype.push.apply( - rules, mixins[m].eval(this.arguments, env).rules); + if (mixins[m].match(this.arguments, env)) { + Array.prototype.push.apply( + rules, mixins[m].eval(this.arguments, env).rules); + } } return rules; } @@ -26,8 +28,13 @@ tree.mixin.Definition = function MixinDefinition(name, params, rules) { this.name = name; this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; this.params = params; + this.arity = params.length; this.rules = rules; this._lookups = {}; + this.required = params.reduce(function (count, p) { + if (p.name && p.value) { return count } + else { return count + 1 } + }, 0); }; tree.mixin.Definition.prototype = { toCSS: function () { return "" }, @@ -39,14 +46,32 @@ tree.mixin.Definition.prototype = { var frame = new(tree.Ruleset)(null, []), context; for (var i = 0, val; i < this.params.length; i++) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val)); - } else { - throw new(Error)("wrong number of arguments for " + this.name); + if (this.params[i].name) { + if (val = (args && args[i]) || this.params[i].value) { + frame.rules.unshift(new(tree.Rule)(this.params[i].name, val)); + } else { + throw new(Error)("wrong number of arguments for " + this.name); + } } } return new(tree.Ruleset)(null, this.rules).evalRules({ frames: [this, frame].concat(env.frames) }); + }, + match: function (args, env) { + var argsLength = (args && args.length) || 0; + + if (argsLength < this.required || argsLength > this.arity) { + return false; + } + + for (var i = 0; i < argsLength; i++) { + if (!this.params[i].name) { + if (args[i].toCSS(env) != this.params[i].value.toCSS(env)) { + return false; + } + } + } + return true; } }; diff --git a/lib/less/tree/rule.js b/lib/less/tree/rule.js index 563363d3..6fe2d394 100644 --- a/lib/less/tree/rule.js +++ b/lib/less/tree/rule.js @@ -27,7 +27,8 @@ tree.Value = function Value(value) { tree.Value.prototype = { eval: function (env) { if (this.value.length === 1) { - return this.value[0].eval(env); + return this.value[0].eval ? this.value[0].eval(env) + : this.value[0]; } else { return this; } diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index d94ad93c..2c362210 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -16,12 +16,15 @@ tree.Ruleset.prototype = { } else if (rule instanceof tree.mixin.Call) { Array.prototype.push.apply(rules, rule.eval(context)); } else { - rules.push(rule.eval(context)); + rules.push(rule.eval ? rule.eval(context) : rule); } }); this.rules = rules; return this; }, + match: function (args) { + return !args || args.length === 0; + }, variables: function (name) { if (this._variables) { return this._variables[name] } else { diff --git a/test/css/mixins-pattern.css b/test/css/mixins-pattern.css new file mode 100644 index 00000000..9672bc55 --- /dev/null +++ b/test/css/mixins-pattern.css @@ -0,0 +1,33 @@ +.zero { + zero: 0; + one: 1; + two: 2; + three: 3; +} +.one { + one: 1; + one-req: 1; + two: 2; + three: 3; +} +.two { + two: 2; + three: 3; +} +.three { + three: 3; +} +.left { + left: 1; +} +.right { + right: 1; +} +.border-right { + color: black; + border-right: 4px; +} +.border-left { + color: black; + border-left: 4px; +} diff --git a/test/less/mixins-pattern.less b/test/less/mixins-pattern.less new file mode 100644 index 00000000..57d0f06f --- /dev/null +++ b/test/less/mixins-pattern.less @@ -0,0 +1,73 @@ +.mixin () { + zero: 0; +} +.mixin (@a: 1px) { + one: 1; +} +.mixin (@a) { + one-req: 1; +} +.mixin (@a: 1px, @b: 2px) { + two: 2; +} + +.mixin (@a: 1px, @b: 2px, @c: 3px) { + three: 3; +} + +.zero { + .mixin(); +} + +.one { + .mixin(1); +} + +.two { + .mixin(1, 2); +} + +.three { + .mixin(1, 2, 3); +} + +// + +.mixout ('left') { + left: 1; +} + +.mixout ('right') { + right: 1; +} + +.left { + .mixout('left'); +} +.right { + .mixout('right'); +} +.none { + .mixout('top'); +} + +// + +.border (@side, @width) { + color: black; + .border-side(@side, @width); +} +.border-side (left, @w) { + border-left: @w; +} +.border-side (right, @w) { + border-right: @w; +} + +.border-right { + .border(right, 4px); +} +.border-left { + .border(left, 4px); +} +