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".");\ | ||||