mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-17 00:18:05 +02:00
Merge branch 'master' into https-ask-for-username-password
This commit is contained in:
@ -52,6 +52,11 @@ func newLogger(config config.AppConfigurer) *logrus.Entry {
|
|||||||
} else {
|
} else {
|
||||||
log = newProductionLogger(config)
|
log = newProductionLogger(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// highly recommended: tail -f development.log | humanlog
|
||||||
|
// https://github.com/aybabtme/humanlog
|
||||||
|
log.Formatter = &logrus.JSONFormatter{}
|
||||||
|
|
||||||
if config.GetUserConfig().GetString("reporting") == "on" {
|
if config.GetUserConfig().GetString("reporting") == "on" {
|
||||||
// this isn't really a secret token: it only has permission to push new rollbar items
|
// this isn't really a secret token: it only has permission to push new rollbar items
|
||||||
hook := rollrus.NewHook("23432119147a4367abf7c0de2aa99a2d", environment)
|
hook := rollrus.NewHook("23432119147a4367abf7c0de2aa99a2d", environment)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
@ -12,11 +13,19 @@ import (
|
|||||||
type Branch struct {
|
type Branch struct {
|
||||||
Name string
|
Name string
|
||||||
Recency string
|
Recency string
|
||||||
|
Pushables string
|
||||||
|
Pullables string
|
||||||
|
Selected bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDisplayStrings returns the dispaly string of branch
|
// GetDisplayStrings returns the dispaly string of branch
|
||||||
func (b *Branch) GetDisplayStrings() []string {
|
func (b *Branch) GetDisplayStrings() []string {
|
||||||
return []string{b.Recency, utils.ColoredString(b.Name, b.GetColor())}
|
displayName := utils.ColoredString(b.Name, b.GetColor())
|
||||||
|
if b.Selected && b.Pushables != "" && b.Pullables != "" {
|
||||||
|
displayName = fmt.Sprintf("↑%s↓%s %s", b.Pushables, b.Pullables, displayName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []string{b.Recency, displayName}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetColor branch color
|
// GetColor branch color
|
||||||
|
@ -154,7 +154,6 @@ func (c *GitCommand) GetStatusFiles() []*File {
|
|||||||
}
|
}
|
||||||
files = append(files, file)
|
files = append(files, file)
|
||||||
}
|
}
|
||||||
c.Log.Info(files) // TODO: use a dumper-esque log here
|
|
||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,19 +207,33 @@ func includesInt(list []int, a int) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetHard does the equivalent of `git reset --hard HEAD`
|
// ResetAndClean removes all unstaged changes and removes all untracked files
|
||||||
func (c *GitCommand) ResetHard() error {
|
func (c *GitCommand) ResetAndClean() error {
|
||||||
return c.Worktree.Reset(&gogit.ResetOptions{Mode: gogit.HardReset})
|
if err := c.OSCommand.RunCommand("git reset --hard HEAD"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.OSCommand.RunCommand("git clean -fd")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpstreamDifferenceCount checks how many pushables/pullables there are for the
|
func (c *GitCommand) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
|
||||||
|
return c.GetCommitDifferences("HEAD", "@{u}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GitCommand) GetBranchUpstreamDifferenceCount(branchName string) (string, string) {
|
||||||
|
upstream := "origin" // hardcoded for now
|
||||||
|
return c.GetCommitDifferences(branchName, fmt.Sprintf("%s/%s", upstream, branchName))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCommitDifferences checks how many pushables/pullables there are for the
|
||||||
// current branch
|
// current branch
|
||||||
func (c *GitCommand) UpstreamDifferenceCount() (string, string) {
|
func (c *GitCommand) GetCommitDifferences(from, to string) (string, string) {
|
||||||
pushableCount, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..HEAD --count")
|
command := "git rev-list %s..%s --count"
|
||||||
|
pushableCount, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf(command, to, from))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "?", "?"
|
return "?", "?"
|
||||||
}
|
}
|
||||||
pullableCount, err := c.OSCommand.RunCommandWithOutput("git rev-list HEAD..@{u} --count")
|
pullableCount, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf(command, from, to))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "?", "?"
|
return "?", "?"
|
||||||
}
|
}
|
||||||
@ -609,3 +622,8 @@ func (c *GitCommand) ApplyPatch(patch string) (string, error) {
|
|||||||
|
|
||||||
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git apply --cached %s", filename))
|
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git apply --cached %s", filename))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *GitCommand) FastForward(branchName string) error {
|
||||||
|
upstream := "origin" // hardcoding for now
|
||||||
|
return c.OSCommand.RunCommand(fmt.Sprintf("git fetch %s %s:%s", upstream, branchName, branchName))
|
||||||
|
}
|
||||||
|
@ -557,8 +557,8 @@ func TestGitCommandMergeStatusFiles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGitCommandUpstreamDifferentCount is a function.
|
// TestGitCommandGetCommitDifferences is a function.
|
||||||
func TestGitCommandUpstreamDifferentCount(t *testing.T) {
|
func TestGitCommandGetCommitDifferences(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
command func(string, ...string) *exec.Cmd
|
command func(string, ...string) *exec.Cmd
|
||||||
@ -610,7 +610,7 @@ func TestGitCommandUpstreamDifferentCount(t *testing.T) {
|
|||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
gitCmd := newDummyGitCommand()
|
gitCmd := newDummyGitCommand()
|
||||||
gitCmd.OSCommand.command = s.command
|
gitCmd.OSCommand.command = s.command
|
||||||
s.test(gitCmd.UpstreamDifferenceCount())
|
s.test(gitCmd.GetCommitDifferences("HEAD", "@{u}"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,25 +7,116 @@ import (
|
|||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/git"
|
"github.com/jesseduffield/lazygit/pkg/git"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// list panel functions
|
||||||
|
|
||||||
|
func (gui *Gui) getSelectedBranch() *commands.Branch {
|
||||||
|
selectedLine := gui.State.Panels.Branches.SelectedLine
|
||||||
|
if selectedLine == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.State.Branches[selectedLine]
|
||||||
|
}
|
||||||
|
|
||||||
|
// may want to standardise how these select methods work
|
||||||
|
func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
// This really shouldn't happen: there should always be a master branch
|
||||||
|
if len(gui.State.Branches) == 0 {
|
||||||
|
return gui.renderString(g, "main", gui.Tr.SLocalize("NoBranchesThisRepo"))
|
||||||
|
}
|
||||||
|
branch := gui.getSelectedBranch()
|
||||||
|
if err := gui.focusPoint(0, gui.State.Panels.Branches.SelectedLine, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
_ = gui.RenderSelectedBranchUpstreamDifferences()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
graph, err := gui.GitCommand.GetBranchGraph(branch.Name)
|
||||||
|
if err != nil && strings.HasPrefix(graph, "fatal: ambiguous argument") {
|
||||||
|
graph = gui.Tr.SLocalize("NoTrackingThisBranch")
|
||||||
|
}
|
||||||
|
_ = gui.renderString(g, "main", graph)
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) RenderSelectedBranchUpstreamDifferences() error {
|
||||||
|
// here we tell the selected branch that it is selected.
|
||||||
|
// this is necessary for showing stats on a branch that is selected, because
|
||||||
|
// the displaystring function doesn't have access to gui state to tell if it's selected
|
||||||
|
for i, branch := range gui.State.Branches {
|
||||||
|
branch.Selected = i == gui.State.Panels.Branches.SelectedLine
|
||||||
|
}
|
||||||
|
|
||||||
|
branch := gui.getSelectedBranch()
|
||||||
|
branch.Pushables, branch.Pullables = gui.GitCommand.GetBranchUpstreamDifferenceCount(branch.Name)
|
||||||
|
return gui.renderListPanel(gui.getBranchesView(gui.g), gui.State.Branches)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gui.refreshStatus is called at the end of this because that's when we can
|
||||||
|
// be sure there is a state.Branches array to pick the current branch from
|
||||||
|
func (gui *Gui) refreshBranches(g *gocui.Gui) error {
|
||||||
|
g.Update(func(g *gocui.Gui) error {
|
||||||
|
builder, err := git.NewBranchListBuilder(gui.Log, gui.GitCommand)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gui.State.Branches = builder.Build()
|
||||||
|
|
||||||
|
gui.refreshSelectedLine(&gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches))
|
||||||
|
if err := gui.resetOrigin(gui.getBranchesView(gui.g)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := gui.RenderSelectedBranchUpstreamDifferences(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.refreshStatus(g)
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleBranchesNextLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
panelState := gui.State.Panels.Branches
|
||||||
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), false)
|
||||||
|
return gui.handleBranchSelect(gui.g, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleBranchesPrevLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
panelState := gui.State.Panels.Branches
|
||||||
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), true)
|
||||||
|
|
||||||
|
return gui.handleBranchSelect(gui.g, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// specific functions
|
||||||
|
|
||||||
func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
||||||
index := gui.getItemPosition(gui.getBranchesView(g))
|
if gui.State.Panels.Branches.SelectedLine == -1 {
|
||||||
if index == 0 {
|
return nil
|
||||||
|
}
|
||||||
|
if gui.State.Panels.Branches.SelectedLine == 0 {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("AlreadyCheckedOutBranch"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("AlreadyCheckedOutBranch"))
|
||||||
}
|
}
|
||||||
branch := gui.getSelectedBranch(gui.getBranchesView(g))
|
branch := gui.getSelectedBranch()
|
||||||
if err := gui.GitCommand.Checkout(branch.Name, false); err != nil {
|
if err := gui.GitCommand.Checkout(branch.Name, false); err != nil {
|
||||||
gui.createErrorPanel(g, err.Error())
|
if err := gui.createErrorPanel(g, err.Error()); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
gui.State.Panels.Branches.SelectedLine = 0
|
||||||
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(g)
|
return gui.refreshSidePanels(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error {
|
||||||
branch := gui.getSelectedBranch(gui.getBranchesView(g))
|
|
||||||
pullRequest := commands.NewPullRequest(gui.GitCommand)
|
pullRequest := commands.NewPullRequest(gui.GitCommand)
|
||||||
|
|
||||||
|
branch := gui.getSelectedBranch()
|
||||||
if err := pullRequest.Create(branch); err != nil {
|
if err := pullRequest.Create(branch); err != nil {
|
||||||
return gui.createErrorPanel(g, err.Error())
|
return gui.createErrorPanel(g, err.Error())
|
||||||
}
|
}
|
||||||
@ -60,7 +151,7 @@ func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error {
|
||||||
branch := gui.getSelectedBranch(v)
|
branch := gui.getSelectedBranch()
|
||||||
message := gui.Tr.SLocalize("SureForceCheckout")
|
message := gui.Tr.SLocalize("SureForceCheckout")
|
||||||
title := gui.Tr.SLocalize("ForceCheckoutBranch")
|
title := gui.Tr.SLocalize("ForceCheckoutBranch")
|
||||||
return gui.createConfirmationPanel(g, v, title, message, func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createConfirmationPanel(g, v, title, message, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -108,8 +199,11 @@ func (gui *Gui) handleForceDeleteBranch(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) deleteBranch(g *gocui.Gui, v *gocui.View, force bool) error {
|
func (gui *Gui) deleteBranch(g *gocui.Gui, v *gocui.View, force bool) error {
|
||||||
|
selectedBranch := gui.getSelectedBranch()
|
||||||
|
if selectedBranch == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
checkedOutBranch := gui.State.Branches[0]
|
checkedOutBranch := gui.State.Branches[0]
|
||||||
selectedBranch := gui.getSelectedBranch(v)
|
|
||||||
if checkedOutBranch.Name == selectedBranch.Name {
|
if checkedOutBranch.Name == selectedBranch.Name {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantDeleteCheckOutBranch"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantDeleteCheckOutBranch"))
|
||||||
}
|
}
|
||||||
@ -144,7 +238,7 @@ func (gui *Gui) deleteNamedBranch(g *gocui.Gui, v *gocui.View, selectedBranch *c
|
|||||||
|
|
||||||
func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error {
|
||||||
checkedOutBranch := gui.State.Branches[0]
|
checkedOutBranch := gui.State.Branches[0]
|
||||||
selectedBranch := gui.getSelectedBranch(v)
|
selectedBranch := gui.getSelectedBranch()
|
||||||
defer gui.refreshSidePanels(g)
|
defer gui.refreshSidePanels(g)
|
||||||
if checkedOutBranch.Name == selectedBranch.Name {
|
if checkedOutBranch.Name == selectedBranch.Name {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantMergeBranchIntoItself"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantMergeBranchIntoItself"))
|
||||||
@ -155,59 +249,36 @@ func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getSelectedBranch(v *gocui.View) *commands.Branch {
|
func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error {
|
||||||
lineNumber := gui.getItemPosition(v)
|
branch := gui.getSelectedBranch()
|
||||||
return gui.State.Branches[lineNumber]
|
if branch == nil {
|
||||||
}
|
return nil
|
||||||
|
|
||||||
func (gui *Gui) renderBranchesOptions(g *gocui.Gui) error {
|
|
||||||
return gui.renderGlobalOptions(g)
|
|
||||||
}
|
|
||||||
|
|
||||||
// may want to standardise how these select methods work
|
|
||||||
func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
if err := gui.renderBranchesOptions(g); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
// This really shouldn't happen: there should always be a master branch
|
if branch.Pushables == "" {
|
||||||
if len(gui.State.Branches) == 0 {
|
return nil
|
||||||
return gui.renderString(g, "main", gui.Tr.SLocalize("NoBranchesThisRepo"))
|
|
||||||
}
|
}
|
||||||
|
if branch.Pushables == "?" {
|
||||||
|
return gui.createErrorPanel(gui.g, "Cannot fast-forward a branch with no upstream")
|
||||||
|
}
|
||||||
|
if branch.Pushables != "0" {
|
||||||
|
return gui.createErrorPanel(gui.g, "Cannot fast-forward a branch with commits to push")
|
||||||
|
}
|
||||||
|
upstream := "origin" // hardcoding for now
|
||||||
|
message := gui.Tr.TemplateLocalize(
|
||||||
|
"Fetching",
|
||||||
|
Teml{
|
||||||
|
"from": fmt.Sprintf("%s/%s", upstream, branch.Name),
|
||||||
|
"to": branch.Name,
|
||||||
|
},
|
||||||
|
)
|
||||||
go func() {
|
go func() {
|
||||||
branch := gui.getSelectedBranch(v)
|
_ = gui.createMessagePanel(gui.g, v, "", message)
|
||||||
diff, err := gui.GitCommand.GetBranchGraph(branch.Name)
|
if err := gui.GitCommand.FastForward(branch.Name); err != nil {
|
||||||
if err != nil && strings.HasPrefix(diff, "fatal: ambiguous argument") {
|
_ = gui.createErrorPanel(gui.g, err.Error())
|
||||||
diff = gui.Tr.SLocalize("NoTrackingThisBranch")
|
} else {
|
||||||
|
_ = gui.closeConfirmationPrompt(gui.g)
|
||||||
|
_ = gui.RenderSelectedBranchUpstreamDifferences()
|
||||||
}
|
}
|
||||||
gui.renderString(g, "main", diff)
|
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// gui.refreshStatus is called at the end of this because that's when we can
|
|
||||||
// be sure there is a state.Branches array to pick the current branch from
|
|
||||||
func (gui *Gui) refreshBranches(g *gocui.Gui) error {
|
|
||||||
g.Update(func(g *gocui.Gui) error {
|
|
||||||
v, err := g.View("branches")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
builder, err := git.NewBranchListBuilder(gui.Log, gui.GitCommand)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
gui.State.Branches = builder.Build()
|
|
||||||
|
|
||||||
v.Clear()
|
|
||||||
list, err := utils.RenderList(gui.State.Branches)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprint(v, list)
|
|
||||||
|
|
||||||
gui.resetOrigin(v)
|
|
||||||
return gui.refreshStatus(g)
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -23,12 +23,12 @@ func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
|
|||||||
gui.SubProcess = sub
|
gui.SubProcess = sub
|
||||||
return gui.Errors.ErrSubProcess
|
return gui.Errors.ErrSubProcess
|
||||||
}
|
}
|
||||||
gui.refreshFiles(g)
|
|
||||||
v.Clear()
|
v.Clear()
|
||||||
v.SetCursor(0, 0)
|
_ = v.SetCursor(0, 0)
|
||||||
g.SetViewOnBottom("commitMessage")
|
_ = v.SetOrigin(0, 0)
|
||||||
gui.switchFocus(g, v, gui.getFilesView(g))
|
_, _ = g.SetViewOnBottom("commitMessage")
|
||||||
return gui.refreshCommits(g)
|
_ = gui.switchFocus(g, v, gui.getFilesView(g))
|
||||||
|
return gui.refreshSidePanels(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCommitClose(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleCommitClose(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
@ -9,29 +9,54 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// list panel functions
|
||||||
|
|
||||||
|
func (gui *Gui) getSelectedCommit(g *gocui.Gui) *commands.Commit {
|
||||||
|
selectedLine := gui.State.Panels.Commits.SelectedLine
|
||||||
|
if selectedLine == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.State.Commits[selectedLine]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
commit := gui.getSelectedCommit(g)
|
||||||
|
if commit == nil {
|
||||||
|
return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gui.focusPoint(0, gui.State.Panels.Commits.SelectedLine, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
commitText, err := gui.GitCommand.Show(commit.Sha)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return gui.renderString(g, "main", commitText)
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
||||||
g.Update(func(*gocui.Gui) error {
|
g.Update(func(*gocui.Gui) error {
|
||||||
commits, err := gui.GitCommand.GetCommits()
|
commits, err := gui.GitCommand.GetCommits()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.State.Commits = commits
|
gui.State.Commits = commits
|
||||||
v, err := g.View("commits")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
v.Clear()
|
gui.refreshSelectedLine(&gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits))
|
||||||
|
|
||||||
list, err := utils.RenderList(gui.State.Commits)
|
list, err := utils.RenderList(gui.State.Commits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v := gui.getCommitsView(gui.g)
|
||||||
|
v.Clear()
|
||||||
fmt.Fprint(v, list)
|
fmt.Fprint(v, list)
|
||||||
|
|
||||||
gui.refreshStatus(g)
|
gui.refreshStatus(g)
|
||||||
if g.CurrentView().Name() == "commits" {
|
if v == g.CurrentView() {
|
||||||
gui.handleCommitSelect(g, v)
|
gui.handleCommitSelect(g, v)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -39,11 +64,27 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleCommitsNextLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
panelState := gui.State.Panels.Commits
|
||||||
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), false)
|
||||||
|
|
||||||
|
return gui.handleCommitSelect(gui.g, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleCommitsPrevLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
panelState := gui.State.Panels.Commits
|
||||||
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), true)
|
||||||
|
|
||||||
|
return gui.handleCommitSelect(gui.g, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// specific functions
|
||||||
|
|
||||||
func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error {
|
func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error {
|
||||||
return gui.createConfirmationPanel(g, commitView, gui.Tr.SLocalize("ResetToCommit"), gui.Tr.SLocalize("SureResetThisCommit"), func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createConfirmationPanel(g, commitView, gui.Tr.SLocalize("ResetToCommit"), gui.Tr.SLocalize("SureResetThisCommit"), func(g *gocui.Gui, v *gocui.View) error {
|
||||||
commit, err := gui.getSelectedCommit(g)
|
commit := gui.getSelectedCommit(g)
|
||||||
if err != nil {
|
if commit == nil {
|
||||||
panic(err)
|
panic(errors.New(gui.Tr.SLocalize("NoCommitsThisBranch")))
|
||||||
}
|
}
|
||||||
if err := gui.GitCommand.ResetToCommit(commit.Sha); err != nil {
|
if err := gui.GitCommand.ResetToCommit(commit.Sha); err != nil {
|
||||||
return gui.createErrorPanel(g, err.Error())
|
return gui.createErrorPanel(g, err.Error())
|
||||||
@ -55,42 +96,21 @@ func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
gui.resetOrigin(commitView)
|
gui.resetOrigin(commitView)
|
||||||
return gui.handleCommitSelect(g, nil)
|
gui.State.Panels.Commits.SelectedLine = 0
|
||||||
|
return gui.handleCommitSelect(g, commitView)
|
||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderCommitsOptions(g *gocui.Gui) error {
|
|
||||||
return gui.renderGlobalOptions(g)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
if err := gui.renderCommitsOptions(g); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
commit, err := gui.getSelectedCommit(g)
|
|
||||||
if err != nil {
|
|
||||||
if err.Error() != gui.Tr.SLocalize("NoCommitsThisBranch") {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch"))
|
|
||||||
}
|
|
||||||
commitText, err := gui.GitCommand.Show(commit.Sha)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return gui.renderString(g, "main", commitText)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
|
||||||
if gui.getItemPosition(v) != 0 {
|
if gui.State.Panels.Commits.SelectedLine != 0 {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlySquashTopmostCommit"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlySquashTopmostCommit"))
|
||||||
}
|
}
|
||||||
if len(gui.State.Commits) == 1 {
|
if len(gui.State.Commits) <= 1 {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("YouNoCommitsToSquash"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("YouNoCommitsToSquash"))
|
||||||
}
|
}
|
||||||
commit, err := gui.getSelectedCommit(g)
|
commit := gui.getSelectedCommit(g)
|
||||||
if err != nil {
|
if commit == nil {
|
||||||
return err
|
return errors.New(gui.Tr.SLocalize("NoCommitsThisBranch"))
|
||||||
}
|
}
|
||||||
if err := gui.GitCommand.SquashPreviousTwoCommits(commit.Name); err != nil {
|
if err := gui.GitCommand.SquashPreviousTwoCommits(commit.Name); err != nil {
|
||||||
return gui.createErrorPanel(g, err.Error())
|
return gui.createErrorPanel(g, err.Error())
|
||||||
@ -113,16 +133,16 @@ func (gui *Gui) anyUnStagedChanges(files []*commands.File) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
|
||||||
if len(gui.State.Commits) == 1 {
|
if len(gui.State.Commits) <= 1 {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("YouNoCommitsToSquash"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("YouNoCommitsToSquash"))
|
||||||
}
|
}
|
||||||
if gui.anyUnStagedChanges(gui.State.Files) {
|
if gui.anyUnStagedChanges(gui.State.Files) {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantFixupWhileUnstagedChanges"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantFixupWhileUnstagedChanges"))
|
||||||
}
|
}
|
||||||
branch := gui.State.Branches[0]
|
branch := gui.State.Branches[0]
|
||||||
commit, err := gui.getSelectedCommit(g)
|
commit := gui.getSelectedCommit(g)
|
||||||
if err != nil {
|
if commit == nil {
|
||||||
return err
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("NoCommitsThisBranch"))
|
||||||
}
|
}
|
||||||
message := gui.Tr.SLocalize("SureFixupThisCommit")
|
message := gui.Tr.SLocalize("SureFixupThisCommit")
|
||||||
gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("Fixup"), message, func(g *gocui.Gui, v *gocui.View) error {
|
gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("Fixup"), message, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -138,7 +158,7 @@ func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
|
||||||
if gui.getItemPosition(gui.getCommitsView(g)) != 0 {
|
if gui.State.Panels.Commits.SelectedLine != 0 {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlyRenameTopCommit"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlyRenameTopCommit"))
|
||||||
}
|
}
|
||||||
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("renameCommit"), func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("renameCommit"), func(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -153,7 +173,7 @@ func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error {
|
||||||
if gui.getItemPosition(gui.getCommitsView(g)) != 0 {
|
if gui.State.Panels.Commits.SelectedLine != 0 {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlyRenameTopCommit"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlyRenameTopCommit"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,19 +184,3 @@ func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getSelectedCommit(g *gocui.Gui) (*commands.Commit, error) {
|
|
||||||
v, err := g.View("commits")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if len(gui.State.Commits) == 0 {
|
|
||||||
return &commands.Commit{}, errors.New(gui.Tr.SLocalize("NoCommitsThisBranch"))
|
|
||||||
}
|
|
||||||
lineNumber := gui.getItemPosition(v)
|
|
||||||
if lineNumber > len(gui.State.Commits)-1 {
|
|
||||||
gui.Log.Info(gui.Tr.SLocalize("PotentialErrInGetselectedCommit"), gui.State.Commits, lineNumber)
|
|
||||||
return gui.State.Commits[len(gui.State.Commits)-1], nil
|
|
||||||
}
|
|
||||||
return gui.State.Commits[lineNumber], nil
|
|
||||||
}
|
|
||||||
|
@ -28,7 +28,7 @@ func (gui *Gui) wrappedConfirmationFunction(function func(*gocui.Gui, *gocui.Vie
|
|||||||
func (gui *Gui) closeConfirmationPrompt(g *gocui.Gui) error {
|
func (gui *Gui) closeConfirmationPrompt(g *gocui.Gui) error {
|
||||||
view, err := g.View("confirmation")
|
view, err := g.View("confirmation")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil // if it's already been closed we can just return
|
||||||
}
|
}
|
||||||
if err := gui.returnFocus(g, view); err != nil {
|
if err := gui.returnFocus(g, view); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -77,11 +77,10 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt
|
|||||||
confirmationView.Wrap = true
|
confirmationView.Wrap = true
|
||||||
confirmationView.FgColor = gocui.ColorWhite
|
confirmationView.FgColor = gocui.ColorWhite
|
||||||
}
|
}
|
||||||
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
confirmationView.Clear()
|
confirmationView.Clear()
|
||||||
|
return gui.switchFocus(gui.g, currentView, confirmationView)
|
||||||
if err := gui.switchFocus(gui.g, currentView, confirmationView); err != nil {
|
})
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return confirmationView, nil
|
return confirmationView, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,79 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// list panel functions
|
||||||
|
|
||||||
|
func (gui *Gui) getSelectedFile(g *gocui.Gui) (*commands.File, error) {
|
||||||
|
selectedLine := gui.State.Panels.Files.SelectedLine
|
||||||
|
if selectedLine == -1 {
|
||||||
|
return &commands.File{}, gui.Errors.ErrNoFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.State.Files[selectedLine], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
file, err := gui.getSelectedFile(g)
|
||||||
|
if err != nil {
|
||||||
|
if err != gui.Errors.ErrNoFiles {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if file.HasMergeConflicts {
|
||||||
|
return gui.refreshMergePanel(g)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gui.focusPoint(0, gui.State.Panels.Files.SelectedLine, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
content := gui.GitCommand.Diff(file, false)
|
||||||
|
return gui.renderString(g, "main", content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) refreshFiles(g *gocui.Gui) error {
|
||||||
|
filesView, err := g.View("files")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gui.refreshStateFiles()
|
||||||
|
|
||||||
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
|
|
||||||
|
filesView.Clear()
|
||||||
|
list, err := utils.RenderList(gui.State.Files)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprint(filesView, list)
|
||||||
|
|
||||||
|
if filesView == g.CurrentView() {
|
||||||
|
return gui.handleFileSelect(g, filesView)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleFilesNextLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
panelState := gui.State.Panels.Files
|
||||||
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), false)
|
||||||
|
|
||||||
|
return gui.handleFileSelect(gui.g, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleFilesPrevLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
panelState := gui.State.Panels.Files
|
||||||
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), true)
|
||||||
|
|
||||||
|
return gui.handleFileSelect(gui.g, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// specific functions
|
||||||
|
|
||||||
func (gui *Gui) stagedFiles() []*commands.File {
|
func (gui *Gui) stagedFiles() []*commands.File {
|
||||||
files := gui.State.Files
|
files := gui.State.Files
|
||||||
result := make([]*commands.File, 0)
|
result := make([]*commands.File, 0)
|
||||||
@ -139,18 +212,6 @@ func (gui *Gui) handleAddPatch(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.Errors.ErrSubProcess
|
return gui.Errors.ErrSubProcess
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getSelectedFile(g *gocui.Gui) (*commands.File, error) {
|
|
||||||
if len(gui.State.Files) == 0 {
|
|
||||||
return &commands.File{}, gui.Errors.ErrNoFiles
|
|
||||||
}
|
|
||||||
filesView, err := g.View("files")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
lineNumber := gui.getItemPosition(filesView)
|
|
||||||
return gui.State.Files[lineNumber], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) handleFileRemove(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleFileRemove(g *gocui.Gui, v *gocui.View) error {
|
||||||
file, err := gui.getSelectedFile(g)
|
file, err := gui.getSelectedFile(g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -194,30 +255,6 @@ func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.refreshFiles(g)
|
return gui.refreshFiles(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderfilesOptions(g *gocui.Gui, file *commands.File) error {
|
|
||||||
return gui.renderGlobalOptions(g)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
file, err := gui.getSelectedFile(g)
|
|
||||||
if err != nil {
|
|
||||||
if err != gui.Errors.ErrNoFiles {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles"))
|
|
||||||
return gui.renderfilesOptions(g, nil)
|
|
||||||
}
|
|
||||||
if err := gui.renderfilesOptions(g, file); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if file.HasMergeConflicts {
|
|
||||||
return gui.refreshMergePanel(g)
|
|
||||||
}
|
|
||||||
|
|
||||||
content := gui.GitCommand.Diff(file, false)
|
|
||||||
return gui.renderString(g, "main", content)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
|
func (gui *Gui) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
|
||||||
if len(gui.stagedFiles()) == 0 && !gui.State.HasMergeConflicts {
|
if len(gui.stagedFiles()) == 0 && !gui.State.HasMergeConflicts {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("NoStagedFilesToCommit"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("NoStagedFilesToCommit"))
|
||||||
@ -309,6 +346,7 @@ func (gui *Gui) refreshStateFiles() {
|
|||||||
// get files to stage
|
// get files to stage
|
||||||
files := gui.GitCommand.GetStatusFiles()
|
files := gui.GitCommand.GetStatusFiles()
|
||||||
gui.State.Files = gui.GitCommand.MergeStatusFiles(gui.State.Files, files)
|
gui.State.Files = gui.GitCommand.MergeStatusFiles(gui.State.Files, files)
|
||||||
|
gui.refreshSelectedLine(&gui.State.Panels.Files.SelectedLine, len(gui.State.Files))
|
||||||
gui.updateHasMergeConflictStatus()
|
gui.updateHasMergeConflictStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,27 +378,6 @@ func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
|
|||||||
return cat, nil
|
return cat, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshFiles(g *gocui.Gui) error {
|
|
||||||
filesView, err := g.View("files")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
gui.refreshStateFiles()
|
|
||||||
|
|
||||||
filesView.Clear()
|
|
||||||
list, err := utils.RenderList(gui.State.Files)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprint(filesView, list)
|
|
||||||
|
|
||||||
gui.correctCursor(filesView)
|
|
||||||
if filesView == g.CurrentView() {
|
|
||||||
gui.handleFileSelect(g, filesView)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error {
|
||||||
if err := gui.createMessagePanel(gui.g, v, "", gui.Tr.SLocalize("PullWait")); err != nil {
|
if err := gui.createMessagePanel(gui.g, v, "", gui.Tr.SLocalize("PullWait")); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -424,7 +441,7 @@ func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool) error
|
|||||||
|
|
||||||
func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
|
||||||
// if we have pullables we'll ask if the user wants to force push
|
// if we have pullables we'll ask if the user wants to force push
|
||||||
_, pullables := gui.GitCommand.UpstreamDifferenceCount()
|
_, pullables := gui.GitCommand.GetCurrentBranchUpstreamDifferenceCount()
|
||||||
if pullables == "?" || pullables == "0" {
|
if pullables == "?" || pullables == "0" {
|
||||||
return gui.pushWithForceFlag(g, v, false)
|
return gui.pushWithForceFlag(g, v, false)
|
||||||
}
|
}
|
||||||
@ -462,9 +479,9 @@ func (gui *Gui) handleAbortMerge(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.refreshFiles(g)
|
return gui.refreshFiles(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleResetHard(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleResetAndClean(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("ClearFilePanel"), gui.Tr.SLocalize("SureResetHardHead"), func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("ClearFilePanel"), gui.Tr.SLocalize("SureResetHardHead"), func(g *gocui.Gui, v *gocui.View) error {
|
||||||
if err := gui.GitCommand.ResetHard(); err != nil {
|
if err := gui.GitCommand.ResetAndClean(); err != nil {
|
||||||
gui.createErrorPanel(g, err.Error())
|
gui.createErrorPanel(g, err.Error())
|
||||||
}
|
}
|
||||||
return gui.refreshFiles(g)
|
return gui.refreshFiles(g)
|
||||||
|
@ -76,13 +76,45 @@ type Gui struct {
|
|||||||
introAgree sync.WaitGroup
|
introAgree sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
type stagingState struct {
|
// for now the staging panel state, unlike the other panel states, is going to be
|
||||||
|
// non-mutative, so that we don't accidentally end up
|
||||||
|
// with mismatches of data. We might change this in the future
|
||||||
|
type stagingPanelState struct {
|
||||||
|
SelectedLine int
|
||||||
StageableLines []int
|
StageableLines []int
|
||||||
HunkStarts []int
|
HunkStarts []int
|
||||||
CurrentLineIndex int
|
|
||||||
Diff string
|
Diff string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type filePanelState struct {
|
||||||
|
SelectedLine int
|
||||||
|
}
|
||||||
|
|
||||||
|
type branchPanelState struct {
|
||||||
|
SelectedLine int
|
||||||
|
}
|
||||||
|
|
||||||
|
type commitPanelState struct {
|
||||||
|
SelectedLine int
|
||||||
|
}
|
||||||
|
|
||||||
|
type stashPanelState struct {
|
||||||
|
SelectedLine int
|
||||||
|
}
|
||||||
|
|
||||||
|
type menuPanelState struct {
|
||||||
|
SelectedLine int
|
||||||
|
}
|
||||||
|
|
||||||
|
type panelStates struct {
|
||||||
|
Files *filePanelState
|
||||||
|
Staging *stagingPanelState
|
||||||
|
Branches *branchPanelState
|
||||||
|
Commits *commitPanelState
|
||||||
|
Stash *stashPanelState
|
||||||
|
Menu *menuPanelState
|
||||||
|
}
|
||||||
|
|
||||||
type guiState struct {
|
type guiState struct {
|
||||||
Files []*commands.File
|
Files []*commands.File
|
||||||
Branches []*commands.Branch
|
Branches []*commands.Branch
|
||||||
@ -96,7 +128,7 @@ type guiState struct {
|
|||||||
EditHistory *stack.Stack
|
EditHistory *stack.Stack
|
||||||
Platform commands.Platform
|
Platform commands.Platform
|
||||||
Updating bool
|
Updating bool
|
||||||
StagingState *stagingState
|
Panels *panelStates
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGui builds a new gui handler
|
// NewGui builds a new gui handler
|
||||||
@ -112,6 +144,13 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma
|
|||||||
Conflicts: make([]commands.Conflict, 0),
|
Conflicts: make([]commands.Conflict, 0),
|
||||||
EditHistory: stack.New(),
|
EditHistory: stack.New(),
|
||||||
Platform: *oSCommand.Platform,
|
Platform: *oSCommand.Platform,
|
||||||
|
Panels: &panelStates{
|
||||||
|
Files: &filePanelState{SelectedLine: -1},
|
||||||
|
Branches: &branchPanelState{SelectedLine: 0},
|
||||||
|
Commits: &commitPanelState{SelectedLine: -1},
|
||||||
|
Stash: &stashPanelState{SelectedLine: -1},
|
||||||
|
Menu: &menuPanelState{SelectedLine: 0},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
gui := &Gui{
|
gui := &Gui{
|
||||||
@ -197,9 +236,11 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
}
|
}
|
||||||
v.Title = gui.Tr.SLocalize("NotEnoughSpace")
|
v.Title = gui.Tr.SLocalize("NotEnoughSpace")
|
||||||
v.Wrap = true
|
v.Wrap = true
|
||||||
g.SetCurrentView(v.Name())
|
g.SetViewOnTop("limit")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
} else {
|
||||||
|
_, _ = g.SetViewOnBottom("limit")
|
||||||
}
|
}
|
||||||
|
|
||||||
g.DeleteView("limit")
|
g.DeleteView("limit")
|
||||||
@ -251,12 +292,13 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
v.FgColor = gocui.ColorWhite
|
v.FgColor = gocui.ColorWhite
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := g.SetView("branches", 0, filesBranchesBoundary+panelSpacing, leftSideWidth, commitsBranchesBoundary, gocui.TOP|gocui.BOTTOM); err != nil {
|
branchesView, err := g.SetView("branches", 0, filesBranchesBoundary+panelSpacing, leftSideWidth, commitsBranchesBoundary, gocui.TOP|gocui.BOTTOM)
|
||||||
|
if err != nil {
|
||||||
if err != gocui.ErrUnknownView {
|
if err != gocui.ErrUnknownView {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Title = gui.Tr.SLocalize("BranchesTitle")
|
branchesView.Title = gui.Tr.SLocalize("BranchesTitle")
|
||||||
v.FgColor = gocui.ColorWhite
|
branchesView.FgColor = gocui.ColorWhite
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := g.SetView("commits", 0, commitsBranchesBoundary+panelSpacing, leftSideWidth, commitsStashBoundary, gocui.TOP|gocui.BOTTOM); err != nil {
|
if v, err := g.SetView("commits", 0, commitsBranchesBoundary+panelSpacing, leftSideWidth, commitsStashBoundary, gocui.TOP|gocui.BOTTOM); err != nil {
|
||||||
@ -346,11 +388,14 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.handleFileSelect(g, filesView)
|
if _, err := gui.g.SetCurrentView(filesView.Name()); err != nil {
|
||||||
gui.refreshFiles(g)
|
return err
|
||||||
gui.refreshBranches(g)
|
}
|
||||||
gui.refreshCommits(g)
|
|
||||||
gui.refreshStashEntries(g)
|
if err := gui.refreshSidePanels(gui.g); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := gui.switchFocus(g, nil, filesView); err != nil {
|
if err := gui.switchFocus(g, nil, filesView); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -362,6 +407,22 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listViews := map[*gocui.View]int{
|
||||||
|
filesView: gui.State.Panels.Files.SelectedLine,
|
||||||
|
branchesView: gui.State.Panels.Branches.SelectedLine,
|
||||||
|
}
|
||||||
|
for view, selectedLine := range listViews {
|
||||||
|
// check if the selected line is now out of view and if so refocus it
|
||||||
|
if err := gui.focusPoint(0, selectedLine, view); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// here is a good place log some stuff
|
||||||
|
// if you download humanlog and do tail -f development.log | humanlog
|
||||||
|
// this will let you see these branches as prettified json
|
||||||
|
// gui.Log.Info(utils.AsJson(gui.State.Branches[0:4]))
|
||||||
|
|
||||||
return gui.resizeCurrentPopupPanel(g)
|
return gui.resizeCurrentPopupPanel(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,8 +477,8 @@ func (gui *Gui) renderAppStatus(g *gocui.Gui) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderGlobalOptions(g *gocui.Gui) error {
|
func (gui *Gui) renderGlobalOptions() error {
|
||||||
return gui.renderOptionsMap(g, map[string]string{
|
return gui.renderOptionsMap(map[string]string{
|
||||||
"PgUp/PgDn": gui.Tr.SLocalize("scroll"),
|
"PgUp/PgDn": gui.Tr.SLocalize("scroll"),
|
||||||
"← → ↑ ↓": gui.Tr.SLocalize("navigate"),
|
"← → ↑ ↓": gui.Tr.SLocalize("navigate"),
|
||||||
"esc/q": gui.Tr.SLocalize("close"),
|
"esc/q": gui.Tr.SLocalize("close"),
|
||||||
|
@ -210,7 +210,7 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
|||||||
ViewName: "files",
|
ViewName: "files",
|
||||||
Key: 'D',
|
Key: 'D',
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleResetHard,
|
Handler: gui.handleResetAndClean,
|
||||||
Description: gui.Tr.SLocalize("resetHard"),
|
Description: gui.Tr.SLocalize("resetHard"),
|
||||||
}, {
|
}, {
|
||||||
ViewName: "files",
|
ViewName: "files",
|
||||||
@ -328,6 +328,12 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
|||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleMerge,
|
Handler: gui.handleMerge,
|
||||||
Description: gui.Tr.SLocalize("mergeIntoCurrentBranch"),
|
Description: gui.Tr.SLocalize("mergeIntoCurrentBranch"),
|
||||||
|
}, {
|
||||||
|
ViewName: "branches",
|
||||||
|
Key: 'f',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleFastForward,
|
||||||
|
Description: gui.Tr.SLocalize("FastForward"),
|
||||||
}, {
|
}, {
|
||||||
ViewName: "commits",
|
ViewName: "commits",
|
||||||
Key: 's',
|
Key: 's',
|
||||||
@ -469,19 +475,33 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Would make these keybindings global but that interferes with editing
|
for _, viewName := range []string{"status", "branches", "files", "commits", "stash", "menu"} {
|
||||||
// input in the confirmation panel
|
|
||||||
for _, viewName := range []string{"status", "files", "branches", "commits", "stash", "menu"} {
|
|
||||||
bindings = append(bindings, []*Binding{
|
bindings = append(bindings, []*Binding{
|
||||||
{ViewName: viewName, Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: gui.nextView},
|
{ViewName: viewName, Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||||
{ViewName: viewName, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.previousView},
|
{ViewName: viewName, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.previousView},
|
||||||
{ViewName: viewName, Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: gui.nextView},
|
{ViewName: viewName, Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||||
{ViewName: viewName, Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.cursorUp},
|
|
||||||
{ViewName: viewName, Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.cursorDown},
|
|
||||||
{ViewName: viewName, Key: 'h', Modifier: gocui.ModNone, Handler: gui.previousView},
|
{ViewName: viewName, Key: 'h', Modifier: gocui.ModNone, Handler: gui.previousView},
|
||||||
{ViewName: viewName, Key: 'l', Modifier: gocui.ModNone, Handler: gui.nextView},
|
{ViewName: viewName, Key: 'l', Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||||
{ViewName: viewName, Key: 'k', Modifier: gocui.ModNone, Handler: gui.cursorUp},
|
}...)
|
||||||
{ViewName: viewName, Key: 'j', Modifier: gocui.ModNone, Handler: gui.cursorDown},
|
}
|
||||||
|
|
||||||
|
listPanelMap := map[string]struct {
|
||||||
|
prevLine func(*gocui.Gui, *gocui.View) error
|
||||||
|
nextLine func(*gocui.Gui, *gocui.View) error
|
||||||
|
}{
|
||||||
|
"menu": {prevLine: gui.handleMenuPrevLine, nextLine: gui.handleMenuNextLine},
|
||||||
|
"files": {prevLine: gui.handleFilesPrevLine, nextLine: gui.handleFilesNextLine},
|
||||||
|
"branches": {prevLine: gui.handleBranchesPrevLine, nextLine: gui.handleBranchesNextLine},
|
||||||
|
"commits": {prevLine: gui.handleCommitsPrevLine, nextLine: gui.handleCommitsNextLine},
|
||||||
|
"stash": {prevLine: gui.handleStashPrevLine, nextLine: gui.handleStashNextLine},
|
||||||
|
}
|
||||||
|
|
||||||
|
for viewName, functions := range listPanelMap {
|
||||||
|
bindings = append(bindings, []*Binding{
|
||||||
|
{ViewName: viewName, Key: 'k', Modifier: gocui.ModNone, Handler: functions.prevLine},
|
||||||
|
{ViewName: viewName, Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: functions.prevLine},
|
||||||
|
{ViewName: viewName, Key: 'j', Modifier: gocui.ModNone, Handler: functions.nextLine},
|
||||||
|
{ViewName: viewName, Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: functions.nextLine},
|
||||||
}...)
|
}...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,19 +8,35 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// list panel functions
|
||||||
|
|
||||||
func (gui *Gui) handleMenuSelect(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleMenuSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
// doing nothing for now
|
return gui.focusPoint(0, gui.State.Panels.Menu.SelectedLine, v)
|
||||||
// but it is needed for switch in newLineFocused
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderMenuOptions(g *gocui.Gui) error {
|
func (gui *Gui) handleMenuNextLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
panelState := gui.State.Panels.Menu
|
||||||
|
gui.changeSelectedLine(&panelState.SelectedLine, v.LinesHeight(), false)
|
||||||
|
|
||||||
|
return gui.handleMenuSelect(g, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleMenuPrevLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
panelState := gui.State.Panels.Menu
|
||||||
|
gui.changeSelectedLine(&panelState.SelectedLine, v.LinesHeight(), true)
|
||||||
|
|
||||||
|
return gui.handleMenuSelect(g, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// specific functions
|
||||||
|
|
||||||
|
func (gui *Gui) renderMenuOptions() error {
|
||||||
optionsMap := map[string]string{
|
optionsMap := map[string]string{
|
||||||
"esc/q": gui.Tr.SLocalize("close"),
|
"esc/q": gui.Tr.SLocalize("close"),
|
||||||
"↑ ↓": gui.Tr.SLocalize("navigate"),
|
"↑ ↓": gui.Tr.SLocalize("navigate"),
|
||||||
"space": gui.Tr.SLocalize("execute"),
|
"space": gui.Tr.SLocalize("execute"),
|
||||||
}
|
}
|
||||||
return gui.renderOptionsMap(g, optionsMap)
|
return gui.renderOptionsMap(optionsMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -46,14 +62,11 @@ func (gui *Gui) createMenu(items interface{}, handlePress func(int) error) error
|
|||||||
menuView.FgColor = gocui.ColorWhite
|
menuView.FgColor = gocui.ColorWhite
|
||||||
menuView.Clear()
|
menuView.Clear()
|
||||||
fmt.Fprint(menuView, list)
|
fmt.Fprint(menuView, list)
|
||||||
|
gui.State.Panels.Menu.SelectedLine = 0
|
||||||
if err := gui.renderMenuOptions(gui.g); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
wrappedHandlePress := func(g *gocui.Gui, v *gocui.View) error {
|
wrappedHandlePress := func(g *gocui.Gui, v *gocui.View) error {
|
||||||
lineNumber := gui.getItemPosition(v)
|
selectedLine := gui.State.Panels.Menu.SelectedLine
|
||||||
return handlePress(lineNumber)
|
return handlePress(selectedLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.g.SetKeybinding("menu", gocui.KeySpace, gocui.ModNone, wrappedHandlePress); err != nil {
|
if err := gui.g.SetKeybinding("menu", gocui.KeySpace, gocui.ModNone, wrappedHandlePress); err != nil {
|
||||||
|
@ -194,9 +194,6 @@ func (gui *Gui) refreshMergePanel(g *gocui.Gui) error {
|
|||||||
gui.State.ConflictIndex = len(gui.State.Conflicts) - 1
|
gui.State.ConflictIndex = len(gui.State.Conflicts) - 1
|
||||||
}
|
}
|
||||||
hasFocus := gui.currentViewName(g) == "main"
|
hasFocus := gui.currentViewName(g) == "main"
|
||||||
if hasFocus {
|
|
||||||
gui.renderMergeOptions(g)
|
|
||||||
}
|
|
||||||
content, err := gui.coloredConflictFile(cat, gui.State.Conflicts, gui.State.ConflictIndex, gui.State.ConflictTop, hasFocus)
|
content, err := gui.coloredConflictFile(cat, gui.State.Conflicts, gui.State.ConflictIndex, gui.State.ConflictTop, hasFocus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -233,8 +230,8 @@ func (gui *Gui) switchToMerging(g *gocui.Gui) error {
|
|||||||
return gui.refreshMergePanel(g)
|
return gui.refreshMergePanel(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderMergeOptions(g *gocui.Gui) error {
|
func (gui *Gui) renderMergeOptions() error {
|
||||||
return gui.renderOptionsMap(g, map[string]string{
|
return gui.renderOptionsMap(map[string]string{
|
||||||
"↑ ↓": gui.Tr.SLocalize("selectHunk"),
|
"↑ ↓": gui.Tr.SLocalize("selectHunk"),
|
||||||
"← →": gui.Tr.SLocalize("navigateConflicts"),
|
"← →": gui.Tr.SLocalize("navigateConflicts"),
|
||||||
"space": gui.Tr.SLocalize("pickHunk"),
|
"space": gui.Tr.SLocalize("pickHunk"),
|
||||||
|
@ -40,22 +40,22 @@ func (gui *Gui) refreshStagingPanel() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentLineIndex int
|
var selectedLine int
|
||||||
if gui.State.StagingState != nil {
|
if gui.State.Panels.Staging != nil {
|
||||||
end := len(stageableLines) - 1
|
end := len(stageableLines) - 1
|
||||||
if end < gui.State.StagingState.CurrentLineIndex {
|
if end < gui.State.Panels.Staging.SelectedLine {
|
||||||
currentLineIndex = end
|
selectedLine = end
|
||||||
} else {
|
} else {
|
||||||
currentLineIndex = gui.State.StagingState.CurrentLineIndex
|
selectedLine = gui.State.Panels.Staging.SelectedLine
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentLineIndex = 0
|
selectedLine = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.State.StagingState = &stagingState{
|
gui.State.Panels.Staging = &stagingPanelState{
|
||||||
StageableLines: stageableLines,
|
StageableLines: stageableLines,
|
||||||
HunkStarts: hunkStarts,
|
HunkStarts: hunkStarts,
|
||||||
CurrentLineIndex: currentLineIndex,
|
SelectedLine: selectedLine,
|
||||||
Diff: diff,
|
Diff: diff,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ func (gui *Gui) handleStagingEscape(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.State.StagingState = nil
|
gui.State.Panels.Staging = nil
|
||||||
|
|
||||||
return gui.switchFocus(gui.g, nil, gui.getFilesView(gui.g))
|
return gui.switchFocus(gui.g, nil, gui.getFilesView(gui.g))
|
||||||
}
|
}
|
||||||
@ -96,9 +96,9 @@ func (gui *Gui) handleStagingNextHunk(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCycleHunk(prev bool) error {
|
func (gui *Gui) handleCycleHunk(prev bool) error {
|
||||||
state := gui.State.StagingState
|
state := gui.State.Panels.Staging
|
||||||
lineNumbers := state.StageableLines
|
lineNumbers := state.StageableLines
|
||||||
currentLine := lineNumbers[state.CurrentLineIndex]
|
currentLine := lineNumbers[state.SelectedLine]
|
||||||
currentHunkIndex := utils.PrevIndex(state.HunkStarts, currentLine)
|
currentHunkIndex := utils.PrevIndex(state.HunkStarts, currentLine)
|
||||||
var newHunkIndex int
|
var newHunkIndex int
|
||||||
if prev {
|
if prev {
|
||||||
@ -115,22 +115,22 @@ func (gui *Gui) handleCycleHunk(prev bool) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.CurrentLineIndex = utils.NextIndex(lineNumbers, state.HunkStarts[newHunkIndex])
|
state.SelectedLine = utils.NextIndex(lineNumbers, state.HunkStarts[newHunkIndex])
|
||||||
|
|
||||||
return gui.focusLineAndHunk()
|
return gui.focusLineAndHunk()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCycleLine(prev bool) error {
|
func (gui *Gui) handleCycleLine(prev bool) error {
|
||||||
state := gui.State.StagingState
|
state := gui.State.Panels.Staging
|
||||||
lineNumbers := state.StageableLines
|
lineNumbers := state.StageableLines
|
||||||
currentLine := lineNumbers[state.CurrentLineIndex]
|
currentLine := lineNumbers[state.SelectedLine]
|
||||||
var newIndex int
|
var newIndex int
|
||||||
if prev {
|
if prev {
|
||||||
newIndex = utils.PrevIndex(lineNumbers, currentLine)
|
newIndex = utils.PrevIndex(lineNumbers, currentLine)
|
||||||
} else {
|
} else {
|
||||||
newIndex = utils.NextIndex(lineNumbers, currentLine)
|
newIndex = utils.NextIndex(lineNumbers, currentLine)
|
||||||
}
|
}
|
||||||
state.CurrentLineIndex = newIndex
|
state.SelectedLine = newIndex
|
||||||
|
|
||||||
return gui.focusLineAndHunk()
|
return gui.focusLineAndHunk()
|
||||||
}
|
}
|
||||||
@ -139,9 +139,9 @@ func (gui *Gui) handleCycleLine(prev bool) error {
|
|||||||
// selected line and size of the hunk
|
// selected line and size of the hunk
|
||||||
func (gui *Gui) focusLineAndHunk() error {
|
func (gui *Gui) focusLineAndHunk() error {
|
||||||
stagingView := gui.getStagingView(gui.g)
|
stagingView := gui.getStagingView(gui.g)
|
||||||
state := gui.State.StagingState
|
state := gui.State.Panels.Staging
|
||||||
|
|
||||||
lineNumber := state.StageableLines[state.CurrentLineIndex]
|
lineNumber := state.StageableLines[state.SelectedLine]
|
||||||
|
|
||||||
// we want the bottom line of the view buffer to ideally be the bottom line
|
// we want the bottom line of the view buffer to ideally be the bottom line
|
||||||
// of the hunk, but if the hunk is too big we'll just go three lines beyond
|
// of the hunk, but if the hunk is too big we'll just go three lines beyond
|
||||||
@ -170,23 +170,7 @@ func (gui *Gui) focusLineAndHunk() error {
|
|||||||
bottomLine = lineNumber + 3
|
bottomLine = lineNumber + 3
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.focusLine(lineNumber, bottomLine, stagingView)
|
return gui.generalFocusLine(lineNumber, bottomLine, stagingView)
|
||||||
}
|
|
||||||
|
|
||||||
// focusLine takes a lineNumber to focus, and a bottomLine to ensure we can see
|
|
||||||
func (gui *Gui) focusLine(lineNumber int, bottomLine int, v *gocui.View) error {
|
|
||||||
_, height := v.Size()
|
|
||||||
overScroll := bottomLine - height + 1
|
|
||||||
if overScroll < 0 {
|
|
||||||
overScroll = 0
|
|
||||||
}
|
|
||||||
if err := v.SetOrigin(0, overScroll); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := v.SetCursor(0, lineNumber-overScroll); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleStageHunk(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleStageHunk(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -198,13 +182,13 @@ func (gui *Gui) handleStageLine(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleStageLineOrHunk(hunk bool) error {
|
func (gui *Gui) handleStageLineOrHunk(hunk bool) error {
|
||||||
state := gui.State.StagingState
|
state := gui.State.Panels.Staging
|
||||||
p, err := git.NewPatchModifier(gui.Log)
|
p, err := git.NewPatchModifier(gui.Log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
currentLine := state.StageableLines[state.CurrentLineIndex]
|
currentLine := state.StageableLines[state.SelectedLine]
|
||||||
var patch string
|
var patch string
|
||||||
if hunk {
|
if hunk {
|
||||||
patch, err = p.ModifyPatchForHunk(state.Diff, state.HunkStarts, currentLine)
|
patch, err = p.ModifyPatchForHunk(state.Diff, state.HunkStarts, currentLine)
|
||||||
|
@ -8,19 +8,46 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// list panel functions
|
||||||
|
|
||||||
|
func (gui *Gui) getSelectedStashEntry(v *gocui.View) *commands.StashEntry {
|
||||||
|
selectedLine := gui.State.Panels.Stash.SelectedLine
|
||||||
|
if selectedLine == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.State.StashEntries[selectedLine]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
stashEntry := gui.getSelectedStashEntry(v)
|
||||||
|
if stashEntry == nil {
|
||||||
|
return gui.renderString(g, "main", gui.Tr.SLocalize("NoStashEntries"))
|
||||||
|
}
|
||||||
|
if err := gui.focusPoint(0, gui.State.Panels.Stash.SelectedLine, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
// doing this asynchronously cos it can take time
|
||||||
|
diff, _ := gui.GitCommand.GetStashEntryDiff(stashEntry.Index)
|
||||||
|
_ = gui.renderString(g, "main", diff)
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
|
func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
|
||||||
g.Update(func(g *gocui.Gui) error {
|
g.Update(func(g *gocui.Gui) error {
|
||||||
v, err := g.View("stash")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
gui.State.StashEntries = gui.GitCommand.GetStashEntries()
|
gui.State.StashEntries = gui.GitCommand.GetStashEntries()
|
||||||
|
|
||||||
v.Clear()
|
gui.refreshSelectedLine(&gui.State.Panels.Stash.SelectedLine, len(gui.State.StashEntries))
|
||||||
|
|
||||||
list, err := utils.RenderList(gui.State.StashEntries)
|
list, err := utils.RenderList(gui.State.StashEntries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v := gui.getStashView(gui.g)
|
||||||
|
v.Clear()
|
||||||
fmt.Fprint(v, list)
|
fmt.Fprint(v, list)
|
||||||
|
|
||||||
return gui.resetOrigin(v)
|
return gui.resetOrigin(v)
|
||||||
@ -28,34 +55,21 @@ func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getSelectedStashEntry(v *gocui.View) *commands.StashEntry {
|
func (gui *Gui) handleStashNextLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
if len(gui.State.StashEntries) == 0 {
|
panelState := gui.State.Panels.Stash
|
||||||
return nil
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.StashEntries), false)
|
||||||
}
|
|
||||||
stashView, _ := gui.g.View("stash")
|
return gui.handleStashEntrySelect(gui.g, v)
|
||||||
lineNumber := gui.getItemPosition(stashView)
|
|
||||||
return gui.State.StashEntries[lineNumber]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderStashOptions(g *gocui.Gui) error {
|
func (gui *Gui) handleStashPrevLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.renderGlobalOptions(g)
|
panelState := gui.State.Panels.Stash
|
||||||
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.StashEntries), true)
|
||||||
|
|
||||||
|
return gui.handleStashEntrySelect(gui.g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
|
// specific functions
|
||||||
if err := gui.renderStashOptions(g); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
stashEntry := gui.getSelectedStashEntry(v)
|
|
||||||
if stashEntry == nil {
|
|
||||||
gui.renderString(g, "main", gui.Tr.SLocalize("NoStashEntries"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
diff, _ := gui.GitCommand.GetStashEntryDiff(stashEntry.Index)
|
|
||||||
gui.renderString(g, "main", diff)
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) handleStashApply(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleStashApply(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.stashDo(g, v, "apply")
|
return gui.stashDo(g, v, "apply")
|
||||||
|
@ -19,7 +19,7 @@ func (gui *Gui) refreshStatus(g *gocui.Gui) error {
|
|||||||
// contents end up cleared
|
// contents end up cleared
|
||||||
g.Update(func(*gocui.Gui) error {
|
g.Update(func(*gocui.Gui) error {
|
||||||
v.Clear()
|
v.Clear()
|
||||||
pushables, pullables := gui.GitCommand.UpstreamDifferenceCount()
|
pushables, pullables := gui.GitCommand.GetCurrentBranchUpstreamDifferenceCount()
|
||||||
fmt.Fprint(v, "↑"+pushables+"↓"+pullables)
|
fmt.Fprint(v, "↑"+pushables+"↓"+pullables)
|
||||||
branches := gui.State.Branches
|
branches := gui.State.Branches
|
||||||
if err := gui.updateHasMergeConflictStatus(); err != nil {
|
if err := gui.updateHasMergeConflictStatus(); err != nil {
|
||||||
@ -42,16 +42,14 @@ func (gui *Gui) refreshStatus(g *gocui.Gui) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderStatusOptions(g *gocui.Gui) error {
|
|
||||||
return gui.renderGlobalOptions(g)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
|
||||||
gui.Updater.CheckForNewUpdate(gui.onUserUpdateCheckFinish, true)
|
gui.Updater.CheckForNewUpdate(gui.onUserUpdateCheckFinish, true)
|
||||||
return gui.createMessagePanel(gui.g, v, "", gui.Tr.SLocalize("CheckingForUpdates"))
|
return gui.createMessagePanel(gui.g, v, "", gui.Tr.SLocalize("CheckingForUpdates"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
blue := color.New(color.FgBlue)
|
||||||
|
|
||||||
dashboardString := strings.Join(
|
dashboardString := strings.Join(
|
||||||
[]string{
|
[]string{
|
||||||
lazygitTitle(),
|
lazygitTitle(),
|
||||||
@ -60,13 +58,10 @@ func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
|
|||||||
"Config Options: https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md",
|
"Config Options: https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md",
|
||||||
"Tutorial: https://youtu.be/VDXvbHZYeKY",
|
"Tutorial: https://youtu.be/VDXvbHZYeKY",
|
||||||
"Raise an Issue: https://github.com/jesseduffield/lazygit/issues",
|
"Raise an Issue: https://github.com/jesseduffield/lazygit/issues",
|
||||||
"Buy Jesse a coffee: https://donorbox.org/lazygit",
|
blue.Sprint("Buy Jesse a coffee: https://donorbox.org/lazygit"), // caffeine ain't free
|
||||||
}, "\n\n")
|
}, "\n\n")
|
||||||
|
|
||||||
if err := gui.renderString(g, "main", dashboardString); err != nil {
|
return gui.renderString(g, "main", dashboardString)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return gui.renderStatusOptions(g)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleOpenConfig(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleOpenConfig(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
@ -13,10 +13,16 @@ import (
|
|||||||
var cyclableViews = []string{"status", "files", "branches", "commits", "stash"}
|
var cyclableViews = []string{"status", "files", "branches", "commits", "stash"}
|
||||||
|
|
||||||
func (gui *Gui) refreshSidePanels(g *gocui.Gui) error {
|
func (gui *Gui) refreshSidePanels(g *gocui.Gui) error {
|
||||||
gui.refreshBranches(g)
|
if err := gui.refreshBranches(g); err != nil {
|
||||||
gui.refreshFiles(g)
|
return err
|
||||||
gui.refreshCommits(g)
|
}
|
||||||
return nil
|
if err := gui.refreshFiles(g); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := gui.refreshCommits(g); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return gui.refreshStashEntries(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) nextView(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) nextView(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -78,9 +84,6 @@ func (gui *Gui) previousView(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error {
|
||||||
mainView, _ := g.View("main")
|
|
||||||
mainView.SetOrigin(0, 0)
|
|
||||||
|
|
||||||
switch v.Name() {
|
switch v.Name() {
|
||||||
case "menu":
|
case "menu":
|
||||||
return gui.handleMenuSelect(g, v)
|
return gui.handleMenuSelect(g, v)
|
||||||
@ -90,6 +93,10 @@ func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.handleFileSelect(g, v)
|
return gui.handleFileSelect(g, v)
|
||||||
case "branches":
|
case "branches":
|
||||||
return gui.handleBranchSelect(g, v)
|
return gui.handleBranchSelect(g, v)
|
||||||
|
case "commits":
|
||||||
|
return gui.handleCommitSelect(g, v)
|
||||||
|
case "stash":
|
||||||
|
return gui.handleStashEntrySelect(g, v)
|
||||||
case "confirmation":
|
case "confirmation":
|
||||||
return nil
|
return nil
|
||||||
case "commitMessage":
|
case "commitMessage":
|
||||||
@ -101,10 +108,6 @@ func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error {
|
|||||||
gui.refreshMergePanel(g)
|
gui.refreshMergePanel(g)
|
||||||
v.Highlight = false
|
v.Highlight = false
|
||||||
return nil
|
return nil
|
||||||
case "commits":
|
|
||||||
return gui.handleCommitSelect(g, v)
|
|
||||||
case "stash":
|
|
||||||
return gui.handleStashEntrySelect(g, v)
|
|
||||||
case "staging":
|
case "staging":
|
||||||
return nil
|
return nil
|
||||||
// return gui.handleStagingSelect(g, v)
|
// return gui.handleStagingSelect(g, v)
|
||||||
@ -164,64 +167,13 @@ func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
|
|||||||
|
|
||||||
g.Cursor = newView.Editable
|
g.Cursor = newView.Editable
|
||||||
|
|
||||||
|
if err := gui.renderPanelOptions(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return gui.newLineFocused(g, newView)
|
return gui.newLineFocused(g, newView)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getItemPosition(v *gocui.View) int {
|
|
||||||
gui.correctCursor(v)
|
|
||||||
_, cy := v.Cursor()
|
|
||||||
_, oy := v.Origin()
|
|
||||||
return oy + cy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) cursorUp(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
// swallowing cursor movements in main
|
|
||||||
if v == nil || v.Name() == "main" {
|
|
||||||
return 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.newLineFocused(g, v)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) cursorDown(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
// swallowing cursor movements in main
|
|
||||||
if v == nil || v.Name() == "main" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cx, cy := v.Cursor()
|
|
||||||
ox, oy := v.Origin()
|
|
||||||
ly := v.LinesHeight() - 1
|
|
||||||
_, height := v.Size()
|
|
||||||
maxY := height - 1
|
|
||||||
|
|
||||||
// if we are at the end we just return
|
|
||||||
if cy+oy == ly {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
if cy < maxY {
|
|
||||||
err = v.SetCursor(cx, cy+1)
|
|
||||||
} else {
|
|
||||||
err = v.SetOrigin(ox, oy+1)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.newLineFocused(g, v)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) resetOrigin(v *gocui.View) error {
|
func (gui *Gui) resetOrigin(v *gocui.View) error {
|
||||||
if err := v.SetCursor(0, 0); err != nil {
|
if err := v.SetCursor(0, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -230,22 +182,43 @@ func (gui *Gui) resetOrigin(v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if the cursor down past the last item, move it to the last line
|
// if the cursor down past the last item, move it to the last line
|
||||||
func (gui *Gui) correctCursor(v *gocui.View) error {
|
func (gui *Gui) focusPoint(cx int, cy int, v *gocui.View) error {
|
||||||
cx, cy := v.Cursor()
|
if cy < 0 {
|
||||||
ox, oy := v.Origin()
|
|
||||||
_, height := v.Size()
|
|
||||||
maxY := height - 1
|
|
||||||
ly := v.LinesHeight() - 1
|
|
||||||
if oy+cy <= ly {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
newCy := utils.Min(ly, maxY)
|
ox, oy := v.Origin()
|
||||||
if err := v.SetCursor(cx, newCy); err != nil {
|
_, height := v.Size()
|
||||||
|
ly := height - 1
|
||||||
|
|
||||||
|
// if line is above origin, move origin and set cursor to zero
|
||||||
|
// if line is below origin + height, move origin and set cursor to max
|
||||||
|
// otherwise set cursor to value - origin
|
||||||
|
if ly > v.LinesHeight() {
|
||||||
|
if err := v.SetCursor(cx, cy); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := v.SetOrigin(ox, ly-newCy); err != nil {
|
if err := v.SetOrigin(ox, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else if cy < oy {
|
||||||
|
if err := v.SetCursor(cx, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := v.SetOrigin(ox, cy); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if cy > oy+ly {
|
||||||
|
if err := v.SetCursor(cx, ly); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := v.SetOrigin(ox, cy-ly); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := v.SetCursor(cx, cy-oy); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,6 +231,9 @@ func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
v.Clear()
|
v.Clear()
|
||||||
|
if err := v.SetOrigin(0, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
output := string(bom.Clean([]byte(s)))
|
output := string(bom.Clean([]byte(s)))
|
||||||
output = utils.NormalizeLinefeeds(output)
|
output = utils.NormalizeLinefeeds(output)
|
||||||
fmt.Fprint(v, output)
|
fmt.Fprint(v, output)
|
||||||
@ -275,8 +251,8 @@ func (gui *Gui) optionsMapToString(optionsMap map[string]string) string {
|
|||||||
return strings.Join(optionsArray, ", ")
|
return strings.Join(optionsArray, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderOptionsMap(g *gocui.Gui, optionsMap map[string]string) error {
|
func (gui *Gui) renderOptionsMap(optionsMap map[string]string) error {
|
||||||
return gui.renderString(g, "options", gui.optionsMapToString(optionsMap))
|
return gui.renderString(gui.g, "options", gui.optionsMapToString(optionsMap))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: refactor properly
|
// TODO: refactor properly
|
||||||
@ -306,6 +282,16 @@ func (gui *Gui) getStagingView(g *gocui.Gui) *gocui.View {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) getMainView(g *gocui.Gui) *gocui.View {
|
||||||
|
v, _ := g.View("main")
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) getStashView(g *gocui.Gui) *gocui.View {
|
||||||
|
v, _ := g.View("stash")
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) trimmedContent(v *gocui.View) string {
|
func (gui *Gui) trimmedContent(v *gocui.View) string {
|
||||||
return strings.TrimSpace(v.Buffer())
|
return strings.TrimSpace(v.Buffer())
|
||||||
}
|
}
|
||||||
@ -336,3 +322,68 @@ func (gui *Gui) resizePopupPanel(g *gocui.Gui, v *gocui.View) error {
|
|||||||
_, err := g.SetView(v.Name(), x0, y0, x1, y1, 0)
|
_, err := g.SetView(v.Name(), x0, y0, x1, y1, 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generalFocusLine takes a lineNumber to focus, and a bottomLine to ensure we can see
|
||||||
|
func (gui *Gui) generalFocusLine(lineNumber int, bottomLine int, v *gocui.View) error {
|
||||||
|
_, height := v.Size()
|
||||||
|
overScroll := bottomLine - height + 1
|
||||||
|
if overScroll < 0 {
|
||||||
|
overScroll = 0
|
||||||
|
}
|
||||||
|
if err := v.SetOrigin(0, overScroll); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := v.SetCursor(0, lineNumber-overScroll); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) changeSelectedLine(line *int, total int, up bool) {
|
||||||
|
if up {
|
||||||
|
if *line == -1 || *line == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
*line -= 1
|
||||||
|
} else {
|
||||||
|
if *line == -1 || *line == total-1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
*line += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) refreshSelectedLine(line *int, total int) {
|
||||||
|
if *line == -1 && total > 0 {
|
||||||
|
*line = 0
|
||||||
|
} else if total-1 < *line {
|
||||||
|
*line = total - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) renderListPanel(v *gocui.View, items interface{}) error {
|
||||||
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
|
list, err := utils.RenderList(items)
|
||||||
|
if err != nil {
|
||||||
|
return gui.createErrorPanel(gui.g, err.Error())
|
||||||
|
}
|
||||||
|
v.Clear()
|
||||||
|
fmt.Fprint(v, list)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) renderPanelOptions() error {
|
||||||
|
currentView := gui.g.CurrentView()
|
||||||
|
switch currentView.Name() {
|
||||||
|
case "menu":
|
||||||
|
return gui.renderMenuOptions()
|
||||||
|
case "main":
|
||||||
|
return gui.renderMergeOptions()
|
||||||
|
default:
|
||||||
|
return gui.renderGlobalOptions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -150,7 +150,7 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
|||||||
Other: "Dit bestand heeft geen merge conflicten",
|
Other: "Dit bestand heeft geen merge conflicten",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SureResetHardHead",
|
ID: "SureResetHardHead",
|
||||||
Other: "Weet je het zeker dat je `reset --hard HEAD` wil uitvoeren? Het kan dat je hierdoor bestanden verliest",
|
Other: "Weet je het zeker dat je `reset --hard HEAD` en `clean -fd` wil uitvoeren? Het kan dat je hierdoor bestanden verliest",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SureTo",
|
ID: "SureTo",
|
||||||
Other: "Weet je het zeker dat je {{.fileName}} wilt {{.deleteVerb}} (je veranderingen zullen worden verwijderd)",
|
Other: "Weet je het zeker dat je {{.fileName}} wilt {{.deleteVerb}} (je veranderingen zullen worden verwijderd)",
|
||||||
@ -396,7 +396,7 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
|||||||
Other: `refresh bestanden`,
|
Other: `refresh bestanden`,
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "resetHard",
|
ID: "resetHard",
|
||||||
Other: `harde reset`,
|
Other: `harde reset and verwijderen ongevolgde bestanden`,
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "mergeIntoCurrentBranch",
|
ID: "mergeIntoCurrentBranch",
|
||||||
Other: `merge in met huidige checked out branch`,
|
Other: `merge in met huidige checked out branch`,
|
||||||
|
@ -158,7 +158,7 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
Other: "This file has no merge conflicts",
|
Other: "This file has no merge conflicts",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SureResetHardHead",
|
ID: "SureResetHardHead",
|
||||||
Other: "Are you sure you want `reset --hard HEAD`? You may lose changes",
|
Other: "Are you sure you want `reset --hard HEAD` and `clean -fd`? You may lose changes",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SureTo",
|
ID: "SureTo",
|
||||||
Other: "Are you sure you want to {{.deleteVerb}} {{.fileName}} (you will lose your changes)?",
|
Other: "Are you sure you want to {{.deleteVerb}} {{.fileName}} (you will lose your changes)?",
|
||||||
@ -404,7 +404,7 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
Other: `refresh files`,
|
Other: `refresh files`,
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "resetHard",
|
ID: "resetHard",
|
||||||
Other: `reset hard`,
|
Other: `reset hard and remove untracked files`,
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "mergeIntoCurrentBranch",
|
ID: "mergeIntoCurrentBranch",
|
||||||
Other: `merge into currently checked out branch`,
|
Other: `merge into currently checked out branch`,
|
||||||
@ -456,6 +456,12 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "CantFindHunk",
|
ID: "CantFindHunk",
|
||||||
Other: `Could not find hunk`,
|
Other: `Could not find hunk`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "FastForward",
|
||||||
|
Other: `fast-forward this branch from its upstream`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "Fetching",
|
||||||
|
Other: "fetching and fast-forwarding {{.from}} -> {{.to}} ...",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
|||||||
Other: "Ten plik nie powoduje konfliktów scalania",
|
Other: "Ten plik nie powoduje konfliktów scalania",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SureResetHardHead",
|
ID: "SureResetHardHead",
|
||||||
Other: "Jesteś pewny, że chcesz wykonać `reset --hard HEAD`? Możesz stracić wprowadzone zmiany",
|
Other: "Jesteś pewny, że chcesz wykonać `reset --hard HEAD` i `clean -fd`? Możesz stracić wprowadzone zmiany",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SureTo",
|
ID: "SureTo",
|
||||||
Other: "Jesteś pewny, że chcesz {{.deleteVerb}} {{.fileName}} (stracisz swoje wprowadzone zmiany)?",
|
Other: "Jesteś pewny, że chcesz {{.deleteVerb}} {{.fileName}} (stracisz swoje wprowadzone zmiany)?",
|
||||||
@ -382,7 +382,7 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
|||||||
Other: `odśwież pliki`,
|
Other: `odśwież pliki`,
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "resetHard",
|
ID: "resetHard",
|
||||||
Other: `zresetuj twardo`,
|
Other: `zresetuj twardo i usuń niepotwierdzone pliki`,
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "mergeIntoCurrentBranch",
|
ID: "mergeIntoCurrentBranch",
|
||||||
Other: `scal do obecnej gałęzi`,
|
Other: `scal do obecnej gałęzi`,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@ -235,3 +236,8 @@ func PrevIndex(numbers []int, currentNumber int) int {
|
|||||||
}
|
}
|
||||||
return end
|
return end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AsJson(i interface{}) string {
|
||||||
|
bytes, _ := json.MarshalIndent(i, "", " ")
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
@ -517,3 +517,14 @@ func TestPrevIndex(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAsJson(t *testing.T) {
|
||||||
|
type myStruct struct {
|
||||||
|
a string
|
||||||
|
}
|
||||||
|
|
||||||
|
output := AsJson(&myStruct{a: "foo"})
|
||||||
|
|
||||||
|
// no idea why this is returning empty hashes but it's works in the app ¯\_(ツ)_/¯
|
||||||
|
assert.EqualValues(t, "{}", output)
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user