1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-11-28 09:08:41 +02:00

support line by line additions in staging and patch building contexts

This commit is contained in:
Jesse Duffield 2019-11-05 14:21:19 +11:00
parent 2344155379
commit 6d5d054c30
9 changed files with 501 additions and 334 deletions

View File

@ -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 {

View File

@ -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"

View File

@ -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,

View File

@ -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)
}

View File

@ -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())
}

View File

@ -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
state := gui.State.Panels.LineByLine
file, err := gui.getSelectedFile(gui.g)
if err != nil {
if err != gui.Errors.ErrNoFiles {
return err
}
return gui.handleStagingEscape(gui.g, nil)
}
if !file.HasUnstagedChanges && !file.HasStagedChanges {
return gui.handleStagingEscape(gui.g, nil)
}
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"))
if (secondaryFocused && !file.HasStagedChanges) || (!secondaryFocused && !file.HasUnstagedChanges) {
secondaryFocused = !secondaryFocused
}
diff, err := gui.GitCommand.ShowCommitFile(commitFile.Sha, commitFile.Name, true)
// 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
}
empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, secondaryFocused)
if err != nil {
return err
}
secondaryColorDiff := gui.GitCommand.PatchManager.RenderPatchForFile(commitFile.Name, false, false, true)
if err != nil {
return err
}
patchParser, err := commands.NewPatchParser(gui.Log, diff)
if err != nil {
return nil
}
if len(patchParser.StageableLines) == 0 {
if empty {
return gui.handleStagingEscape(gui.g, 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.Staging = &stagingPanelState{
PatchParser: patchParser,
SelectedLineIdx: selectedLineIdx,
SelectMode: selectMode,
FirstLineIdx: firstLineIdx,
LastLineIdx: lastLineIdx,
Diff: diff,
SecondaryFocused: secondaryFocused,
}
if err := gui.refreshView(); err != nil {
return err
}
if err := gui.focusSelection(selectMode == HUNK); 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)
})
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
gui.State.Panels.LineByLine = nil
return gui.switchFocus(gui.g, nil, gui.getCommitFilesView())
}
func (gui *Gui) handleStagingPrevLine(g *gocui.Gui, v *gocui.View) error {
return gui.handleCycleLine(-1)
}
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.SecondaryFocused {
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("CantStageStaged"))
@ -359,34 +117,3 @@ func (gui *Gui) applySelection(reverse bool) error {
}
return nil
}
func (gui *Gui) handleToggleSelectRange(g *gocui.Gui, v *gocui.View) error {
state := gui.State.Panels.Staging
if state.SelectMode == RANGE {
state.SelectMode = LINE
} else {
state.SelectMode = RANGE
}
state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
return gui.refreshView()
}
func (gui *Gui) handleToggleSelectHunk(g *gocui.Gui, v *gocui.View) error {
state := gui.State.Panels.Staging
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.refreshView(); err != nil {
return err
}
return gui.focusSelection(state.SelectMode == HUNK)
}

View File

@ -434,7 +434,7 @@ func addDutch(i18nObject *i18n.Bundle) error {
ID: "StageLine",
Other: `stage lijn`,
}, &i18n.Message{
ID: "EscapeStaging",
ID: "ReturnToFilesPanel",
Other: `ga terug naar het bestanden paneel`,
}, &i18n.Message{
ID: "CantFindHunks",
@ -754,6 +754,9 @@ func addDutch(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "jump",
Other: "jump to panel",
}, &i18n.Message{
ID: "ExitLineByLineMode",
Other: `exit line-by-line mode`,
},
)
}

View File

@ -501,13 +501,13 @@ func addEnglish(i18nObject *i18n.Bundle) error {
},
&i18n.Message{
ID: "TogglePanel",
Other: `toggle staged/unstaged panel focus`,
Other: `switch to other panel`,
},
&i18n.Message{
ID: "CantStageStaged",
Other: `You can't stage an already staged change!`,
}, &i18n.Message{
ID: "EscapeStaging",
ID: "ReturnToFilesPanel",
Other: `return to files panel`,
}, &i18n.Message{
ID: "CantFindHunks",
@ -815,6 +815,9 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "enterFile",
Other: "enter file to add selected lines to the patch",
}, &i18n.Message{
ID: "ExitLineByLineMode",
Other: `exit line-by-line mode`,
},
)
}

View File

@ -420,7 +420,7 @@ func addPolish(i18nObject *i18n.Bundle) error {
ID: "StageLine",
Other: `zatwierdź linię`,
}, &i18n.Message{
ID: "EscapeStaging",
ID: "ReturnToFilesPanel",
Other: `wróć do panelu plików`,
}, &i18n.Message{
ID: "CantFindHunks",
@ -737,6 +737,9 @@ func addPolish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "jump",
Other: "jump to panel",
}, &i18n.Message{
ID: "ExitLineByLineMode",
Other: `exit line-by-line mode`,
},
)
}