summaryrefslogtreecommitdiffstats
path: root/pkg/gui
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2018-12-05 19:33:46 +1100
committerJesse Duffield <jessedduffield@gmail.com>2018-12-05 19:33:46 +1100
commitc0f9795910dd840fb83e6992f7f59c77ec4c13fc (patch)
tree05fe245b822008f458025a5dd75cae384bfda845 /pkg/gui
parent658e5a9faf8409c62f11f3ad6d636d0255e450f4 (diff)
staging lines and hunks
Diffstat (limited to 'pkg/gui')
-rw-r--r--pkg/gui/confirmation_panel.go12
-rw-r--r--pkg/gui/files_panel.go7
-rw-r--r--pkg/gui/gui.go1
-rw-r--r--pkg/gui/keybindings.go55
-rw-r--r--pkg/gui/staging_panel.go166
-rw-r--r--pkg/gui/view_helpers.go1
6 files changed, 182 insertions, 60 deletions
diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go
index 58577e430..afe53ca2c 100644
--- a/pkg/gui/confirmation_panel.go
+++ b/pkg/gui/confirmation_panel.go
@@ -8,6 +8,7 @@ package gui
import (
"strings"
+ "time"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
@@ -73,6 +74,7 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt
return nil, err
}
confirmationView.Title = title
+ confirmationView.Wrap = true
confirmationView.FgColor = gocui.ColorWhite
}
confirmationView.Clear()
@@ -137,7 +139,15 @@ func (gui *Gui) createMessagePanel(g *gocui.Gui, currentView *gocui.View, title,
}
func (gui *Gui) createErrorPanel(g *gocui.Gui, message string) error {
- gui.Log.Error(message)
+ go func() {
+ // when reporting is switched on this log call sometimes introduces
+ // a delay on the error panel popping up. Here I'm adding a second wait
+ // so that the error is logged while the user is reading the error message
+ time.Sleep(time.Second)
+ gui.Log.Error(message)
+ }()
+
+ // gui.Log.WithField("staging", "staging").Info("creating confirmation panel")
currentView := g.CurrentView()
colorFunction := color.New(color.FgRed).SprintFunc()
coloredMessage := colorFunction(strings.TrimSpace(message))
diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go
index 3404903ef..a16ef4909 100644
--- a/pkg/gui/files_panel.go
+++ b/pkg/gui/files_panel.go
@@ -57,10 +57,13 @@ func (gui *Gui) handleSwitchToStagingPanel(g *gocui.Gui, v *gocui.View) error {
}
return nil
}
- if !file.Tracked || !file.HasUnstagedChanges {
+ if !file.HasUnstagedChanges {
+ gui.Log.WithField("staging", "staging").Info("making error panel")
return gui.createErrorPanel(g, gui.Tr.SLocalize("FileStagingRequirements"))
}
- gui.switchFocus(g, v, stagingView)
+ if err := gui.switchFocus(g, v, stagingView); err != nil {
+ return err
+ }
return gui.refreshStagingPanel()
}
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 67415c5ce..9f25121d5 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -222,7 +222,6 @@ func (gui *Gui) layout(g *gocui.Gui) error {
return err
}
v.Title = gui.Tr.SLocalize("StagingTitle")
- v.Wrap = true
v.Highlight = true
v.FgColor = gocui.ColorWhite
if _, err := g.SetViewOnBottom("staging"); err != nil {
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index 2c47ab4f3..4158bedb7 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -392,25 +392,64 @@ func (gui *Gui) GetKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.handleMenuClose,
}, {
- ViewName: "staging",
- Key: gocui.KeyEsc,
- Modifier: gocui.ModNone,
- Handler: gui.handleStagingEscape,
+ ViewName: "staging",
+ Key: gocui.KeyEsc,
+ Modifier: gocui.ModNone,
+ Handler: gui.handleStagingEscape,
+ KeyReadable: "esc",
+ Description: gui.Tr.SLocalize("EscapeStaging"),
}, {
ViewName: "staging",
Key: gocui.KeyArrowUp,
Modifier: gocui.ModNone,
- Handler: gui.handleStagingKeyUp,
+ Handler: gui.handleStagingPrevLine,
}, {
ViewName: "staging",
Key: gocui.KeyArrowDown,
Modifier: gocui.ModNone,
- Handler: gui.handleStagingKeyDown,
+ Handler: gui.handleStagingNextLine,
}, {
ViewName: "staging",
- Key: gocui.KeySpace,
+ Key: 'k',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleStagingPrevLine,
+ }, {
+ ViewName: "staging",
+ Key: 'j',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleStagingNextLine,
+ }, {
+ ViewName: "staging",
+ Key: gocui.KeyArrowLeft,
+ Modifier: gocui.ModNone,
+ Handler: gui.handleStagingPrevHunk,
+ }, {
+ ViewName: "staging",
+ Key: gocui.KeyArrowRight,
+ Modifier: gocui.ModNone,
+ Handler: gui.handleStagingNextHunk,
+ }, {
+ ViewName: "staging",
+ Key: 'h',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleStagingPrevHunk,
+ }, {
+ ViewName: "staging",
+ Key: 'l',
Modifier: gocui.ModNone,
- Handler: gui.handleStageLine,
+ Handler: gui.handleStagingNextHunk,
+ }, {
+ ViewName: "staging",
+ Key: gocui.KeySpace,
+ Modifier: gocui.ModNone,
+ Handler: gui.handleStageLine,
+ Description: gui.Tr.SLocalize("StageLine"),
+ }, {
+ ViewName: "staging",
+ Key: 'a',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleStageHunk,
+ Description: gui.Tr.SLocalize("StageHunk"),
},
}
diff --git a/pkg/gui/staging_panel.go b/pkg/gui/staging_panel.go
index be207c2fb..cba7d7638 100644
--- a/pkg/gui/staging_panel.go
+++ b/pkg/gui/staging_panel.go
@@ -2,19 +2,13 @@ package gui
import (
"errors"
- "io/ioutil"
-
- "github.com/davecgh/go-spew/spew"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/git"
+ "github.com/jesseduffield/lazygit/pkg/utils"
)
func (gui *Gui) refreshStagingPanel() error {
- // get the currently selected file. Get the diff of that file directly, not
- // using any custom diff tools.
- // parse the file to find out where the chunks and unstaged changes are
-
file, err := gui.getSelectedFile(gui.g)
if err != nil {
if err != gui.Errors.ErrNoFiles {
@@ -31,10 +25,6 @@ func (gui *Gui) refreshStagingPanel() error {
diff := gui.GitCommand.Diff(file, true)
colorDiff := gui.GitCommand.Diff(file, false)
- gui.Log.WithField("staging", "staging").Info("DIFF IS:")
- gui.Log.WithField("staging", "staging").Info(spew.Sdump(diff))
- gui.Log.WithField("staging", "staging").Info("hello")
-
if len(diff) < 2 {
return gui.handleStagingEscape(gui.g, nil)
}
@@ -73,9 +63,9 @@ func (gui *Gui) refreshStagingPanel() error {
return errors.New("No lines to stage")
}
- stagingView := gui.getStagingView(gui.g)
- stagingView.SetCursor(0, stageableLines[currentLineIndex])
- stagingView.SetOrigin(0, 0)
+ if err := gui.focusLineAndHunk(); err != nil {
+ return err
+ }
return gui.renderString(gui.g, "staging", colorDiff)
}
@@ -84,57 +74,130 @@ func (gui *Gui) handleStagingEscape(g *gocui.Gui, v *gocui.View) error {
return err
}
+ gui.State.StagingState = nil
+
return gui.switchFocus(gui.g, nil, gui.getFilesView(gui.g))
}
-// nextNumber returns the next index, cycling if we reach the end
-func nextIndex(numbers []int, currentNumber int) int {
- for index, number := range numbers {
- if number > currentNumber {
- return index
- }
- }
- return 0
+func (gui *Gui) handleStagingPrevLine(g *gocui.Gui, v *gocui.View) error {
+ return gui.handleCycleLine(true)
}
-// prevNumber returns the next number, cycling if we reach the end
-func prevIndex(numbers []int, currentNumber int) int {
- end := len(numbers) - 1
- for i := end; i >= 0; i -= 1 {
- if numbers[i] < currentNumber {
- return i
- }
- }
- return end
+func (gui *Gui) handleStagingNextLine(g *gocui.Gui, v *gocui.View) error {
+ return gui.handleCycleLine(false)
}
-func (gui *Gui) handleStagingKeyUp(g *gocui.Gui, v *gocui.View) error {
- return gui.handleCycleLine(true)
+func (gui *Gui) handleStagingPrevHunk(g *gocui.Gui, v *gocui.View) error {
+ return gui.handleCycleHunk(true)
}
-func (gui *Gui) handleStagingKeyDown(g *gocui.Gui, v *gocui.View) error {
- return gui.handleCycleLine(false)
+func (gui *Gui) handleStagingNextHunk(g *gocui.Gui, v *gocui.View) error {
+ return gui.handleCycleHunk(false)
}
-func (gui *Gui) handleCycleLine(up bool) error {
+func (gui *Gui) handleCycleHunk(prev bool) error {
state := gui.State.StagingState
lineNumbers := state.StageableLines
currentLine := lineNumbers[state.CurrentLineIndex]
- var newIndex int
- if up {
- newIndex = prevIndex(lineNumbers, currentLine)
+ currentHunkIndex := utils.PrevIndex(state.HunkStarts, currentLine)
+ var newHunkIndex int
+ if prev {
+ if currentHunkIndex == 0 {
+ newHunkIndex = len(state.HunkStarts) - 1
+ } else {
+ newHunkIndex = currentHunkIndex - 1
+ }
} else {
- newIndex = nextIndex(lineNumbers, currentLine)
+ if currentHunkIndex == len(state.HunkStarts)-1 {
+ newHunkIndex = 0
+ } else {
+ newHunkIndex = currentHunkIndex + 1
+ }
}
+ state.CurrentLineIndex = utils.NextIndex(lineNumbers, state.HunkStarts[newHunkIndex])
+
+ return gui.focusLineAndHunk()
+}
+
+func (gui *Gui) handleCycleLine(prev bool) error {
+ state := gui.State.StagingState
+ lineNumbers := state.StageableLines
+ currentLine := lineNumbers[state.CurrentLineIndex]
+ var newIndex int
+ if prev {
+ newIndex = utils.PrevIndex(lineNumbers, currentLine)
+ } else {
+ newIndex = utils.NextIndex(lineNumbers, currentLine)
+ }
state.CurrentLineIndex = newIndex
+
+ return gui.focusLineAndHunk()
+}
+
+// focusLineAndHunk works out the best focus for the staging panel given the
+// selected line and size of the hunk
+func (gui *Gui) focusLineAndHunk() error {
stagingView := gui.getStagingView(gui.g)
- stagingView.SetCursor(0, lineNumbers[newIndex])
- stagingView.SetOrigin(0, 0)
+ state := gui.State.StagingState
+
+ lineNumber := state.StageableLines[state.CurrentLineIndex]
+
+ // 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
+ // the currently selected line so that the user can see the context
+ var bottomLine int
+ nextHunkStartIndex := utils.NextIndex(state.HunkStarts, lineNumber)
+ if nextHunkStartIndex == 0 {
+ // for now linesHeight is an efficient means of getting the number of lines
+ // in the patch. However if we introduce word wrap we'll need to update this
+ bottomLine = stagingView.LinesHeight() - 1
+ } else {
+ bottomLine = state.HunkStarts[nextHunkStartIndex] - 1
+ }
+
+ hunkStartIndex := utils.PrevIndex(state.HunkStarts, lineNumber)
+ hunkStart := state.HunkStarts[hunkStartIndex]
+ // if it's the first hunk we'll also show the diff header
+ if hunkStartIndex == 0 {
+ hunkStart = 0
+ }
+
+ _, height := stagingView.Size()
+ // if this hunk is too big, we will just ensure that the user can at least
+ // see three lines of context below the cursor
+ if bottomLine-hunkStart > height {
+ bottomLine = lineNumber + 3
+ }
+
+ return gui.focusLine(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 {
+ return gui.handleStageLineOrHunk(true)
+}
+
func (gui *Gui) handleStageLine(g *gocui.Gui, v *gocui.View) error {
+ return gui.handleStageLineOrHunk(false)
+}
+
+func (gui *Gui) handleStageLineOrHunk(hunk bool) error {
state := gui.State.StagingState
p, err := git.NewPatchModifier(gui.Log)
if err != nil {
@@ -142,22 +205,31 @@ func (gui *Gui) handleStageLine(g *gocui.Gui, v *gocui.View) error {
}
currentLine := state.StageableLines[state.CurrentLineIndex]
- patch, err := p.ModifyPatch(state.Diff, currentLine)
+ var patch string
+ if hunk {
+ patch, err = p.ModifyPatchForHunk(state.Diff, state.HunkStarts, currentLine)
+ } else {
+ patch, err = p.ModifyPatchForLine(state.Diff, currentLine)
+ }
if err != nil {
return err
}
// for logging purposes
- ioutil.WriteFile("patch.diff", []byte(patch), 0600)
+ // ioutil.WriteFile("patch.diff", []byte(patch), 0600)
// apply the patch then refresh this panel
// create a new temp file with the patch, then call git apply with that patch
_, err = gui.GitCommand.ApplyPatch(patch)
if err != nil {
- panic(err)
+ return err
}
- gui.refreshStagingPanel()
- gui.refreshFiles(gui.g)
+ if err := gui.refreshFiles(gui.g); err != nil {
+ return err
+ }
+ if err := gui.refreshStagingPanel(); err != nil {
+ return err
+ }
return nil
}
diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go
index e6970d92f..525b05f97 100644
--- a/pkg/gui/view_helpers.go
+++ b/pkg/gui/view_helpers.go
@@ -259,7 +259,6 @@ func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) error {
output := string(bom.Clean([]byte(s)))
output = utils.NormalizeLinefeeds(output)
fmt.Fprint(v, output)
- v.Wrap = true
return nil
})
return nil