diff options
| -rw-r--r-- | pkg/git/repository.go | 4 | ||||
| -rw-r--r-- | pkg/git/stash.go | 104 | ||||
| -rw-r--r-- | pkg/git/status.go | 67 | ||||
| -rw-r--r-- | pkg/gui/gui-navigate.go | 54 | ||||
| -rw-r--r-- | pkg/gui/gui-util.go | 78 | ||||
| -rw-r--r-- | pkg/gui/gui.go | 16 | ||||
| -rw-r--r-- | pkg/gui/keybindings.go | 100 | ||||
| -rw-r--r-- | pkg/gui/mainview.go | 44 | ||||
| -rw-r--r-- | pkg/gui/stagedview.go | 54 | ||||
| -rw-r--r-- | pkg/gui/stashview.go | 47 | ||||
| -rw-r--r-- | pkg/gui/statusview.go | 158 | ||||
| -rw-r--r-- | pkg/gui/unstagedview.go | 50 |
12 files changed, 682 insertions, 94 deletions
diff --git a/pkg/git/repository.go b/pkg/git/repository.go index e75281c..7d1fd73 100644 --- a/pkg/git/repository.go +++ b/pkg/git/repository.go @@ -25,6 +25,7 @@ type RepoEntity struct { Remotes []*Remote Commit *Commit Commits []*Commit + Stasheds []*StashedItem State RepoState } @@ -85,6 +86,9 @@ func InitializeRepository(directory string) (entity *RepoEntity, err error) { entity.loadRemotes() // set the active branch to repositories HEAD entity.Branch = entity.getActiveBranch() + if err = entity.loadStashedItems(); err != nil { + // TODO: fix here. + } if len(entity.Remotes) > 0 { // TODO: tend to take origin/master as default entity.Remote = entity.Remotes[0] diff --git a/pkg/git/stash.go b/pkg/git/stash.go new file mode 100644 index 0000000..caaaf90 --- /dev/null +++ b/pkg/git/stash.go @@ -0,0 +1,104 @@ +package git + +import ( + "regexp" + "strings" + "strconv" + + log "github.com/sirupsen/logrus" +) + +var stashCommand = "stash" + +type StashedItem struct { + StashID int + BranchName string + Hash string + Description string +} + +// // StashOption is the option argument for git stash command +// type StashOption string + +// var ( +// StashPop StashOption = "pop" +// StashPush StashOption = "push" +// StashDrop StashOption = "drop" +// ) + +// // Stash used when you want to record the current state of the working +// // directory and the index, but want to go back to a clean working directory. +// func Stash(entity *RepoEntity, option StashOption) error { +// args := make([]string, 0) +// args = append(args, stashCommand) + +// if err := GenericGitCommand(entity.AbsPath, args); err != nil { +// log.Warn("Error while stashing") +// return err +// } +// return nil +// } + +func stashGet(entity *RepoEntity, option string) string { + args := make([]string, 0) + args = append(args, stashCommand) + args = append(args, option) + out, err := GenericGitCommandWithOutput(entity.AbsPath, args) + if err != nil { + log.Warn("Error while stash command") + return "?" + } + return out +} + +func (entity *RepoEntity) loadStashedItems() error { + entity.Stasheds = make([]*StashedItem, 0) + output := stashGet(entity, "list") + stashIDRegex := regexp.MustCompile(`stash@{[\d]+}:`) + stashIDRegexInt := regexp.MustCompile(`[\d]+`) + stashBranchRegex := regexp.MustCompile(`[\w]+: `) + stashHashRegex := regexp.MustCompile(`[\w]{7}`) + + stashlist := strings.Split(output, "\n") + for _, stashitem := range stashlist { + // find id + id := stashIDRegexInt.FindString(stashIDRegex.FindString(stashitem)) + i, err := strconv.Atoi(id) + if err != nil { + // probably something isn't right let's continue over this iteration + log.Trace("cannot initiate stashed item") + continue + } + // trim id section + trimmed := stashIDRegex.Split(stashitem, 2)[1] + + // find branch + stashBranchRegexMatch :=stashBranchRegex.FindString(trimmed) + branchName := stashBranchRegexMatch[:len(stashBranchRegexMatch)-2] + + // trim branch section + trimmed = stashBranchRegex.Split(trimmed, 2)[1] + hash := stashHashRegex.FindString(trimmed) + + // trim hash + desc := stashHashRegex.Split(trimmed, 2)[1][1:] + + entity.Stasheds = append(entity.Stasheds, &StashedItem{ + StashID: i, + BranchName: branchName, + Hash: hash, + Description: desc, + }) + } + return nil +} + +func (entity *RepoEntity) Stash() (error) { + args := make([]string, 0) + args = append(args, stashCommand) + if err := GenericGitCommand(entity.AbsPath, args); err != nil { + log.Warn("Error while stashing") + return err + } + return nil +}
\ No newline at end of file diff --git a/pkg/git/status.go b/pkg/git/status.go new file mode 100644 index 0000000..e1207ee --- /dev/null +++ b/pkg/git/status.go @@ -0,0 +1,67 @@ +package git + +import ( + "os" + "regexp" + "strings" + + log "github.com/sirupsen/logrus" +) + +var statusCommand = "status" + +type File struct { + Name string + AbsPath string + X FileStatus + Y FileStatus +} + +type FileStatus rune + +var ( + StatusNotupdated FileStatus = ' ' + StatusModified FileStatus = 'M' + StatusAdded FileStatus = 'A' + StatusDeleted FileStatus = 'D' + StatusRenamed FileStatus = 'R' + StatusCopied FileStatus = 'C' + StatusUntracked FileStatus = '?' + StatusIgnored FileStatus = '!' +) + +func shortStatus(entity *RepoEntity, option string) string { + args := make([]string, 0) + args = append(args, statusCommand) + args = append(args, option) + args = append(args, "--short") + out, err := GenericGitCommandWithOutput(entity.AbsPath, args) + if err != nil { + log.Warn("Error while status command") + return "?" + } + return out +} + +func (entity *RepoEntity) LoadFiles() ([]*File, error) { + files := make([]*File, 0) + output := shortStatus(entity, "--untracked-files=all") + if len(output) == 0 { + return files, nil + } + fileslist := strings.Split(output, "\n") + for _, file := range fileslist { + x := rune(file[0]) + y := rune(file[1]) + relativePathRegex := regexp.MustCompile(`[(\w|/|.)]+`) + path := relativePathRegex.FindString(file[2:]) + + files = append(files, &File{ + Name: path, + AbsPath: entity.AbsPath + string(os.PathSeparator) + path, + X: FileStatus(x), + Y: FileStatus(y), + }) + } + return files, nil +} diff --git a/pkg/gui/gui-navigate.go b/pkg/gui/gui-navigate.go deleted file mode 100644 index 56d96de..0000000 --- a/pkg/gui/gui-navigate.go +++ /dev/null @@ -1,54 +0,0 @@ -package gui - -import ( - log "github.com/sirupsen/logrus" - "github.com/jroimartin/gocui" -) - -// focus to next view -func (gui *Gui) nextView(g *gocui.Gui, v *gocui.View) error { - var focusedViewName string - if v == nil || v.Name() == mainViews[len(mainViews)-1].Name { - focusedViewName = mainViews[0].Name - } else { - for i := range mainViews { - if v.Name() == mainViews[i].Name { - focusedViewName = mainViews[i+1].Name - break - } - if i == len(mainViews)-1 { - return nil - } - } - } - if _, err := g.SetCurrentView(focusedViewName); err != nil { - log.Warn("Loading view cannot be focused.") - return nil - } - gui.updateKeyBindingsView(g, focusedViewName) - return nil -} - -// focus to previous view -func (gui *Gui) previousView(g *gocui.Gui, v *gocui.View) error { - var focusedViewName string - if v == nil || v.Name() == mainViews[0].Name { - focusedViewName = mainViews[len(mainViews)-1].Name - } else { - for i := range mainViews { - if v.Name() == mainViews[i].Name { - focusedViewName = mainViews[i-1].Name - break - } - if i == len(mainViews)-1 { - return nil - } - } - } - if _, err := g.SetCurrentView(focusedViewName); err != nil { - log.Warn("Loading view cannot be focused.") - return nil - } - gui.updateKeyBindingsView(g, focusedViewName) - return nil -} diff --git a/pkg/gui/gui-util.go b/pkg/gui/gui-util.go index 2ae8cae..2338bac 100644 --- a/pkg/gui/gui-util.go +++ b/pkg/gui/gui-util.go @@ -1,11 +1,10 @@ package gui import ( - "sort" - "github.com/isacikgoz/gitbatch/pkg/git" "github.com/isacikgoz/gitbatch/pkg/helpers" "github.com/jroimartin/gocui" + log "github.com/sirupsen/logrus" ) // refreshes the side views of the application for given git.RepoEntity struct @@ -26,6 +25,58 @@ func (gui *Gui) refreshViews(g *gocui.Gui, entity *git.RepoEntity) error { return err } +// focus to next view +func (gui *Gui) nextViewOfGroup(g *gocui.Gui, v *gocui.View, group []viewFeature) error { + var focusedViewName string + if v == nil || v.Name() == group[len(group)-1].Name { + focusedViewName = group[0].Name + } else { + for i := range group { + if v.Name() == group[i].Name { + focusedViewName = group[i+1].Name + break + } + if i == len(group)-1 { + return nil + } + } + } + if _, err := g.SetCurrentView(focusedViewName); err != nil { + log.WithFields(log.Fields{ + "view": focusedViewName, + }).Warn("View cannot be focused.") + return nil + } + gui.updateKeyBindingsView(g, focusedViewName) + return nil +} + +// focus to previous view +func (gui *Gui) previousViewOfGroup(g *gocui.Gui, v *gocui.View, group []viewFeature) error { + var focusedViewName string + if v == nil || v.Name() == group[0].Name { + focusedViewName = group[len(group)-1].Name + } else { + for i := range group { + if v.Name() == group[i].Name { + focusedViewName = group[i-1].Name + break + } + if i == len(group)-1 { + return nil + } + } + } + if _, err := g.SetCurrentView(focusedViewName); err != nil { + log.WithFields(log.Fields{ + "view": focusedViewName, + }).Warn("View cannot be focused.") + return nil + } + gui.updateKeyBindingsView(g, focusedViewName) + return nil +} + // siwtch the app mode // TODO: switching can be made with conventional iteration func (gui *Gui) switchMode(g *gocui.Gui, v *gocui.View) error { @@ -110,29 +161,6 @@ func writeRightHandSide(v *gocui.View, text string, cx, cy int) error { return nil } -// sortByName sorts the repositories by A to Z order -func (gui *Gui) sortByName(g *gocui.Gui, v *gocui.View) error { - sort.Sort(git.Alphabetical(gui.State.Repositories)) - gui.refreshAfterSort(g) - return nil -} - -// sortByMod sorts the repositories according to last modifed date -// the top element will be the last modified -func (gui *Gui) sortByMod(g *gocui.Gui, v *gocui.View) error { - sort.Sort(git.LastModified(gui.State.Repositories)) - gui.refreshAfterSort(g) - return nil -} - -// utility function that refreshes main and side views after that -func (gui *Gui) refreshAfterSort(g *gocui.Gui) error { - gui.refreshMain(g) - entity := gui.getSelectedRepository() - gui.refreshViews(g, entity) - return nil -} - // cursor down acts like half-page down for faster scrolling func (gui *Gui) fastCursorDown(g *gocui.Gui, v *gocui.View) error { if v != nil { diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 0598383..5c32bc0 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -194,6 +194,22 @@ func (gui *Gui) layout(g *gocui.Gui) error { return nil } +// focus to next view +func (gui *Gui) nextMainView(g *gocui.Gui, v *gocui.View) error { + if err := gui.nextViewOfGroup(g, v, mainViews); err != nil { + return err + } + return nil +} + +// focus to previous view +func (gui *Gui) previousMainView(g *gocui.Gui, v *gocui.View) error { + if err := gui.previousViewOfGroup(g, v, mainViews); err != nil { + return err + } + return nil +} + // quit from the gui and end its loop func (gui *Gui) quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index b2622bc..55c86bd 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -43,7 +43,7 @@ func (gui *Gui) generateKeybindings() error { View: view.Name, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, - Handler: gui.previousView, + Handler: gui.previousMainView, Display: "←", Description: "Previous Panel", Vital: false, @@ -51,7 +51,7 @@ func (gui *Gui) generateKeybindings() error { View: view.Name, Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, - Handler: gui.nextView, + Handler: gui.nextMainView, Display: "→", Description: "Next Panel", Vital: false, @@ -59,7 +59,7 @@ func (gui *Gui) generateKeybindings() error { View: view.Name, Key: 'l', Modifier: gocui.ModNone, - Handler: gui.nextView, + Handler: gui.nextMainView, Display: "l", Description: "Previous Panel", Vital: false, @@ -67,7 +67,7 @@ func (gui *Gui) generateKeybindings() error { View: view.Name, Key: 'h', Modifier: gocui.ModNone, - Handler: gui.previousView, + Handler: gui.previousMainView, Display: "h", Description: "Next Panel", Vital: false, @@ -77,7 +77,89 @@ func (gui *Gui) generateKeybindings() error { gui.KeyBindings = append(gui.KeyBindings, binding) } } + // Statusviews common keybindings + for _, view := range statusViews { + statusKeybindings := []*KeyBinding{ + { + View: view.Name, + Key: 'c', + Modifier: gocui.ModNone, + Handler: gui.closeStatusView, + Display: "c", + Description: "Close/Cancel", + Vital: true, + }, { + View: view.Name, + Key: gocui.KeyArrowLeft, + Modifier: gocui.ModNone, + Handler: gui.previousStatusView, + Display: "←", + Description: "Previous Panel", + Vital: false, + }, { + View: view.Name, + Key: gocui.KeyArrowRight, + Modifier: gocui.ModNone, + Handler: gui.nextStatusView, + Display: "→", + Description: "Next Panel", + Vital: false, + }, { + View: view.Name, + Key: 'l', + Modifier: gocui.ModNone, + Handler: gui.nextStatusView, + Display: "l", + Description: "Previous Panel", + Vital: false, + }, { + View: view.Name, + Key: 'h', + Modifier: gocui.ModNone, + Handler: gui.previousStatusView, + Display: "h", + Description: "Next Panel", + Vital: false, + }, { + View: view.Name, + Key: gocui.KeyArrowUp, + Modifier: gocui.ModNone, + Handler: gui.statusCursorUp, + Display: "↑", + Description: "Up", + Vital: false, + }, { + View: view.Name, + Key: gocui.KeyArrowDown, + Modifier: gocui.ModNone, + Handler: gui.statusCursorDown, + Display: "↓", + Description: "Down", + Vital: false, + }, { + View: view.Name, + Key: 'k', + Modifier: gocui.ModNone, + Handler: gui.statusCursorUp, + Display: "k", + Description: "Up", + Vital: false, + }, { + View: view.Name, + Key: 'j', + Modifier: gocui.ModNone, + Handler: gui.statusCursorDown, + Display: "j", + Description: "Down", + Vital: false, + }, + } + for _, binding := range statusKeybindings { + gui.KeyBindings = append(gui.KeyBindings, binding) + } + } individualKeybindings := []*KeyBinding{ + // Main view controls { View: mainViewFeature.Name, Key: gocui.KeyArrowUp, @@ -167,6 +249,14 @@ func (gui *Gui) generateKeybindings() error { Description: "Sort repositories by Modification date", Vital: false, }, { + View: mainViewFeature.Name, + Key: 's', + Modifier: gocui.ModNone, + Handler: gui.openStatusView, + Display: "s", + Description: "Open Status", + Vital: true, + }, { View: "", Key: gocui.KeyCtrlC, Modifier: gocui.ModNone, @@ -412,7 +502,7 @@ func (gui *Gui) generateKeybindings() error { Display: "c", Description: "close/cancel", Vital: true, - }, + }, } for _, binding := range individualKeybindings { gui.KeyBindings = append(gui.KeyBindings, binding) diff --git a/pkg/gui/mainview.go b/pkg/gui/mainview.go index 58f8422..f912382 100644 --- a/pkg/gui/mainview.go +++ b/pkg/gui/mainview.go @@ -2,6 +2,7 @@ package gui import ( "fmt" + "sort" "github.com/isacikgoz/gitbatch/pkg/git" "github.com/isacikgoz/gitbatch/pkg/queue" @@ -36,6 +37,19 @@ func (gui *Gui) fillMain(g *gocui.Gui) error { return nil } +// refresh the main view and re-render the repository representations +func (gui *Gui) refreshMain(g *gocui.Gui) error { + mainView, err := g.View(mainViewFeature.Name) + if err != nil { + return err + } + mainView.Clear() + for _, r := range gui.State.Repositories { + fmt.Fprintln(mainView, gui.displayString(r)) + } + return nil +} + // moves the cursor downwards for the main view and if it goes to bottom it // prevents from going further func (gui *Gui) cursorDown(g *gocui.Gui, v *gocui.View) error { @@ -181,15 +195,25 @@ func (gui *Gui) unmarkAllRepositories(g *gocui.Gui, v *gocui.View) error { return nil } -// refresh the main view and re-render the repository representations -func (gui *Gui) refreshMain(g *gocui.Gui) error { - mainView, err := g.View(mainViewFeature.Name) - if err != nil { - return err - } - mainView.Clear() - for _, r := range gui.State.Repositories { - fmt.Fprintln(mainView, gui.displayString(r)) - } +// sortByName sorts the repositories by A to Z order +func (gui *Gui) sortByName(g *gocui.Gui, v *gocui.View) error { + sort.Sort(git.Alphabetical(gui.State.Repositories)) + gui.refreshAfterSort(g) + return nil +} + +// sortByMod sorts the repositories according to last modifed date +// the top element will be the last modified +func (gui *Gui) sortByMod(g *gocui.Gui, v *gocui.View) error { + sort.Sort(git.LastModified(gui.State.Repositories)) + gui.refreshAfterSort(g) + return nil +} + +// utility function that refreshes main and side views after that +func (gui *Gui) refreshAfterSort(g *gocui.Gui) error { + gui.refreshMain(g) + entity := gui.getSelectedRepository() + gui.refreshViews(g, entity) return nil } diff --git a/pkg/gui/stagedview.go b/pkg/gui/stagedview.go new file mode 100644 index 0000000..e3684f0 --- /dev/null +++ b/pkg/gui/stagedview.go @@ -0,0 +1,54 @@ +package gui + +import ( + "fmt" + + "github.com/isacikgoz/gitbatch/pkg/git" + "github.com/jroimartin/gocui" +) + +// staged view +func (gui *Gui) openStageView(g *gocui.Gui) error { + maxX, maxY := g.Size() + + v, err := g.SetView(stageViewFeature.Name, 6, 5, maxX/2-1, int(0.75*float32(maxY))-1) + if err != nil { + if err != gocui.ErrUnknownView { + return err + } + v.Title = stageViewFeature.Title + v.Wrap = true + } + entity := gui.getSelectedRepository() + if err := refreshStagedView(g, entity); err != nil { + return err + } + gui.updateKeyBindingsView(g, stageViewFeature.Name) + if _, err := g.SetCurrentView(stageViewFeature.Name); err != nil { + return err + } + return nil +} + +// refresh the main view and re-render the repository representations +func refreshStagedView(g *gocui.Gui, entity *git.RepoEntity) error { + stageView, err := g.View(stageViewFeature.Name) + if err != nil { + return err + } + stageView.Clear() + _, cy := stageView.Cursor() + _, oy := stageView.Origin() + files, _, err := generateFileLists(entity) + if err != nil { + return err + } + for i, file := range files { + var prefix string + if i == cy+oy { + prefix = prefix + selectionIndicator + } + fmt.Fprintf(stageView, "%s%s%s %s\n", prefix, green.Sprint(string(file.X)), red.Sprint(string(file.Y)), file.Name) + } + return nil +} diff --git a/pkg/gui/stashview.go b/pkg/gui/stashview.go new file mode 100644 index 0000000..8cdc46c --- /dev/null +++ b/pkg/gui/stashview.go @@ -0,0 +1,47 @@ +package gui + +import ( + "fmt" + + "github.com/isacikgoz/gitbatch/pkg/git" + "github.com/jroimartin/gocui" +) + +// stash view +func (gui *Gui) openStashView(g *gocui.Gui) error { + maxX, maxY := g.Size() + + v, err := g.SetView(stashViewFeature.Name, 6, int(0.75*float32(maxY)), maxX-6, maxY-3) + if err != nil { + if err != gocui.ErrUnknownView { + return err + } + v.Title = stashViewFeature.Title + v.Wrap = true + } + entity := gui.getSelectedRepository() + if err := refreshStashView(g, entity); err != nil { + return err + } + return nil +} + +// refresh the main view and re-render the repository representations +func refreshStashView(g *gocui.Gui, entity *git.RepoEntity) error { + stashView, err := g.View(stashViewFeature.Name) + if err != nil { + return err + } + stashView.Clear() + _, cy := stashView.Cursor() + _, oy := stashView.Origin() + stashedItems := entity.Stasheds + for i, stashedItem := range stashedItems { + var prefix string + if i == cy+oy { + prefix = prefix + selectionIndicator + } + fmt.Fprintf(stashView, "%s%d %s: %s (%s)\n", prefix, stashedItem.StashID, cyan.Sprint(stashedItem.BranchName), stashedItem.Description, stashedItem.Hash) + } + return nil +}
\ No newline at end of file diff --git a/pkg/gui/statusview.go b/pkg/gui/statusview.go new file mode 100644 index 0000000..b1873e8 --- /dev/null +++ b/pkg/gui/statusview.go @@ -0,0 +1,158 @@ +package gui + +import ( + "fmt" + + "github.com/isacikgoz/gitbatch/pkg/git" + "github.com/jroimartin/gocui" +) + +var ( + statusHeaderViewFeature = viewFeature{Name: "status-header", Title: " Status Header "} + // statusViewFeature = viewFeature{Name: "status", Title: " Status "} + stageViewFeature = viewFeature{Name: "staged", Title: " Staged "} + unstageViewFeature = viewFeature{Name: "unstaged", Title: " Unstaged "} + stashViewFeature = viewFeature{Name: "stash", Title: " Stash "} + + statusViews = []viewFeature{stageViewFeature, unstageViewFeature, stashViewFeature} +) + +// open the status layout +func (gui *Gui) openStatusView(g *gocui.Gui, v *gocui.View) error { + gui.openStatusHeaderView(g) + gui.openStageView(g) + gui.openUnStagedView(g) + gui.openStashView(g) + return nil +} + +// focus to next view +func (gui *Gui) nextStatusView(g *gocui.Gui, v *gocui.View) error { + if err := gui.nextViewOfGroup(g, v, statusViews); err != nil { + return err + } + return nil +} + +// focus to previous view +func (gui *Gui) previousStatusView(g *gocui.Gui, v *gocui.View) error { + if err := gui.previousViewOfGroup(g, v, statusViews); err != nil { + return err + } + return nil +} + +// moves the cursor downwards for the main view and if it goes to bottom it +// prevents from going further +func (gui *Gui) statusCursorDown(g *gocui.Gui, v *gocui.View) error { + if v != nil { + cx, cy := v.Cursor() + ox, oy := v.Origin() + ly := len(v.BufferLines()) - 2 // why magic number? have no idea + + // if we are at the end we just return + if cy+oy == ly { + return nil + } + if err := v.SetCursor(cx, cy+1); err != nil { + + if err := v.SetOrigin(ox, oy+1); err != nil { + return err + } + } + entity := gui.getSelectedRepository() + if err := refreshStatusView(v.Name(), g, entity); err != nil { + return err + } + } + return nil +} + +// moves the cursor upwards for the main view +func (gui *Gui) statusCursorUp(g *gocui.Gui, v *gocui.View) error { + if v != nil { + ox, oy := v.Origin() + cx, cy := v.Cursor() + if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 { + if err := v.SetOrigin(ox, oy-1); err != nil { + return err + } + } + entity := gui.getSelectedRepository() + if err := refreshStatusView(v.Name(), g, entity); err != nil { + return err + } + } + return nil +} + +// header og the status layout +func (gui *Gui) openStatusHeaderView(g *gocui.Gui) error { + maxX, _ := g.Size() + entity := gui.getSelectedRepository() + v, err := g.SetView(statusHeaderViewFeature.Name, 6, 2, maxX-6, 4) + if err != nil { + if err != gocui.ErrUnknownView { + return err + } + fmt.Fprintln(v, entity.AbsPath) + // v.Frame = false + v.Wrap = true + } + return nil +} + +// close the opened stat views +func (gui *Gui) closeStatusView(g *gocui.Gui, v *gocui.View) error { + if err := g.DeleteView(stashViewFeature.Name); err != nil { + return err + } + if err := g.DeleteView(unstageViewFeature.Name); err != nil { + return err + } + if err := g.DeleteView(stageViewFeature.Name); err != nil { + return err + } + if err := g.DeleteView(statusHeaderViewFeature.Name); err != nil { + return err + } + if _, err := g.SetCurrentView(mainViewFeature.Name); err != nil { + return err + } + gui.updateKeyBindingsView(g, mainViewFeature.Name) + return nil +} + +func generateFileLists(entity *git.RepoEntity) (staged, unstaged []*git.File, err error) { + files, err := entity.LoadFiles() + if err != nil { + return nil, nil, err + } + for _, file := range files { + if file.X != git.StatusNotupdated && file.X != git.StatusUntracked && file.X != git.StatusIgnored { + staged = append(staged, file) + } + if file.Y != git.StatusNotupdated { + unstaged = append(unstaged, file) + } + } + return staged, unstaged, err +} + +func refreshStatusView(viewName string, g *gocui.Gui, entity *git.RepoEntity) error { + switch viewName { + case stageViewFeature.Name: + if err := refreshStagedView(g, entity); err != nil { + return err + } + case unstageViewFeature.Name: + if err := refreshUnstagedView(g, entity); err != nil { + return err + } + case stashViewFeature.Name: + if err := refreshStashView(g, entity); err != nil { + return err + } + } + return nil +} diff --git a/pkg/gui/unstagedview.go b/pkg/gui/unstagedview.go new file mode 100644 index 0000000..5ee25a7 --- /dev/null +++ b/pkg/gui/unstagedview.go @@ -0,0 +1,50 @@ +package gui + +import ( + "fmt" + + "github.com/isacikgoz/gitbatch/pkg/git" + "github.com/jroimartin/gocui" +) + +// not staged view +func (gui *Gui) openUnStagedView(g *gocui.Gui) error { + maxX, maxY := g.Size() + + v, err := g.SetView(unstageViewFeature.Name, maxX/2+1, 5, maxX-6, int(0.75*float32(maxY))-1) + if err != nil { + if err != gocui.ErrUnknownView { + return err + } + v.Title = unstageViewFeature.Title + v.Wrap = true + } + entity := gui.getSelectedRepository() + if err := refreshUnstagedView(g, entity); err != nil { + return err + } + return nil +} + +// refresh the main view and re-render the repository representations +func refreshUnstagedView(g *gocui.Gui, entity *git.RepoEntity) error { + stageView, err := g.View(unstageViewFeature.Name) + if err != nil { + return err + } + stageView.Clear() + _, cy := stageView.Cursor() + _, oy := stageView.Origin() + _, files, err := generateFileLists(entity) + if err != nil { + return err + } + for i, file := range files { + var prefix string + if i == cy+oy { + prefix = prefix + selectionIndicator + } + fmt.Fprintf(stageView, "%s%s%s %s\n", prefix, green.Sprint(string(file.X)), red.Sprint(string(file.Y)), file.Name) + } + return nil +}
\ No newline at end of file |
