detached rulesets

This commit is contained in:
Luke Page
2014-02-09 22:20:08 +00:00
parent e5702ca70e
commit f7414a1072
7 changed files with 97 additions and 15 deletions

View File

@@ -143,6 +143,7 @@ tree:
- <%= build.lib %>/tree/quoted.js
- <%= build.lib %>/tree/rule.js
- <%= build.lib %>/tree/ruleset.js
- <%= build.lib %>/tree/ruleset-call.js
- <%= build.lib %>/tree/selector.js
- <%= build.lib %>/tree/unicode-descriptor.js
- <%= build.lib %>/tree/url.js

View File

@@ -120,6 +120,7 @@ require('./tree/media');
require('./tree/unicode-descriptor');
require('./tree/negative');
require('./tree/extend');
require('./tree/ruleset-call');
var isUrlRe = /^(?:https?:)?\/\//i;

View File

@@ -729,7 +729,7 @@ less.Parser = function Parser(env) {
while (current)
{
node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() ||
mixin.call() || this.comment() || this.directive();
mixin.call() || this.comment() || this.rulesetCall() || this.directive();
if (node) {
root.push(node);
} else {
@@ -1027,6 +1027,19 @@ less.Parser = function Parser(env) {
if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; }
},
//
// The variable part of a variable definition. Used in the `rule` parser
//
// @fink();
//
rulesetCall: function () {
var name;
if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) {
return new tree.RulesetCall(name[1]);
}
},
//
// extend syntax - used to extend selectors
//
@@ -1126,7 +1139,7 @@ less.Parser = function Parser(env) {
while (true) {
if (isCall) {
arg = parsers.expression();
arg = parsers.blockRuleset() || parsers.expression();
} else {
parsers.comments();
if (input.charAt(i) === '.' && $re(/^\.{3}/)) {
@@ -1154,7 +1167,7 @@ less.Parser = function Parser(env) {
if (isCall) {
// Variable
if (arg.value.length == 1) {
if (arg.value && arg.value.length == 1) {
val = arg.value[0];
}
} else {
@@ -1442,6 +1455,14 @@ less.Parser = function Parser(env) {
}
},
blockRuleset: function() {
var block = this.block();
if (block) {
block = new tree.Ruleset(null, block);
}
return block;
},
//
// div, .class, body > p {...}
//
@@ -1484,25 +1505,33 @@ less.Parser = function Parser(env) {
}
},
rule: function (tryAnonymous) {
var name, value, c = input.charAt(i), important, merge;
var name, value, c = input.charAt(i), important, merge, isVariable;
save();
if (c === '.' || c === '#' || c === '&') { return; }
name = this.variable() || this.ruleProperty();
if (name) {
// prefer to try to parse first if its a variable or we are compressing
// but always fallback on the other one
value = !tryAnonymous && (env.compress || (name.charAt && (name.charAt(0) === '@'))) ?
(this.value() || this.anonymousValue()) :
(this.anonymousValue() || this.value());
important = this.important();
isVariable = typeof name === "string";
// a name returned by this.ruleProperty() is always an array of the form:
// [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
// where each item is a tree.Keyword or tree.Variable
merge = name.pop && name.pop().value;
if (isVariable) {
value = this.blockRuleset();
}
if (!value) {
// prefer to try to parse first if its a variable or we are compressing
// but always fallback on the other one
value = !tryAnonymous && (env.compress || isVariable) ?
(this.value() || this.anonymousValue()) :
(this.anonymousValue() || this.value());
important = this.important();
// a name returned by this.ruleProperty() is always an array of the form:
// [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
// where each item is a tree.Keyword or tree.Variable
merge = !isVariable && name.pop().value;
}
if (value && this.end()) {
return new (tree.Rule)(name, value, important, merge, memo, env.currentFileInfo);

View File

@@ -0,0 +1,15 @@
(function (tree) {
tree.RulesetCall = function (variable) {
this.variable = variable;
};
tree.RulesetCall.prototype = {
type: "RulesetCall",
accept: function (visitor) {
},
eval: function (env) {
return new(tree.Variable)(this.variable).eval(env);
}
};
})(require('../tree'));

View File

@@ -90,6 +90,19 @@ tree.Ruleset.prototype = {
rsRuleCnt += rules.length - 1;
i += rules.length-1;
ruleset.resetCache();
} else if (rsRules[i] instanceof tree.RulesetCall) {
/*jshint loopfunc:true */
rules = rsRules[i].eval(env).rules.filter(function(r) {
if ((r instanceof tree.Rule) && r.variable) {
// do not pollute the scope at all
return false;
}
return true;
});
rsRules.splice.apply(rsRules, [i, 1].concat(rules));
rsRuleCnt += rules.length - 1;
i += rules.length-1;
ruleset.resetCache();
}
}

View File

@@ -0,0 +1,7 @@
.wrap-selector {
color: black;
}
.wrap-selector {
color: black;
background: white;
}

View File

@@ -0,0 +1,16 @@
@ruleset: {
color: black;
background: white;
};
.wrap-mixin(@ruleset) {
.wrap-selector {
@ruleset();
}
};
.wrap-mixin({
color: black;
});
.wrap-mixin(@ruleset);