mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-17 00:18:05 +02:00
follow cursor when staging and unstaging a file rename
This commit is contained in:
@ -1,6 +1,10 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
// File : A file from git status
|
// File : A file from git status
|
||||||
// duplicating this for now
|
// duplicating this for now
|
||||||
@ -17,6 +21,18 @@ type File struct {
|
|||||||
ShortStatus string // e.g. 'AD', ' A', 'M ', '??'
|
ShortStatus string // e.g. 'AD', ' A', 'M ', '??'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RENAME_SEPARATOR = " -> "
|
||||||
|
|
||||||
func (f *File) IsRename() bool {
|
func (f *File) IsRename() bool {
|
||||||
return strings.Contains(f.Name, " -> ")
|
return strings.Contains(f.Name, RENAME_SEPARATOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Names returns an array containing just the filename, or in the case of a rename, the after filename and the before filename
|
||||||
|
func (f *File) Names() []string {
|
||||||
|
return strings.Split(f.Name, RENAME_SEPARATOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if the file names are the same or if a a file rename includes the filename of the other
|
||||||
|
func (f *File) Matches(f2 *File) bool {
|
||||||
|
return utils.StringArraysOverlap(f.Names(), f2.Names())
|
||||||
}
|
}
|
||||||
|
@ -279,7 +279,7 @@ func (c *GitCommand) StashSave(message string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MergeStatusFiles merge status files
|
// MergeStatusFiles merge status files
|
||||||
func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []*File) []*File {
|
func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []*File, selectedFile *File) []*File {
|
||||||
if len(oldFiles) == 0 {
|
if len(oldFiles) == 0 {
|
||||||
return newFiles
|
return newFiles
|
||||||
}
|
}
|
||||||
@ -290,10 +290,15 @@ func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []*File) []*File {
|
|||||||
result := []*File{}
|
result := []*File{}
|
||||||
for _, oldFile := range oldFiles {
|
for _, oldFile := range oldFiles {
|
||||||
for newIndex, newFile := range newFiles {
|
for newIndex, newFile := range newFiles {
|
||||||
if oldFile.Name == newFile.Name {
|
if includesInt(appendedIndexes, newIndex) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// if we just staged B and in doing so created 'A -> B' and we are currently have oldFile: A and newFile: 'A -> B', we want to wait until we come across B so the our cursor isn't jumping anywhere
|
||||||
|
waitForMatchingFile := selectedFile != nil && newFile.IsRename() && !selectedFile.IsRename() && newFile.Matches(selectedFile) && !oldFile.Matches(selectedFile)
|
||||||
|
|
||||||
|
if oldFile.Matches(newFile) && !waitForMatchingFile {
|
||||||
result = append(result, newFile)
|
result = append(result, newFile)
|
||||||
appendedIndexes = append(appendedIndexes, newIndex)
|
appendedIndexes = append(appendedIndexes, newIndex)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -541,7 +541,7 @@ func TestGitCommandMergeStatusFiles(t *testing.T) {
|
|||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
gitCmd := NewDummyGitCommand()
|
gitCmd := NewDummyGitCommand()
|
||||||
|
|
||||||
s.test(gitCmd.MergeStatusFiles(s.oldFiles, s.newFiles))
|
s.test(gitCmd.MergeStatusFiles(s.oldFiles, s.newFiles, nil))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -410,14 +410,27 @@ func (gui *Gui) handleRefreshFiles(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshStateFiles() error {
|
func (gui *Gui) refreshStateFiles() error {
|
||||||
|
// keep track of where the cursor is currently and the current file names
|
||||||
|
// when we refresh, go looking for a matching name
|
||||||
|
// move the cursor to there.
|
||||||
|
selectedFile, _ := gui.getSelectedFile()
|
||||||
|
|
||||||
// get files to stage
|
// get files to stage
|
||||||
files := gui.GitCommand.GetStatusFiles(commands.GetStatusFileOptions{})
|
files := gui.GitCommand.GetStatusFiles(commands.GetStatusFileOptions{})
|
||||||
gui.State.Files = gui.GitCommand.MergeStatusFiles(gui.State.Files, files)
|
gui.State.Files = gui.GitCommand.MergeStatusFiles(gui.State.Files, files, selectedFile)
|
||||||
|
|
||||||
if err := gui.fileWatcher.addFilesToFileWatcher(files); err != nil {
|
if err := gui.fileWatcher.addFilesToFileWatcher(files); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// let's try to find our file again and move the cursor to that
|
||||||
|
for idx, f := range gui.State.Files {
|
||||||
|
if selectedFile != nil && f.Matches(selectedFile) {
|
||||||
|
gui.State.Panels.Files.SelectedLine = idx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gui.refreshSelectedLine(&gui.State.Panels.Files.SelectedLine, len(gui.State.Files))
|
gui.refreshSelectedLine(&gui.State.Panels.Files.SelectedLine, len(gui.State.Files))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -322,3 +322,15 @@ func FindStringSubmatch(str string, regexpStr string) (bool, []string) {
|
|||||||
match := re.FindStringSubmatch(str)
|
match := re.FindStringSubmatch(str)
|
||||||
return len(match) > 0, match
|
return len(match) > 0, match
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StringArraysOverlap(strArrA []string, strArrB []string) bool {
|
||||||
|
for _, first := range strArrA {
|
||||||
|
for _, second := range strArrB {
|
||||||
|
if first == second {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user