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

22 participants

James Foster Paul Young Aaron Harp Ted Halmrast Julien Bouquillon Charles Lowell Daniël van de Burgt Leon Sorokin Eugene Chernyshov Carl Helmertz Nate Bedortha joeldbirch Colin Snover Xavier Ho Donald Harvey Regis Philibert Dave Geddes Urs Braem Yann 'Aaunel' Eves-Hollis John Reading Matthew Dean Alexis Sellier
James Foster

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

Paul Young

This is great!

Aaron Harp

Yeah, this looks awesome.

Ted Halmrast

Nice!

James Foster

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

Enjoy

Paul Young

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!

James Foster

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

Original branch at jamesfoster@req268

Julien Bouquillon

OMG +1 great work

Charles Lowell

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

Daniël van de Burgt

Do want

Leon Sorokin

+1

Eugene Chernyshov

+1

Nate Bedortha

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.

James Foster

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.

Colin Snover

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?

James Foster

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

Charles Lowell

@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.

Xavier Ho

Any progress on this ticket?

Paul Young

Would love to see named arguments added.

Donald Harvey

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

Regis Philibert

Woah, I'm tweeting this, cheers mate!

Dave Geddes

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
James Foster jamesfoster Implement named parameters 1857b7c
James Foster 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
James Foster

Rebased and fixed issue with trailing space.

Alexis Sellier cloudhead merged commit a2df119 into from
Yann 'Aaunel' Eves-Hollis

+1

Matthew Dean

Named parameters? Hooray!

This breaks the mixins-args test. Fix pending

John Reading

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!

Kai Dorschner krnlde referenced this pull request in leafo/lessphp
Open

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. James Foster

    Implement named parameters

    jamesfoster authored
  2. James Foster

    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.
30 lib/less/parser.js
View
@@ -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);
}
},
7 lib/less/tree/element.js
View
@@ -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) {
21 lib/less/tree/mixin.js
View
@@ -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;
}
}
20 test/css/mixins-named-args.css
View
@@ -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;
+}
31 test/less/mixins-named-args.less
View
@@ -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.