Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Implemented named arguments #268

Merged
merged 2 commits into from
@jamesfoster

Hey Alexis,

Here's an implementation of named parameters. For example:

.mixin(@a: 1, @b: 2, @c: 3, @d: 4, @e: 5) {
  ...
}

.class {
  .mixin(1, 2, 3, 4, 10);
  .mixin(@e: 10);
}

If you wanted to override the 5th argument you would need to know the default value for the first 4 before supplying the 5th and if the defaults changed you now have to find everywhere where you've done this.

Not good.

Now you just specify the name of the argument you want to override.

It might be nice to throw an error if you place any named arguments before any "positional" arguments. as it stands it works regardless but you may get unexpected results.

Cheers

James

@paulyoung

This is great!

@arharp

Yeah, this looks awesome.

@dimensia

Nice!

@jamesfoster

This should really be a second pull request. Just "fully" implemented the & parent selector.

Enjoy

@paulyoung

Another killer feature!

Here's the usage. This:

#box {
    #other-box {
        margin: 10px 0 0;
    }
}

.ie7 {
    #box {
        #other-box {
            margin: 5px 0 0;
        }
    }
}

Becomes:

#box {
    #other-box {
        margin: 10px 0 0;

        .ie7 & {
            margin: 5px 0 0;
        }
    }
}

This removes the need for duplicate rules in separate blocks, and also overcomes the need to match the structure exactly within those blocks to overcome specificity issues. e.g. This wouldn't have worked for the above:

.ie7 {
    #other-box {
        margin: 5px 0 0;
    }
}

Nice work!

@jamesfoster

Just rebased to make it easier to merge. Also squashed the named arguments feature into a single commit.

Original branch at jamesfoster@req268

@revolunet

OMG +1 great work

@cowboyd

This has been on my wish list for a very long time, and I'd love to see it in one form or another.

@thatdutchguy

Do want

@leeoniya

+1

@Evgenus

+1

@oknoway

Hard to think of anything I've wanted more than a parent selector. Great work!

@joeldbirch

Really looking forward to being able to use this.

@jamesfoster

Thanks for all the comments. This is one of those things I've wanted for a while myself too. my friend brought it up some time later and the solution just hit me.

Not sure what's holding up the merge but @cloudhead assures me it's coming.

@csnover

773229f applies cleanly against the current master but does not work (causes a JS error); 2e042e9 applies and works fine. I wonder if it would be easier to get this landed if it’s split into two pull reqs?

@jamesfoster

ok. thanks for this. I'll split the requests up and add the above case to the tests and fix it.

@cowboyd

@jamesfoster Your parent selector fixes were merged, but did you ever resubmit the named parameters enhancement. I really, really, really think that this is a killer feature, and deserves another PR.

@Spaxe

Any progress on this ticket?

@paulyoung

Would love to see named arguments added.

@donaldharvey

Currently working on an updated version of this here; will be submitting another pull request after I've tested it a bit more thoroughly.

@regisphilibert

Woah, I'm tweeting this, cheers mate!

@geddski

What ever happened to this? I found myself needing named arguments and wishing LESS had it. Seeing a pull request open for a year that has it is pretty frustrating. I see why people are switching to stylus/sass.

jamesfoster added some commits
@jamesfoster jamesfoster Implement named parameters 1857b7c
@jamesfoster jamesfoster Fix trailing space when parent selector is the last part of the selec…
…tor.

'''
.foo {
  .bar & {
    &:hover {
      color: orange;
    }
  }
}
'''

now outputs

'''
.bar .foo:hover {
  color: orange;
}
'''
1bb3dc1
@jamesfoster

Rebased and fixed issue with trailing space.

@cloudhead cloudhead merged commit a2df119 into less:master
@aaunel

+1

@matthew-dean

Named parameters? Hooray!

This breaks the mixins-args test. Fix pending

@jreading

This needs to be:

varargs.push(args[j].value.eval(env));

Hi John.

Thanks for catching this. I wasn't able to run the tests because I didn't have node.js. It would be great if someone could implement the tests using qunit or similar so they run in the browser!

@krnlde krnlde referenced this pull request in leafo/lessphp
Closed

Bootstrap 3 fails to compile #432

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 30, 2012
  1. @jamesfoster

    Implement named parameters

    jamesfoster authored
  2. @jamesfoster

    Fix trailing space when parent selector is the last part of the selec…

    jamesfoster authored
    …tor.
    
    '''
    .foo {
      .bar & {
        &:hover {
          color: orange;
        }
      }
    }
    '''
    
    now outputs
    
    '''
    .bar .foo:hover {
      color: orange;
    }
    '''
This page is out of date. Refresh to see the latest.
View
30 lib/less/parser.js
@@ -752,7 +752,7 @@ less.Parser = function Parser(env) {
// selector for now.
//
call: function () {
- var elements = [], e, c, args, index = i, s = input.charAt(i), important = false;
+ var elements = [], e, c, args = [], arg, index = i, s = input.charAt(i), name, value, important = false;
if (s !== '.' && s !== '#') { return }
@@ -760,14 +760,38 @@ less.Parser = function Parser(env) {
elements.push(new(tree.Element)(c, e, i));
c = $('>');
}
- $('(') && (args = $(this.entities.arguments)) && $(')');
+ if ($('(')) {
+ while (arg = $(this.expression)) {
+ value = arg;
+ name = null;
+
+ // Variable
+ if (arg.value.length = 1) {
+ var val = arg.value[0];
+ if (val instanceof tree.Variable) {
+ if ($(':')) {
+ if (value = $(this.expression)) {
+ name = val.name;
+ } else {
+ throw new(Error)("Expected value");
+ }
+ }
+ }
+ }
+
+ args.push({ name: name, value: value });
+
+ if (! $(',')) { break }
+ }
+ if (! $(')')) throw new(Error)("Expected )");
+ }
if ($(this.important)) {
important = true;
}
if (elements.length > 0 && ($(';') || peek('}'))) {
- return new(tree.mixin.Call)(elements, args || [], index, env.filename, important);
+ return new(tree.mixin.Call)(elements, args, index, env.filename, important);
}
},
View
7 lib/less/tree/element.js
@@ -19,7 +19,12 @@ tree.Element.prototype.eval = function (env) {
this.index);
};
tree.Element.prototype.toCSS = function (env) {
- return this.combinator.toCSS(env || {}) + (this.value.toCSS ? this.value.toCSS(env) : this.value);
+ var value = (this.value.toCSS ? this.value.toCSS(env) : this.value);
+ if (value == '' && this.combinator.value.charAt(0) == '&') {
+ return '';
+ } else {
+ return this.combinator.toCSS(env || {}) + value;
+ }
};
tree.Combinator = function (value) {
View
21 lib/less/tree/mixin.js
@@ -14,7 +14,9 @@ tree.mixin.Call.prototype = {
for (var i = 0; i < env.frames.length; i++) {
if ((mixins = env.frames[i].find(this.selector)).length > 0) {
- args = this.arguments && this.arguments.map(function (a) { return a.eval(env) });
+ args = this.arguments && this.arguments.map(function (a) {
+ return { name: a.name, value: a.value.eval(env) };
+ });
for (var m = 0; m < mixins.length; m++) {
if (mixins[m].match(args, env)) {
try {
@@ -69,9 +71,18 @@ tree.mixin.Definition.prototype = {
rulesets: function () { return this.parent.rulesets.apply(this) },
evalParams: function (env, args) {
- var frame = new(tree.Ruleset)(null, []), varargs;
+ var frame = new(tree.Ruleset)(null, []), varargs, arg;
for (var i = 0, val, name; i < this.params.length; i++) {
+ arg = args && args[i]
+
+ if (arg && arg.name) {
+ frame.rules.unshift(new(tree.Rule)(arg.name, arg.value.eval(env)));
+ args.splice(i, 1);
+ i--;
+ continue;
+ }
+
if (name = this.params[i].name) {
if (this.params[i].variadic && args) {
varargs = [];
@@ -79,7 +90,7 @@ tree.mixin.Definition.prototype = {
varargs.push(args[j].eval(env));
}
frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env)));
- } else if (val = (args && args[i]) || this.params[i].value) {
+ } else if (val = (arg && arg.value) || this.params[i].value) {
frame.rules.unshift(new(tree.Rule)(name, val.eval(env)));
} else {
throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
@@ -93,7 +104,7 @@ tree.mixin.Definition.prototype = {
var frame = this.evalParams(env, args), context, _arguments = [], rules, start;
for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) {
- _arguments.push(args[i] || this.params[i].value);
+ _arguments.push((args[i] && args[i].value) || this.params[i].value);
}
frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
@@ -123,7 +134,7 @@ tree.mixin.Definition.prototype = {
for (var i = 0; i < len; i++) {
if (!this.params[i].name) {
- if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
+ if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
return false;
}
}
View
20 test/css/mixins-named-args.css
@@ -0,0 +1,20 @@
+.named-arg {
+ color: blue;
+ width: 5px;
+ height: 99%;
+ text-align: center;
+}
+.class {
+ width: 5px;
+ height: 19%;
+}
+.named-args2 {
+ width: 15px;
+ height: 49%;
+ color: #646464;
+}
+.named-args3 {
+ width: 5px;
+ height: 29%;
+ color: #123456;
+}
View
31 test/less/mixins-named-args.less
@@ -0,0 +1,31 @@
+.mixin (@a: 1px, @b: 50%) {
+ width: @a * 5;
+ height: @b - 1%;
+}
+.mixin (@a: 1px, @b: 50%) when (@b > 75%){
+ text-align: center;
+}
+
+.named-arg {
+ color: blue;
+ .mixin(@b: 100%);
+}
+
+.class {
+ @var: 20%;
+ .mixin(@b: @var);
+}
+
+.mixin2 (@a: 1px, @b: 50%, @c: 50) {
+ width: @a * 5;
+ height: @b - 1%;
+ color: #000000 + @c;
+}
+
+.named-args2 {
+ .mixin2(3px, @c: 100);
+}
+
+.named-args3 {
+ .mixin2(@b: 30%, @c: #123456);
+}
Something went wrong with that request. Please try again.