mercurial/crecord.py
changeset 42573 9ac1a5a4a64f
parent 42572 cd4f1b7c3192
child 42659 701341f57ceb
--- a/mercurial/crecord.py	Mon Jul 08 13:06:46 2019 -0700
+++ b/mercurial/crecord.py	Mon Jul 08 13:10:34 2019 -0700
@@ -608,6 +608,7 @@
 
         # the currently selected header, hunk, or hunk-line
         self.currentselecteditem = self.headerlist[0]
+        self.lastapplieditem = None
 
         # updated when printing out patch-display -- the 'lines' here are the
         # line positions *in the pad*, not on the screen.
@@ -839,6 +840,8 @@
         """
         if item is None:
             item = self.currentselecteditem
+            # Only set this when NOT using 'toggleall'
+            self.lastapplieditem = item
 
         item.applied = not item.applied
 
@@ -932,6 +935,45 @@
                     self.toggleapply(item)
         self.waslasttoggleallapplied = not self.waslasttoggleallapplied
 
+    def toggleallbetween(self):
+        "toggle applied on or off for all items in range [lastapplied,current]."
+        if (not self.lastapplieditem or
+            self.currentselecteditem == self.lastapplieditem):
+            # Treat this like a normal 'x'/' '
+            self.toggleapply()
+            return
+
+        startitem = self.lastapplieditem
+        enditem = self.currentselecteditem
+        # Verify that enditem is "after" startitem, otherwise swap them.
+        for direction in ['forward', 'reverse']:
+            nextitem = startitem.nextitem()
+            while nextitem and nextitem != enditem:
+                nextitem = nextitem.nextitem()
+            if nextitem:
+                break
+            # Looks like we went the wrong direction :)
+            startitem, enditem = enditem, startitem
+
+        if not nextitem:
+            # We didn't find a path going either forward or backward? Don't know
+            # how this can happen, let's not crash though.
+            return
+
+        nextitem = startitem
+        # Switch all items to be the opposite state of the currently selected
+        # item. Specifically:
+        #  [ ] startitem
+        #  [x] middleitem
+        #  [ ] enditem  <-- currently selected
+        # This will turn all three on, since the currently selected item is off.
+        # This does *not* invert each item (i.e. middleitem stays marked/on)
+        desiredstate = not self.currentselecteditem.applied
+        while nextitem != enditem.nextitem():
+            if nextitem.applied != desiredstate:
+                self.toggleapply(item=nextitem)
+            nextitem = nextitem.nextitem()
+
     def togglefolded(self, item=None, foldparent=False):
         "toggle folded flag of specified item (defaults to currently selected)"
         if item is None:
@@ -1464,6 +1506,7 @@
               x [space] : (un-)select item ([~]/[x] = partly/fully applied)
                 [enter] : (un-)select item and go to next item of same type
                       A : (un-)select all items
+                      X : (un-)select all items between current and most-recent
     up/down-arrow [k/j] : go to previous/next unfolded item
         pgup/pgdn [K/J] : go to previous/next item of same type
  right/left-arrow [l/h] : go to child item / parent item
@@ -1755,6 +1798,8 @@
         elif keypressed in ['\n', 'KEY_ENTER']:
             self.toggleapply()
             self.nextsametype(test=test)
+        elif keypressed in ['X']:
+            self.toggleallbetween()
         elif keypressed in ['A']:
             self.toggleall()
         elif keypressed in ['e']: