summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2019-11-05 14:21:19 +1100
committerJesse Duffield <jessedduffield@gmail.com>2019-11-05 19:22:01 +1100
commit6d5d054c30050bc66c9c3e8697982c6c801a2a62 (patch)
tree58e08dbfadbfb0af55799e810b1f684e8e1d5527
parent234415537943c090a3f9c3fa3ad7889b6998f9d2 (diff)
support line by line additions in staging and patch building contexts
-rw-r--r--pkg/gui/commit_files_panel.go4
-rw-r--r--pkg/gui/gui.go6
-rw-r--r--pkg/gui/keybindings.go113
-rw-r--r--pkg/gui/line_by_line_panel.go254
-rw-r--r--pkg/gui/patch_building_panel.go88
-rw-r--r--pkg/gui/staging_panel.go345
-rw-r--r--pkg/i18n/dutch.go5
-rw-r--r--pkg/i18n/english.go7
-rw-r--r--pkg/i18n/polish.go5
9 files changed, 497 insertions, 330 deletions
diff --git a/pkg/gui/commit_files_panel.go b/pkg/gui/commit_files_panel.go
index 43bc8ee22..d6350ce18 100644
--- a/pkg/gui/commit_files_panel.go
+++ b/pkg/gui/commit_files_panel.go
@@ -181,13 +181,13 @@ func (gui *Gui) handleEnterCommitFile(g *gocui.Gui, v *gocui.View) error {
}
}
- if err := gui.changeContext("main", "staging"); err != nil {
+ if err := gui.changeContext("main", "patch-building"); err != nil {
return err
}
if err := gui.switchFocus(g, v, gui.getMainView()); err != nil {
return err
}
- return gui.refreshStagingPanel()
+ return gui.refreshPatchBuildingPanel()
}
if gui.GitCommand.PatchManager != nil && gui.GitCommand.PatchManager.CommitSha != commitFile.Sha {
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 54c85bfe4..43a2478a8 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -82,7 +82,7 @@ type Gui 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 {
+type lineByLinePanelState struct {
SelectedLineIdx int
FirstLineIdx int
LastLineIdx int
@@ -130,7 +130,7 @@ type panelStates struct {
Commits *commitPanelState
Stash *stashPanelState
Menu *menuPanelState
- Staging *stagingPanelState
+ LineByLine *lineByLinePanelState
Merging *mergingPanelState
CommitFiles *commitFilesPanelState
}
@@ -391,7 +391,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
main := "main"
secondary := "secondary"
- swappingMainPanels := gui.State.Panels.Staging != nil && gui.State.Panels.Staging.SecondaryFocused
+ swappingMainPanels := gui.State.Panels.LineByLine != nil && gui.State.Panels.LineByLine.SecondaryFocused
if swappingMainPanels {
main = "secondary"
secondary = "main"
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index 7718535ed..fadf0c0c6 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -58,6 +58,8 @@ func (b *Binding) GetKey() string {
return "PgUp"
case 65507:
return "PgDn"
+ case 9:
+ return "tab"
}
return string(key)
@@ -638,61 +640,61 @@ func (gui *Gui) GetContextMap() map[string]map[string][]*Binding {
Key: gocui.KeyEsc,
Modifier: gocui.ModNone,
Handler: gui.handleStagingEscape,
- Description: gui.Tr.SLocalize("EscapeStaging"),
+ Description: gui.Tr.SLocalize("ReturnToFilesPanel"),
}, {
ViewName: "main",
Key: gocui.KeyArrowUp,
Modifier: gocui.ModNone,
- Handler: gui.handleStagingPrevLine,
+ Handler: gui.handleSelectPrevLine,
Description: gui.Tr.SLocalize("PrevLine"),
}, {
ViewName: "main",
Key: gocui.KeyArrowDown,
Modifier: gocui.ModNone,
- Handler: gui.handleStagingNextLine,
+ Handler: gui.handleSelectNextLine,
Description: gui.Tr.SLocalize("NextLine"),
}, {
ViewName: "main",
Key: 'k',
Modifier: gocui.ModNone,
- Handler: gui.handleStagingPrevLine,
+ Handler: gui.handleSelectPrevLine,
}, {
ViewName: "main",
Key: 'j',
Modifier: gocui.ModNone,
- Handler: gui.handleStagingNextLine,
+ Handler: gui.handleSelectNextLine,
}, {
ViewName: "main",
Key: gocui.MouseWheelUp,
Modifier: gocui.ModNone,
- Handler: gui.handleStagingPrevLine,
+ Handler: gui.handleSelectPrevLine,
}, {
ViewName: "main",
Key: gocui.MouseWheelDown,
Modifier: gocui.ModNone,
- Handler: gui.handleStagingNextLine,
+ Handler: gui.handleSelectNextLine,
}, {
ViewName: "main",
Key: gocui.KeyArrowLeft,
Modifier: gocui.ModNone,
- Handler: gui.handleStagingPrevHunk,
+ Handler: gui.handleSelectPrevHunk,
Description: gui.Tr.SLocalize("PrevHunk"),
}, {
ViewName: "main",
Key: gocui.KeyArrowRight,
Modifier: gocui.ModNone,
- Handler: gui.handleStagingNextHunk,
+ Handler: gui.handleSelectNextHunk,
Description: gui.Tr.SLocalize("NextHunk"),
}, {
ViewName: "main",
Key: 'h',
Modifier: gocui.ModNone,
- Handler: gui.handleStagingPrevHunk,
+ Handler: gui.handleSelectPrevHunk,
}, {
ViewName: "main",
Key: 'l',
Modifier: gocui.ModNone,
- Handler: gui.handleStagingNextHunk,
+ Handler: gui.handleSelectNextHunk,
}, {
ViewName: "main",
Key: gocui.KeySpace,
@@ -725,13 +727,100 @@ func (gui *Gui) GetContextMap() map[string]map[string][]*Binding {
Description: gui.Tr.SLocalize("TogglePanel"),
},
},
+ "patch-building": {
+ {
+ ViewName: "main",
+ Key: gocui.KeyEsc,
+ Modifier: gocui.ModNone,
+ Handler: gui.handleEscapePatchBuildingPanel,
+ Description: gui.Tr.SLocalize("ExitLineByLineMode"),
+ }, {
+ ViewName: "main",
+ Key: gocui.KeyArrowUp,
+ Modifier: gocui.ModNone,
+ Handler: gui.handleSelectPrevLine,
+ Description: gui.Tr.SLocalize("PrevLine"),
+ }, {
+ ViewName: "main",
+ Key: gocui.KeyArrowDown,
+ Modifier: gocui.ModNone,
+ Handler: gui.handleSelectNextLine,
+ Description: gui.Tr.SLocalize("NextLine"),
+ }, {
+ ViewName: "main",
+ Key: 'k',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleSelectPrevLine,
+ }, {
+ ViewName: "main",
+ Key: 'j',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleSelectNextLine,
+ }, {
+ ViewName: "main",
+ Key: gocui.MouseWheelUp,
+ Modifier: gocui.ModNone,
+ Handler: gui.handleSelectPrevLine,
+ }, {
+ ViewName: "main",
+ Key: gocui.MouseWheelDown,
+ Modifier: gocui.ModNone,
+ Handler: gui.handleSelectNextLine,
+ }, {
+ ViewName: "main",
+ Key: gocui.KeyArrowLeft,
+ Modifier: gocui.ModNone,
+ Handler: gui.handleSelectPrevHunk,
+ Description: gui.Tr.SLocalize("PrevHunk"),
+ }, {
+ ViewName: "main",
+ Key: gocui.KeyArrowRight,
+ Modifier: gocui.ModNone,
+ Handler: gui.handleSelectNextHunk,
+ Description: gui.Tr.SLocalize("NextHunk"),
+ }, {
+ ViewName: "main",
+ Key: 'h',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleSelectPrevHunk,
+ }, {
+ ViewName: "main",
+ Key: 'l',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleSelectNextHunk,
+ }, {
+ ViewName: "main",
+ Key: gocui.KeySpace,
+ Modifier: gocui.ModNone,
+ Handler: gui.handleAddSelectionToPatch,
+ Description: gui.Tr.SLocalize("StageSelection"),
+ }, {
+ ViewName: "main",
+ Key: 'd',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleRemoveSelectionFromPatch,
+ Description: gui.Tr.SLocalize("ResetSelection"),
+ }, {
+ ViewName: "main",
+ Key: 'v',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleToggleSelectRange,
+ Description: gui.Tr.SLocalize("ToggleDragSelect"),
+ }, {
+ ViewName: "main",
+ Key: 'a',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleToggleSelectHunk,
+ Description: gui.Tr.SLocalize("ToggleSelectHunk"),
+ },
+ },
"merging": {
{
ViewName: "main",
Key: gocui.KeyEsc,
Modifier: gocui.ModNone,
Handler: gui.handleEscapeMerge,
- Description: gui.Tr.SLocalize("EscapeStaging"),
+ Description: gui.Tr.SLocalize("ReturnToFilesPanel"),
}, {
ViewName: "main",
Key: gocui.KeySpace,
diff --git a/pkg/gui/line_by_line_panel.go b/pkg/gui/line_by_line_panel.go
new file mode 100644
index 000000000..f36441823
--- /dev/null
+++ b/pkg/gui/line_by_line_panel.go
@@ -0,0 +1,254 @@
+package gui
+
+import (
+ "github.com/jesseduffield/gocui"
+ "github.com/jesseduffield/lazygit/pkg/commands"
+)
+
+// Currently there are two 'pseudo-panels' that make use of this 'pseudo-panel'.
+// One is the staging panel where we stage files line-by-line, the other is the
+// patch building panel where we add lines of an old commit's file to a patch.
+// This file contains the logic around selecting lines and displaying the diffs
+// staging_panel.go and patch_building_panel.go have functions specific to their
+// use cases
+
+// these represent what select mode we're in
+const (
+ LINE = iota
+ RANGE
+ HUNK
+)
+
+// returns whether the patch is empty so caller can escape if necessary
+// both diffs should be non-coloured because we'll parse them and colour them here
+func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, secondaryFocused bool) (bool, error) {
+ state := gui.State.Panels.LineByLine
+
+ patchParser, err := commands.NewPatchParser(gui.Log, diff)
+ if err != nil {
+ return false, nil
+ }
+
+ if len(patchParser.StageableLines) == 0 {
+ return true, nil
+ }
+
+ var selectedLineIdx int
+ var firstLineIdx int
+ var lastLineIdx int
+ selectMode := LINE
+ if state != nil {
+ if state.SelectMode == HUNK {
+ // this is tricky: we need to find out which hunk we just staged based on our old `state.PatchParser` (as opposed to the new `patchParser`)
+ // we do this by getting the first line index of the original hunk, then
+ // finding the next stageable line, then getting its containing hunk
+ // in the new diff
+ selectMode = HUNK
+ prevNewHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 0)
+ selectedLineIdx = patchParser.GetNextStageableLineIndex(prevNewHunk.FirstLineIdx)
+ newHunk := patchParser.GetHunkContainingLine(selectedLineIdx, 0)
+ firstLineIdx, lastLineIdx = newHunk.FirstLineIdx, newHunk.LastLineIdx
+ } else {
+ selectedLineIdx = patchParser.GetNextStageableLineIndex(state.SelectedLineIdx)
+ firstLineIdx, lastLineIdx = selectedLineIdx, selectedLineIdx
+ }
+ } else {
+ selectedLineIdx = patchParser.StageableLines[0]
+ firstLineIdx, lastLineIdx = selectedLineIdx, selectedLineIdx
+ }
+
+ gui.State.Panels.LineByLine = &lineByLinePanelState{
+ PatchParser: patchParser,
+ SelectedLineIdx: selectedLineIdx,
+ SelectMode: selectMode,
+ FirstLineIdx: firstLineIdx,
+ LastLineIdx: lastLineIdx,
+ Diff: diff,
+ SecondaryFocused: secondaryFocused,
+ }
+
+ if err := gui.refreshMainView(); err != nil {
+ return false, err
+ }
+
+ if err := gui.focusSelection(selectMode == HUNK); err != nil {
+ return false, err
+ }
+
+ secondaryView := gui.getSecondaryView()
+ secondaryView.Highlight = true
+ secondaryView.Wrap = false
+
+ secondaryPatchParser, err := commands.NewPatchParser(gui.Log, secondaryDiff)
+ if err != nil {
+ return false, nil
+ }
+
+ gui.g.Update(func(*gocui.Gui) error {
+ return gui.setViewContent(gui.g, gui.getSecondaryView(), secondaryPatchParser.Render(-1, -1, nil))
+ })
+
+ return false, nil
+}
+
+func (gui *Gui) handleSelectPrevLine(g *gocui.Gui, v *gocui.View) error {
+ return gui.handleCycleLine(-1)
+}
+
+func (gui *Gui) handleSelectNextLine(g *gocui.Gui, v *gocui.View) error {
+ return gui.handleCycleLine(1)
+}
+
+func (gui *Gui) handleSelectPrevHunk(g *gocui.Gui, v *gocui.View) error {
+ return gui.handleCycleHunk(-1)
+}
+
+func (gui *Gui) handleSelectNextHunk(g *gocui.Gui, v *gocui.View) error {
+ return gui.handleCycleHunk(1)
+}
+
+func (gui *Gui) handleCycleHunk(change int) error {
+ state := gui.State.Panels.LineByLine
+ newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, change)
+ state.SelectedLineIdx = state.PatchParser.GetNextStageableLineIndex(newHunk.FirstLineIdx)
+ if state.SelectMode == HUNK {
+ state.FirstLineIdx, state.LastLineIdx = newHunk.FirstLineIdx, newHunk.LastLineIdx
+ } else {
+ state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
+ }
+
+ if err := gui.refreshMainView(); err != nil {
+ return err
+ }
+
+ return gui.focusSelection(true)
+}
+
+func (gui *Gui) handleCycleLine(change int) error {
+ state := gui.State.Panels.LineByLine
+
+ if state.SelectMode == HUNK {
+ return gui.handleCycleHunk(change)
+ }
+
+ newSelectedLineIdx := state.SelectedLineIdx + change
+ if newSelectedLineIdx < 0 {
+ newSelectedLineIdx = 0
+ } else if newSelectedLineIdx > len(state.PatchParser.PatchLines)-1 {
+ newSelectedLineIdx = len(state.PatchParser.PatchLines) - 1
+ }
+
+ state.SelectedLineIdx = newSelectedLineIdx
+
+ if state.SelectMode == RANGE {
+ if state.SelectedLineIdx < state.FirstLineIdx {
+ state.FirstLineIdx = state.SelectedLineIdx
+ } else {
+ state.LastLineIdx = state.SelectedLineIdx
+ }
+ } else {
+ state.LastLineIdx = state.SelectedLineIdx
+ state.FirstLineIdx = state.SelectedLineIdx
+ }
+
+ if err := gui.refreshMainView(); err != nil {
+ return err
+ }
+
+ return gui.focusSelection(false)
+}
+
+func (gui *Gui) refreshMainView() error {
+ state := gui.State.Panels.LineByLine
+
+ var includedLineIndices []int
+ // I'd prefer not to have knowledge of contexts using this file but I'm not sure
+ // how to get around this
+ if gui.State.Contexts["main"] == "patch-building" {
+ filename := gui.State.CommitFiles[gui.State.Panels.CommitFiles.SelectedLine].Name
+ includedLineIndices = gui.GitCommand.PatchManager.GetFileIncLineIndices(filename)
+ }
+ colorDiff := state.PatchParser.Render(state.FirstLineIdx, state.LastLineIdx, includedLineIndices)
+
+ mainView := gui.getMainView()
+ mainView.Highlight = true
+ mainView.Wrap = false
+
+ gui.g.Update(func(*gocui.Gui) error {
+ return gui.setViewContent(gui.g, gui.getMainView(), colorDiff)
+ })
+
+ return nil
+}
+
+// focusSelection works out the best focus for the staging panel given the
+// selected line and size of the hunk
+func (gui *Gui) focusSelection(includeCurrentHunk bool) error {
+ stagingView := gui.getMainView()
+ state := gui.State.Panels.LineByLine
+
+ _, viewHeight := stagingView.Size()
+ bufferHeight := viewHeight - 1
+ _, origin := stagingView.Origin()
+
+ firstLineIdx := state.SelectedLineIdx
+ lastLineIdx := state.SelectedLineIdx
+
+ if includeCurrentHunk {
+ hunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 0)
+ firstLineIdx = hunk.FirstLineIdx
+ lastLineIdx = hunk.LastLineIdx
+ }
+
+ margin := 0 // we may want to have a margin in place to show context but right now I'm thinking we keep this at zero
+
+ var newOrigin int
+ if firstLineIdx-origin < margin {
+ newOrigin = firstLineIdx - margin
+ } else if lastLineIdx-origin > bufferHeight-margin {
+ newOrigin = lastLineIdx - bufferHeight + margin
+ } else {
+ newOrigin = origin
+ }
+
+ gui.g.Update(func(*gocui.Gui) error {
+ if err := stagingView.SetOrigin(0, newOrigin); err != nil {
+ return err
+ }
+
+ return stagingView.SetCursor(0, state.SelectedLineIdx-newOrigin)
+ })
+
+ return nil
+}
+
+func (gui *Gui) handleToggleSelectRange(g *gocui.Gui, v *gocui.View) error {
+ state := gui.State.Panels.LineByLine
+ if state.SelectMode == RANGE {
+ state.SelectMode = LINE
+ } else {
+ state.SelectMode = RANGE
+ }
+ state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
+
+ return gui.refreshMainView()
+}
+
+func (gui *Gui) handleToggleSelectHunk(g *gocui.Gui, v *gocui.View) error {
+ state := gui.State.Panels.LineByLine
+
+ if state.SelectMode == HUNK {
+ state.SelectMode = LINE
+ state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
+ } else {
+ state.SelectMode = HUNK
+ selectedHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 0)
+ state.FirstLineIdx, state.LastLineIdx = selectedHunk.FirstLineIdx, selectedHunk.LastLineIdx
+ }
+
+ if err := gui.refreshMainView(); err != nil {
+ return err
+ }
+
+ return gui.focusSelection(state.SelectMode == HUNK)
+}
diff --git a/pkg/gui/patch_building_panel.go b/pkg/gui/patch_building_panel.go
new file mode 100644
index 000000000..cc16111b7
--- /dev/null
+++ b/pkg/gui/patch_building_panel.go
@@ -0,0 +1,88 @@
+package gui
+
+import (
+ "github.com/jesseduffield/gocui"
+)
+
+func (gui *Gui) refreshPatchBuildingPanel() error {
+ gui.State.SplitMainPanel = true
+
+ // get diff from commit file that's currently selected
+ commitFile := gui.getSelectedCommitFile(gui.g)
+ if commitFile == nil {
+ return gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
+ }
+
+ diff, err := gui.GitCommand.ShowCommitFile(commitFile.Sha, commitFile.Name, true)
+ if err != nil {
+ return err
+ }
+
+ secondaryDiff := gui.GitCommand.PatchManager.RenderPatchForFile(commitFile.Name, true, false, true)
+ if err != nil {
+ return err
+ }
+
+ gui.Log.Warn(secondaryDiff)
+
+ empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, false)
+ if err != nil {
+ return err
+ }
+
+ if empty {
+ return gui.handleStagingEscape(gui.g, nil)
+ }
+
+ return nil
+}
+
+func (gui *Gui) handleAddSelectionToPatch(g *gocui.Gui, v *gocui.View) error {
+ state := gui.State.Panels.LineByLine
+
+ // add range of lines to those set for the file
+ commitFile := gui.getSelectedCommitFile(gui.g)
+ if commitFile == nil {
+ return gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
+ }
+
+ gui.GitCommand.PatchManager.AddFileLineRange(commitFile.Name, state.FirstLineIdx, state.LastLineIdx)
+
+ if err := gui.refreshCommitFilesView(); err != nil {
+ return err
+ }
+
+ if err := gui.refreshPatchBuildingPanel(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (gui *Gui) handleRemoveSelectionFromPatch(g *gocui.Gui, v *gocui.View) error {
+ state := gui.State.Panels.LineByLine
+
+ // add range of lines to those set for the file
+ commitFile := gui.getSelectedCommitFile(gui.g)
+ if commitFile == nil {
+ return gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
+ }
+
+ gui.GitCommand.PatchManager.RemoveFileLineRange(commitFile.Name, state.FirstLineIdx, state.LastLineIdx)
+
+ if err := gui.refreshCommitFilesView(); err != nil {
+ return err
+ }
+
+ if err := gui.refreshPatchBuildingPanel(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (gui *Gui) handleEscapePatchBuildingPanel(g *gocui.Gui, v *gocui.View) error {
+ gui.State.Panels.LineByLine = nil
+
+ return gui.switchFocus(gui.g, nil, gui.getCommitFilesView())
+}
diff --git a/pkg/gui/staging_panel.go b/pkg/gui/staging_panel.go
index 47240d87e..55d38f282 100644
--- a/pkg/gui/staging_panel.go
+++ b/pkg/gui/staging_panel.go
@@ -1,329 +1,87 @@
package gui
import (
+ "strings"
+
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
)
-// these represent what select mode we're in
-const (
- LINE = iota
- RANGE
- HUNK
-)
-
func (gui *Gui) refreshStagingPanel() error {
- state := gui.State.Panels.Staging
-
- // file, err := gui.getSelectedFile(gui.g)
- // if err != nil {
- // if err != gui.Errors.ErrNoFiles {
- // return err
- // }
- // return gui.handleStagingEscape(gui.g, nil)
- // }
-
gui.State.SplitMainPanel = true
- secondaryFocused := false
- if state != nil {
- secondaryFocused = state.SecondaryFocused
- }
-
- // if !file.HasUnstagedChanges && !file.HasStagedChanges {
- // return gui.handleStagingEscape(gui.g, nil)
- // }
-
- // if (secondaryFocused && !file.HasStagedChanges) || (!secondaryFocused && !file.HasUnstagedChanges) {
- // secondaryFocused = !secondaryFocused
- // }
-
- // getDiffs := func() (string, string) {
- // // note for custom diffs, we'll need to send a flag here saying not to use the custom diff
- // diff := gui.GitCommand.Diff(file, true, secondaryFocused)
- // secondaryColorDiff := gui.GitCommand.Diff(file, false, !secondaryFocused)
- // return diff, secondaryColorDiff
- // }
-
- // diff, secondaryColorDiff := getDiffs()
-
- // // if we have e.g. a deleted file with nothing else to the diff will have only
- // // 4-5 lines in which case we'll swap panels
- // if len(strings.Split(diff, "\n")) < 5 {
- // if len(strings.Split(secondaryColorDiff, "\n")) < 5 {
- // return gui.handleStagingEscape(gui.g, nil)
- // }
- // secondaryFocused = !secondaryFocused
- // diff, secondaryColorDiff = getDiffs()
- // }
-
- // get diff from commit file that's currently selected
- commitFile := gui.getSelectedCommitFile(gui.g)
- if commitFile == nil {
- return gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
- }
-
- diff, err := gui.GitCommand.ShowCommitFile(commitFile.Sha, commitFile.Name, true)
- if err != nil {
- return err
- }
+ state := gui.State.Panels.LineByLine
- secondaryColorDiff := gui.GitCommand.PatchManager.RenderPatchForFile(commitFile.Name, false, false, true)
- if err != nil {
- return err
- }
-
- patchParser, err := commands.NewPatchParser(gui.Log, diff)
+ file, err := gui.getSelectedFile(gui.g)
if err != nil {
- return nil
+ if err != gui.Errors.ErrNoFiles {
+ return err
+ }
+ return gui.handleStagingEscape(gui.g, nil)
}
- if len(patchParser.StageableLines) == 0 {
+ if !file.HasUnstagedChanges && !file.HasStagedChanges {
return gui.handleStagingEscape(gui.g, nil)
}
- var selectedLineIdx int
- var firstLineIdx int
- var lastLineIdx int
- selectMode := LINE
+ secondaryFocused := false
if state != nil {
- if state.SelectMode == HUNK {
- // this is tricky: we need to find out which hunk we just staged based on our old `state.PatchParser` (as opposed to the new `patchParser`)
- // we do this by getting the first line index of the original hunk, then
- // finding the next stageable line, then getting its containing hunk
- // in the new diff
- selectMode = HUNK
- prevNewHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 0)
- selectedLineIdx = patchParser.GetNextStageableLineIndex(prevNewHunk.FirstLineIdx)
- newHunk := patchParser.GetHunkContainingLine(selectedLineIdx, 0)
- firstLineIdx, lastLineIdx = newHunk.FirstLineIdx, newHunk.LastLineIdx
- } else {
- selectedLineIdx = patchParser.GetNextStageableLineIndex(state.SelectedLineIdx)
- firstLineIdx, lastLineIdx = selectedLineIdx, selectedLineIdx
- }
- } else {
- selectedLineIdx = patchParser.StageableLines[0]
- firstLineIdx, lastLineIdx = selectedLineIdx, selectedLineIdx
+ secondaryFocused = state.SecondaryFocused
}
- gui.State.Panels.Staging = &stagingPanelState{
- PatchParser: patchParser,
- SelectedLineIdx: selectedLineIdx,
- SelectMode: selectMode,
- FirstLineIdx: firstLineIdx,
- LastLineIdx: lastLineIdx,
- Diff: diff,
- SecondaryFocused: secondaryFocused,
+ if (secondaryFocused && !file.HasStagedChanges) || (!secondaryFocused && !file.HasUnstagedChanges) {
+ secondaryFocused = !secondaryFocused
}
- if err := gui.refreshView(); err != nil {
- return err
+ // note for custom diffs, we'll need to send a flag here saying not to use the custom diff
+ diff := gui.GitCommand.Diff(file, true, secondaryFocused)
+ secondaryDiff := gui.GitCommand.Diff(file, true, !secondaryFocused)
+
+ // if we have e.g. a deleted file with nothing else to the diff will have only
+ // 4-5 lines in which case we'll swap panels
+ if len(strings.Split(diff, "\n")) < 5 {
+ if len(strings.Split(secondaryDiff, "\n")) < 5 {
+ return gui.handleStagingEscape(gui.g, nil)
+ }
+ secondaryFocused = !secondaryFocused
+ diff, secondaryDiff = secondaryDiff, diff
}
- if err := gui.focusSelection(selectMode == HUNK); err != nil {
+ empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, secondaryFocused)
+ if err != nil {
return err
}
- secondaryView := gui.getSecondaryView()
- secondaryView.Highlight = true
- secondaryView.Wrap = false
-
- gui.g.Update(func(*gocui.Gui) error {
- return gui.setViewContent(gui.g, gui.getSecondaryView(), secondaryColorDiff)
- })
+ if empty {
+ return gui.handleStagingEscape(gui.g, nil)
+ }
return nil
}
func (gui *Gui) handleTogglePanel(g *gocui.Gui, v *gocui.View) error {
- state := gui.State.Panels.Staging
+ state := gui.State.Panels.LineByLine
state.SecondaryFocused = !state.SecondaryFocused
return gui.refreshStagingPanel()
}
func (gui *Gui) handleStagingEscape(g *gocui.Gui, v *gocui.View) error {
- gui.State.Panels.Staging = nil
-
- return gui.switchFocus(gui.g, nil, gui.getCommitFilesView())
-}
-
-func (gui *Gui) handleStagingPrevLine(g *gocui.Gui, v *gocui.View) error {
- return gui.handleCycleLine(-1)
-}
+ gui.State.Panels.LineByLine = nil
-func (gui *Gui) handleStagingNextLine(g *gocui.Gui, v *gocui.View) error {
- return gui.handleCycleLine(1)
-}
-
-func (gui *Gui) handleStagingPrevHunk(g *gocui.Gui, v *gocui.View) error {
- return gui.handleCycleHunk(-1)
-}
-
-func (gui *Gui) handleStagingNextHunk(g *gocui.Gui, v *gocui.View) error {
- return gui.handleCycleHunk(1)
-}
-
-func (gui *Gui) handleCycleHunk(change int) error {
- state := gui.State.Panels.Staging
- newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, change)
- state.SelectedLineIdx = state.PatchParser.GetNextStageableLineIndex(newHunk.FirstLineIdx)
- if state.SelectMode == HUNK {
- state.FirstLineIdx, state.LastLineIdx = newHunk.FirstLineIdx, newHunk.LastLineIdx
- } else {
- state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
- }
-
- if err := gui.refreshView(); err != nil {
- return err
- }
-
- return gui.focusSelection(true)
-}
-
-func (gui *Gui) handleCycleLine(change int) error {
- state := gui.State.Panels.Staging
-
- if state.SelectMode == HUNK {
- return gui.handleCycleHunk(change)
- }
-
- newSelectedLineIdx := state.SelectedLineIdx + change
- if newSelectedLineIdx < 0 {
- newSelectedLineIdx = 0
- } else if newSelectedLineIdx > len(state.PatchParser.PatchLines)-1 {
- newSelectedLineIdx = len(state.PatchParser.PatchLines) - 1
- }
-
- state.SelectedLineIdx = newSelectedLineIdx
-
- if state.SelectMode == RANGE {
- if state.SelectedLineIdx < state.FirstLineIdx {
- state.FirstLineIdx = state.SelectedLineIdx
- } else {
- state.LastLineIdx = state.SelectedLineIdx
- }
- } else {
- state.LastLineIdx = state.SelectedLineIdx
- state.FirstLineIdx = state.SelectedLineIdx
- }
-
- if err := gui.refreshView(); err != nil {
- return err
- }
-
- return gui.focusSelection(false)
-}
-
-func (gui *Gui) refreshView() error {
- state := gui.State.Panels.Staging
-
- filename := gui.State.CommitFiles[gui.State.Panels.CommitFiles.SelectedLine].Name
-
- colorDiff := state.PatchParser.Render(state.FirstLineIdx, state.LastLineIdx, gui.GitCommand.PatchManager.GetFileIncLineIndices(filename))
-
- mainView := gui.getMainView()
- mainView.Highlight = true
- mainView.Wrap = false
-
- gui.g.Update(func(*gocui.Gui) error {
- return gui.setViewContent(gui.g, gui.getMainView(), colorDiff)
- })
-
- return nil
-}
-
-// focusSelection works out the best focus for the staging panel given the
-// selected line and size of the hunk
-func (gui *Gui) focusSelection(includeCurrentHunk bool) error {
- stagingView := gui.getMainView()
- state := gui.State.Panels.Staging
-
- _, viewHeight := stagingView.Size()
- bufferHeight := viewHeight - 1
- _, origin := stagingView.Origin()
-
- firstLineIdx := state.SelectedLineIdx
- lastLineIdx := state.SelectedLineIdx
-
- if includeCurrentHunk {
- hunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 0)
- firstLineIdx = hunk.FirstLineIdx
- lastLineIdx = hunk.LastLineIdx
- }
-
- margin := 0 // we may want to have a margin in place to show context but right now I'm thinking we keep this at zero
-
- var newOrigin int
- if firstLineIdx-origin < margin {
- newOrigin = firstLineIdx - margin
- } else if lastLineIdx-origin > bufferHeight-margin {
- newOrigin = lastLineIdx - bufferHeight + margin
- } else {
- newOrigin = origin
- }
-
- gui.g.Update(func(*gocui.Gui) error {
- if err := stagingView.SetOrigin(0, newOrigin); err != nil {
- return err
- }
-
- return stagingView.SetCursor(0, state.SelectedLineIdx-newOrigin)
- })
-
- return nil
+ return gui.switchFocus(gui.g, nil, gui.getFilesView())
}
func (gui *Gui) handleStageSelection(g *gocui.Gui, v *gocui.View) error {
- state := gui.State.Panels.Staging
-
- // add range of lines to those set for the file
- commitFile := gui.getSelectedCommitFile(gui.g)
- if commitFile == nil {
- return gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
- }
-
- gui.GitCommand.PatchManager.AddFileLineRange(commitFile.Name, state.FirstLineIdx, state.LastLineIdx)
-
- if err := gui.refreshCommitFilesView(); err != nil {
- return err
- }
-
- if err := gui.refreshStagingPanel(); err != nil {
- return err
- }
-
- return nil
-
- // return gui.applySelection(false)
+ return gui.applySelection(false)
}
func (gui *Gui) handleResetSelection(g *gocui.Gui, v *gocui.View) error {
- state := gui.State.Panels.Staging
-
- // add range of lines to those set for the file
- commitFile := gui.getSelectedCommitFile(gui.g)
- if commitFile == nil {
- return gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
- }
-
- gui.GitCommand.PatchManager.RemoveFileLineRange(commitFile.Name, state.FirstLineIdx, state.LastLineIdx)
-
- if err := gui.refreshCommitFilesView(); err != nil {
- return err
- }
-
- if err := gui.refreshStagingPanel(); err != nil {
- return err
- }
-
- return nil
-
- // return gui.applySelection(true)
+ return gui.applySelection(true)
}
func (gui *Gui) applySelection(reverse bool) error {
- state := gui.State.Panels.Staging
+ state := gui.State.Panels.LineByLine
if !reverse && state.