Skip to content

Commit

Permalink
redoing
Browse files Browse the repository at this point in the history
  • Loading branch information
jesseduffield committed Mar 24, 2020
1 parent 7c8df28 commit daecdd7
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 18 deletions.
3 changes: 2 additions & 1 deletion docs/Config.md
Expand Up @@ -83,6 +83,8 @@ Default path for the config file:
prevTab: '['
nextScreenMode: '+'
prevScreenMode: '_'
undo: 'z'
redo: '<c-z>'
status:
checkForUpdate: 'u'
recentRepos: '<enter>'
Expand Down Expand Up @@ -138,7 +140,6 @@ Default path for the config file:
toggleDragSelect-alt: 'V'
toggleSelectHunk: 'a'
pickBothHunks: 'b'
undo: 'z'
```

## Platform Defaults
Expand Down
3 changes: 2 additions & 1 deletion pkg/config/app_config.go
Expand Up @@ -314,6 +314,8 @@ keybinding:
prevTab: '['
nextScreenMode: '+'
prevScreenMode: '_'
undo: 'z'
redo: '<c-z>'
status:
checkForUpdate: 'u'
recentRepos: '<enter>'
Expand Down Expand Up @@ -370,7 +372,6 @@ keybinding:
toggleDragSelect-alt: 'V'
toggleSelectHunk: 'a'
pickBothHunks: 'b'
undo: 'z'
`)
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/gui/gui.go
Expand Up @@ -229,6 +229,9 @@ type guiState struct {
type UndoState struct {
ReflogKey string
ReflogIdx int
// this is the index of the most recent reflog entry that the user initiated themselves
// (as opposed to being created by an undo or redo action)
UndoCount int
}

// for now the split view will always be on
Expand Down
11 changes: 9 additions & 2 deletions pkg/gui/keybindings.go
Expand Up @@ -315,11 +315,18 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
},
{
ViewName: "",
Key: gui.getKey("main.undo"),
Key: gui.getKey("universal.undo"),
Modifier: gocui.ModNone,
Handler: gui.reflogUndo,
Description: gui.Tr.SLocalize("undoReflog"),
},
{
ViewName: "",
Key: gui.getKey("universal.redo"),
Modifier: gocui.ModNone,
Handler: gui.reflogRedo,
Description: gui.Tr.SLocalize("redoReflog"),
},
{
ViewName: "status",
Key: gui.getKey("universal.edit"),
Expand Down Expand Up @@ -1349,7 +1356,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
{
ViewName: "main",
Contexts: []string{"merging"},
Key: gui.getKey("main.undo"),
Key: gui.getKey("universal.undo"),
Modifier: gocui.ModNone,
Handler: gui.handlePopFileSnapshot,
Description: gui.Tr.SLocalize("undo"),
Expand Down
2 changes: 1 addition & 1 deletion pkg/gui/merge_panel.go
Expand Up @@ -255,7 +255,7 @@ func (gui *Gui) renderMergeOptions() error {
fmt.Sprintf("%s %s", gui.getKeyDisplay("universal.prevBlock"), gui.getKeyDisplay("universal.nextBlock")): gui.Tr.SLocalize("navigateConflicts"),
gui.getKeyDisplay("universal.select"): gui.Tr.SLocalize("pickHunk"),
gui.getKeyDisplay("main.pickBothHunks"): gui.Tr.SLocalize("pickBothHunks"),
gui.getKeyDisplay("main.undo"): gui.Tr.SLocalize("undo"),
gui.getKeyDisplay("universal.undo"): gui.Tr.SLocalize("undo"),
})
}

Expand Down
83 changes: 70 additions & 13 deletions pkg/gui/reflog_panel.go
Expand Up @@ -104,7 +104,7 @@ func (gui *Gui) handleCreateReflogResetMenu(g *gocui.Gui, v *gocui.View) error {

type reflogAction struct {
regexStr string
action func(match []string, commitSha string, prevCommitSha string, onDone func()) (bool, error)
action func(match []string, commitSha string, onDone func()) (bool, error)
}

func (gui *Gui) reflogKey(reflogCommit *commands.Commit) string {
Expand All @@ -130,30 +130,31 @@ func (gui *Gui) setUndoReflogKey(key string) {
func (gui *Gui) reflogUndo(g *gocui.Gui, v *gocui.View) error {
reflogCommits := gui.State.ReflogCommits

// if the index of the previous reflog entry has changed, we need to start from the beginning, because it means there's been user input.
startIndex := gui.State.Undo.ReflogIdx
if gui.idxOfUndoReflogKey(gui.State.Undo.ReflogKey) != gui.State.Undo.ReflogIdx {
gui.State.Undo.UndoCount = 0
startIndex = 0
}

reflogActions := []reflogAction{
{
regexStr: `^checkout: moving from ([\S]+)`,
action: func(match []string, commitSha string, prevCommitSha string, onDone func()) (bool, error) {
action: func(match []string, commitSha string, onDone func()) (bool, error) {
if len(match) <= 1 {
return false, nil
}
return true, gui.handleCheckoutRef(match[1], handleCheckoutRefOptions{onDone: onDone, waitingStatus: gui.Tr.SLocalize("UndoingStatus")})
},
},
{
regexStr: `^commit|^rebase -i \(start\)`,
action: func(match []string, commitSha string, prevCommitSha string, onDone func()) (bool, error) {
return true, gui.handleHardResetWithAutoStash(prevCommitSha, onDone)
regexStr: `^commit|^rebase -i \(start\)|^reset: moving to|^pull`,
action: func(match []string, commitSha string, onDone func()) (bool, error) {
return true, gui.handleHardResetWithAutoStash(commitSha, onDone)
},
},
}

// if the index of the previous reflog entry has changed, we need to start from the beginning, because it means there's been user input.
startIndex := gui.State.Undo.ReflogIdx
if gui.idxOfUndoReflogKey(gui.State.Undo.ReflogKey) != gui.State.Undo.ReflogIdx {
startIndex = 0
}

for offsetIdx, reflogCommit := range reflogCommits[startIndex:] {
i := offsetIdx + startIndex
for _, action := range reflogActions {
Expand All @@ -167,12 +168,68 @@ func (gui *Gui) reflogUndo(g *gocui.Gui, v *gocui.View) error {
prevCommitSha = reflogCommits[i+1].Sha
}

nextKey := gui.reflogKey(gui.State.ReflogCommits[i+1])
nextKey := gui.reflogKey(reflogCommits[i+1])
onDone := func() {
gui.setUndoReflogKey(nextKey)
gui.State.Undo.UndoCount++
}

isMatchingAction, err := action.action(match, prevCommitSha, onDone)
if !isMatchingAction {
continue
}

return err
}
}

return nil
}

func (gui *Gui) reflogRedo(g *gocui.Gui, v *gocui.View) error {
reflogCommits := gui.State.ReflogCommits

// if the index of the previous reflog entry has changed there is nothing to redo because there's been a user action
startIndex := gui.State.Undo.ReflogIdx
if gui.idxOfUndoReflogKey(gui.State.Undo.ReflogKey) != gui.State.Undo.ReflogIdx || startIndex == 0 || gui.State.Undo.UndoCount == 0 {
return nil
}

reflogActions := []reflogAction{
{
regexStr: `^checkout: moving from [\S]+ to ([\S]+)`,
action: func(match []string, commitSha string, onDone func()) (bool, error) {
if len(match) <= 1 {
return false, nil
}
return true, gui.handleCheckoutRef(match[1], handleCheckoutRefOptions{onDone: onDone, waitingStatus: gui.Tr.SLocalize("RedoingStatus")})
},
},
{
regexStr: `^commit|^rebase -i \(start\)|^reset: moving to|^pull`,
action: func(match []string, commitSha string, onDone func()) (bool, error) {
return true, gui.handleHardResetWithAutoStash(commitSha, onDone)
},
},
}

for i := startIndex - 1; i > 0; i++ {
reflogCommit := reflogCommits[i]

for _, action := range reflogActions {
re := regexp.MustCompile(action.regexStr)
match := re.FindStringSubmatch(reflogCommit.Name)
if len(match) == 0 {
continue
}

prevKey := gui.reflogKey(reflogCommits[i-1])
onDone := func() {
gui.setUndoReflogKey(prevKey)
gui.State.Undo.UndoCount--
}

isMatchingAction, err := action.action(match, reflogCommit.Sha, prevCommitSha, onDone)
isMatchingAction, err := action.action(match, reflogCommit.Sha, onDone)
if !isMatchingAction {
continue
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/i18n/english.go
Expand Up @@ -363,6 +363,9 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "undoReflog",
Other: "undo (via reflog) (experimental)",
}, &i18n.Message{
ID: "redoReflog",
Other: "redo (via reflog) (experimental)",
}, &i18n.Message{
ID: "pop",
Other: "pop",
Expand Down Expand Up @@ -747,6 +750,9 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "UndoingStatus",
Other: "undoing",
}, &i18n.Message{
ID: "RedoingStatus",
Other: "redoing",
}, &i18n.Message{
ID: "CheckingOutStatus",
Other: "checking out",
Expand Down

0 comments on commit daecdd7

Please sign in to comment.