crecord: provide 'X' as a range-select mechanism
Differential Revision: https://phab.mercurial-scm.org/D6621
--- 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']:
--- a/tests/test-commit-interactive-curses.t Mon Jul 08 13:06:46 2019 -0700
+++ b/tests/test-commit-interactive-curses.t Mon Jul 08 13:10:34 2019 -0700
@@ -327,6 +327,50 @@
hello world
lower
+Test range select: unselect 3, 5, and 6, reselect 5, then go back up to 2 and
+press 'X', unselecting (because 2 is currently selected) 5 (because it's the
+start of the range) and 4, leaving 3 unselected.
+
+ $ hg init $TESTTMP/range_select
+ $ cd $TESTTMP/range_select
+ >>> open('range_select', 'wb').write(b"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n") and None
+ $ hg add range_select
+ $ cat <<EOF >testModeCommands
+ > KEY_RIGHT
+ > KEY_RIGHT
+ > KEY_DOWN
+ > KEY_DOWN
+ > KEY_ENTER
+ > KEY_DOWN
+ > KEY_ENTER
+ > x
+ > KEY_UP
+ > x
+ > KEY_UP
+ > KEY_UP
+ > KEY_UP
+ > X
+ > c
+ > EOF
+ $ hg commit -i -m "range_select" -d "0 0"
+ $ hg cat -r tip range_select
+ 1
+ 7
+ 8
+ 9
+ 10
+ $ cat range_select
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+
Check ui.interface logic for the chunkselector
The default interface is text