[Pkg-javascript-commits] [less.js] 02/14: Cleaned up parent selector replacement inside nested selectors :not() and added unit test.
Jonas Smedegaard
dr at jones.dk
Mon Oct 26 23:29:15 UTC 2015
This is an automated email from the git hooks/post-receive script.
js pushed a commit to annotated tag v2.3.1
in repository less.js.
commit 20e555bf4749abd43a9a89337278ab3c54465d1d
Author: jurcovicovam <meri at meri.org>
Date: Tue Jan 20 13:40:56 2015 +0100
Cleaned up parent selector replacement inside nested selectors :not() and
added unit test.
---
lib/less/tree/ruleset.js | 416 ++++++++++++++++++++++-------------------------
test/css/selectors.css | 9 +
test/less/selectors.less | 15 +-
3 files changed, 221 insertions(+), 219 deletions(-)
diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js
index b087723..0d46593 100644
--- a/lib/less/tree/ruleset.js
+++ b/lib/less/tree/ruleset.js
@@ -427,244 +427,224 @@ Ruleset.prototype.joinSelectors = function (paths, context, selectors) {
this.joinSelector(paths, context, selectors[s]);
}
};
-function replaceNextPartMeri(attachToPrefix, replacement, appender, originalSelector) {
- var newSelectorPath, afterParentJoin, lastSelector, newJoinedSelectorEmpty, newJoinedSelector;
- // our new selector path
- newSelectorPath = [];
- // selectors from the parent after the join
- afterParentJoin = [];
- newJoinedSelectorEmpty = true;
-
- //construct the joined selector - if & is the first thing this will be empty,
- // if not newJoinedSelector will be the last set of elements in the selector
- if (attachToPrefix.length > 0) {
- newSelectorPath = attachToPrefix.slice(0);
- lastSelector = newSelectorPath.pop();
- newJoinedSelector = originalSelector.createDerived(lastSelector.elements.slice(0));
- newJoinedSelectorEmpty = false;
- }
- else {
- newJoinedSelector = originalSelector.createDerived([]);
- }
-
- //put together the parent selectors after the join
- if (replacement.length > 1) {
- afterParentJoin = afterParentJoin.concat(replacement.slice(1));
- }
-
- if (replacement.length > 0) {
- newJoinedSelectorEmpty = false;
-
- // /deep/ is a combinator that is valid without anything in front of it
- // so if the & does not have a combinator that is "" or " " then
- // and there is a combinator on the parent, then grab that.
- // this also allows + a { & .b { .a & { ... though not sure why you would want to do that
- var combinator = appender.combinator,
- parentEl = replacement[0].elements[0];
- if (combinator.emptyOrWhitespace && !parentEl.combinator.emptyOrWhitespace) {
- combinator = parentEl.combinator;
- }
- // join the elements so far with the first part of the parent
- newJoinedSelector.elements.push(new Element(combinator, parentEl.value, appender.index, appender.currentFileInfo));
- newJoinedSelector.elements = newJoinedSelector.elements.concat(replacement[0].elements.slice(1));
- }
-
- if (!newJoinedSelectorEmpty) {
- // now add the joined selector
- newSelectorPath.push(newJoinedSelector);
- }
-
- // and the rest of the parent
- newSelectorPath = newSelectorPath.concat(afterParentJoin);
- return newSelectorPath;
-}
-function replaceInnerParentSelector(paths, context, selector) {
- var i, j, k, currentElements, newSelectors, selectorsMultiplied, parentSel, sel, el, encounteredParentSelector = false;
- function findNestedSelector(element) {
- var maybeSelector;
- if (element.value.type !== 'Paren')
- return null;
-
- maybeSelector = element.value.value;
- if (maybeSelector.type !== 'Selector')
- return null;
-
- return maybeSelector;
- }
-// the elements from the current selector so far
- currentElements = [];
- // the current list of new selectors to add to the path.
- // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
- // by the parents
- newSelectors = [
- []
- ];
-
- for (i = 0; i < selector.elements.length; i++) {
- el = selector.elements[i];
- // non parent reference elements just get added
- if (el.value !== "&") {
- var nestedSelector = findNestedSelector(el);
- if (nestedSelector != null) {
- selectorsMultiplied = [];
- // merge the current list of non parent selector elements
- // on to the current list of selectors to add
- this.mergeElementsOnToSelectors(currentElements, newSelectors);
-
- var nestedPathsWtf = [], replaced;
- replaced = replaceInnerParentSelector.call(this, nestedPathsWtf, context, nestedSelector);
- encounteredParentSelector = encounteredParentSelector || replaced;
- for (k = 0; k < nestedPathsWtf.length; k++) {
-
- var replacementParen = new Paren(nestedPathsWtf[0][0]);
- var replacementElement = new Element(null, replacementParen, el.index, el.currentFileInfo);
- var replacementSelector = new Selector([replacementElement]);
- var replacements = [replacementSelector];
- //replaceNextPartMeri(attachToPrefix, replacement, appender, originalSelector)
- for (j = 0; j < newSelectors.length; j++) {
- sel = newSelectors[j];
- var entirelyNewSelectorPath = replaceNextPartMeri(sel, replacements, el, selector);
- selectorsMultiplied.push(entirelyNewSelectorPath); // this causes cucle
- }
- newSelectors = selectorsMultiplied;
- currentElements = [];
- }
- } else {
- currentElements.push(el);
- }
+Ruleset.prototype.joinSelector = function (paths, context, selector) {
+ // replace all parent selectors inside `inSelector` by content of `context` array
+ // resulting selectors are returned inside `paths` array
+ // returns true if `inSelector` contained at least one parent selector
+ function replaceParentSelector(paths, context, inSelector) {
+ // The paths are [[Selector]]
+ // The first list is a list of comma separated selectors
+ // The inner list is a list of inheritance separated selectors
+ // e.g.
+ // .a, .b {
+ // .c {
+ // }
+ // }
+ // == [[.a] [.c]] [[.b] [.c]]
+ //
+ var i, j, k, currentElements, newSelectors, selectorsMultiplied, sel, el, hadParentSelector = false;
+ function findNestedSelector(element) {
+ var maybeSelector;
+ if (element.value.type !== 'Paren')
+ return null;
+
+ maybeSelector = element.value.value;
+ if (maybeSelector.type !== 'Selector')
+ return null;
+
+ return maybeSelector;
+ }
- } else {
- encounteredParentSelector = true;
- // the new list of selectors to add
- selectorsMultiplied = [];
-
- // merge the current list of non parent selector elements
- // on to the current list of selectors to add
- this.mergeElementsOnToSelectors(currentElements, newSelectors);
-
- // loop through our current selectors
- for (j = 0; j < newSelectors.length; j++) {
- sel = newSelectors[j];
- // if we don't have any parent paths, the & might be in a mixin so that it can be used
- // whether there are parents or not
- if (context.length === 0) {
- // the combinator used on el should now be applied to the next element instead so that
- // it is not lost
- if (sel.length > 0) {
- sel[0].elements = sel[0].elements.slice(0);
- sel[0].elements.push(new Element(el.combinator, '', el.index, el.currentFileInfo));
- }
- selectorsMultiplied.push(sel);
+ // the elements from the current selector so far
+ currentElements = [];
+ // the current list of new selectors to add to the path.
+ // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
+ // by the parents
+ newSelectors = [
+ []
+ ];
+
+ for (i = 0; i < inSelector.elements.length; i++) {
+ el = inSelector.elements[i];
+ // non parent reference elements just get added
+ if (el.value !== "&") {
+ var nestedSelector = findNestedSelector(el);
+ if (nestedSelector != null) {
+ // merge the current list of non parent selector elements
+ // on to the current list of selectors to add
+ mergeElementsOnToSelectors(currentElements, newSelectors);
+
+ var nestedPaths = [], replaced;
+ replaced = replaceParentSelector(nestedPaths, context, nestedSelector);
+ hadParentSelector = hadParentSelector || replaced;
+ for (k = 0; k < nestedPaths.length; k++) {
+ var replacementParen = new Paren(nestedPaths[0][0]);
+ var replacementElement = new Element(null, replacementParen, el.index, el.currentFileInfo);
+ var replacementSelector = new Selector([replacementElement]);
+ newSelectors = addAllReplacementsIntoPath(newSelectors, [replacementSelector], el, inSelector);
+ }
+ currentElements = [];
+
+ } else {
+ currentElements.push(el);
+ }
+
+ } else {
+ hadParentSelector = true;
+ // the new list of selectors to add
+ selectorsMultiplied = [];
+
+ // merge the current list of non parent selector elements
+ // on to the current list of selectors to add
+ mergeElementsOnToSelectors(currentElements, newSelectors);
+
+ // loop through our current selectors
+ for (j = 0; j < newSelectors.length; j++) {
+ sel = newSelectors[j];
+ // if we don't have any parent paths, the & might be in a mixin so that it can be used
+ // whether there are parents or not
+ if (context.length === 0) {
+ // the combinator used on el should now be applied to the next element instead so that
+ // it is not lost
+ if (sel.length > 0) {
+ sel[0].elements.push(new Element(el.combinator, '', el.index, el.currentFileInfo));
+ }
+ selectorsMultiplied.push(sel);
+ }
+ else {
+ // and the parent selectors
+ for (k = 0; k < context.length; k++) {
+ // We need to put the current selectors
+ // then join the last selector's elements on to the parents selectors
+ var newSelectorPath = addReplacementIntoPath(sel, context[k], el, inSelector);
+ // add that to our new set of selectors
+ selectorsMultiplied.push(newSelectorPath);
+ }
+ }
+ }
+
+ // our new selectors has been multiplied, so reset the state
+ newSelectors = selectorsMultiplied;
+ currentElements = [];
+ }
}
- else {
- // and the parent selectors
- for (k = 0; k < context.length; k++) {
- parentSel = context[k];
- // We need to put the current selectors
- // then join the last selector's elements on to the parents selectors
- var newSelectorPath = replaceNextPartMeri(sel, parentSel, el, selector);
- // add that to our new set of selectors
- selectorsMultiplied.push(newSelectorPath);
- }
+
+ // if we have any elements left over (e.g. .a& .b == .b)
+ // add them on to all the current selectors
+ mergeElementsOnToSelectors(currentElements, newSelectors);
+
+ for (i = 0; i < newSelectors.length; i++) {
+ if (newSelectors[i].length > 0) {
+ paths.push(newSelectors[i]);
+ }
}
- }
- // our new selectors has been multiplied, so reset the state
- newSelectors = selectorsMultiplied;
- currentElements = [];
+ return hadParentSelector;
}
- }
- // if we have any elements left over (e.g. .a& .b == .b)
- // add them on to all the current selectors
- this.mergeElementsOnToSelectors(currentElements, newSelectors);
+ // joins selector path from `beginningPath` with selector path in `addPath`
+ // `replacedElement` contains element that is being replaced by `addPath`
+ // returns concatenated path
+ function addReplacementIntoPath(beginningPath, addPath, replacedElement, originalSelector) {
+ var newSelectorPath, lastSelector, newJoinedSelector;
+ // our new selector path
+ newSelectorPath = [];
- for (i = 0; i < newSelectors.length; i++) {
- if (newSelectors[i].length > 0) {
- paths.push(newSelectors[i]);
- }
- }
+ //construct the joined selector - if & is the first thing this will be empty,
+ // if not newJoinedSelector will be the last set of elements in the selector
+ if (beginningPath.length > 0) {
+ newSelectorPath = beginningPath.slice(0);
+ lastSelector = newSelectorPath.pop();
+ newJoinedSelector = originalSelector.createDerived(lastSelector.elements.slice(0));
+ }
+ else {
+ newJoinedSelector = originalSelector.createDerived([]);
+ }
- return encounteredParentSelector;
-}
-Ruleset.prototype.joinSelector = function (paths, context, selector) {
+ if (addPath.length > 0) {
+ // /deep/ is a combinator that is valid without anything in front of it
+ // so if the & does not have a combinator that is "" or " " then
+ // and there is a combinator on the parent, then grab that.
+ // this also allows + a { & .b { .a & { ... though not sure why you would want to do that
+ var combinator = replacedElement.combinator, parentEl = addPath[0].elements[0];
+ if (combinator.emptyOrWhitespace && !parentEl.combinator.emptyOrWhitespace) {
+ combinator = parentEl.combinator;
+ }
+ // join the elements so far with the first part of the parent
+ newJoinedSelector.elements.push(new Element(combinator, parentEl.value, replacedElement.index, replacedElement.currentFileInfo));
+ newJoinedSelector.elements = newJoinedSelector.elements.concat(addPath[0].elements.slice(1));
+ }
- var i, hasParentSelector, el;
-
- for (i = 0; i < selector.elements.length; i++) {
- el = selector.elements[i];
- if (el.value === '&') {
- hasParentSelector = true;
- }
- }
-
- if (false) {
- if (context.length > 0) {
- console.log('context: full');
- for (i = 0; i < context.length; i++) {
- paths.push(context[i].concat(selector));
- }
- }
- else {
- console.log('context: empty');
- paths.push([selector]);
- }
- return;
- }
-
- // The paths are [[Selector]]
- // The first list is a list of comma separated selectors
- // The inner list is a list of inheritance separated selectors
- // e.g.
- // .a, .b {
- // .c {
- // }
- // }
- // == [[.a] [.c]] [[.b] [.c]]
- //
- var newPaths = [];
- var encounteredParentSelector = replaceInnerParentSelector.call(this, newPaths, context, selector);
-
- if (true && !encounteredParentSelector) {
- newPaths = [];
- if (context.length > 0) {
- console.log('context: full');
- for (i = 0; i < context.length; i++) {
- newPaths.push(context[i].concat(selector));
- }
+ // now add the joined selector - but only if it is not empty
+ if (newJoinedSelector.elements.length !== 0) {
+ newSelectorPath.push(newJoinedSelector);
+ }
+
+ //put together the parent selectors after the join (e.g. the rest of the parent)
+ if (addPath.length > 1) {
+ newSelectorPath = newSelectorPath.concat(addPath.slice(1));
+ }
+ return newSelectorPath;
}
- else {
- console.log('context: empty');
- newPaths.push([selector]);
+
+ // joins selector path from `beginningPath` with every selector path in `addPaths` array
+ // `replacedElement` contains element that is being replaced by `addPath`
+ // returns array with all concatenated paths
+ function addAllReplacementsIntoPath( beginningPath, addPaths, replacedElement, originalSelector) {
+ var j, result = [];
+ for (j = 0; j < beginningPath.length; j++) {
+ var newSelectorPath = addReplacementIntoPath(beginningPath[j], addPaths, replacedElement, originalSelector);
+ result.push(newSelectorPath);
+ }
+ return result;
}
- }
- for (i = 0; i < newPaths.length; i++) {
- paths.push(newPaths[i]);
- }
-};
-Ruleset.prototype.mergeElementsOnToSelectors = function(elements, selectors) {
- var i, sel;
+ function mergeElementsOnToSelectors(elements, selectors) {
+ var i, sel;
- if (elements.length === 0) {
- return ;
- }
- if (selectors.length === 0) {
- selectors.push([ new Selector(elements) ]);
- return;
+ if (elements.length === 0) {
+ return ;
+ }
+ if (selectors.length === 0) {
+ selectors.push([ new Selector(elements) ]);
+ return;
+ }
+
+ for (i = 0; i < selectors.length; i++) {
+ sel = selectors[i];
+
+ // if the previous thing in sel is a parent this needs to join on to it
+ if (sel.length > 0) {
+ sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
+ }
+ else {
+ sel.push(new Selector(elements));
+ }
+ }
}
- for (i = 0; i < selectors.length; i++) {
- sel = selectors[i];
+ // joinSelector code follows
+ var i, newPaths, hadParentSelector;
- // if the previous thing in sel is a parent this needs to join on to it
- if (sel.length > 0) {
- sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
+ newPaths = [];
+ hadParentSelector = replaceParentSelector(newPaths, context, selector);
+
+ if (!hadParentSelector) {
+ if (context.length > 0) {
+ newPaths = [];
+ for (i = 0; i < context.length; i++) {
+ newPaths.push(context[i].concat(selector));
+ }
}
else {
- sel.push(new Selector(elements));
+ newPaths = [[selector]];
}
}
+
+ for (i = 0; i < newPaths.length; i++) {
+ paths.push(newPaths[i]);
+ }
+
};
module.exports = Ruleset;
diff --git a/test/css/selectors.css b/test/css/selectors.css
index 85f95b5..b114b62 100644
--- a/test/css/selectors.css
+++ b/test/css/selectors.css
@@ -154,3 +154,12 @@ blank blank blank blank blank blank blank blank blank blank blank blank blank bl
.blood {
color: red;
}
+.foo:not(.tst.only-nested:hover) {
+ test: only-nested;
+}
+.foo.nestend-and-non-nested:not(.tst.nestend-and-non-nested:hover) {
+ test: nestend-and-non-nested;
+}
+.selector:not(:hover) {
+ test: global scope;
+}
diff --git a/test/less/selectors.less b/test/less/selectors.less
index ccc57da..b98fca2 100644
--- a/test/less/selectors.less
+++ b/test/less/selectors.less
@@ -156,4 +156,17 @@ blank blank blank blank blank blank blank blank blank blank blank blank blank bl
*/
@{selector} {
color: red;
-}
\ No newline at end of file
+}
+.only-nested {
+ .foo:not(.tst&:hover) {
+ test: only-nested;
+ }
+}
+.nestend-and-non-nested {
+ .foo&:not(.tst&:hover) {
+ test: nestend-and-non-nested;
+ }
+}
+.selector:not(&:hover) {
+ test: global scope;
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/less.js.git
More information about the Pkg-javascript-commits
mailing list