1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-07-31 14:24:25 +03:00
This commit is contained in:
Jesse Duffield
2020-08-18 22:02:35 +10:00
parent 3c87ff4eff
commit f5b22d94d9
14 changed files with 259 additions and 152 deletions

View File

@ -34,28 +34,24 @@ func (gui *Gui) handleBranchSelect() error {
return gui.renderDiff() return gui.renderDiff()
} }
refreshOpts := refreshMainOpts{ var task updateTask
main: &viewUpdateOpts{
title: "Log",
task: {
kind: RENDER_STRING,
str: gui.Tr.SLocalize("NoBranchesThisRepo"),
},
},
}
branch := gui.getSelectedBranch() branch := gui.getSelectedBranch()
if branch == nil { if branch == nil {
refreshOpts.main.task = func() error { return gui.newStringTask("main", gui.Tr.SLocalize("NoBranchesThisRepo")) } task = gui.createRenderStringTask(gui.Tr.SLocalize("NoBranchesThisRepo"))
} else { } else {
cmd := gui.OSCommand.ExecutableFromString( cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.GetBranchGraphCmdStr(branch.Name), gui.GitCommand.GetBranchGraphCmdStr(branch.Name),
) )
refreshOpts.main.task = func() error { return gui.newPtyTask("main", cmd) } task = gui.createRunPtyTask(cmd)
} }
return gui.refreshMain(refreshOpts) return gui.refreshMain(refreshMainOpts{
main: &viewUpdateOpts{
title: "Log",
task: task,
},
})
} }
// gui.refreshStatus is called at the end of this because that's when we can // gui.refreshStatus is called at the end of this because that's when we can

View File

@ -21,29 +21,29 @@ func (gui *Gui) handleCommitFileSelect() error {
return nil return nil
} }
gui.getMainView().Title = "Patch"
if gui.currentViewName() == "commitFiles" { if gui.currentViewName() == "commitFiles" {
gui.handleEscapeLineByLinePanel() gui.handleEscapeLineByLinePanel()
} }
commitFile := gui.getSelectedCommitFile() commitFile := gui.getSelectedCommitFile()
if commitFile == nil { if commitFile == nil {
// TODO: consider making it so that we can also render strings to our own view through some common interface, or just render this to the main view for consistency
gui.renderString("commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) gui.renderString("commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
return nil return nil
} }
if err := gui.refreshSecondaryPatchPanel(); err != nil {
return err
}
cmd := gui.OSCommand.ExecutableFromString( cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowCommitFileCmdStr(commitFile.Sha, commitFile.Name, false), gui.GitCommand.ShowCommitFileCmdStr(commitFile.Sha, commitFile.Name, false),
) )
if err := gui.newPtyTask("main", cmd); err != nil { task := gui.createRunPtyTask(cmd)
gui.Log.Error(err)
}
return nil return gui.refreshMain(refreshMainOpts{
main: &viewUpdateOpts{
title: "Patch",
task: task,
},
secondary: gui.secondaryPatchPanelUpdateOpts(),
})
} }
func (gui *Gui) handleSwitchToCommitsPanel(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleSwitchToCommitsPanel(g *gocui.Gui, v *gocui.View) error {

View File

@ -26,9 +26,8 @@ func (gui *Gui) handleCommitSelect() error {
return nil return nil
} }
// this probably belongs in an 'onFocus' function than a 'commit selected' function if gui.inDiffMode() {
if err := gui.refreshSecondaryPatchPanel(); err != nil { return gui.renderDiff()
return err
} }
state := gui.State.Panels.Commits state := gui.State.Panels.Commits
@ -41,27 +40,26 @@ func (gui *Gui) handleCommitSelect() error {
}() }()
} }
gui.getMainView().Title = "Patch"
gui.getSecondaryView().Title = "Custom Patch"
gui.handleEscapeLineByLinePanel() gui.handleEscapeLineByLinePanel()
var task updateTask
commit := gui.getSelectedCommit() commit := gui.getSelectedCommit()
if commit == nil { if commit == nil {
return gui.newStringTask("main", gui.Tr.SLocalize("NoCommitsThisBranch")) task = gui.createRenderStringTask(gui.Tr.SLocalize("NoCommitsThisBranch"))
} else {
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.FilterPath),
)
task = gui.createRunPtyTask(cmd)
} }
if gui.inDiffMode() { return gui.refreshMain(refreshMainOpts{
return gui.renderDiff() main: &viewUpdateOpts{
} title: "Patch",
task: task,
cmd := gui.OSCommand.ExecutableFromString( },
gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.FilterPath), secondary: gui.secondaryPatchPanelUpdateOpts(),
) })
if err := gui.newPtyTask("main", cmd); err != nil {
gui.Log.Error(err)
}
return nil
} }
// during startup, the bottleneck is fetching the reflog entries. We need these // during startup, the bottleneck is fetching the reflog entries. We need these

View File

@ -18,20 +18,21 @@ func (gui *Gui) exitDiffMode() error {
} }
func (gui *Gui) renderDiff() error { func (gui *Gui) renderDiff() error {
filterArg := ""
if gui.inFilterMode() {
filterArg = fmt.Sprintf(" -- %s", gui.State.FilterPath)
}
cmd := gui.OSCommand.ExecutableFromString(
fmt.Sprintf("git diff --color %s %s", gui.diffStr(), filterArg),
)
task := gui.createRunPtyTask(cmd)
return gui.refreshMain(refreshMainOpts{ return gui.refreshMain(refreshMainOpts{
main: &viewUpdateOpts{ main: &viewUpdateOpts{
title: "Diff", title: "Diff",
task: func() error { task: task,
filterArg := ""
if gui.inFilterMode() {
filterArg = fmt.Sprintf(" -- %s", gui.State.FilterPath)
}
cmd := gui.OSCommand.ExecutableFromString(
fmt.Sprintf("git diff --color %s %s", gui.diffStr(), filterArg),
)
return gui.newPtyTask("main", cmd)
},
}, },
}) })
} }

View File

@ -37,12 +37,16 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
file := gui.getSelectedFile() file := gui.getSelectedFile()
if file == nil { if file == nil {
gui.splitMainPanel(false) return gui.refreshMain(refreshMainOpts{
gui.getMainView().Title = "" main: &viewUpdateOpts{
return gui.newStringTask("main", gui.Tr.SLocalize("NoChangedFiles")) title: "",
task: gui.createRenderStringTask(gui.Tr.SLocalize("NoChangedFiles")),
},
})
} }
if !alreadySelected { if !alreadySelected {
// TODO: pull into update task interface
if err := gui.resetOrigin(gui.getMainView()); err != nil { if err := gui.resetOrigin(gui.getMainView()); err != nil {
return err return err
} }
@ -52,36 +56,30 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
} }
if file.HasInlineMergeConflicts { if file.HasInlineMergeConflicts {
gui.getMainView().Title = gui.Tr.SLocalize("MergeConflictsTitle")
gui.splitMainPanel(false)
return gui.refreshMergePanel() return gui.refreshMergePanel()
} }
if file.HasStagedChanges && file.HasUnstagedChanges {
gui.splitMainPanel(true)
gui.getMainView().Title = gui.Tr.SLocalize("UnstagedChanges")
gui.getSecondaryView().Title = gui.Tr.SLocalize("StagedChanges")
cmdStr := gui.GitCommand.DiffCmdStr(file, false, true)
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
if err := gui.newPtyTask("secondary", cmd); err != nil {
return err
}
} else {
gui.splitMainPanel(false)
if file.HasUnstagedChanges {
gui.getMainView().Title = gui.Tr.SLocalize("UnstagedChanges")
} else {
gui.getMainView().Title = gui.Tr.SLocalize("StagedChanges")
}
}
cmdStr := gui.GitCommand.DiffCmdStr(file, false, !file.HasUnstagedChanges && file.HasStagedChanges) cmdStr := gui.GitCommand.DiffCmdStr(file, false, !file.HasUnstagedChanges && file.HasStagedChanges)
cmd := gui.OSCommand.ExecutableFromString(cmdStr) cmd := gui.OSCommand.ExecutableFromString(cmdStr)
if err := gui.newPtyTask("main", cmd); err != nil {
return err refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
title: gui.Tr.SLocalize("UnstagedChanges"),
task: gui.createRunPtyTask(cmd),
}}
if file.HasStagedChanges && file.HasUnstagedChanges {
cmdStr := gui.GitCommand.DiffCmdStr(file, false, true)
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
refreshOpts.secondary = &viewUpdateOpts{
title: gui.Tr.SLocalize("StagedChanges"),
task: gui.createRunPtyTask(cmd),
}
} else if !file.HasUnstagedChanges {
refreshOpts.main.title = gui.Tr.SLocalize("StagedChanges")
} }
return nil return gui.refreshMain(refreshOpts)
} }
func (gui *Gui) refreshFiles() error { func (gui *Gui) refreshFiles() error {

View File

@ -4,7 +4,14 @@ import "os/exec"
type viewUpdateOpts struct { type viewUpdateOpts struct {
title string title string
task func() error
// awkwardly calling this noWrap because of how hard Go makes it to have
// a boolean option that defaults to true
noWrap bool
highlight bool
task updateTask
} }
type refreshMainOpts struct { type refreshMainOpts struct {
@ -15,19 +22,100 @@ type refreshMainOpts struct {
// constants for updateTask's kind field // constants for updateTask's kind field
const ( const (
RENDER_STRING = iota RENDER_STRING = iota
RENDER_STRING_WITHOUT_SCROLL
RUN_FUNCTION RUN_FUNCTION
RUN_COMMAND RUN_COMMAND
RUN_PTY
) )
type updateTask struct { type updateTask interface {
kind int GetKind() int
str string
f func(chan struct{}) error
cmd *exec.Cmd
} }
func (gui *Gui) createRenderStringTask(str string) { type renderStringTask struct {
str string
}
func (t *renderStringTask) GetKind() int {
return RENDER_STRING
}
func (gui *Gui) createRenderStringTask(str string) *renderStringTask {
return &renderStringTask{str: str}
}
type renderStringWithoutScrollTask struct {
str string
}
func (t *renderStringWithoutScrollTask) GetKind() int {
return RENDER_STRING_WITHOUT_SCROLL
}
func (gui *Gui) createRenderStringWithoutScrollTask(str string) *renderStringWithoutScrollTask {
return &renderStringWithoutScrollTask{str: str}
}
type runCommandTask struct {
cmd *exec.Cmd
}
func (t *runCommandTask) GetKind() int {
return RUN_COMMAND
}
func (gui *Gui) createRunCommandTask(cmd *exec.Cmd) *runCommandTask {
return &runCommandTask{cmd: cmd}
}
type runPtyTask struct {
cmd *exec.Cmd
}
func (t *runPtyTask) GetKind() int {
return RUN_PTY
}
func (gui *Gui) createRunPtyTask(cmd *exec.Cmd) *runPtyTask {
return &runPtyTask{cmd: cmd}
}
type runFunctionTask struct {
f func(chan struct{}) error
}
func (t *runFunctionTask) GetKind() int {
return RUN_FUNCTION
}
func (gui *Gui) createRunFunctionTask(f func(chan struct{}) error) *runFunctionTask {
return &runFunctionTask{f: f}
}
func (gui *Gui) runTaskForView(viewName string, task updateTask) error {
switch task.GetKind() {
case RENDER_STRING:
specificTask := task.(*renderStringTask)
return gui.newStringTask(viewName, specificTask.str)
case RENDER_STRING_WITHOUT_SCROLL:
specificTask := task.(*renderStringWithoutScrollTask)
return gui.newStringTaskWithoutScroll(viewName, specificTask.str)
case RUN_FUNCTION:
specificTask := task.(*runFunctionTask)
return gui.newTask(viewName, specificTask.f)
case RUN_COMMAND:
specificTask := task.(*runCommandTask)
return gui.newCmdTask(viewName, specificTask.cmd)
case RUN_PTY:
specificTask := task.(*runPtyTask)
return gui.newPtyTask(viewName, specificTask.cmd)
}
return nil
} }
func (gui *Gui) refreshMain(opts refreshMainOpts) error { func (gui *Gui) refreshMain(opts refreshMainOpts) error {
@ -36,7 +124,10 @@ func (gui *Gui) refreshMain(opts refreshMainOpts) error {
if opts.main != nil { if opts.main != nil {
mainView.Title = opts.main.title mainView.Title = opts.main.title
if err := opts.main.task(); err != nil { mainView.Wrap = !opts.main.noWrap
mainView.Highlight = opts.main.highlight // TODO: see what the default should be
if err := gui.runTaskForView("main", opts.main.task); err != nil {
gui.Log.Error(err) gui.Log.Error(err)
return nil return nil
} }
@ -46,7 +137,9 @@ func (gui *Gui) refreshMain(opts refreshMainOpts) error {
if opts.secondary != nil { if opts.secondary != nil {
secondaryView.Title = opts.secondary.title secondaryView.Title = opts.secondary.title
if err := opts.secondary.task(); err != nil { secondaryView.Wrap = !opts.secondary.noWrap
mainView.Highlight = opts.main.highlight // TODO: see what the default should be
if err := gui.runTaskForView("secondary", opts.secondary.task); err != nil {
gui.Log.Error(err) gui.Log.Error(err)
return nil return nil
} }

View File

@ -238,14 +238,13 @@ func (gui *Gui) refreshMergePanel() error {
return err return err
} }
mainView := gui.getMainView() return gui.refreshMain(refreshMainOpts{
mainView.Wrap = false main: &viewUpdateOpts{
title: gui.Tr.SLocalize("MergeConflictsTitle"),
if err := gui.newStringTaskWithoutScroll("main", content); err != nil { task: gui.createRenderStringWithoutScrollTask(content),
return err noWrap: true,
} },
})
return nil
} }
func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) { func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {

View File

@ -80,13 +80,14 @@ func (gui *Gui) handleEscapePatchBuildingPanel() error {
if gui.GitCommand.PatchManager.IsEmpty() { if gui.GitCommand.PatchManager.IsEmpty() {
gui.GitCommand.PatchManager.Reset() gui.GitCommand.PatchManager.Reset()
gui.splitMainPanel(false)
} }
return gui.switchContext(gui.Contexts.BranchCommits.Files.Context) return gui.switchContext(gui.Contexts.BranchCommits.Files.Context)
} }
func (gui *Gui) refreshSecondaryPatchPanel() error { func (gui *Gui) refreshSecondaryPatchPanel() error {
// TODO: swap out for secondaryPatchPanelUpdateOpts
if gui.GitCommand.PatchManager.CommitSelected() { if gui.GitCommand.PatchManager.CommitSelected() {
gui.splitMainPanel(true) gui.splitMainPanel(true)
secondaryView := gui.getSecondaryView() secondaryView := gui.getSecondaryView()
@ -103,3 +104,18 @@ func (gui *Gui) refreshSecondaryPatchPanel() error {
return nil return nil
} }
func (gui *Gui) secondaryPatchPanelUpdateOpts() *viewUpdateOpts {
if gui.GitCommand.PatchManager.CommitSelected() {
patch := gui.GitCommand.PatchManager.RenderAggregatedPatchColored(false)
return &viewUpdateOpts{
title: "Custom Patch",
noWrap: true,
highlight: true,
task: gui.createRenderStringWithoutScrollTask(patch),
}
}
return nil
}

View File

@ -23,24 +23,24 @@ func (gui *Gui) handleReflogCommitSelect() error {
return gui.renderDiff() return gui.renderDiff()
} }
refreshOpts := refreshMainOpts{
main: &viewUpdateOpts{
title: "Reflog Entry",
},
}
commit := gui.getSelectedReflogCommit() commit := gui.getSelectedReflogCommit()
var task updateTask
if commit == nil { if commit == nil {
refreshOpts.main.task = func() error { return gui.newStringTask("main", "No reflog history") } task = gui.createRenderStringTask("No reflog history")
} else { } else {
cmd := gui.OSCommand.ExecutableFromString( cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.FilterPath), gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.FilterPath),
) )
refreshOpts.main.task = func() error { return gui.newPtyTask("main", cmd) } task = gui.createRunPtyTask(cmd)
} }
return gui.refreshMain(refreshOpts) return gui.refreshMain(refreshMainOpts{
main: &viewUpdateOpts{
title: "Reflog Entry",
task: task,
},
})
} }
// the reflogs panel is the only panel where we cache data, in that we only // the reflogs panel is the only panel where we cache data, in that we only

View File

@ -24,27 +24,27 @@ func (gui *Gui) handleRemoteBranchSelect() error {
return nil return nil
} }
gui.splitMainPanel(false)
gui.getMainView().Title = "Remote Branch"
remoteBranch := gui.getSelectedRemoteBranch()
if remoteBranch == nil {
return gui.newStringTask("main", "No branches for this remote")
}
if gui.inDiffMode() { if gui.inDiffMode() {
return gui.renderDiff() return gui.renderDiff()
} }
cmd := gui.OSCommand.ExecutableFromString( var task updateTask
gui.GitCommand.GetBranchGraphCmdStr(remoteBranch.FullName()), remoteBranch := gui.getSelectedRemoteBranch()
) if remoteBranch == nil {
if err := gui.newCmdTask("main", cmd); err != nil { task = gui.createRenderStringTask("No branches for this remote")
gui.Log.Error(err) } else {
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.GetBranchGraphCmdStr(remoteBranch.FullName()),
)
task = gui.createRunCommandTask(cmd)
} }
return nil return gui.refreshMain(refreshMainOpts{
main: &viewUpdateOpts{
title: "Remote Branch",
task: task,
},
})
} }
func (gui *Gui) handleRemoteBranchesEscape(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleRemoteBranchesEscape(g *gocui.Gui, v *gocui.View) error {

View File

@ -27,19 +27,24 @@ func (gui *Gui) handleRemoteSelect() error {
return nil return nil
} }
gui.splitMainPanel(false)
gui.getMainView().Title = "Remote"
remote := gui.getSelectedRemote()
if remote == nil {
return gui.newStringTask("main", "No remotes")
}
if gui.inDiffMode() { if gui.inDiffMode() {
return gui.renderDiff() return gui.renderDiff()
} }
return gui.newStringTask("main", fmt.Sprintf("%s\nUrls:\n%s", utils.ColoredString(remote.Name, color.FgGreen), strings.Join(remote.Urls, "\n"))) var task updateTask
remote := gui.getSelectedRemote()
if remote == nil {
task = gui.createRenderStringTask("No remotes")
} else {
task = gui.createRenderStringTask(fmt.Sprintf("%s\nUrls:\n%s", utils.ColoredString(remote.Name, color.FgGreen), strings.Join(remote.Urls, "\n")))
}
return gui.refreshMain(refreshMainOpts{
main: &viewUpdateOpts{
title: "Remote",
task: task,
},
})
} }
func (gui *Gui) refreshRemotes() error { func (gui *Gui) refreshRemotes() error {

View File

@ -22,6 +22,10 @@ func (gui *Gui) handleStashEntrySelect() error {
return nil return nil
} }
if gui.inDiffMode() {
return gui.renderDiff()
}
gui.splitMainPanel(false) gui.splitMainPanel(false)
gui.getMainView().Title = "Stash" gui.getMainView().Title = "Stash"
@ -31,10 +35,6 @@ func (gui *Gui) handleStashEntrySelect() error {
return gui.newStringTask("main", gui.Tr.SLocalize("NoStashEntries")) return gui.newStringTask("main", gui.Tr.SLocalize("NoStashEntries"))
} }
if gui.inDiffMode() {
return gui.renderDiff()
}
cmd := gui.OSCommand.ExecutableFromString( cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowStashEntryCmdStr(stashEntry.Index), gui.GitCommand.ShowStashEntryCmdStr(stashEntry.Index),
) )

View File

@ -93,10 +93,6 @@ func (gui *Gui) handleStatusSelect() error {
return nil return nil
} }
gui.splitMainPanel(false)
gui.getMainView().Title = ""
if gui.inDiffMode() { if gui.inDiffMode() {
return gui.renderDiff() return gui.renderDiff()
} }
@ -114,7 +110,12 @@ func (gui *Gui) handleStatusSelect() error {
magenta.Sprint("Become a sponsor (github is matching all donations for 12 months): https://github.com/sponsors/jesseduffield"), // caffeine ain't free magenta.Sprint("Become a sponsor (github is matching all donations for 12 months): https://github.com/sponsors/jesseduffield"), // caffeine ain't free
}, "\n\n") }, "\n\n")
return gui.newStringTask("main", dashboardString) return gui.refreshMain(refreshMainOpts{
main: &viewUpdateOpts{
title: "",
task: gui.createRenderStringTask(dashboardString),
},
})
} }
func (gui *Gui) handleOpenConfig(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleOpenConfig(g *gocui.Gui, v *gocui.View) error {

View File

@ -22,27 +22,27 @@ func (gui *Gui) handleTagSelect() error {
return nil return nil
} }
gui.splitMainPanel(false)
gui.getMainView().Title = "Tag"
tag := gui.getSelectedTag()
if tag == nil {
return gui.newStringTask("main", "No tags")
}
if gui.inDiffMode() { if gui.inDiffMode() {
return gui.renderDiff() return gui.renderDiff()
} }
cmd := gui.OSCommand.ExecutableFromString( var task updateTask
gui.GitCommand.GetBranchGraphCmdStr(tag.Name), tag := gui.getSelectedTag()
) if tag == nil {
if err := gui.newCmdTask("main", cmd); err != nil { task = gui.createRenderStringTask("No tags")
gui.Log.Error(err) } else {
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.GetBranchGraphCmdStr(tag.Name),
)
task = gui.createRunCommandTask(cmd)
} }
return nil return gui.refreshMain(refreshMainOpts{
main: &viewUpdateOpts{
title: "Tag",
task: task,
},
})
} }
func (gui *Gui) refreshTags() error { func (gui *Gui) refreshTags() error {