1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-04 03:48:07 +02:00

got this bad boy compiling again

This commit is contained in:
Jesse Duffield 2018-08-13 21:16:21 +10:00
parent 97cff65612
commit 9e725ae24e
13 changed files with 267 additions and 241 deletions

View File

@ -2,9 +2,9 @@ package app
import (
"io"
"os"
"github.com/Sirupsen/logrus"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui"
@ -18,7 +18,21 @@ type App struct {
Log *logrus.Logger
OSCommand *commands.OSCommand
GitCommand *commands.GitCommand
Gui *gocui.Gui
Gui *gui.Gui
}
func newLogger(config config.AppConfigurer) *logrus.Logger {
log := logrus.New()
if !config.GetDebug() {
log.Out = nil
return log
}
file, err := os.OpenFile("development.log", os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
panic("unable to log to file") // TODO: don't panic (also, remove this call to the `panic` function)
}
log.Out = file
return log
}
// NewApp retruns a new applications
@ -28,7 +42,7 @@ func NewApp(config config.AppConfigurer) (*App, error) {
Config: config,
}
var err error
app.Log = logrus.New()
app.Log = newLogger(config)
app.OSCommand, err = commands.NewOSCommand(app.Log)
if err != nil {
return nil, err
@ -37,7 +51,7 @@ func NewApp(config config.AppConfigurer) (*App, error) {
if err != nil {
return nil, err
}
app.Gui, err = gui.NewGui(app.Log, app.GitCommand, config.GetVersion())
app.Gui, err = gui.NewGui(app.Log, app.GitCommand, app.OSCommand, config.GetVersion())
if err != nil {
return nil, err
}

View File

@ -30,7 +30,7 @@ type StashEntry struct {
// Conflict : A git conflict with a start middle and end corresponding to line
// numbers in the file where the conflict bars appear
type Conflict struct {
start int
middle int
end int
Start int
Middle int
End int
}

View File

@ -8,7 +8,6 @@ import (
"strings"
"github.com/Sirupsen/logrus"
"github.com/jesseduffield/gocui"
gitconfig "github.com/tcnksm/go-gitconfig"
)
@ -105,28 +104,34 @@ func (c *OSCommand) GetOpenCommand() (string, string, error) {
// VsCodeOpenFile opens the file in code, with the -r flag to open in the
// current window
func (c *OSCommand) VsCodeOpenFile(g *gocui.Gui, filename string) (string, error) {
return c.RunCommand("code -r " + filename)
// each of these open files needs to have the same function signature because
// they're being passed as arguments into another function,
// but only editFile actually returns a *exec.Cmd
func (c *OSCommand) VsCodeOpenFile(filename string) (*exec.Cmd, error) {
_, err := c.RunCommand("code -r " + filename)
return nil, err
}
// SublimeOpenFile opens the filein sublime
// may be deprecated in the future
func (c *OSCommand) SublimeOpenFile(g *gocui.Gui, filename string) (string, error) {
return c.RunCommand("subl " + filename)
func (c *OSCommand) SublimeOpenFile(filename string) (*exec.Cmd, error) {
_, err := c.RunCommand("subl " + filename)
return nil, err
}
// OpenFile opens a file with the given
func (c *OSCommand) OpenFile(g *gocui.Gui, filename string) (string, error) {
func (c *OSCommand) OpenFile(filename string) (*exec.Cmd, error) {
cmdName, cmdTrail, err := c.GetOpenCommand()
if err != nil {
return "", err
return nil, err
}
return c.RunCommand(cmdName + " " + filename + cmdTrail)
_, err = c.RunCommand(cmdName + " " + filename + cmdTrail)
return nil, err
}
// EditFile opens a file in a subprocess using whatever editor is available,
// falling back to core.editor, VISUAL, EDITOR, then vi
func (c *OSCommand) editFile(g *gocui.Gui, filename string) (string, error) {
func (c *OSCommand) EditFile(filename string) (*exec.Cmd, error) {
editor, _ := gitconfig.Global("core.editor")
if editor == "" {
editor = os.Getenv("VISUAL")
@ -140,10 +145,9 @@ func (c *OSCommand) editFile(g *gocui.Gui, filename string) (string, error) {
}
}
if editor == "" {
return "", ErrNoEditorDefined
return nil, ErrNoEditorDefined
}
c.PrepareSubProcess(editor, filename)
return "", nil
return c.PrepareSubProcess(editor, filename)
}
// PrepareSubProcess iniPrepareSubProcessrocess then tells the Gui to switch to it

View File

@ -117,7 +117,7 @@ func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
return nil
}
// 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
// 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 {
@ -135,7 +135,7 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error {
fmt.Fprintln(v, branch.GetDisplayString())
}
gui.resetOrigin(v)
return refreshStatus(g)
return gui.refreshStatus(g)
})
return nil
}

View File

@ -34,7 +34,7 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error {
shaColor.Fprint(v, commit.Sha+" ")
white.Fprintln(v, commit.Name)
}
refreshStatus(g)
gui.refreshStatus(g)
if g.CurrentView().Name() == "commits" {
gui.handleCommitSelect(g, v)
}
@ -105,7 +105,7 @@ func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
if err := gui.refreshCommits(g); err != nil {
panic(err)
}
refreshStatus(g)
gui.refreshStatus(g)
return gui.handleCommitSelect(g, v)
}
@ -138,7 +138,7 @@ func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
if err := gui.refreshCommits(g); err != nil {
panic(err)
}
return refreshStatus(g)
return gui.refreshStatus(g)
}, nil)
return nil
}

View File

@ -8,6 +8,7 @@ import (
// "strings"
"errors"
"os/exec"
"strings"
"github.com/fatih/color"
@ -171,7 +172,7 @@ func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error {
gui.renderfilesOptions(g, &file)
var content string
if file.HasMergeConflicts {
return refreshMergePanel(g)
return gui.refreshMergePanel(g)
}
content = gui.GitCommand.Diff(file)
@ -191,9 +192,9 @@ func (gui *Gui) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
return nil
}
// HandleCommitEditorPress - handle when the user wants to commit changes via
// handleCommitEditorPress - handle when the user wants to commit changes via
// their editor rather than via the popup panel
func (gui *Gui) HandleCommitEditorPress(g *gocui.Gui, filesView *gocui.View) error {
func (gui *Gui) handleCommitEditorPress(g *gocui.Gui, filesView *gocui.View) error {
if len(gui.stagedFiles(gui.State.Files)) == 0 && !gui.State.HasMergeConflicts {
return gui.createErrorPanel(g, "There are no staged files to commit")
}
@ -214,7 +215,7 @@ func (gui *Gui) PrepareSubProcess(g *gocui.Gui, commands ...string) error {
return nil
}
func (gui *Gui) genericFileOpen(g *gocui.Gui, v *gocui.View, open func(*gocui.Gui, string) (string, error)) error {
func (gui *Gui) genericFileOpen(g *gocui.Gui, v *gocui.View, open func(string) (*exec.Cmd, error)) error {
file, err := gui.getSelectedFile(g)
if err != nil {
if err != errNoFiles {
@ -222,26 +223,31 @@ func (gui *Gui) genericFileOpen(g *gocui.Gui, v *gocui.View, open func(*gocui.Gu
}
return nil
}
if _, err := open(g, file.Name); err != nil {
sub, err := open(file.Name)
if err != nil {
return gui.createErrorPanel(g, err.Error())
}
if sub != nil {
gui.SubProcess = sub
return ErrSubProcess
}
return nil
}
func (gui *Gui) handleFileEdit(g *gocui.Gui, v *gocui.View) error {
return gui.genericFileOpen(g, v, gui.editFile)
return gui.genericFileOpen(g, v, gui.OSCommand.EditFile)
}
func (gui *Gui) handleFileOpen(g *gocui.Gui, v *gocui.View) error {
return gui.genericFileOpen(g, v, gui.openFile)
return gui.genericFileOpen(g, v, gui.OSCommand.OpenFile)
}
func (gui *Gui) handleSublimeFileOpen(g *gocui.Gui, v *gocui.View) error {
return gui.genericFileOpen(g, v, gui.sublimeOpenFile)
return gui.genericFileOpen(g, v, gui.OSCommand.SublimeOpenFile)
}
func (gui *Gui) handleVsCodeFileOpen(g *gocui.Gui, v *gocui.View) error {
return gui.genericFileOpen(g, v, gui.vsCodeOpenFile)
return gui.genericFileOpen(g, v, gui.OSCommand.VsCodeOpenFile)
}
func (gui *Gui) handleRefreshFiles(g *gocui.Gui, v *gocui.View) error {
@ -250,13 +256,13 @@ func (gui *Gui) handleRefreshFiles(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) refreshStateFiles() {
// get files to stage
files := getGitStatusFiles()
gui.State.Files = mergeGitStatusFiles(gui.State.Files, files)
updateHasMergeConflictStatus()
files := gui.GitCommand.GetStatusFiles()
gui.State.Files = gui.GitCommand.MergeStatusFiles(gui.State.Files, files)
gui.updateHasMergeConflictStatus()
}
func (gui *Gui) updateHasMergeConflictStatus() error {
merging, err := isInMergeState()
merging, err := gui.GitCommand.IsInMergeState()
if err != nil {
return err
}
@ -290,7 +296,7 @@ func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
}
return "", gui.renderString(g, "main", "No file to display")
}
cat, err := catFile(item.Name)
cat, err := gui.GitCommand.CatFile(item.Name)
if err != nil {
panic(err)
}
@ -302,12 +308,12 @@ func (gui *Gui) refreshFiles(g *gocui.Gui) error {
if err != nil {
return err
}
refreshStateFiles()
gui.refreshStateFiles()
filesView.Clear()
for _, file := range gui.State.Files {
renderFile(file, filesView)
gui.renderFile(file, filesView)
}
correctCursor(filesView)
gui.correctCursor(filesView)
if filesView == g.CurrentView() {
gui.handleFileSelect(g, filesView)
}
@ -315,14 +321,14 @@ func (gui *Gui) refreshFiles(g *gocui.Gui) error {
}
func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error {
createMessagePanel(g, v, "", "Pulling...")
gui.createMessagePanel(g, v, "", "Pulling...")
go func() {
if output, err := gitPull(); err != nil {
if output, err := gui.GitCommand.Pull(); err != nil {
gui.createErrorPanel(g, output)
} else {
gui.closeConfirmationPrompt(g)
refreshCommits(g)
refreshStatus(g)
gui.refreshCommits(g)
gui.refreshStatus(g)
}
gui.refreshFiles(g)
}()
@ -330,15 +336,15 @@ func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
createMessagePanel(g, v, "", "Pushing...")
gui.createMessagePanel(g, v, "", "Pushing...")
go func() {
branchName = gui.State.Branches[0].Name
if output, err := commands.Push(branchName); err != nil {
branchName := gui.State.Branches[0].Name
if output, err := gui.GitCommand.Push(branchName); err != nil {
gui.createErrorPanel(g, output)
} else {
gui.closeConfirmationPrompt(g)
refreshCommits(g)
refreshStatus(g)
gui.refreshCommits(g)
gui.refreshStatus(g)
}
}()
return nil
@ -360,22 +366,22 @@ func (gui *Gui) handleSwitchToMerge(g *gocui.Gui, v *gocui.View) error {
return gui.createErrorPanel(g, "This file has no merge conflicts")
}
gui.switchFocus(g, v, mergeView)
return refreshMergePanel(g)
return gui.refreshMergePanel(g)
}
func (gui *Gui) handleAbortMerge(g *gocui.Gui, v *gocui.View) error {
output, err := gitAbortMerge()
output, err := gui.GitCommand.AbortMerge()
if err != nil {
return gui.createErrorPanel(g, output)
}
createMessagePanel(g, v, "", "Merge aborted")
refreshStatus(g)
gui.createMessagePanel(g, v, "", "Merge aborted")
gui.refreshStatus(g)
return gui.refreshFiles(g)
}
func (gui *Gui) handleResetHard(g *gocui.Gui, v *gocui.View) error {
return gui.createConfirmationPanel(g, v, "Clear file panel", "Are you sure you want `reset --hard HEAD`? You may lose changes", func(g *gocui.Gui, v *gocui.View) error {
if err := commands.ResetHard(); err != nil {
if err := gui.GitCommand.ResetHard(); err != nil {
gui.createErrorPanel(g, err.Error())
}
return gui.refreshFiles(g)

View File

@ -37,12 +37,27 @@ type Gui struct {
OSCommand *commands.OSCommand
Version string
SubProcess *exec.Cmd
State StateType
State guiState
}
type guiState struct {
Files []commands.File
Branches []commands.Branch
Commits []commands.Commit
StashEntries []commands.StashEntry
PreviousView string
HasMergeConflicts bool
ConflictIndex int
ConflictTop bool
Conflicts []commands.Conflict
EditHistory *stack.Stack
Platform platform
Version string
}
// NewGui builds a new gui handler
func NewGui(log *logrus.Logger, gitCommand *commands.GitCommand, oSCommand *commands.OSCommand, version string) (*Gui, error) {
initialState := StateType{
initialState := guiState{
Files: make([]commands.File, 0),
PreviousView: "files",
Commits: make([]commands.Commit, 0),
@ -64,21 +79,6 @@ func NewGui(log *logrus.Logger, gitCommand *commands.GitCommand, oSCommand *comm
}, nil
}
type StateType struct {
Files []commands.File
Branches []commands.Branch
Commits []commands.Commit
StashEntries []commands.StashEntry
PreviousView string
HasMergeConflicts bool
ConflictIndex int
ConflictTop bool
Conflicts []commands.Conflict
EditHistory *stack.Stack
Platform platform
Version string
}
type platform struct {
os string
shell string
@ -105,7 +105,7 @@ func getPlatform() platform {
}
}
func scrollUpMain(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) scrollUpMain(g *gocui.Gui, v *gocui.View) error {
mainView, _ := g.View("main")
ox, oy := mainView.Origin()
if oy >= 1 {
@ -114,7 +114,7 @@ func scrollUpMain(g *gocui.Gui, v *gocui.View) error {
return nil
}
func scrollDownMain(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) scrollDownMain(g *gocui.Gui, v *gocui.View) error {
mainView, _ := g.View("main")
ox, oy := mainView.Origin()
if oy < len(mainView.BufferLines()) {
@ -123,8 +123,8 @@ func scrollDownMain(g *gocui.Gui, v *gocui.View) error {
return nil
}
func handleRefresh(g *gocui.Gui, v *gocui.View) error {
return refreshSidePanels(g)
func (gui *Gui) handleRefresh(g *gocui.Gui, v *gocui.View) error {
return gui.refreshSidePanels(g)
}
func max(a, b int) int {
@ -257,35 +257,35 @@ func (gui *Gui) layout(g *gocui.Gui) error {
// these are only called once
gui.handleFileSelect(g, filesView)
gui.refreshFiles(g)
refreshBranches(g)
refreshCommits(g)
refreshStashEntries(g)
nextView(g, nil)
gui.refreshBranches(g)
gui.refreshCommits(g)
gui.refreshStashEntries(g)
gui.nextView(g, nil)
}
resizePopupPanels(g)
gui.resizePopupPanels(g)
return nil
}
func fetch(g *gocui.Gui) error {
gitFetch()
refreshStatus(g)
func (gui *Gui) fetch(g *gocui.Gui) error {
gui.GitCommand.Fetch()
gui.refreshStatus(g)
return nil
}
func updateLoader(g *gocui.Gui) error {
func (gui *Gui) updateLoader(g *gocui.Gui) error {
if confirmationView, _ := g.View("confirmation"); confirmationView != nil {
content := gui.trimmedContent(confirmationView)
if strings.Contains(content, "...") {
staticContent := strings.Split(content, "...")[0] + "..."
gui.renderString(g, "confirmation", staticContent+" "+loader())
gui.renderString(g, "confirmation", staticContent+" "+gui.loader())
}
}
return nil
}
func goEvery(g *gocui.Gui, interval time.Duration, function func(*gocui.Gui) error) {
func (gui *Gui) goEvery(g *gocui.Gui, interval time.Duration, function func(*gocui.Gui) error) {
go func() {
for range time.Tick(interval) {
function(g)
@ -293,36 +293,36 @@ func goEvery(g *gocui.Gui, interval time.Duration, function func(*gocui.Gui) err
}()
}
func resizePopupPanels(g *gocui.Gui) error {
func (gui *Gui) resizePopupPanels(g *gocui.Gui) error {
v := g.CurrentView()
if v.Name() == "commitMessage" || v.Name() == "confirmation" {
return resizePopupPanel(g, v)
return gui.resizePopupPanel(g, v)
}
return nil
}
// Run setup the gui with keybindings and start the mainloop
func (gui *Gui) Run() (*exec.Cmd, error) {
func (gui *Gui) Run() error {
g, err := gocui.NewGui(gocui.OutputNormal, OverlappingEdges)
if err != nil {
return nil, err
return err
}
defer g.Close()
g.FgColor = gocui.ColorDefault
goEvery(g, time.Second*60, fetch)
goEvery(g, time.Second*10, gui.refreshFiles)
goEvery(g, time.Millisecond*10, updateLoader)
gui.goEvery(g, time.Second*60, gui.fetch)
gui.goEvery(g, time.Second*10, gui.refreshFiles)
gui.goEvery(g, time.Millisecond*10, gui.updateLoader)
g.SetManagerFunc(gui.layout)
if err = gui.keybindings(g); err != nil {
return nil, err
return err
}
err = g.MainLoop()
return nil, err
return err
}
// RunWithSubprocesses loops, instantiating a new gocui.Gui with each iteration
@ -342,6 +342,6 @@ func (gui *Gui) RunWithSubprocesses() {
}
}
func quit(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}

View File

@ -70,15 +70,15 @@ func (gui *Gui) keybindings(g *gocui.Gui) error {
// input in the confirmation panel
for _, viewName := range []string{"files", "branches", "commits", "stash"} {
bindings = append(bindings, []Binding{
{ViewName: viewName, Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: nextView},
{ViewName: viewName, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: previousView},
{ViewName: viewName, Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: nextView},
{ViewName: viewName, Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: cursorUp},
{ViewName: viewName, Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: cursorDown},
{ViewName: viewName, Key: 'h', Modifier: gocui.ModNone, Handler: previousView},
{ViewName: viewName, Key: 'l', Modifier: gocui.ModNone, Handler: nextView},
{ViewName: viewName, Key: 'k', Modifier: gocui.ModNone, Handler: cursorUp},
{ViewName: viewName, Key: 'j', Modifier: gocui.ModNone, Handler: cursorDown},
{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.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: '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},
}...)
}

View File

@ -16,89 +16,89 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
)
func findConflicts(content string) ([]commands.Conflict, error) {
func (gui *Gui) findConflicts(content string) ([]commands.Conflict, error) {
conflicts := make([]commands.Conflict, 0)
var newConflict conflict
var newConflict commands.Conflict
for i, line := range utils.SplitLines(content) {
if line == "<<<<<<< HEAD" || line == "<<<<<<< MERGE_HEAD" || line == "<<<<<<< Updated upstream" {
newConflict = conflict{start: i}
newConflict = commands.Conflict{Start: i}
} else if line == "=======" {
newConflict.middle = i
newConflict.Middle = i
} else if strings.HasPrefix(line, ">>>>>>> ") {
newConflict.end = i
newConflict.End = i
conflicts = append(conflicts, newConflict)
}
}
return conflicts, nil
}
func shiftConflict(conflicts []commands.Conflict) (commands.Conflict, []commands.Conflict) {
func (gui *Gui) shiftConflict(conflicts []commands.Conflict) (commands.Conflict, []commands.Conflict) {
return conflicts[0], conflicts[1:]
}
func shouldHighlightLine(index int, conflict commands.Conflict, top bool) bool {
return (index >= conflict.start && index <= conflict.middle && top) || (index >= conflict.middle && index <= conflict.end && !top)
func (gui *Gui) shouldHighlightLine(index int, conflict commands.Conflict, top bool) bool {
return (index >= conflict.Start && index <= conflict.Middle && top) || (index >= conflict.Middle && index <= conflict.End && !top)
}
func coloredConflictFile(content string, conflicts []commands.Conflict, conflictIndex int, conflictTop, hasFocus bool) (string, error) {
func (gui *Gui) coloredConflictFile(content string, conflicts []commands.Conflict, conflictIndex int, conflictTop, hasFocus bool) (string, error) {
if len(conflicts) == 0 {
return content, nil
}
conflict, remainingConflicts := shiftConflict(conflicts)
conflict, remainingConflicts := gui.shiftConflict(conflicts)
var outputBuffer bytes.Buffer
for i, line := range splitLines(content) {
for i, line := range utils.SplitLines(content) {
colourAttr := color.FgWhite
if i == conflict.start || i == conflict.middle || i == conflict.end {
if i == conflict.Start || i == conflict.Middle || i == conflict.End {
colourAttr = color.FgRed
}
colour := color.New(colourAttr)
if hasFocus && conflictIndex < len(conflicts) && conflicts[conflictIndex] == conflict && shouldHighlightLine(i, conflict, conflictTop) {
if hasFocus && conflictIndex < len(conflicts) && conflicts[conflictIndex] == conflict && gui.shouldHighlightLine(i, conflict, conflictTop) {
colour.Add(color.Bold)
}
if i == conflict.end && len(remainingConflicts) > 0 {
conflict, remainingConflicts = shiftConflict(remainingConflicts)
if i == conflict.End && len(remainingConflicts) > 0 {
conflict, remainingConflicts = gui.shiftConflict(remainingConflicts)
}
outputBuffer.WriteString(coloredStringDirect(line, colour) + "\n")
outputBuffer.WriteString(utils.ColoredStringDirect(line, colour) + "\n")
}
return outputBuffer.String(), nil
}
func handleSelectTop(g *gocui.Gui, v *gocui.View) error {
state.ConflictTop = true
return refreshMergePanel(g)
func (gui *Gui) handleSelectTop(g *gocui.Gui, v *gocui.View) error {
gui.State.ConflictTop = true
return gui.refreshMergePanel(g)
}
func handleSelectBottom(g *gocui.Gui, v *gocui.View) error {
state.ConflictTop = false
return refreshMergePanel(g)
func (gui *Gui) handleSelectBottom(g *gocui.Gui, v *gocui.View) error {
gui.State.ConflictTop = false
return gui.refreshMergePanel(g)
}
func handleSelectNextConflict(g *gocui.Gui, v *gocui.View) error {
if state.ConflictIndex >= len(state.Conflicts)-1 {
func (gui *Gui) handleSelectNextConflict(g *gocui.Gui, v *gocui.View) error {
if gui.State.ConflictIndex >= len(gui.State.Conflicts)-1 {
return nil
}
state.ConflictIndex++
return refreshMergePanel(g)
gui.State.ConflictIndex++
return gui.refreshMergePanel(g)
}
func handleSelectPrevConflict(g *gocui.Gui, v *gocui.View) error {
if state.ConflictIndex <= 0 {
func (gui *Gui) handleSelectPrevConflict(g *gocui.Gui, v *gocui.View) error {
if gui.State.ConflictIndex <= 0 {
return nil
}
state.ConflictIndex--
return refreshMergePanel(g)
gui.State.ConflictIndex--
return gui.refreshMergePanel(g)
}
func isIndexToDelete(i int, conflict commands.Conflict, pick string) bool {
return i == conflict.middle ||
i == conflict.start ||
i == conflict.end ||
func (gui *Gui) isIndexToDelete(i int, conflict commands.Conflict, pick string) bool {
return i == conflict.Middle ||
i == conflict.Start ||
i == conflict.End ||
pick != "both" &&
(pick == "bottom" && i > conflict.start && i < conflict.middle) ||
(pick == "top" && i > conflict.middle && i < conflict.end)
(pick == "bottom" && i > conflict.Start && i < conflict.Middle) ||
(pick == "top" && i > conflict.Middle && i < conflict.End)
}
func resolveConflict(g *gocui.Gui, conflict commands.Conflict, pick string) error {
func (gui *Gui) resolveConflict(g *gocui.Gui, conflict commands.Conflict, pick string) error {
gitFile, err := gui.getSelectedFile(g)
if err != nil {
return err
@ -116,126 +116,121 @@ func resolveConflict(g *gocui.Gui, conflict commands.Conflict, pick string) erro
if err != nil {
break
}
if !isIndexToDelete(i, conflict, pick) {
if !gui.isIndexToDelete(i, conflict, pick) {
output += line
}
}
devLog(output)
gui.Log.Info(output)
return ioutil.WriteFile(gitFile.Name, []byte(output), 0644)
}
func pushFileSnapshot(g *gocui.Gui) error {
func (gui *Gui) pushFileSnapshot(g *gocui.Gui) error {
gitFile, err := gui.getSelectedFile(g)
if err != nil {
return err
}
content, err := catFile(gitFile.Name)
content, err := gui.GitCommand.CatFile(gitFile.Name)
if err != nil {
return err
}
state.EditHistory.Push(content)
gui.State.EditHistory.Push(content)
return nil
}
func handlePopFileSnapshot(g *gocui.Gui, v *gocui.View) error {
if state.EditHistory.Len() == 0 {
func (gui *Gui) handlePopFileSnapshot(g *gocui.Gui, v *gocui.View) error {
if gui.State.EditHistory.Len() == 0 {
return nil
}
prevContent := state.EditHistory.Pop().(string)
prevContent := gui.State.EditHistory.Pop().(string)
gitFile, err := gui.getSelectedFile(g)
if err != nil {
return err
}
ioutil.WriteFile(gitFile.Name, []byte(prevContent), 0644)
return refreshMergePanel(g)
return gui.refreshMergePanel(g)
}
func handlePickHunk(g *gocui.Gui, v *gocui.View) error {
conflict := state.Conflicts[state.ConflictIndex]
pushFileSnapshot(g)
func (gui *Gui) handlePickHunk(g *gocui.Gui, v *gocui.View) error {
conflict := gui.State.Conflicts[gui.State.ConflictIndex]
gui.pushFileSnapshot(g)
pick := "bottom"
if state.ConflictTop {
if gui.State.ConflictTop {
pick = "top"
}
err := resolveConflict(g, conflict, pick)
err := gui.resolveConflict(g, conflict, pick)
if err != nil {
panic(err)
}
refreshMergePanel(g)
gui.refreshMergePanel(g)
return nil
}
func handlePickBothHunks(g *gocui.Gui, v *gocui.View) error {
conflict := state.Conflicts[state.ConflictIndex]
pushFileSnapshot(g)
err := resolveConflict(g, conflict, "both")
func (gui *Gui) handlePickBothHunks(g *gocui.Gui, v *gocui.View) error {
conflict := gui.State.Conflicts[gui.State.ConflictIndex]
gui.pushFileSnapshot(g)
err := gui.resolveConflict(g, conflict, "both")
if err != nil {
panic(err)
}
return refreshMergePanel(g)
return gui.refreshMergePanel(g)
}
func currentViewName(g *gocui.Gui) string {
currentView := g.CurrentView()
return currentView.Name()
}
func refreshMergePanel(g *gocui.Gui) error {
cat, err := catSelectedFile(g)
func (gui *Gui) refreshMergePanel(g *gocui.Gui) error {
cat, err := gui.catSelectedFile(g)
if err != nil {
return err
}
state.Conflicts, err = findConflicts(cat)
gui.State.Conflicts, err = gui.findConflicts(cat)
if err != nil {
return err
}
if len(state.Conflicts) == 0 {
return handleCompleteMerge(g)
} else if state.ConflictIndex > len(state.Conflicts)-1 {
state.ConflictIndex = len(state.Conflicts) - 1
if len(gui.State.Conflicts) == 0 {
return gui.handleCompleteMerge(g)
} else if gui.State.ConflictIndex > len(gui.State.Conflicts)-1 {
gui.State.ConflictIndex = len(gui.State.Conflicts) - 1
}
hasFocus := currentViewName(g) == "main"
hasFocus := gui.currentViewName(g) == "main"
if hasFocus {
renderMergeOptions(g)
gui.renderMergeOptions(g)
}
content, err := coloredConflictFile(cat, state.Conflicts, state.ConflictIndex, state.ConflictTop, hasFocus)
content, err := gui.coloredConflictFile(cat, gui.State.Conflicts, gui.State.ConflictIndex, gui.State.ConflictTop, hasFocus)
if err != nil {
return err
}
if err := scrollToConflict(g); err != nil {
if err := gui.scrollToConflict(g); err != nil {
return err
}
return gui.renderString(g, "main", content)
}
func scrollToConflict(g *gocui.Gui) error {
func (gui *Gui) scrollToConflict(g *gocui.Gui) error {
mainView, err := g.View("main")
if err != nil {
return err
}
if len(state.Conflicts) == 0 {
if len(gui.State.Conflicts) == 0 {
return nil
}
conflict := state.Conflicts[state.ConflictIndex]
conflict := gui.State.Conflicts[gui.State.ConflictIndex]
ox, _ := mainView.Origin()
_, height := mainView.Size()
conflictMiddle := (conflict.end + conflict.start) / 2
conflictMiddle := (conflict.End + conflict.Start) / 2
newOriginY := int(math.Max(0, float64(conflictMiddle-(height/2))))
return mainView.SetOrigin(ox, newOriginY)
}
func switchToMerging(g *gocui.Gui) error {
state.ConflictIndex = 0
state.ConflictTop = true
func (gui *Gui) switchToMerging(g *gocui.Gui) error {
gui.State.ConflictIndex = 0
gui.State.ConflictTop = true
_, err := g.SetCurrentView("main")
if err != nil {
return err
}
return refreshMergePanel(g)
return gui.refreshMergePanel(g)
}
func renderMergeOptions(g *gocui.Gui) error {
func (gui *Gui) renderMergeOptions(g *gocui.Gui) error {
return gui.renderOptionsMap(g, map[string]string{
"↑ ↓": "select hunk",
"← →": "navigate conflicts",
@ -245,7 +240,7 @@ func renderMergeOptions(g *gocui.Gui) error {
})
}
func handleEscapeMerge(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handleEscapeMerge(g *gocui.Gui, v *gocui.View) error {
filesView, err := g.View("files")
if err != nil {
return err
@ -254,12 +249,12 @@ func handleEscapeMerge(g *gocui.Gui, v *gocui.View) error {
return gui.switchFocus(g, v, filesView)
}
func handleCompleteMerge(g *gocui.Gui) error {
func (gui *Gui) handleCompleteMerge(g *gocui.Gui) error {
filesView, err := g.View("files")
if err != nil {
return err
}
stageSelectedFile(g)
gui.stageSelectedFile(g)
gui.refreshFiles(g)
return gui.switchFocus(g, nil, filesView)
}

View File

@ -4,17 +4,18 @@ import (
"fmt"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
)
func refreshStashEntries(g *gocui.Gui) error {
func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
g.Update(func(g *gocui.Gui) error {
v, err := g.View("stash")
if err != nil {
panic(err)
}
state.StashEntries = getGitStashEntries()
gui.State.StashEntries = gui.GitCommand.GetStashEntries()
v.Clear()
for _, stashEntry := range state.StashEntries {
for _, stashEntry := range gui.State.StashEntries {
fmt.Fprintln(v, stashEntry.DisplayString)
}
return gui.resetOrigin(v)
@ -22,15 +23,15 @@ func refreshStashEntries(g *gocui.Gui) error {
return nil
}
func getSelectedStashEntry(v *gocui.View) *StashEntry {
if len(state.StashEntries) == 0 {
func (gui *Gui) getSelectedStashEntry(v *gocui.View) *commands.StashEntry {
if len(gui.State.StashEntries) == 0 {
return nil
}
lineNumber := gui.getItemPosition(v)
return &state.StashEntries[lineNumber]
return &gui.State.StashEntries[lineNumber]
}
func renderStashOptions(g *gocui.Gui) error {
func (gui *Gui) renderStashOptions(g *gocui.Gui) error {
return gui.renderOptionsMap(g, map[string]string{
"space": "apply",
"g": "pop",
@ -39,54 +40,54 @@ func renderStashOptions(g *gocui.Gui) error {
})
}
func handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
if err := renderStashOptions(g); err != nil {
func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
if err := gui.renderStashOptions(g); err != nil {
return err
}
go func() {
stashEntry := getSelectedStashEntry(v)
stashEntry := gui.getSelectedStashEntry(v)
if stashEntry == nil {
gui.renderString(g, "main", "No stash entries")
return
}
diff, _ := getStashEntryDiff(stashEntry.Index)
diff, _ := gui.GitCommand.GetStashEntryDiff(stashEntry.Index)
gui.renderString(g, "main", diff)
}()
return nil
}
func handleStashApply(g *gocui.Gui, v *gocui.View) error {
return stashDo(g, v, "apply")
func (gui *Gui) handleStashApply(g *gocui.Gui, v *gocui.View) error {
return gui.stashDo(g, v, "apply")
}
func handleStashPop(g *gocui.Gui, v *gocui.View) error {
return stashDo(g, v, "pop")
func (gui *Gui) handleStashPop(g *gocui.Gui, v *gocui.View) error {
return gui.stashDo(g, v, "pop")
}
func handleStashDrop(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handleStashDrop(g *gocui.Gui, v *gocui.View) error {
return gui.createConfirmationPanel(g, v, "Stash drop", "Are you sure you want to drop this stash entry?", func(g *gocui.Gui, v *gocui.View) error {
return stashDo(g, v, "drop")
return gui.stashDo(g, v, "drop")
}, nil)
}
func stashDo(g *gocui.Gui, v *gocui.View, method string) error {
stashEntry := getSelectedStashEntry(v)
func (gui *Gui) stashDo(g *gocui.Gui, v *gocui.View, method string) error {
stashEntry := gui.getSelectedStashEntry(v)
if stashEntry == nil {
return gui.createErrorPanel(g, "No stash to "+method)
}
if output, err := gitStashDo(stashEntry.Index, method); err != nil {
if output, err := gui.GitCommand.StashDo(stashEntry.Index, method); err != nil {
gui.createErrorPanel(g, output)
}
refreshStashEntries(g)
gui.refreshStashEntries(g)
return gui.refreshFiles(g)
}
func handleStashSave(g *gocui.Gui, filesView *gocui.View) error {
func (gui *Gui) handleStashSave(g *gocui.Gui, filesView *gocui.View) error {
gui.createPromptPanel(g, filesView, "Stash changes", func(g *gocui.Gui, v *gocui.View) error {
if output, err := gitStashSave(gui.trimmedContent(v)); err != nil {
if output, err := gui.GitCommand.StashSave(gui.trimmedContent(v)); err != nil {
gui.createErrorPanel(g, output)
}
refreshStashEntries(g)
gui.refreshStashEntries(g)
return gui.refreshFiles(g)
})
return nil

View File

@ -5,9 +5,10 @@ import (
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func refreshStatus(g *gocui.Gui) error {
func (gui *Gui) refreshStatus(g *gocui.Gui) error {
v, err := g.View("status")
if err != nil {
panic(err)
@ -17,22 +18,22 @@ func refreshStatus(g *gocui.Gui) error {
// contents end up cleared
g.Update(func(*gocui.Gui) error {
v.Clear()
pushables, pullables := git.UpstreamDifferenceCount()
pushables, pullables := gui.GitCommand.UpstreamDifferenceCount()
fmt.Fprint(v, "↑"+pushables+"↓"+pullables)
branches := state.Branches
if err := updateHasMergeConflictStatus(); err != nil {
branches := gui.State.Branches
if err := gui.updateHasMergeConflictStatus(); err != nil {
return err
}
if state.HasMergeConflicts {
fmt.Fprint(v, coloredString(" (merging)", color.FgYellow))
if gui.State.HasMergeConflicts {
fmt.Fprint(v, utils.ColoredString(" (merging)", color.FgYellow))
}
if len(branches) == 0 {
return nil
}
branch := branches[0]
name := coloredString(branch.Name, branch.getColor())
repo := getCurrentProject()
name := utils.ColoredString(branch.Name, branch.GetColor())
repo := utils.GetCurrentRepoName()
fmt.Fprint(v, " "+repo+" → "+name)
return nil
})

View File

@ -13,7 +13,7 @@ var cyclableViews = []string{"files", "branches", "commits", "stash"}
func (gui *Gui) refreshSidePanels(g *gocui.Gui) error {
gui.refreshBranches(g)
gui.gui.refreshFiles(g)
gui.refreshFiles(g)
gui.refreshCommits(g)
return nil
}
@ -38,7 +38,7 @@ func (gui *Gui) nextView(g *gocui.Gui, v *gocui.View) error {
if err != nil {
panic(err)
}
return gui.gui.switchFocus(g, v, focusedView)
return gui.switchFocus(g, v, focusedView)
}
func (gui *Gui) previousView(g *gocui.Gui, v *gocui.View) error {
@ -52,7 +52,7 @@ func (gui *Gui) previousView(g *gocui.Gui, v *gocui.View) error {
break
}
if i == len(cyclableViews)-1 {
devLog(v.Name() + " is not in the list of views")
gui.Log.Info(v.Name() + " is not in the list of views")
return nil
}
}
@ -72,27 +72,27 @@ func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error {
case "files":
return gui.handleFileSelect(g, v)
case "branches":
return handleBranchSelect(g, v)
return gui.handleBranchSelect(g, v)
case "confirmation":
return nil
case "commitMessage":
return handleCommitFocused(g, v)
return gui.handleCommitFocused(g, v)
case "main":
// TODO: pull this out into a 'view focused' function
refreshMergePanel(g)
gui.refreshMergePanel(g)
v.Highlight = false
return nil
case "commits":
return handleCommitSelect(g, v)
return gui.handleCommitSelect(g, v)
case "stash":
return handleStashEntrySelect(g, v)
return gui.handleStashEntrySelect(g, v)
default:
panic("No view matching newLineFocused switch statement")
}
}
func (gui *Gui) returnFocus(g *gocui.Gui, v *gocui.View) error {
previousView, err := g.View(state.PreviousView)
previousView, err := g.View(gui.State.PreviousView)
if err != nil {
panic(err)
}
@ -105,16 +105,16 @@ func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
// we should never stack confirmation panels
if oldView != nil && oldView.Name() != "confirmation" {
oldView.Highlight = false
devLog("setting previous view to:", oldView.Name())
state.PreviousView = oldView.Name()
gui.Log.Info("setting previous view to:", oldView.Name())
gui.State.PreviousView = oldView.Name()
}
newView.Highlight = true
devLog("new focused view is " + newView.Name())
gui.Log.Info("new focused view is " + newView.Name())
if _, err := g.SetCurrentView(newView.Name()); err != nil {
return err
}
g.Cursor = newView.Editable
return newLineFocused(g, newView)
return gui.newLineFocused(g, newView)
}
func (gui *Gui) getItemPosition(v *gocui.View) int {
@ -138,7 +138,7 @@ func (gui *Gui) cursorUp(g *gocui.Gui, v *gocui.View) error {
}
}
newLineFocused(g, v)
gui.newLineFocused(g, v)
return nil
}
@ -159,7 +159,7 @@ func (gui *Gui) cursorDown(g *gocui.Gui, v *gocui.View) error {
}
}
newLineFocused(g, v)
gui.newLineFocused(g, v)
return nil
}
@ -207,7 +207,7 @@ func (gui *Gui) optionsMapToString(optionsMap map[string]string) string {
}
func (gui *Gui) renderOptionsMap(g *gocui.Gui, optionsMap map[string]string) error {
return gui.renderString(g, "options", optionsMapToString(optionsMap))
return gui.renderString(g, "options", gui.optionsMapToString(optionsMap))
}
func (gui *Gui) loader() string {
@ -237,3 +237,8 @@ func (gui *Gui) getCommitMessageView(g *gocui.Gui) *gocui.View {
func (gui *Gui) trimmedContent(v *gocui.View) string {
return strings.TrimSpace(v.Buffer())
}
func (gui *Gui) currentViewName(g *gocui.Gui) string {
currentView := g.CurrentView()
return currentView.Name()
}

View File

@ -46,8 +46,8 @@ func ColoredStringDirect(str string, colour *color.Color) string {
return colour.SprintFunc()(fmt.Sprint(str))
}
// GetCurrentProject gets the repo's base name
func GetCurrentProject() string {
// GetCurrentRepoName gets the repo's base name
func GetCurrentRepoName() string {
pwd, err := os.Getwd()
if err != nil {
log.Fatalln(err.Error())