Skip to content
This repository

Implemented named arguments #268

Merged
merged 2 commits into from almost 2 years ago
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
arharp commented May 17, 2011

Yeah, this looks awesome.

dimensia

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 https://github.com/jamesfoster/less.js/commit/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

Carl Helmertz

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

added some commits April 30, 2012
James Foster Implement named parameters 1857b7c
James Foster 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.

Urs Braem

+1

Alexis Sellier cloudhead merged commit a2df119 into from April 30, 2012
Alexis Sellier cloudhead closed this April 30, 2012
Yann 'Aaunel' Eves-Hollis

+1

Matthew Dean
Collaborator

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 June 05, 2013
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

Showing 2 unique commits by 1 author.

Apr 30, 2012
James Foster Implement named parameters 1857b7c
James Foster 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
This page is out of date. Refresh to see the latest.
30  lib/less/parser.js
@@ -752,7 +752,7 @@ less.Parser = function Parser(env) {
752 752
                 // selector for now.
753 753
                 //
754 754
                 call: function () {
755  
-                    var elements = [], e, c, args, index = i, s = input.charAt(i), important = false;
  755
+                    var elements = [], e, c, args = [], arg, index = i, s = input.charAt(i), name, value, important = false;
756 756
 
757 757
                     if (s !== '.' && s !== '#') { return }
758 758
 
@@ -760,14 +760,38 @@ less.Parser = function Parser(env) {
760 760
                         elements.push(new(tree.Element)(c, e, i));
761 761
                         c = $('>');
762 762
                     }
763  
-                    $('(') && (args = $(this.entities.arguments)) && $(')');
  763
+                    if ($('(')) {
  764
+                        while (arg = $(this.expression)) {
  765
+                            value = arg;
  766
+                            name = null;
  767
+
  768
+                            // Variable
  769
+                            if (arg.value.length = 1) {
  770
+                                var val = arg.value[0];
  771
+                                if (val instanceof tree.Variable) {
  772
+                                    if ($(':')) {
  773
+                                        if (value = $(this.expression)) {
  774
+                                            name = val.name;
  775
+                                        } else {
  776
+                                            throw new(Error)("Expected value");
  777
+                                        }
  778
+                                    }
  779
+                                }
  780
+                            }
  781
+
  782
+                            args.push({ name: name, value: value });
  783
+
  784
+                            if (! $(',')) { break }
  785
+                        }
  786
+                        if (! $(')')) throw new(Error)("Expected )");
  787
+                    }
764 788
 
765 789
                     if ($(this.important)) {
766 790
                         important = true;
767 791
                     }
768 792
 
769 793
                     if (elements.length > 0 && ($(';') || peek('}'))) {
770  
-                        return new(tree.mixin.Call)(elements, args || [], index, env.filename, important);
  794
+                        return new(tree.mixin.Call)(elements, args, index, env.filename, important);
771 795
                     }
772 796
                 },
773 797
 
7  lib/less/tree/element.js
@@ -19,7 +19,12 @@ tree.Element.prototype.eval = function (env) {
19 19
                              this.index);
20 20
 };
21 21
 tree.Element.prototype.toCSS = function (env) {
22  
-    return this.combinator.toCSS(env || {}) + (this.value.toCSS ? this.value.toCSS(env) : this.value);
  22
+	var value = (this.value.toCSS ? this.value.toCSS(env) : this.value);
  23
+	if (value == '' && this.combinator.value.charAt(0) == '&') {
  24
+		return '';
  25
+	} else {
  26
+		return this.combinator.toCSS(env || {}) + value;
  27
+	}
23 28
 };
24 29
 
25 30
 tree.Combinator = function (value) {
21  lib/less/tree/mixin.js
@@ -14,7 +14,9 @@ tree.mixin.Call.prototype = {
14 14
 
15 15
         for (var i = 0; i < env.frames.length; i++) {
16 16
             if ((mixins = env.frames[i].find(this.selector)).length > 0) {
17  
-                args = this.arguments && this.arguments.map(function (a) { return a.eval(env) });
  17
+                args = this.arguments && this.arguments.map(function (a) {
  18
+                    return { name: a.name, value: a.value.eval(env) };
  19
+                });
18 20
                 for (var m = 0; m < mixins.length; m++) {
19 21
                     if (mixins[m].match(args, env)) {
20 22
                         try {
@@ -69,9 +71,18 @@ tree.mixin.Definition.prototype = {
69 71
     rulesets:  function ()     { return this.parent.rulesets.apply(this) },
70 72
 
71 73
     evalParams: function (env, args) {
72  
-        var frame = new(tree.Ruleset)(null, []), varargs;
  74
+        var frame = new(tree.Ruleset)(null, []), varargs, arg;
73 75
 
74 76
         for (var i = 0, val, name; i < this.params.length; i++) {
  77
+            arg = args && args[i]
  78
+
  79
+            if (arg && arg.name) {
  80
+                frame.rules.unshift(new(tree.Rule)(arg.name, arg.value.eval(env)));
  81
+                args.splice(i, 1);
  82
+                i--;
  83
+                continue;
  84
+            }
  85
+			
75 86
             if (name = this.params[i].name) {
76 87
                 if (this.params[i].variadic && args) {
77 88
                     varargs = [];
@@ -79,7 +90,7 @@ tree.mixin.Definition.prototype = {
79 90
                         varargs.push(args[j].eval(env));
80 91
                     }
81 92
                     frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env)));
82  
-                } else if (val = (args && args[i]) || this.params[i].value) {
  93
+                } else if (val = (arg && arg.value) || this.params[i].value) {
83 94
                     frame.rules.unshift(new(tree.Rule)(name, val.eval(env)));
84 95
                 } else {
85 96
                     throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
@@ -93,7 +104,7 @@ tree.mixin.Definition.prototype = {
93 104
         var frame = this.evalParams(env, args), context, _arguments = [], rules, start;
94 105
 
95 106
         for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) {
96  
-            _arguments.push(args[i] || this.params[i].value);
  107
+            _arguments.push((args[i] && args[i].value) || this.params[i].value);
97 108
         }
98 109
         frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
99 110
 
@@ -123,7 +134,7 @@ tree.mixin.Definition.prototype = {
123 134
 
124 135
         for (var i = 0; i < len; i++) {
125 136
             if (!this.params[i].name) {
126  
-                if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
  137
+                if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
127 138
                     return false;
128 139
                 }
129 140
             }
20  test/css/mixins-named-args.css
... ...
@@ -0,0 +1,20 @@
  1
+.named-arg {
  2
+  color: blue;
  3
+  width: 5px;
  4
+  height: 99%;
  5
+  text-align: center;
  6
+}
  7
+.class {
  8
+  width: 5px;
  9
+  height: 19%;
  10
+}
  11
+.named-args2 {
  12
+  width: 15px;
  13
+  height: 49%;
  14
+  color: #646464;
  15
+}
  16
+.named-args3 {
  17
+  width: 5px;
  18
+  height: 29%;
  19
+  color: #123456;
  20
+}
31  test/less/mixins-named-args.less
... ...
@@ -0,0 +1,31 @@
  1
+.mixin (@a: 1px, @b: 50%) {
  2
+  width: @a * 5;
  3
+  height: @b - 1%;
  4
+}
  5
+.mixin (@a: 1px, @b: 50%) when (@b > 75%){
  6
+  text-align: center;
  7
+}
  8
+ 
  9
+.named-arg {
  10
+  color: blue;
  11
+  .mixin(@b: 100%);
  12
+}
  13
+ 
  14
+.class {
  15
+  @var: 20%;
  16
+  .mixin(@b: @var);
  17
+}
  18
+
  19
+.mixin2 (@a: 1px, @b: 50%, @c: 50) {
  20
+  width: @a * 5;
  21
+  height: @b - 1%;
  22
+  color: #000000 + @c;
  23
+}
  24
+
  25
+.named-args2 {
  26
+  .mixin2(3px, @c: 100);
  27
+}
  28
+
  29
+.named-args3 {
  30
+  .mixin2(@b: 30%, @c: #123456);
  31
+}
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.