mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-02-03 13:21:56 +02:00
big refactor to give our enums actual types
This commit is contained in:
parent
9e85d37fb9
commit
7d62f103e4
@ -10,8 +10,10 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PatchLineKind int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PATCH_HEADER = iota
|
PATCH_HEADER PatchLineKind = iota
|
||||||
COMMIT_SHA
|
COMMIT_SHA
|
||||||
COMMIT_DESCRIPTION
|
COMMIT_DESCRIPTION
|
||||||
HUNK_HEADER
|
HUNK_HEADER
|
||||||
@ -24,7 +26,7 @@ const (
|
|||||||
// the job of this file is to parse a diff, find out where the hunks begin and end, which lines are stageable, and how to find the next hunk from the current position or the next stageable line from the current position.
|
// the job of this file is to parse a diff, find out where the hunks begin and end, which lines are stageable, and how to find the next hunk from the current position or the next stageable line from the current position.
|
||||||
|
|
||||||
type PatchLine struct {
|
type PatchLine struct {
|
||||||
Kind int
|
Kind PatchLineKind
|
||||||
Content string // something like '+ hello' (note the first character is not removed)
|
Content string // something like '+ hello' (note the first character is not removed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +146,7 @@ func parsePatch(patch string) ([]int, []int, []*PatchLine, error) {
|
|||||||
pastFirstHunkHeader := false
|
pastFirstHunkHeader := false
|
||||||
pastCommitDescription := true
|
pastCommitDescription := true
|
||||||
patchLines := make([]*PatchLine, len(lines))
|
patchLines := make([]*PatchLine, len(lines))
|
||||||
var lineKind int
|
var lineKind PatchLineKind
|
||||||
var firstChar string
|
var firstChar string
|
||||||
for index, line := range lines {
|
for index, line := range lines {
|
||||||
firstChar = " "
|
firstChar = " "
|
||||||
|
@ -9,8 +9,10 @@ type Dimensions struct {
|
|||||||
Y1 int
|
Y1 int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Direction int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ROW = iota
|
ROW Direction = iota
|
||||||
COLUMN
|
COLUMN
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,10 +28,10 @@ const (
|
|||||||
|
|
||||||
type Box struct {
|
type Box struct {
|
||||||
// Direction decides how the children boxes are laid out. ROW means the children will each form a row i.e. that they will be stacked on top of eachother.
|
// Direction decides how the children boxes are laid out. ROW means the children will each form a row i.e. that they will be stacked on top of eachother.
|
||||||
Direction int // ROW or COLUMN
|
Direction Direction
|
||||||
|
|
||||||
// function which takes the width and height assigned to the box and decides which orientation it will have
|
// function which takes the width and height assigned to the box and decides which orientation it will have
|
||||||
ConditionalDirection func(width int, height int) int
|
ConditionalDirection func(width int, height int) Direction
|
||||||
|
|
||||||
Children []*Box
|
Children []*Box
|
||||||
|
|
||||||
@ -120,7 +122,7 @@ func (b *Box) isStatic() bool {
|
|||||||
return b.Size > 0
|
return b.Size > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Box) getDirection(width int, height int) int {
|
func (b *Box) getDirection(width int, height int) Direction {
|
||||||
if b.ConditionalDirection != nil {
|
if b.ConditionalDirection != nil {
|
||||||
return b.ConditionalDirection(width, height)
|
return b.ConditionalDirection(width, height)
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ func TestArrangeWindows(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Box with COLUMN direction only on wide boxes with narrow box",
|
"Box with COLUMN direction only on wide boxes with narrow box",
|
||||||
&Box{ConditionalDirection: func(width int, height int) int {
|
&Box{ConditionalDirection: func(width int, height int) Direction {
|
||||||
if width > 4 {
|
if width > 4 {
|
||||||
return COLUMN
|
return COLUMN
|
||||||
} else {
|
} else {
|
||||||
@ -111,7 +111,7 @@ func TestArrangeWindows(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Box with COLUMN direction only on wide boxes with wide box",
|
"Box with COLUMN direction only on wide boxes with wide box",
|
||||||
&Box{ConditionalDirection: func(width int, height int) int {
|
&Box{ConditionalDirection: func(width int, height int) Direction {
|
||||||
if width > 4 {
|
if width > 4 {
|
||||||
return COLUMN
|
return COLUMN
|
||||||
} else {
|
} else {
|
||||||
|
@ -293,7 +293,7 @@ func (gui *Gui) deleteNamedBranch(selectedBranch *models.Branch, force bool) err
|
|||||||
}
|
}
|
||||||
return gui.createErrorPanel(errMessage)
|
return gui.createErrorPanel(errMessage)
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{BRANCHES}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{BRANCHES}})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -408,7 +408,7 @@ func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error {
|
|||||||
} else {
|
} else {
|
||||||
err := gui.GitCommand.FastForward(branch.Name, remoteName, remoteBranchName, gui.promptUserForCredential)
|
err := gui.GitCommand.FastForward(branch.Name, remoteName, remoteBranchName, gui.promptUserForCredential)
|
||||||
gui.handleCredentialsPopup(err)
|
gui.handleCredentialsPopup(err)
|
||||||
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{BRANCHES}})
|
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{BRANCHES}})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
|
@ -437,7 +437,7 @@ func (gui *Gui) handleCommitRevert(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
gui.State.Panels.Commits.SelectedLineIdx++
|
gui.State.Panels.Commits.SelectedLineIdx++
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI, scope: []int{COMMITS, BRANCHES}})
|
return gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI, scope: []RefreshableView{COMMITS, BRANCHES}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleViewCommitFiles() error {
|
func (gui *Gui) handleViewCommitFiles() error {
|
||||||
@ -527,7 +527,7 @@ func (gui *Gui) handleCreateLightweightTag(commitSha string) error {
|
|||||||
if err := gui.GitCommand.CreateLightweightTag(response, commitSha); err != nil {
|
if err := gui.GitCommand.CreateLightweightTag(response, commitSha); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{COMMITS, TAGS}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{COMMITS, TAGS}})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -560,7 +560,7 @@ func (gui *Gui) handleOpenSearchForCommitsPanel(g *gocui.Gui, v *gocui.View) err
|
|||||||
// we usually lazyload these commits but now that we're searching we need to load them now
|
// we usually lazyload these commits but now that we're searching we need to load them now
|
||||||
if gui.State.Panels.Commits.LimitCommits {
|
if gui.State.Panels.Commits.LimitCommits {
|
||||||
gui.State.Panels.Commits.LimitCommits = false
|
gui.State.Panels.Commits.LimitCommits = false
|
||||||
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{COMMITS}}); err != nil {
|
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{COMMITS}}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -572,7 +572,7 @@ func (gui *Gui) handleGotoBottomForCommitsPanel(g *gocui.Gui, v *gocui.View) err
|
|||||||
// we usually lazyload these commits but now that we're searching we need to load them now
|
// we usually lazyload these commits but now that we're searching we need to load them now
|
||||||
if gui.State.Panels.Commits.LimitCommits {
|
if gui.State.Panels.Commits.LimitCommits {
|
||||||
gui.State.Panels.Commits.LimitCommits = false
|
gui.State.Panels.Commits.LimitCommits = false
|
||||||
if err := gui.refreshSidePanels(refreshOptions{mode: SYNC, scope: []int{COMMITS}}); err != nil {
|
if err := gui.refreshSidePanels(refreshOptions{mode: SYNC, scope: []RefreshableView{COMMITS}}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,10 @@ import (
|
|||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ContextKind int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SIDE_CONTEXT int = iota
|
SIDE_CONTEXT ContextKind = iota
|
||||||
MAIN_CONTEXT
|
MAIN_CONTEXT
|
||||||
TEMPORARY_POPUP
|
TEMPORARY_POPUP
|
||||||
PERSISTENT_POPUP
|
PERSISTENT_POPUP
|
||||||
@ -126,7 +128,7 @@ type Context interface {
|
|||||||
HandleFocus() error
|
HandleFocus() error
|
||||||
HandleFocusLost() error
|
HandleFocusLost() error
|
||||||
HandleRender() error
|
HandleRender() error
|
||||||
GetKind() int
|
GetKind() ContextKind
|
||||||
GetViewName() string
|
GetViewName() string
|
||||||
GetWindowName() string
|
GetWindowName() string
|
||||||
SetWindowName(string)
|
SetWindowName(string)
|
||||||
@ -143,7 +145,7 @@ type BasicContext struct {
|
|||||||
OnFocusLost func() error
|
OnFocusLost func() error
|
||||||
OnRender func() error
|
OnRender func() error
|
||||||
OnGetOptionsMap func() map[string]string
|
OnGetOptionsMap func() map[string]string
|
||||||
Kind int
|
Kind ContextKind
|
||||||
Key string
|
Key string
|
||||||
ViewName string
|
ViewName string
|
||||||
}
|
}
|
||||||
@ -194,7 +196,7 @@ func (c BasicContext) HandleFocusLost() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c BasicContext) GetKind() int {
|
func (c BasicContext) GetKind() ContextKind {
|
||||||
return c.Kind
|
return c.Kind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ func (gui *Gui) handleCreateDiscardMenu() error {
|
|||||||
if err := gui.GitCommand.DiscardAllDirChanges(node); err != nil {
|
if err := gui.GitCommand.DiscardAllDirChanges(node); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -28,7 +28,7 @@ func (gui *Gui) handleCreateDiscardMenu() error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ func (gui *Gui) handleCreateDiscardMenu() error {
|
|||||||
if err := gui.GitCommand.DiscardAllFileChanges(file); err != nil {
|
if err := gui.GitCommand.DiscardAllFileChanges(file); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ func (gui *Gui) handleCreateDiscardMenu() error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
42
pkg/gui/errors.go
Normal file
42
pkg/gui/errors.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package gui
|
||||||
|
|
||||||
|
import "github.com/go-errors/errors"
|
||||||
|
|
||||||
|
// SentinelErrors are the errors that have special meaning and need to be checked
|
||||||
|
// by calling functions. The less of these, the better
|
||||||
|
type SentinelErrors struct {
|
||||||
|
ErrSubProcess error
|
||||||
|
ErrNoFiles error
|
||||||
|
ErrSwitchRepo error
|
||||||
|
ErrRestart error
|
||||||
|
}
|
||||||
|
|
||||||
|
const UNKNOWN_VIEW_ERROR_MSG = "unknown view"
|
||||||
|
|
||||||
|
// GenerateSentinelErrors makes the sentinel errors for the gui. We're defining it here
|
||||||
|
// because we can't do package-scoped errors with localization, and also because
|
||||||
|
// it seems like package-scoped variables are bad in general
|
||||||
|
// https://dave.cheney.net/2017/06/11/go-without-package-scoped-variables
|
||||||
|
// In the future it would be good to implement some of the recommendations of
|
||||||
|
// that article. For now, if we don't need an error to be a sentinel, we will just
|
||||||
|
// define it inline. This has implications for error messages that pop up everywhere
|
||||||
|
// in that we'll be duplicating the default values. We may need to look at
|
||||||
|
// having a default localisation bundle defined, and just using keys-only when
|
||||||
|
// localising things in the code.
|
||||||
|
func (gui *Gui) GenerateSentinelErrors() {
|
||||||
|
gui.Errors = SentinelErrors{
|
||||||
|
ErrSubProcess: errors.New(gui.Tr.RunningSubprocess),
|
||||||
|
ErrNoFiles: errors.New(gui.Tr.NoChangedFiles),
|
||||||
|
ErrSwitchRepo: errors.New("switching repo"),
|
||||||
|
ErrRestart: errors.New("restarting"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) sentinelErrorsArr() []error {
|
||||||
|
return []error{
|
||||||
|
gui.Errors.ErrSubProcess,
|
||||||
|
gui.Errors.ErrNoFiles,
|
||||||
|
gui.Errors.ErrSwitchRepo,
|
||||||
|
gui.Errors.ErrRestart,
|
||||||
|
}
|
||||||
|
}
|
@ -117,7 +117,7 @@ func (gui *Gui) watchFilesForChanges() {
|
|||||||
}
|
}
|
||||||
// only refresh if we're not already
|
// only refresh if we're not already
|
||||||
if !gui.State.IsRefreshingFiles {
|
if !gui.State.IsRefreshingFiles {
|
||||||
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{FILES}})
|
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// watch for errors
|
// watch for errors
|
||||||
|
@ -256,7 +256,7 @@ func (gui *Gui) handleFilePress() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.refreshSidePanels(refreshOptions{scope: []int{FILES}}); err != nil {
|
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +287,7 @@ func (gui *Gui) handleStageAll(g *gocui.Gui, v *gocui.View) error {
|
|||||||
_ = gui.surfaceError(err)
|
_ = gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.refreshSidePanels(refreshOptions{scope: []int{FILES}}); err != nil {
|
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,7 +333,7 @@ func (gui *Gui) handleIgnoreFile() error {
|
|||||||
if err := gui.GitCommand.Ignore(node.GetPath()); err != nil {
|
if err := gui.GitCommand.Ignore(node.GetPath()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -346,7 +346,7 @@ func (gui *Gui) handleIgnoreFile() error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleWIPCommitPress(g *gocui.Gui, filesView *gocui.View) error {
|
func (gui *Gui) handleWIPCommitPress(g *gocui.Gui, filesView *gocui.View) error {
|
||||||
@ -522,7 +522,7 @@ func (gui *Gui) handleFileOpen(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleRefreshFiles(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleRefreshFiles(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshStateFiles() error {
|
func (gui *Gui) refreshStateFiles() error {
|
||||||
|
@ -22,14 +22,40 @@ func (gui *Gui) rerenderViewsWithScreenModeDependentContent() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: GENERICS
|
||||||
|
func nextIntInCycle(sl []WindowMaximisation, current WindowMaximisation) WindowMaximisation {
|
||||||
|
for i, val := range sl {
|
||||||
|
if val == current {
|
||||||
|
if i == len(sl)-1 {
|
||||||
|
return sl[0]
|
||||||
|
}
|
||||||
|
return sl[i+1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sl[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: GENERICS
|
||||||
|
func prevIntInCycle(sl []WindowMaximisation, current WindowMaximisation) WindowMaximisation {
|
||||||
|
for i, val := range sl {
|
||||||
|
if val == current {
|
||||||
|
if i > 0 {
|
||||||
|
return sl[i-1]
|
||||||
|
}
|
||||||
|
return sl[len(sl)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sl[len(sl)-1]
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) nextScreenMode(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) nextScreenMode(g *gocui.Gui, v *gocui.View) error {
|
||||||
gui.State.ScreenMode = utils.NextIntInCycle([]int{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode)
|
gui.State.ScreenMode = nextIntInCycle([]WindowMaximisation{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode)
|
||||||
|
|
||||||
return gui.rerenderViewsWithScreenModeDependentContent()
|
return gui.rerenderViewsWithScreenModeDependentContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) prevScreenMode(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) prevScreenMode(g *gocui.Gui, v *gocui.View) error {
|
||||||
gui.State.ScreenMode = utils.PrevIntInCycle([]int{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode)
|
gui.State.ScreenMode = prevIntInCycle([]WindowMaximisation{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode)
|
||||||
|
|
||||||
return gui.rerenderViewsWithScreenModeDependentContent()
|
return gui.rerenderViewsWithScreenModeDependentContent()
|
||||||
}
|
}
|
||||||
@ -179,7 +205,7 @@ func (gui *Gui) fetch(canPromptForCredentials bool) (err error) {
|
|||||||
_ = gui.createErrorPanel(gui.Tr.PassUnameWrong)
|
_ = gui.createErrorPanel(gui.Tr.PassUnameWrong)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = gui.refreshSidePanels(refreshOptions{scope: []int{BRANCHES, COMMITS, REMOTES, TAGS}, mode: ASYNC})
|
_ = gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, COMMITS, REMOTES, TAGS}, mode: ASYNC})
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/golang-collections/collections/stack"
|
"github.com/golang-collections/collections/stack"
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
@ -33,8 +31,14 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// screen sizing determines how much space your selected window takes up (window
|
||||||
|
// as in panel, not your terminal's window). Sometimes you want a bit more space
|
||||||
|
// to see the contents of a panel, and this keeps track of how much maximisation
|
||||||
|
// you've set
|
||||||
|
type WindowMaximisation int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SCREEN_NORMAL int = iota
|
SCREEN_NORMAL WindowMaximisation = iota
|
||||||
SCREEN_HALF
|
SCREEN_HALF
|
||||||
SCREEN_FULL
|
SCREEN_FULL
|
||||||
)
|
)
|
||||||
@ -44,45 +48,6 @@ const StartupPopupVersion = 3
|
|||||||
// OverlappingEdges determines if panel edges overlap
|
// OverlappingEdges determines if panel edges overlap
|
||||||
var OverlappingEdges = false
|
var OverlappingEdges = false
|
||||||
|
|
||||||
// SentinelErrors are the errors that have special meaning and need to be checked
|
|
||||||
// by calling functions. The less of these, the better
|
|
||||||
type SentinelErrors struct {
|
|
||||||
ErrSubProcess error
|
|
||||||
ErrNoFiles error
|
|
||||||
ErrSwitchRepo error
|
|
||||||
ErrRestart error
|
|
||||||
}
|
|
||||||
|
|
||||||
const UNKNOWN_VIEW_ERROR_MSG = "unknown view"
|
|
||||||
|
|
||||||
// GenerateSentinelErrors makes the sentinel errors for the gui. We're defining it here
|
|
||||||
// because we can't do package-scoped errors with localization, and also because
|
|
||||||
// it seems like package-scoped variables are bad in general
|
|
||||||
// https://dave.cheney.net/2017/06/11/go-without-package-scoped-variables
|
|
||||||
// In the future it would be good to implement some of the recommendations of
|
|
||||||
// that article. For now, if we don't need an error to be a sentinel, we will just
|
|
||||||
// define it inline. This has implications for error messages that pop up everywhere
|
|
||||||
// in that we'll be duplicating the default values. We may need to look at
|
|
||||||
// having a default localisation bundle defined, and just using keys-only when
|
|
||||||
// localising things in the code.
|
|
||||||
func (gui *Gui) GenerateSentinelErrors() {
|
|
||||||
gui.Errors = SentinelErrors{
|
|
||||||
ErrSubProcess: errors.New(gui.Tr.RunningSubprocess),
|
|
||||||
ErrNoFiles: errors.New(gui.Tr.NoChangedFiles),
|
|
||||||
ErrSwitchRepo: errors.New("switching repo"),
|
|
||||||
ErrRestart: errors.New("restarting"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) sentinelErrorsArr() []error {
|
|
||||||
return []error{
|
|
||||||
gui.Errors.ErrSubProcess,
|
|
||||||
gui.Errors.ErrNoFiles,
|
|
||||||
gui.Errors.ErrSwitchRepo,
|
|
||||||
gui.Errors.ErrRestart,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gui wraps the gocui Gui object which handles rendering and events
|
// Gui wraps the gocui Gui object which handles rendering and events
|
||||||
type Gui struct {
|
type Gui struct {
|
||||||
g *gocui.Gui
|
g *gocui.Gui
|
||||||
@ -151,7 +116,7 @@ type lBlPanelState struct {
|
|||||||
LastLineIdx int
|
LastLineIdx int
|
||||||
Diff string
|
Diff string
|
||||||
PatchParser *patch.PatchParser
|
PatchParser *patch.PatchParser
|
||||||
SelectMode int // one of LINE, HUNK, or RANGE
|
SelectMode SelectMode
|
||||||
SecondaryFocused bool // this is for if we show the left or right panel
|
SecondaryFocused bool // this is for if we show the left or right panel
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,8 +220,10 @@ type searchingState struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// startup stages so we don't need to load everything at once
|
// startup stages so we don't need to load everything at once
|
||||||
|
type StartupStage int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
INITIAL = iota
|
INITIAL StartupStage = iota
|
||||||
COMPLETE
|
COMPLETE
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -331,13 +298,13 @@ type guiState struct {
|
|||||||
RetainOriginalDir bool
|
RetainOriginalDir bool
|
||||||
IsRefreshingFiles bool
|
IsRefreshingFiles bool
|
||||||
Searching searchingState
|
Searching searchingState
|
||||||
ScreenMode int
|
ScreenMode WindowMaximisation
|
||||||
SideView *gocui.View
|
SideView *gocui.View
|
||||||
Ptmx *os.File
|
Ptmx *os.File
|
||||||
PrevMainWidth int
|
PrevMainWidth int
|
||||||
PrevMainHeight int
|
PrevMainHeight int
|
||||||
OldInformation string
|
OldInformation string
|
||||||
StartupStage int // one of INITIAL and COMPLETE. Allows us to not load everything at once
|
StartupStage StartupStage // Allows us to not load everything at once
|
||||||
|
|
||||||
Modes Modes
|
Modes Modes
|
||||||
|
|
||||||
|
@ -16,8 +16,10 @@ import (
|
|||||||
// use cases
|
// use cases
|
||||||
|
|
||||||
// these represent what select mode we're in
|
// these represent what select mode we're in
|
||||||
|
type SelectMode int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LINE = iota
|
LINE SelectMode = iota
|
||||||
RANGE
|
RANGE
|
||||||
HUNK
|
HUNK
|
||||||
)
|
)
|
||||||
|
@ -23,7 +23,7 @@ type ListContext struct {
|
|||||||
|
|
||||||
Gui *Gui
|
Gui *Gui
|
||||||
ResetMainViewOriginOnFocus bool
|
ResetMainViewOriginOnFocus bool
|
||||||
Kind int
|
Kind ContextKind
|
||||||
ParentContext Context
|
ParentContext Context
|
||||||
// we can't know on the calling end whether a Context is actually a nil value without reflection, so we're storing this flag here to tell us. There has got to be a better way around this.
|
// we can't know on the calling end whether a Context is actually a nil value without reflection, so we're storing this flag here to tell us. There has got to be a better way around this.
|
||||||
hasParent bool
|
hasParent bool
|
||||||
@ -102,7 +102,7 @@ func (lc *ListContext) GetKey() string {
|
|||||||
return lc.ContextKey
|
return lc.ContextKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lc *ListContext) GetKind() int {
|
func (lc *ListContext) GetKind() ContextKind {
|
||||||
return lc.Kind
|
return lc.Kind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,8 +20,10 @@ type refreshMainOpts struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// constants for updateTask's kind field
|
// constants for updateTask's kind field
|
||||||
|
type TaskKind int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RENDER_STRING = iota
|
RENDER_STRING TaskKind = iota
|
||||||
RENDER_STRING_WITHOUT_SCROLL
|
RENDER_STRING_WITHOUT_SCROLL
|
||||||
RUN_FUNCTION
|
RUN_FUNCTION
|
||||||
RUN_COMMAND
|
RUN_COMMAND
|
||||||
@ -29,14 +31,14 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type updateTask interface {
|
type updateTask interface {
|
||||||
GetKind() int
|
GetKind() TaskKind
|
||||||
}
|
}
|
||||||
|
|
||||||
type renderStringTask struct {
|
type renderStringTask struct {
|
||||||
str string
|
str string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *renderStringTask) GetKind() int {
|
func (t *renderStringTask) GetKind() TaskKind {
|
||||||
return RENDER_STRING
|
return RENDER_STRING
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +50,7 @@ type renderStringWithoutScrollTask struct {
|
|||||||
str string
|
str string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *renderStringWithoutScrollTask) GetKind() int {
|
func (t *renderStringWithoutScrollTask) GetKind() TaskKind {
|
||||||
return RENDER_STRING_WITHOUT_SCROLL
|
return RENDER_STRING_WITHOUT_SCROLL
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +63,7 @@ type runCommandTask struct {
|
|||||||
prefix string
|
prefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *runCommandTask) GetKind() int {
|
func (t *runCommandTask) GetKind() TaskKind {
|
||||||
return RUN_COMMAND
|
return RUN_COMMAND
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +80,7 @@ type runPtyTask struct {
|
|||||||
prefix string
|
prefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *runPtyTask) GetKind() int {
|
func (t *runPtyTask) GetKind() TaskKind {
|
||||||
return RUN_PTY
|
return RUN_PTY
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +97,7 @@ type runFunctionTask struct {
|
|||||||
f func(chan struct{}) error
|
f func(chan struct{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *runFunctionTask) GetKind() int {
|
func (t *runFunctionTask) GetKind() TaskKind {
|
||||||
return RUN_FUNCTION
|
return RUN_FUNCTION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +303,7 @@ func (gui *Gui) handleEscapeMerge() error {
|
|||||||
gui.takeOverScrolling()
|
gui.takeOverScrolling()
|
||||||
|
|
||||||
gui.State.Panels.Merging.EditHistory = stack.New()
|
gui.State.Panels.Merging.EditHistory = stack.New()
|
||||||
if err := gui.refreshSidePanels(refreshOptions{scope: []int{FILES}}); err != nil {
|
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// it's possible this method won't be called from the merging view so we need to
|
// it's possible this method won't be called from the merging view so we need to
|
||||||
@ -318,7 +318,7 @@ func (gui *Gui) handleCompleteMerge() error {
|
|||||||
if err := gui.stageSelectedFile(); err != nil {
|
if err := gui.stageSelectedFile(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := gui.refreshSidePanels(refreshOptions{scope: []int{FILES}}); err != nil {
|
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// if we got conflicts after unstashing, we don't want to call any git
|
// if we got conflicts after unstashing, we don't want to call any git
|
||||||
|
@ -63,7 +63,7 @@ func (gui *Gui) handleDeleteRemoteBranch(g *gocui.Gui, v *gocui.View) error {
|
|||||||
err := gui.GitCommand.DeleteRemoteBranch(remoteBranch.RemoteName, remoteBranch.Name, gui.promptUserForCredential)
|
err := gui.GitCommand.DeleteRemoteBranch(remoteBranch.RemoteName, remoteBranch.Name, gui.promptUserForCredential)
|
||||||
gui.handleCredentialsPopup(err)
|
gui.handleCredentialsPopup(err)
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{BRANCHES, REMOTES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, REMOTES}})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -94,7 +94,7 @@ func (gui *Gui) handleSetBranchUpstream(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{BRANCHES, REMOTES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, REMOTES}})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ func (gui *Gui) handleAddRemote(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if err := gui.GitCommand.AddRemote(remoteName, remoteUrl); err != nil {
|
if err := gui.GitCommand.AddRemote(remoteName, remoteUrl); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{REMOTES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{REMOTES}})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -116,7 +116,7 @@ func (gui *Gui) handleRemoveRemote(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{BRANCHES, REMOTES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, REMOTES}})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -164,7 +164,7 @@ func (gui *Gui) handleEditRemote(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if err := gui.GitCommand.UpdateRemoteUrl(updatedRemoteName, updatedRemoteUrl); err != nil {
|
if err := gui.GitCommand.UpdateRemoteUrl(updatedRemoteName, updatedRemoteUrl); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{BRANCHES, REMOTES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, REMOTES}})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -184,6 +184,6 @@ func (gui *Gui) handleFetchRemote(g *gocui.Gui, v *gocui.View) error {
|
|||||||
err := gui.GitCommand.FetchRemote(remote.Name, gui.promptUserForCredential)
|
err := gui.GitCommand.FetchRemote(remote.Name, gui.promptUserForCredential)
|
||||||
gui.handleCredentialsPopup(err)
|
gui.handleCredentialsPopup(err)
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{BRANCHES, REMOTES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, REMOTES}})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ func (gui *Gui) resetToRef(ref string, strength string, options oscommands.RunCo
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.refreshSidePanels(refreshOptions{scope: []int{FILES, BRANCHES, REFLOG, COMMITS}}); err != nil {
|
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES, BRANCHES, REFLOG, COMMITS}}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ func (gui *Gui) applySelection(reverse bool, state *lBlPanelState) error {
|
|||||||
state.SelectMode = LINE
|
state.SelectMode = LINE
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.refreshSidePanels(refreshOptions{scope: []int{FILES}}); err != nil {
|
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := gui.refreshStagingPanel(false, -1, state); err != nil {
|
if err := gui.refreshStagingPanel(false, -1, state); err != nil {
|
||||||
|
@ -110,7 +110,7 @@ func (gui *Gui) stashDo(method string) error {
|
|||||||
if err := gui.GitCommand.StashDo(stashEntry.Index, method); err != nil {
|
if err := gui.GitCommand.StashDo(stashEntry.Index, method); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{STASH, FILES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{STASH, FILES}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleStashSave(stashFunc func(message string) error) error {
|
func (gui *Gui) handleStashSave(stashFunc func(message string) error) error {
|
||||||
@ -124,7 +124,7 @@ func (gui *Gui) handleStashSave(stashFunc func(message string) error) error {
|
|||||||
if err := stashFunc(stashComment); err != nil {
|
if err := stashFunc(stashComment); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{STASH, FILES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{STASH, FILES}})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ func (gui *Gui) removeSubmodule(submodule *models.SubmoduleConfig) error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{SUBMODULES, FILES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES, FILES}})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ func (gui *Gui) resetSubmodule(submodule *models.SubmoduleConfig) error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{FILES, SUBMODULES}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES, SUBMODULES}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleAddSubmodule() error {
|
func (gui *Gui) handleAddSubmodule() error {
|
||||||
@ -144,7 +144,7 @@ func (gui *Gui) handleAddSubmodule() error {
|
|||||||
err := gui.GitCommand.SubmoduleAdd(submoduleName, submodulePath, submoduleUrl)
|
err := gui.GitCommand.SubmoduleAdd(submoduleName, submodulePath, submoduleUrl)
|
||||||
gui.handleCredentialsPopup(err)
|
gui.handleCredentialsPopup(err)
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{SUBMODULES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -164,7 +164,7 @@ func (gui *Gui) handleEditSubmoduleUrl(submodule *models.SubmoduleConfig) error
|
|||||||
err := gui.GitCommand.SubmoduleUpdateUrl(submodule.Name, submodule.Path, newUrl)
|
err := gui.GitCommand.SubmoduleUpdateUrl(submodule.Name, submodule.Path, newUrl)
|
||||||
gui.handleCredentialsPopup(err)
|
gui.handleCredentialsPopup(err)
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{SUBMODULES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -175,7 +175,7 @@ func (gui *Gui) handleSubmoduleInit(submodule *models.SubmoduleConfig) error {
|
|||||||
err := gui.GitCommand.SubmoduleInit(submodule.Path)
|
err := gui.GitCommand.SubmoduleInit(submodule.Path)
|
||||||
gui.handleCredentialsPopup(err)
|
gui.handleCredentialsPopup(err)
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{SUBMODULES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{SUBMODULES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -233,7 +233,7 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{SUBMODULES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -245,7 +245,7 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{SUBMODULES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -257,7 +257,7 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{SUBMODULES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -271,6 +271,6 @@ func (gui *Gui) handleUpdateSubmodule(submodule *models.SubmoduleConfig) error {
|
|||||||
err := gui.GitCommand.SubmoduleUpdate(submodule.Path)
|
err := gui.GitCommand.SubmoduleUpdate(submodule.Path)
|
||||||
gui.handleCredentialsPopup(err)
|
gui.handleCredentialsPopup(err)
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{SUBMODULES}})
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ func (gui *Gui) handleDeleteTag(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if err := gui.GitCommand.DeleteTag(tag.Name); err != nil {
|
if err := gui.GitCommand.DeleteTag(tag.Name); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{COMMITS, TAGS}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{COMMITS, TAGS}})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ func (gui *Gui) handleCreateTag(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if err := gui.GitCommand.CreateLightweightTag(tagName, ""); err != nil {
|
if err := gui.GitCommand.CreateLightweightTag(tagName, ""); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(refreshOptions{scope: []int{COMMITS, TAGS}, then: func() {
|
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{COMMITS, TAGS}, then: func() {
|
||||||
// find the index of the tag and set that as the currently selected line
|
// find the index of the tag and set that as the currently selected line
|
||||||
for i, tag := range gui.State.Tags {
|
for i, tag := range gui.State.Tags {
|
||||||
if tag.Name == tagName {
|
if tag.Name == tagName {
|
||||||
|
@ -17,15 +17,17 @@ import (
|
|||||||
// the reflog will read UUCBA, and when I read the first two undos, I know to skip the following
|
// the reflog will read UUCBA, and when I read the first two undos, I know to skip the following
|
||||||
// two user actions, meaning we end up undoing reflog entry C. Redoing works in a similar way.
|
// two user actions, meaning we end up undoing reflog entry C. Redoing works in a similar way.
|
||||||
|
|
||||||
|
type ReflogActionKind int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CHECKOUT = iota
|
CHECKOUT ReflogActionKind = iota
|
||||||
COMMIT
|
COMMIT
|
||||||
REBASE
|
REBASE
|
||||||
CURRENT_REBASE
|
CURRENT_REBASE
|
||||||
)
|
)
|
||||||
|
|
||||||
type reflogAction struct {
|
type reflogAction struct {
|
||||||
kind int // one of CHECKOUT, REBASE, and COMMIT
|
kind ReflogActionKind
|
||||||
from string
|
from string
|
||||||
to string
|
to string
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,10 @@ func (gui *Gui) getCyclableWindows() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// models/views that we can refresh
|
// models/views that we can refresh
|
||||||
|
type RefreshableView int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
COMMITS = iota
|
COMMITS RefreshableView = iota
|
||||||
BRANCHES
|
BRANCHES
|
||||||
FILES
|
FILES
|
||||||
STASH
|
STASH
|
||||||
@ -28,8 +30,8 @@ const (
|
|||||||
SUBMODULES
|
SUBMODULES
|
||||||
)
|
)
|
||||||
|
|
||||||
func getScopeNames(scopes []int) []string {
|
func getScopeNames(scopes []RefreshableView) []string {
|
||||||
scopeNameMap := map[int]string{
|
scopeNameMap := map[RefreshableView]string{
|
||||||
COMMITS: "commits",
|
COMMITS: "commits",
|
||||||
BRANCHES: "branches",
|
BRANCHES: "branches",
|
||||||
FILES: "files",
|
FILES: "files",
|
||||||
@ -49,7 +51,7 @@ func getScopeNames(scopes []int) []string {
|
|||||||
return scopeNames
|
return scopeNames
|
||||||
}
|
}
|
||||||
|
|
||||||
func getModeName(mode int) string {
|
func getModeName(mode RefreshMode) string {
|
||||||
switch mode {
|
switch mode {
|
||||||
case SYNC:
|
case SYNC:
|
||||||
return "sync"
|
return "sync"
|
||||||
@ -62,20 +64,22 @@ func getModeName(mode int) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RefreshMode int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SYNC = iota // wait until everything is done before returning
|
SYNC RefreshMode = iota // wait until everything is done before returning
|
||||||
ASYNC // return immediately, allowing each independent thing to update itself
|
ASYNC // return immediately, allowing each independent thing to update itself
|
||||||
BLOCK_UI // wrap code in an update call to ensure UI updates all at once and keybindings aren't executed till complete
|
BLOCK_UI // wrap code in an update call to ensure UI updates all at once and keybindings aren't executed till complete
|
||||||
)
|
)
|
||||||
|
|
||||||
type refreshOptions struct {
|
type refreshOptions struct {
|
||||||
then func()
|
then func()
|
||||||
scope []int // e.g. []int{COMMITS, BRANCHES}. Leave empty to refresh everything
|
scope []RefreshableView // e.g. []int{COMMITS, BRANCHES}. Leave empty to refresh everything
|
||||||
mode int // one of SYNC (default), ASYNC, and BLOCK_UI
|
mode RefreshMode // one of SYNC (default), ASYNC, and BLOCK_UI
|
||||||
}
|
}
|
||||||
|
|
||||||
func intArrToMap(arr []int) map[int]bool {
|
func arrToMap(arr []RefreshableView) map[RefreshableView]bool {
|
||||||
output := map[int]bool{}
|
output := map[RefreshableView]bool{}
|
||||||
for _, el := range arr {
|
for _, el := range arr {
|
||||||
output[el] = true
|
output[el] = true
|
||||||
}
|
}
|
||||||
@ -99,11 +103,11 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
|||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
f := func() {
|
f := func() {
|
||||||
var scopeMap map[int]bool
|
var scopeMap map[RefreshableView]bool
|
||||||
if len(options.scope) == 0 {
|
if len(options.scope) == 0 {
|
||||||
scopeMap = intArrToMap([]int{COMMITS, BRANCHES, FILES, STASH, REFLOG, TAGS, REMOTES, STATUS})
|
scopeMap = arrToMap([]RefreshableView{COMMITS, BRANCHES, FILES, STASH, REFLOG, TAGS, REMOTES, STATUS})
|
||||||
} else {
|
} else {
|
||||||
scopeMap = intArrToMap(options.scope)
|
scopeMap = arrToMap(options.scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
if scopeMap[COMMITS] || scopeMap[BRANCHES] || scopeMap[REFLOG] {
|
if scopeMap[COMMITS] || scopeMap[BRANCHES] || scopeMap[REFLOG] {
|
||||||
|
@ -26,7 +26,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -39,7 +39,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -52,7 +52,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -65,7 +65,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -78,7 +78,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -91,7 +91,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{FILES}})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user