Details
Details
- Reviewers
pulkit - Group Reviewers
hg-reviewers - Commits
- rHG9ac1a5a4a64f: crecord: provide 'X' as a range-select mechanism
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Lint
Lint Skipped - Unit
Unit Tests Skipped
pulkit |
hg-reviewers |
Lint Skipped |
Unit Tests Skipped |
Path | Packages | |||
---|---|---|---|---|
M | mercurial/crecord.py (45 lines) | |||
M | tests/test-commit-interactive-curses.t (44 lines) |
Commit | Parents | Author | Summary | Date |
---|---|---|---|---|
370834f14196 | f3790fd8549e | Kyle Lippincott | Jul 8 2019, 4:10 PM |
# long as not explicitly set to a falsy value - especially, | # long as not explicitly set to a falsy value - especially, | ||||
# when not set at all. This is to stay most compatible with | # when not set at all. This is to stay most compatible with | ||||
# previous (color only) behaviour. | # previous (color only) behaviour. | ||||
uicolor = stringutil.parsebool(self.ui.config('ui', 'color')) | uicolor = stringutil.parsebool(self.ui.config('ui', 'color')) | ||||
self.usecolor = uicolor is not False | self.usecolor = uicolor is not False | ||||
# the currently selected header, hunk, or hunk-line | # the currently selected header, hunk, or hunk-line | ||||
self.currentselecteditem = self.headerlist[0] | self.currentselecteditem = self.headerlist[0] | ||||
self.lastapplieditem = None | |||||
# updated when printing out patch-display -- the 'lines' here are the | # updated when printing out patch-display -- the 'lines' here are the | ||||
# line positions *in the pad*, not on the screen. | # line positions *in the pad*, not on the screen. | ||||
self.selecteditemstartline = 0 | self.selecteditemstartline = 0 | ||||
self.selecteditemendline = None | self.selecteditemendline = None | ||||
# define indentation levels | # define indentation levels | ||||
self.headerindentnumchars = 0 | self.headerindentnumchars = 0 | ||||
def toggleapply(self, item=None): | def toggleapply(self, item=None): | ||||
""" | """ | ||||
toggle the applied flag of the specified item. if no item is specified, | toggle the applied flag of the specified item. if no item is specified, | ||||
toggle the flag of the currently selected item. | toggle the flag of the currently selected item. | ||||
""" | """ | ||||
if item is None: | if item is None: | ||||
item = self.currentselecteditem | item = self.currentselecteditem | ||||
# Only set this when NOT using 'toggleall' | |||||
self.lastapplieditem = item | |||||
item.applied = not item.applied | item.applied = not item.applied | ||||
if isinstance(item, uiheader): | if isinstance(item, uiheader): | ||||
item.partial = False | item.partial = False | ||||
if item.applied: | if item.applied: | ||||
# apply all its hunks | # apply all its hunks | ||||
for hnk in item.hunks: | for hnk in item.hunks: | ||||
if item.applied: | if item.applied: | ||||
self.toggleapply(item) | self.toggleapply(item) | ||||
else: | else: | ||||
for item in self.headerlist: | for item in self.headerlist: | ||||
if not item.applied: | if not item.applied: | ||||
self.toggleapply(item) | self.toggleapply(item) | ||||
self.waslasttoggleallapplied = not self.waslasttoggleallapplied | 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): | def togglefolded(self, item=None, foldparent=False): | ||||
"toggle folded flag of specified item (defaults to currently selected)" | "toggle folded flag of specified item (defaults to currently selected)" | ||||
if item is None: | if item is None: | ||||
item = self.currentselecteditem | item = self.currentselecteditem | ||||
if foldparent or (isinstance(item, uiheader) and item.neverunfolded): | if foldparent or (isinstance(item, uiheader) and item.neverunfolded): | ||||
if not isinstance(item, uiheader): | if not isinstance(item, uiheader): | ||||
# we need to select the parent item in this case | # we need to select the parent item in this case | ||||
self.currentselecteditem = item = item.parentitem() | self.currentselecteditem = item = item.parentitem() | ||||
you are running (commit/shelve/revert), after confirming the selected | you are running (commit/shelve/revert), after confirming the selected | ||||
changes, the unselected changes are still present in your working copy, so you | changes, the unselected changes are still present in your working copy, so you | ||||
can use crecord multiple times to split large changes into smaller changesets. | can use crecord multiple times to split large changes into smaller changesets. | ||||
the following are valid keystrokes: | the following are valid keystrokes: | ||||
x [space] : (un-)select item ([~]/[x] = partly/fully applied) | x [space] : (un-)select item ([~]/[x] = partly/fully applied) | ||||
[enter] : (un-)select item and go to next item of same type | [enter] : (un-)select item and go to next item of same type | ||||
A : (un-)select all items | 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 | up/down-arrow [k/j] : go to previous/next unfolded item | ||||
pgup/pgdn [K/J] : go to previous/next item of same type | pgup/pgdn [K/J] : go to previous/next item of same type | ||||
right/left-arrow [l/h] : go to child item / parent item | right/left-arrow [l/h] : go to child item / parent item | ||||
shift-left-arrow [H] : go to parent header / fold selected header | shift-left-arrow [H] : go to parent header / fold selected header | ||||
g : go to the top | g : go to the top | ||||
G : go to the bottom | G : go to the bottom | ||||
f : fold / unfold item, hiding/revealing its children | f : fold / unfold item, hiding/revealing its children | ||||
F : fold / unfold parent item and all of its ancestors | F : fold / unfold parent item and all of its ancestors | ||||
elif test and keypressed in ['R']: | elif test and keypressed in ['R']: | ||||
self.opts['review'] = True | self.opts['review'] = True | ||||
return True | return True | ||||
elif keypressed in [' ', 'x']: | elif keypressed in [' ', 'x']: | ||||
self.toggleapply() | self.toggleapply() | ||||
elif keypressed in ['\n', 'KEY_ENTER']: | elif keypressed in ['\n', 'KEY_ENTER']: | ||||
self.toggleapply() | self.toggleapply() | ||||
self.nextsametype(test=test) | self.nextsametype(test=test) | ||||
elif keypressed in ['X']: | |||||
self.toggleallbetween() | |||||
elif keypressed in ['A']: | elif keypressed in ['A']: | ||||
self.toggleall() | self.toggleall() | ||||
elif keypressed in ['e']: | elif keypressed in ['e']: | ||||
self.toggleedit(test=test) | self.toggleedit(test=test) | ||||
elif keypressed in ["f"]: | elif keypressed in ["f"]: | ||||
self.togglefolded() | self.togglefolded() | ||||
elif keypressed in ["F"]: | elif keypressed in ["F"]: | ||||
self.togglefolded(foldparent=True) | self.togglefolded(foldparent=True) |
10 | 10 | ||||
y | y | ||||
$ hg cat -r . x | $ hg cat -r . x | ||||
foo | foo | ||||
hello world | hello world | ||||
lower | 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 | Check ui.interface logic for the chunkselector | ||||
The default interface is text | The default interface is text | ||||
$ cp $HGRCPATH.pretest $HGRCPATH | $ cp $HGRCPATH.pretest $HGRCPATH | ||||
$ chunkselectorinterface() { | $ chunkselectorinterface() { | ||||
> "$PYTHON" <<EOF | > "$PYTHON" <<EOF | ||||
> from mercurial import hg, pycompat, ui;\ | > from mercurial import hg, pycompat, ui;\ | ||||
> repo = hg.repository(ui.ui.load(), b".");\ | > repo = hg.repository(ui.ui.load(), b".");\ |