[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