mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-11 12:25:47 +02:00
Merge 2b4ccbf853
into 223978eb82
This commit is contained in:
commit
d4847eabc4
24 changed files with 417 additions and 117 deletions
|
@ -113,6 +113,9 @@ gui:
|
||||||
# paragraphs of markdown text.
|
# paragraphs of markdown text.
|
||||||
wrapLinesInStagingView: true
|
wrapLinesInStagingView: true
|
||||||
|
|
||||||
|
# If true, show a selection when the main view is focused.
|
||||||
|
showSelectionInFocusedMainView: false
|
||||||
|
|
||||||
# One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
# One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||||
language: auto
|
language: auto
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,38 @@ func (self *Patch) LineNumberOfLine(idx int) int {
|
||||||
return hunk.newStart + offset
|
return hunk.newStart + offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Takes a line number in the new file and returns the line index in the patch.
|
||||||
|
// This is the opposite of LineNumberOfLine.
|
||||||
|
// If the line number is not contained in any of the hunks, it returns the
|
||||||
|
// closest position.
|
||||||
|
func (self *Patch) PatchLineForLineNumber(lineNumber int) int {
|
||||||
|
if len(self.hunks) == 0 {
|
||||||
|
return len(self.header)
|
||||||
|
}
|
||||||
|
|
||||||
|
for hunkIdx, hunk := range self.hunks {
|
||||||
|
if lineNumber <= hunk.newStart {
|
||||||
|
return self.HunkStartIdx(hunkIdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lineNumber < hunk.newStart+hunk.newLength() {
|
||||||
|
lines := hunk.bodyLines
|
||||||
|
offset := lineNumber - hunk.newStart
|
||||||
|
for i, line := range lines {
|
||||||
|
if offset == 0 {
|
||||||
|
return self.HunkStartIdx(hunkIdx) + i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if line.Kind == ADDITION || line.Kind == CONTEXT {
|
||||||
|
offset--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.LineCount() - 1
|
||||||
|
}
|
||||||
|
|
||||||
// Returns hunk index containing the line at the given patch line index
|
// Returns hunk index containing the line at the given patch line index
|
||||||
func (self *Patch) HunkContainingLine(idx int) int {
|
func (self *Patch) HunkContainingLine(idx int) int {
|
||||||
for hunkIdx, hunk := range self.hunks {
|
for hunkIdx, hunk := range self.hunks {
|
||||||
|
|
|
@ -105,6 +105,8 @@ type GuiConfig struct {
|
||||||
// makes it much easier to work with diffs that have long lines, e.g.
|
// makes it much easier to work with diffs that have long lines, e.g.
|
||||||
// paragraphs of markdown text.
|
// paragraphs of markdown text.
|
||||||
WrapLinesInStagingView bool `yaml:"wrapLinesInStagingView"`
|
WrapLinesInStagingView bool `yaml:"wrapLinesInStagingView"`
|
||||||
|
// If true, show a selection when the main view is focused.
|
||||||
|
ShowSelectionInFocusedMainView bool `yaml:"showSelectionInFocusedMainView"`
|
||||||
// One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
// One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||||
Language string `yaml:"language" jsonschema:"enum=auto,enum=en,enum=zh-TW,enum=zh-CN,enum=pl,enum=nl,enum=ja,enum=ko,enum=ru"`
|
Language string `yaml:"language" jsonschema:"enum=auto,enum=en,enum=zh-TW,enum=zh-CN,enum=pl,enum=nl,enum=ja,enum=ko,enum=ru"`
|
||||||
// Format used when displaying time e.g. commit time.
|
// Format used when displaying time e.g. commit time.
|
||||||
|
@ -728,23 +730,24 @@ type IconProperties struct {
|
||||||
func GetDefaultConfig() *UserConfig {
|
func GetDefaultConfig() *UserConfig {
|
||||||
return &UserConfig{
|
return &UserConfig{
|
||||||
Gui: GuiConfig{
|
Gui: GuiConfig{
|
||||||
ScrollHeight: 2,
|
ScrollHeight: 2,
|
||||||
ScrollPastBottom: true,
|
ScrollPastBottom: true,
|
||||||
ScrollOffMargin: 2,
|
ScrollOffMargin: 2,
|
||||||
ScrollOffBehavior: "margin",
|
ScrollOffBehavior: "margin",
|
||||||
TabWidth: 4,
|
TabWidth: 4,
|
||||||
MouseEvents: true,
|
MouseEvents: true,
|
||||||
SkipDiscardChangeWarning: false,
|
SkipDiscardChangeWarning: false,
|
||||||
SkipStashWarning: false,
|
SkipStashWarning: false,
|
||||||
SidePanelWidth: 0.3333,
|
SidePanelWidth: 0.3333,
|
||||||
ExpandFocusedSidePanel: false,
|
ExpandFocusedSidePanel: false,
|
||||||
ExpandedSidePanelWeight: 2,
|
ExpandedSidePanelWeight: 2,
|
||||||
MainPanelSplitMode: "flexible",
|
MainPanelSplitMode: "flexible",
|
||||||
EnlargedSideViewLocation: "left",
|
EnlargedSideViewLocation: "left",
|
||||||
WrapLinesInStagingView: true,
|
WrapLinesInStagingView: true,
|
||||||
Language: "auto",
|
ShowSelectionInFocusedMainView: false,
|
||||||
TimeFormat: "02 Jan 06",
|
Language: "auto",
|
||||||
ShortTimeFormat: time.Kitchen,
|
TimeFormat: "02 Jan 06",
|
||||||
|
ShortTimeFormat: time.Kitchen,
|
||||||
Theme: ThemeConfig{
|
Theme: ThemeConfig{
|
||||||
ActiveBorderColor: []string{"green", "bold"},
|
ActiveBorderColor: []string{"green", "bold"},
|
||||||
SearchingActiveBorderColor: []string{"cyan", "bold"},
|
SearchingActiveBorderColor: []string{"cyan", "bold"},
|
||||||
|
|
|
@ -228,3 +228,7 @@ func (self *BaseContext) Title() string {
|
||||||
func (self *BaseContext) TotalContentHeight() int {
|
func (self *BaseContext) TotalContentHeight() int {
|
||||||
return self.view.ViewLinesHeight()
|
return self.view.ViewLinesHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *BaseContext) SetHighlightOnFocus(value bool) {
|
||||||
|
self.highlightOnFocus = value
|
||||||
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ func NewMainContext(
|
||||||
WindowName: windowName,
|
WindowName: windowName,
|
||||||
Key: key,
|
Key: key,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
HighlightOnFocus: false,
|
HighlightOnFocus: c.UserConfig().Gui.ShowSelectionInFocusedMainView,
|
||||||
})),
|
})),
|
||||||
SearchTrait: NewSearchTrait(c),
|
SearchTrait: NewSearchTrait(c),
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,8 +54,9 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||||
|
|
||||||
gpgHelper := helpers.NewGpgHelper(helperCommon)
|
gpgHelper := helpers.NewGpgHelper(helperCommon)
|
||||||
viewHelper := helpers.NewViewHelper(helperCommon, gui.State.Contexts)
|
viewHelper := helpers.NewViewHelper(helperCommon, gui.State.Contexts)
|
||||||
|
windowHelper := helpers.NewWindowHelper(helperCommon, viewHelper)
|
||||||
patchBuildingHelper := helpers.NewPatchBuildingHelper(helperCommon)
|
patchBuildingHelper := helpers.NewPatchBuildingHelper(helperCommon)
|
||||||
stagingHelper := helpers.NewStagingHelper(helperCommon)
|
stagingHelper := helpers.NewStagingHelper(helperCommon, windowHelper)
|
||||||
mergeConflictsHelper := helpers.NewMergeConflictsHelper(helperCommon)
|
mergeConflictsHelper := helpers.NewMergeConflictsHelper(helperCommon)
|
||||||
searchHelper := helpers.NewSearchHelper(helperCommon)
|
searchHelper := helpers.NewSearchHelper(helperCommon)
|
||||||
|
|
||||||
|
@ -75,7 +76,6 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||||
rebaseHelper,
|
rebaseHelper,
|
||||||
)
|
)
|
||||||
bisectHelper := helpers.NewBisectHelper(helperCommon)
|
bisectHelper := helpers.NewBisectHelper(helperCommon)
|
||||||
windowHelper := helpers.NewWindowHelper(helperCommon, viewHelper)
|
|
||||||
modeHelper := helpers.NewModeHelper(
|
modeHelper := helpers.NewModeHelper(
|
||||||
helperCommon,
|
helperCommon,
|
||||||
diffHelper,
|
diffHelper,
|
||||||
|
@ -115,6 +115,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||||
AmendHelper: helpers.NewAmendHelper(helperCommon, gpgHelper),
|
AmendHelper: helpers.NewAmendHelper(helperCommon, gpgHelper),
|
||||||
FixupHelper: helpers.NewFixupHelper(helperCommon),
|
FixupHelper: helpers.NewFixupHelper(helperCommon),
|
||||||
Commits: commitsHelper,
|
Commits: commitsHelper,
|
||||||
|
CommitFiles: helpers.NewCommitFilesHelper(helperCommon),
|
||||||
Snake: helpers.NewSnakeHelper(helperCommon),
|
Snake: helpers.NewSnakeHelper(helperCommon),
|
||||||
Diff: diffHelper,
|
Diff: diffHelper,
|
||||||
Repos: reposHelper,
|
Repos: reposHelper,
|
||||||
|
|
|
@ -390,7 +390,7 @@ func (self *CommitFilesController) toggleForPatch(selectedNodes []*filetree.Comm
|
||||||
toggle := func() error {
|
toggle := func() error {
|
||||||
return self.c.WithWaitingStatus(self.c.Tr.UpdatingPatch, func(gocui.Task) error {
|
return self.c.WithWaitingStatus(self.c.Tr.UpdatingPatch, func(gocui.Task) error {
|
||||||
if !self.c.Git().Patch.PatchBuilder.Active() {
|
if !self.c.Git().Patch.PatchBuilder.Active() {
|
||||||
if err := self.startPatchBuilder(); err != nil {
|
if err := self.c.Helpers().CommitFiles.StartPatchBuilder(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -429,7 +429,7 @@ func (self *CommitFilesController) toggleForPatch(selectedNodes []*filetree.Comm
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
from, to, reverse := self.currentFromToReverseForPatchBuilding()
|
from, to, reverse := self.c.Helpers().CommitFiles.CurrentFromToReverseForPatchBuilding()
|
||||||
if self.c.Git().Patch.PatchBuilder.Active() && self.c.Git().Patch.PatchBuilder.NewPatchRequired(from, to, reverse) {
|
if self.c.Git().Patch.PatchBuilder.Active() && self.c.Git().Patch.PatchBuilder.NewPatchRequired(from, to, reverse) {
|
||||||
self.c.Confirm(types.ConfirmOpts{
|
self.c.Confirm(types.ConfirmOpts{
|
||||||
Title: self.c.Tr.DiscardPatch,
|
Title: self.c.Tr.DiscardPatch,
|
||||||
|
@ -451,72 +451,8 @@ func (self *CommitFilesController) toggleAllForPatch(_ *filetree.CommitFileNode)
|
||||||
return self.toggleForPatch([]*filetree.CommitFileNode{root})
|
return self.toggleForPatch([]*filetree.CommitFileNode{root})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitFilesController) startPatchBuilder() error {
|
|
||||||
commitFilesContext := self.context()
|
|
||||||
|
|
||||||
canRebase := commitFilesContext.GetCanRebase()
|
|
||||||
from, to, reverse := self.currentFromToReverseForPatchBuilding()
|
|
||||||
|
|
||||||
self.c.Git().Patch.PatchBuilder.Start(from, to, reverse, canRebase)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *CommitFilesController) currentFromToReverseForPatchBuilding() (string, string, bool) {
|
|
||||||
commitFilesContext := self.context()
|
|
||||||
|
|
||||||
from, to := commitFilesContext.GetFromAndToForDiff()
|
|
||||||
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from)
|
|
||||||
return from, to, reverse
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *CommitFilesController) enter(node *filetree.CommitFileNode) error {
|
func (self *CommitFilesController) enter(node *filetree.CommitFileNode) error {
|
||||||
return self.enterCommitFile(node, types.OnFocusOpts{ClickedWindowName: "", ClickedViewLineIdx: -1})
|
return self.c.Helpers().CommitFiles.EnterCommitFile(node, types.OnFocusOpts{ClickedWindowName: "", ClickedViewLineIdx: -1, ClickedViewRealLineIdx: -1})
|
||||||
}
|
|
||||||
|
|
||||||
func (self *CommitFilesController) enterCommitFile(node *filetree.CommitFileNode, opts types.OnFocusOpts) error {
|
|
||||||
if node.File == nil {
|
|
||||||
return self.handleToggleCommitFileDirCollapsed(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.c.AppState.DiffContextSize == 0 {
|
|
||||||
return fmt.Errorf(self.c.Tr.Actions.NotEnoughContextToStage,
|
|
||||||
keybindings.Label(self.c.UserConfig().Keybinding.Universal.IncreaseContextInDiffView))
|
|
||||||
}
|
|
||||||
|
|
||||||
enterTheFile := func() error {
|
|
||||||
if !self.c.Git().Patch.PatchBuilder.Active() {
|
|
||||||
if err := self.startPatchBuilder(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.c.Context().Push(self.c.Contexts().CustomPatchBuilder, opts)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
from, to, reverse := self.currentFromToReverseForPatchBuilding()
|
|
||||||
if self.c.Git().Patch.PatchBuilder.Active() && self.c.Git().Patch.PatchBuilder.NewPatchRequired(from, to, reverse) {
|
|
||||||
self.c.Confirm(types.ConfirmOpts{
|
|
||||||
Title: self.c.Tr.DiscardPatch,
|
|
||||||
Prompt: self.c.Tr.DiscardPatchConfirm,
|
|
||||||
HandleConfirm: func() error {
|
|
||||||
self.c.Git().Patch.PatchBuilder.Reset()
|
|
||||||
return enterTheFile()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return enterTheFile()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *CommitFilesController) handleToggleCommitFileDirCollapsed(node *filetree.CommitFileNode) error {
|
|
||||||
self.context().CommitFileTreeViewModel.ToggleCollapsed(node.GetInternalPath())
|
|
||||||
|
|
||||||
self.c.PostRefreshUpdate(self.context())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: this is very similar to handleToggleFileTreeView, could be DRY'd with generics
|
// NOTE: this is very similar to handleToggleFileTreeView, could be DRY'd with generics
|
||||||
|
@ -545,11 +481,35 @@ func (self *CommitFilesController) expandAll() error {
|
||||||
|
|
||||||
func (self *CommitFilesController) GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error {
|
func (self *CommitFilesController) GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error {
|
||||||
return func(mainViewName string, clickedLineIdx int) error {
|
return func(mainViewName string, clickedLineIdx int) error {
|
||||||
node := self.getSelectedItem()
|
clickedFile, line, ok := self.c.Helpers().Staging.GetFileAndLineForClickedDiffLine(mainViewName, clickedLineIdx)
|
||||||
if node != nil && node.File != nil {
|
if !ok {
|
||||||
return self.enterCommitFile(node, types.OnFocusOpts{ClickedWindowName: mainViewName, ClickedViewLineIdx: clickedLineIdx})
|
line = -1
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
node := self.getSelectedItem()
|
||||||
|
if node == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !node.IsFile() && ok {
|
||||||
|
relativePath, err := filepath.Rel(self.c.Git().RepoPaths.RepoPath(), clickedFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
relativePath = "./" + relativePath
|
||||||
|
self.context().CommitFileTreeViewModel.ExpandToPath(relativePath)
|
||||||
|
self.c.PostRefreshUpdate(self.context())
|
||||||
|
|
||||||
|
idx, ok := self.context().CommitFileTreeViewModel.GetIndexForPath(relativePath)
|
||||||
|
if ok {
|
||||||
|
self.context().SetSelectedLineIdx(idx)
|
||||||
|
self.context().GetViewTrait().FocusPoint(
|
||||||
|
self.context().ModelIndexToViewIndex(idx))
|
||||||
|
node = self.context().GetSelected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.Helpers().CommitFiles.EnterCommitFile(node, types.OnFocusOpts{ClickedWindowName: "main", ClickedViewLineIdx: line, ClickedViewRealLineIdx: line})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -325,11 +325,34 @@ func (self *FilesController) GetOnClick() func() error {
|
||||||
|
|
||||||
func (self *FilesController) GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error {
|
func (self *FilesController) GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error {
|
||||||
return func(mainViewName string, clickedLineIdx int) error {
|
return func(mainViewName string, clickedLineIdx int) error {
|
||||||
node := self.getSelectedItem()
|
clickedFile, line, ok := self.c.Helpers().Staging.GetFileAndLineForClickedDiffLine(mainViewName, clickedLineIdx)
|
||||||
if node != nil && node.File != nil {
|
if !ok {
|
||||||
return self.EnterFile(types.OnFocusOpts{ClickedWindowName: mainViewName, ClickedViewLineIdx: clickedLineIdx})
|
line = -1
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
node := self.context().GetSelected()
|
||||||
|
if node == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !node.IsFile() && ok {
|
||||||
|
relativePath, err := filepath.Rel(self.c.Git().RepoPaths.RepoPath(), clickedFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
relativePath = "./" + relativePath
|
||||||
|
self.context().FileTreeViewModel.ExpandToPath(relativePath)
|
||||||
|
self.c.PostRefreshUpdate(self.context())
|
||||||
|
|
||||||
|
idx, ok := self.context().FileTreeViewModel.GetIndexForPath(relativePath)
|
||||||
|
if ok {
|
||||||
|
self.context().SetSelectedLineIdx(idx)
|
||||||
|
self.context().GetViewTrait().FocusPoint(
|
||||||
|
self.context().ModelIndexToViewIndex(idx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.EnterFile(types.OnFocusOpts{ClickedWindowName: mainViewName, ClickedViewLineIdx: line, ClickedViewRealLineIdx: line})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,7 +534,7 @@ func (self *FilesController) getSelectedFile() *models.File {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) enter() error {
|
func (self *FilesController) enter() error {
|
||||||
return self.EnterFile(types.OnFocusOpts{ClickedWindowName: "", ClickedViewLineIdx: -1})
|
return self.EnterFile(types.OnFocusOpts{ClickedWindowName: "", ClickedViewLineIdx: -1, ClickedViewRealLineIdx: -1})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) collapseAll() error {
|
func (self *FilesController) collapseAll() error {
|
||||||
|
|
87
pkg/gui/controllers/helpers/commit_files_helper.go
Normal file
87
pkg/gui/controllers/helpers/commit_files_helper.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommitFilesHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommitFilesHelper(c *HelperCommon) *CommitFilesHelper {
|
||||||
|
return &CommitFilesHelper{
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommitFilesHelper) EnterCommitFile(node *filetree.CommitFileNode, opts types.OnFocusOpts) error {
|
||||||
|
if node.File == nil {
|
||||||
|
self.handleToggleCommitFileDirCollapsed(node)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.c.AppState.DiffContextSize == 0 {
|
||||||
|
return fmt.Errorf(self.c.Tr.Actions.NotEnoughContextToStage,
|
||||||
|
keybindings.Label(self.c.UserConfig().Keybinding.Universal.IncreaseContextInDiffView))
|
||||||
|
}
|
||||||
|
|
||||||
|
enterTheFile := func() error {
|
||||||
|
if !self.c.Git().Patch.PatchBuilder.Active() {
|
||||||
|
if err := self.StartPatchBuilder(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.Context().Push(self.c.Contexts().CustomPatchBuilder, opts)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
from, to, reverse := self.CurrentFromToReverseForPatchBuilding()
|
||||||
|
if self.c.Git().Patch.PatchBuilder.Active() && self.c.Git().Patch.PatchBuilder.NewPatchRequired(from, to, reverse) {
|
||||||
|
self.c.Confirm(types.ConfirmOpts{
|
||||||
|
Title: self.c.Tr.DiscardPatch,
|
||||||
|
Prompt: self.c.Tr.DiscardPatchConfirm,
|
||||||
|
HandleConfirm: func() error {
|
||||||
|
self.c.Git().Patch.PatchBuilder.Reset()
|
||||||
|
return enterTheFile()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return enterTheFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommitFilesHelper) context() *context.CommitFilesContext {
|
||||||
|
return self.c.Contexts().CommitFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommitFilesHelper) handleToggleCommitFileDirCollapsed(node *filetree.CommitFileNode) {
|
||||||
|
self.context().CommitFileTreeViewModel.ToggleCollapsed(node.GetInternalPath())
|
||||||
|
|
||||||
|
self.c.PostRefreshUpdate(self.context())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommitFilesHelper) StartPatchBuilder() error {
|
||||||
|
commitFilesContext := self.context()
|
||||||
|
|
||||||
|
canRebase := commitFilesContext.GetCanRebase()
|
||||||
|
from, to, reverse := self.CurrentFromToReverseForPatchBuilding()
|
||||||
|
|
||||||
|
self.c.Git().Patch.PatchBuilder.Start(from, to, reverse, canRebase)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommitFilesHelper) CurrentFromToReverseForPatchBuilding() (string, string, bool) {
|
||||||
|
commitFilesContext := self.context()
|
||||||
|
|
||||||
|
from, to := commitFilesContext.GetFromAndToForDiff()
|
||||||
|
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from)
|
||||||
|
return from, to, reverse
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ type Helpers struct {
|
||||||
AmendHelper *AmendHelper
|
AmendHelper *AmendHelper
|
||||||
FixupHelper *FixupHelper
|
FixupHelper *FixupHelper
|
||||||
Commits *CommitsHelper
|
Commits *CommitsHelper
|
||||||
|
CommitFiles *CommitFilesHelper
|
||||||
Snake *SnakeHelper
|
Snake *SnakeHelper
|
||||||
// lives in context package because our contexts need it to render to main
|
// lives in context package because our contexts need it to render to main
|
||||||
Diff *DiffHelper
|
Diff *DiffHelper
|
||||||
|
@ -73,6 +74,7 @@ func NewStubHelpers() *Helpers {
|
||||||
AmendHelper: &AmendHelper{},
|
AmendHelper: &AmendHelper{},
|
||||||
FixupHelper: &FixupHelper{},
|
FixupHelper: &FixupHelper{},
|
||||||
Commits: &CommitsHelper{},
|
Commits: &CommitsHelper{},
|
||||||
|
CommitFiles: &CommitFilesHelper{},
|
||||||
Snake: &SnakeHelper{},
|
Snake: &SnakeHelper{},
|
||||||
Diff: &DiffHelper{},
|
Diff: &DiffHelper{},
|
||||||
Repos: &ReposHelper{},
|
Repos: &ReposHelper{},
|
||||||
|
|
|
@ -29,7 +29,11 @@ func (self *PatchBuildingHelper) ValidateNormalWorkingTreeState() (bool, error)
|
||||||
|
|
||||||
// takes us from the patch building panel back to the commit files panel
|
// takes us from the patch building panel back to the commit files panel
|
||||||
func (self *PatchBuildingHelper) Escape() {
|
func (self *PatchBuildingHelper) Escape() {
|
||||||
self.c.Context().Pop()
|
if parentCtx := self.c.Contexts().CustomPatchBuilder.GetParentContext(); parentCtx != nil {
|
||||||
|
self.c.Context().Push(parentCtx, types.OnFocusOpts{})
|
||||||
|
} else {
|
||||||
|
self.c.Context().Pop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// kills the custom patch and returns us back to the commit files panel if needed
|
// kills the custom patch and returns us back to the commit files panel if needed
|
||||||
|
@ -53,8 +57,10 @@ func (self *PatchBuildingHelper) Reset() error {
|
||||||
|
|
||||||
func (self *PatchBuildingHelper) RefreshPatchBuildingPanel(opts types.OnFocusOpts) {
|
func (self *PatchBuildingHelper) RefreshPatchBuildingPanel(opts types.OnFocusOpts) {
|
||||||
selectedLineIdx := -1
|
selectedLineIdx := -1
|
||||||
|
selectedRealLineIdx := -1
|
||||||
if opts.ClickedWindowName == "main" {
|
if opts.ClickedWindowName == "main" {
|
||||||
selectedLineIdx = opts.ClickedViewLineIdx
|
selectedLineIdx = opts.ClickedViewLineIdx
|
||||||
|
selectedRealLineIdx = opts.ClickedViewRealLineIdx
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.c.Git().Patch.PatchBuilder.Active() {
|
if !self.c.Git().Patch.PatchBuilder.Active() {
|
||||||
|
@ -86,7 +92,7 @@ func (self *PatchBuildingHelper) RefreshPatchBuildingPanel(opts types.OnFocusOpt
|
||||||
|
|
||||||
oldState := context.GetState()
|
oldState := context.GetState()
|
||||||
|
|
||||||
state := patch_exploring.NewState(diff, selectedLineIdx, context.GetView(), oldState)
|
state := patch_exploring.NewState(diff, selectedLineIdx, selectedRealLineIdx, context.GetView(), oldState)
|
||||||
context.SetState(state)
|
context.SetState(state)
|
||||||
if state == nil {
|
if state == nil {
|
||||||
self.Escape()
|
self.Escape()
|
||||||
|
|
|
@ -1,20 +1,26 @@
|
||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/patch_exploring"
|
"github.com/jesseduffield/lazygit/pkg/gui/patch_exploring"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StagingHelper struct {
|
type StagingHelper struct {
|
||||||
c *HelperCommon
|
c *HelperCommon
|
||||||
|
windowHelper *WindowHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStagingHelper(
|
func NewStagingHelper(
|
||||||
c *HelperCommon,
|
c *HelperCommon,
|
||||||
|
windowHelper *WindowHelper,
|
||||||
) *StagingHelper {
|
) *StagingHelper {
|
||||||
return &StagingHelper{
|
return &StagingHelper{
|
||||||
c: c,
|
c: c,
|
||||||
|
windowHelper: windowHelper,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,12 +36,16 @@ func (self *StagingHelper) RefreshStagingPanel(focusOpts types.OnFocusOpts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mainSelectedLineIdx := -1
|
mainSelectedLineIdx := -1
|
||||||
|
mainSelectedRealLineIdx := -1
|
||||||
secondarySelectedLineIdx := -1
|
secondarySelectedLineIdx := -1
|
||||||
|
secondarySelectedRealLineIdx := -1
|
||||||
if focusOpts.ClickedViewLineIdx > 0 {
|
if focusOpts.ClickedViewLineIdx > 0 {
|
||||||
if secondaryFocused {
|
if secondaryFocused {
|
||||||
secondarySelectedLineIdx = focusOpts.ClickedViewLineIdx
|
secondarySelectedLineIdx = focusOpts.ClickedViewLineIdx
|
||||||
|
secondarySelectedRealLineIdx = focusOpts.ClickedViewRealLineIdx
|
||||||
} else {
|
} else {
|
||||||
mainSelectedLineIdx = focusOpts.ClickedViewLineIdx
|
mainSelectedLineIdx = focusOpts.ClickedViewLineIdx
|
||||||
|
mainSelectedRealLineIdx = focusOpts.ClickedViewRealLineIdx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,11 +73,11 @@ func (self *StagingHelper) RefreshStagingPanel(focusOpts types.OnFocusOpts) {
|
||||||
secondaryContext.GetMutex().Lock()
|
secondaryContext.GetMutex().Lock()
|
||||||
|
|
||||||
mainContext.SetState(
|
mainContext.SetState(
|
||||||
patch_exploring.NewState(mainDiff, mainSelectedLineIdx, mainContext.GetView(), mainContext.GetState()),
|
patch_exploring.NewState(mainDiff, mainSelectedLineIdx, mainSelectedRealLineIdx, mainContext.GetView(), mainContext.GetState()),
|
||||||
)
|
)
|
||||||
|
|
||||||
secondaryContext.SetState(
|
secondaryContext.SetState(
|
||||||
patch_exploring.NewState(secondaryDiff, secondarySelectedLineIdx, secondaryContext.GetView(), secondaryContext.GetState()),
|
patch_exploring.NewState(secondaryDiff, secondarySelectedLineIdx, secondarySelectedRealLineIdx, secondaryContext.GetView(), secondaryContext.GetState()),
|
||||||
)
|
)
|
||||||
|
|
||||||
mainState := mainContext.GetState()
|
mainState := mainContext.GetState()
|
||||||
|
@ -124,3 +134,20 @@ func (self *StagingHelper) secondaryStagingFocused() bool {
|
||||||
func (self *StagingHelper) mainStagingFocused() bool {
|
func (self *StagingHelper) mainStagingFocused() bool {
|
||||||
return self.c.Context().CurrentStatic().GetKey() == self.c.Contexts().Staging.GetKey()
|
return self.c.Context().CurrentStatic().GetKey() == self.c.Contexts().Staging.GetKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *StagingHelper) GetFileAndLineForClickedDiffLine(windowName string, lineIdx int) (string, int, bool) {
|
||||||
|
v, _ := self.c.GocuiGui().View(self.windowHelper.GetViewNameForWindow(windowName))
|
||||||
|
hyperlink, ok := v.HyperLinkInLine(lineIdx, "lazygit-edit:")
|
||||||
|
if !ok {
|
||||||
|
return "", 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
re := regexp.MustCompile(`^lazygit-edit://(.+?):(\d+)$`)
|
||||||
|
matches := re.FindStringSubmatch(hyperlink)
|
||||||
|
if matches == nil {
|
||||||
|
return "", 0, false
|
||||||
|
}
|
||||||
|
filepath := matches[1]
|
||||||
|
lineNumber := utils.MustConvertToInt(matches[2])
|
||||||
|
return filepath, lineNumber, true
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,13 @@ func NewMainViewController(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||||
|
var goIntoDescription string
|
||||||
|
// We only want to show the "enter" menu item if the user config is true;
|
||||||
|
// leaving the description empty causes it to be hidden
|
||||||
|
if self.c.UserConfig().Gui.ShowSelectionInFocusedMainView {
|
||||||
|
goIntoDescription = self.c.Tr.EnterStaging
|
||||||
|
}
|
||||||
|
|
||||||
return []*types.Binding{
|
return []*types.Binding{
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
|
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
|
||||||
|
@ -43,6 +50,11 @@ func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*ty
|
||||||
Handler: self.escape,
|
Handler: self.escape,
|
||||||
Description: self.c.Tr.ExitFocusedMainView,
|
Description: self.c.Tr.ExitFocusedMainView,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.GoInto),
|
||||||
|
Handler: self.enter,
|
||||||
|
Description: goIntoDescription,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// overriding this because we want to read all of the task's output before we start searching
|
// overriding this because we want to read all of the task's output before we start searching
|
||||||
Key: opts.GetKey(opts.Config.Universal.StartSearch),
|
Key: opts.GetKey(opts.Config.Universal.StartSearch),
|
||||||
|
@ -79,6 +91,14 @@ func (self *MainViewController) Context() types.Context {
|
||||||
return self.context
|
return self.context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *MainViewController) GetOnFocus() func(types.OnFocusOpts) {
|
||||||
|
return func(opts types.OnFocusOpts) {
|
||||||
|
if opts.ClickedWindowName != "" {
|
||||||
|
self.context.GetView().FocusPoint(0, opts.ClickedViewLineIdx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *MainViewController) togglePanel() error {
|
func (self *MainViewController) togglePanel() error {
|
||||||
if self.otherContext.GetView().Visible {
|
if self.otherContext.GetView().Visible {
|
||||||
self.otherContext.SetParentContext(self.context.GetParentContext())
|
self.otherContext.SetParentContext(self.context.GetParentContext())
|
||||||
|
@ -93,7 +113,20 @@ func (self *MainViewController) escape() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *MainViewController) enter() error {
|
||||||
|
parentCtx := self.context.GetParentContext()
|
||||||
|
if parentCtx.GetOnClickFocusedMainView() != nil {
|
||||||
|
return parentCtx.GetOnClickFocusedMainView()(
|
||||||
|
self.context.GetViewName(), self.context.GetView().SelectedLineIdx())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (self *MainViewController) onClick(opts gocui.ViewMouseBindingOpts) error {
|
func (self *MainViewController) onClick(opts gocui.ViewMouseBindingOpts) error {
|
||||||
|
if self.context.GetView().Highlight && opts.Y != opts.PreviousY {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
parentCtx := self.context.GetParentContext()
|
parentCtx := self.context.GetParentContext()
|
||||||
if parentCtx.GetOnClickFocusedMainView() != nil {
|
if parentCtx.GetOnClickFocusedMainView() != nil {
|
||||||
return parentCtx.GetOnClickFocusedMainView()(self.context.GetViewName(), opts.Y)
|
return parentCtx.GetOnClickFocusedMainView()(self.context.GetViewName(), opts.Y)
|
||||||
|
|
|
@ -163,9 +163,15 @@ func (self *PatchExplorerController) GetMouseKeybindings(opts types.KeybindingsO
|
||||||
return self.withRenderAndFocus(self.HandleMouseDown)()
|
return self.withRenderAndFocus(self.HandleMouseDown)()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, line, ok := self.c.Helpers().Staging.GetFileAndLineForClickedDiffLine(self.context.GetWindowName(), opts.Y)
|
||||||
|
if !ok {
|
||||||
|
line = -1
|
||||||
|
}
|
||||||
|
|
||||||
self.c.Context().Push(self.context, types.OnFocusOpts{
|
self.c.Context().Push(self.context, types.OnFocusOpts{
|
||||||
ClickedWindowName: self.context.GetWindowName(),
|
ClickedWindowName: self.context.GetWindowName(),
|
||||||
ClickedViewLineIdx: opts.Y,
|
ClickedViewLineIdx: opts.Y,
|
||||||
|
ClickedViewRealLineIdx: line,
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,6 +50,42 @@ func (self *SwitchToDiffFilesController) GetKeybindings(opts types.KeybindingsOp
|
||||||
return bindings
|
return bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *SwitchToDiffFilesController) GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error {
|
||||||
|
return func(mainViewName string, clickedLineIdx int) error {
|
||||||
|
clickedFile, line, ok := self.c.Helpers().Staging.GetFileAndLineForClickedDiffLine(mainViewName, clickedLineIdx)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.enter(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
context := self.c.Contexts().CommitFiles
|
||||||
|
var node *filetree.CommitFileNode
|
||||||
|
|
||||||
|
relativePath, err := filepath.Rel(self.c.Git().RepoPaths.RepoPath(), clickedFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
relativePath = "./" + relativePath
|
||||||
|
context.CommitFileTreeViewModel.ExpandToPath(relativePath)
|
||||||
|
self.c.PostRefreshUpdate(context)
|
||||||
|
|
||||||
|
idx, ok := context.CommitFileTreeViewModel.GetIndexForPath(relativePath)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
context.SetSelectedLineIdx(idx)
|
||||||
|
context.GetViewTrait().FocusPoint(
|
||||||
|
context.ModelIndexToViewIndex(idx))
|
||||||
|
node = context.GetSelected()
|
||||||
|
self.c.Contexts().CustomPatchBuilder.SetParentContext(self.context)
|
||||||
|
return self.c.Helpers().CommitFiles.EnterCommitFile(node, types.OnFocusOpts{ClickedWindowName: "main", ClickedViewLineIdx: line, ClickedViewRealLineIdx: line})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *SwitchToDiffFilesController) Context() types.Context {
|
func (self *SwitchToDiffFilesController) Context() types.Context {
|
||||||
return self.context
|
return self.context
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package controllers
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This controller is for all contexts that can focus their main view.
|
// This controller is for all contexts that can focus their main view.
|
||||||
|
@ -60,23 +61,31 @@ func (self *SwitchToFocusedMainViewController) Context() types.Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SwitchToFocusedMainViewController) onClickMain(opts gocui.ViewMouseBindingOpts) error {
|
func (self *SwitchToFocusedMainViewController) onClickMain(opts gocui.ViewMouseBindingOpts) error {
|
||||||
return self.focusMainView("main")
|
return self.focusMainView("main", opts.Y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SwitchToFocusedMainViewController) onClickSecondary(opts gocui.ViewMouseBindingOpts) error {
|
func (self *SwitchToFocusedMainViewController) onClickSecondary(opts gocui.ViewMouseBindingOpts) error {
|
||||||
return self.focusMainView("secondary")
|
return self.focusMainView("secondary", opts.Y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SwitchToFocusedMainViewController) handleFocusMainView() error {
|
func (self *SwitchToFocusedMainViewController) handleFocusMainView() error {
|
||||||
return self.focusMainView("main")
|
return self.focusMainView("main", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SwitchToFocusedMainViewController) focusMainView(mainViewName string) error {
|
func (self *SwitchToFocusedMainViewController) focusMainView(mainViewName string, clickedViewLineIdx int) error {
|
||||||
mainViewContext := self.c.Helpers().Window.GetContextForWindow(mainViewName)
|
mainViewContext := self.c.Helpers().Window.GetContextForWindow(mainViewName)
|
||||||
mainViewContext.SetParentContext(self.context)
|
mainViewContext.SetParentContext(self.context)
|
||||||
if context, ok := mainViewContext.(types.ISearchableContext); ok {
|
if context, ok := mainViewContext.(types.ISearchableContext); ok {
|
||||||
context.ClearSearchString()
|
context.ClearSearchString()
|
||||||
}
|
}
|
||||||
self.c.Context().Push(mainViewContext, types.OnFocusOpts{})
|
onFocusOpts := types.OnFocusOpts{ClickedWindowName: mainViewName}
|
||||||
|
if clickedViewLineIdx >= 0 {
|
||||||
|
onFocusOpts.ClickedViewLineIdx = clickedViewLineIdx
|
||||||
|
} else {
|
||||||
|
mainView := mainViewContext.GetView()
|
||||||
|
lineIdx := mainView.OriginY() + mainView.Height()/2
|
||||||
|
onFocusOpts.ClickedViewLineIdx = lo.Clamp(lineIdx, 0, mainView.LinesHeight()-1)
|
||||||
|
}
|
||||||
|
self.c.Context().Push(mainViewContext, onFocusOpts)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package controllers
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ViewSelectionControllerFactory struct {
|
type ViewSelectionControllerFactory struct {
|
||||||
|
@ -61,10 +62,21 @@ func (self *ViewSelectionController) handleLineChange(delta int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
v := self.Context().GetView()
|
v := self.Context().GetView()
|
||||||
if delta < 0 {
|
if self.context.GetView().Highlight {
|
||||||
v.ScrollUp(-delta)
|
lineIdxBefore := v.CursorY() + v.OriginY()
|
||||||
|
lineIdxAfter := lo.Clamp(lineIdxBefore+delta, 0, v.ViewLinesHeight()-1)
|
||||||
|
if delta == -1 {
|
||||||
|
checkScrollUp(self.Context().GetViewTrait(), self.c.UserConfig(), lineIdxBefore, lineIdxAfter)
|
||||||
|
} else if delta == 1 {
|
||||||
|
checkScrollDown(self.Context().GetViewTrait(), self.c.UserConfig(), lineIdxBefore, lineIdxAfter)
|
||||||
|
}
|
||||||
|
v.FocusPoint(0, lineIdxAfter)
|
||||||
} else {
|
} else {
|
||||||
v.ScrollDown(delta)
|
if delta < 0 {
|
||||||
|
v.ScrollUp(-delta)
|
||||||
|
} else {
|
||||||
|
v.ScrollDown(delta)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +102,11 @@ func (self *ViewSelectionController) handleNextPage() error {
|
||||||
|
|
||||||
func (self *ViewSelectionController) handleGotoTop() error {
|
func (self *ViewSelectionController) handleGotoTop() error {
|
||||||
v := self.Context().GetView()
|
v := self.Context().GetView()
|
||||||
self.handleLineChange(-v.ViewLinesHeight())
|
if self.context.GetView().Highlight {
|
||||||
|
v.FocusPoint(0, 0)
|
||||||
|
} else {
|
||||||
|
self.handleLineChange(-v.ViewLinesHeight())
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +115,11 @@ func (self *ViewSelectionController) handleGotoBottom() error {
|
||||||
manager.ReadToEnd(func() {
|
manager.ReadToEnd(func() {
|
||||||
self.c.OnUIThread(func() error {
|
self.c.OnUIThread(func() error {
|
||||||
v := self.Context().GetView()
|
v := self.Context().GetView()
|
||||||
self.handleLineChange(v.ViewLinesHeight())
|
if self.context.GetView().Highlight {
|
||||||
|
v.FocusPoint(0, v.ViewLinesHeight()-1)
|
||||||
|
} else {
|
||||||
|
self.handleLineChange(v.ViewLinesHeight())
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -446,6 +446,11 @@ func (gui *Gui) onUserConfigLoaded() error {
|
||||||
|
|
||||||
gui.g.Mouse = userConfig.Gui.MouseEvents
|
gui.g.Mouse = userConfig.Gui.MouseEvents
|
||||||
|
|
||||||
|
if gui.State != nil {
|
||||||
|
gui.Contexts().Normal.SetHighlightOnFocus(userConfig.Gui.ShowSelectionInFocusedMainView)
|
||||||
|
gui.Contexts().NormalSecondary.SetHighlightOnFocus(userConfig.Gui.ShowSelectionInFocusedMainView)
|
||||||
|
}
|
||||||
|
|
||||||
// originally we could only hide the command log permanently via the config
|
// originally we could only hide the command log permanently via the config
|
||||||
// but now we do it via state. So we need to still support the config for the
|
// but now we do it via state. So we need to still support the config for the
|
||||||
// sake of backwards compatibility. We're making use of short circuiting here
|
// sake of backwards compatibility. We're making use of short circuiting here
|
||||||
|
|
|
@ -39,7 +39,7 @@ const (
|
||||||
HUNK
|
HUNK
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewState(diff string, selectedLineIdx int, view *gocui.View, oldState *State) *State {
|
func NewState(diff string, selectedLineIdx int, selectedRealLineIdx int, view *gocui.View, oldState *State) *State {
|
||||||
if oldState != nil && diff == oldState.diff && selectedLineIdx == -1 {
|
if oldState != nil && diff == oldState.diff && selectedLineIdx == -1 {
|
||||||
// if we're here then we can return the old state. If selectedLineIdx was not -1
|
// if we're here then we can return the old state. If selectedLineIdx was not -1
|
||||||
// then that would mean we were trying to click and potentially drag a range, which
|
// then that would mean we were trying to click and potentially drag a range, which
|
||||||
|
@ -55,6 +55,10 @@ func NewState(diff string, selectedLineIdx int, view *gocui.View, oldState *Stat
|
||||||
|
|
||||||
viewLineIndices, patchLineIndices := wrapPatchLines(diff, view)
|
viewLineIndices, patchLineIndices := wrapPatchLines(diff, view)
|
||||||
|
|
||||||
|
if selectedRealLineIdx != -1 {
|
||||||
|
selectedLineIdx = patch.PatchLineForLineNumber(selectedRealLineIdx)
|
||||||
|
}
|
||||||
|
|
||||||
rangeStartLineIdx := 0
|
rangeStartLineIdx := 0
|
||||||
if oldState != nil {
|
if oldState != nil {
|
||||||
rangeStartLineIdx = oldState.rangeStartLineIdx
|
rangeStartLineIdx = oldState.rangeStartLineIdx
|
||||||
|
|
|
@ -220,6 +220,9 @@ type IViewTrait interface {
|
||||||
type OnFocusOpts struct {
|
type OnFocusOpts struct {
|
||||||
ClickedWindowName string
|
ClickedWindowName string
|
||||||
ClickedViewLineIdx int
|
ClickedViewLineIdx int
|
||||||
|
|
||||||
|
// If not -1, takes precedence over ClickedViewLineIdx.
|
||||||
|
ClickedViewRealLineIdx int
|
||||||
}
|
}
|
||||||
|
|
||||||
type OnFocusLostOpts struct {
|
type OnFocusLostOpts struct {
|
||||||
|
|
|
@ -519,6 +519,7 @@ type TranslationSet struct {
|
||||||
EmptyPatchError string
|
EmptyPatchError string
|
||||||
EnterCommitFile string
|
EnterCommitFile string
|
||||||
EnterCommitFileTooltip string
|
EnterCommitFileTooltip string
|
||||||
|
EnterStaging string
|
||||||
ExitCustomPatchBuilder string
|
ExitCustomPatchBuilder string
|
||||||
ExitFocusedMainView string
|
ExitFocusedMainView string
|
||||||
EnterUpstream string
|
EnterUpstream string
|
||||||
|
@ -1607,6 +1608,7 @@ func EnglishTranslationSet() *TranslationSet {
|
||||||
EmptyPatchError: "Patch is still empty. Add some files or lines to your patch first.",
|
EmptyPatchError: "Patch is still empty. Add some files or lines to your patch first.",
|
||||||
EnterCommitFile: "Enter file / Toggle directory collapsed",
|
EnterCommitFile: "Enter file / Toggle directory collapsed",
|
||||||
EnterCommitFileTooltip: "If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory.",
|
EnterCommitFileTooltip: "If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory.",
|
||||||
|
EnterStaging: "Enter staging/patch building",
|
||||||
ExitCustomPatchBuilder: `Exit custom patch builder`,
|
ExitCustomPatchBuilder: `Exit custom patch builder`,
|
||||||
ExitFocusedMainView: "Exit back to side panel",
|
ExitFocusedMainView: "Exit back to side panel",
|
||||||
EnterUpstream: `Enter upstream as '<remote> <branchname>'`,
|
EnterUpstream: `Enter upstream as '<remote> <branchname>'`,
|
||||||
|
|
|
@ -527,6 +527,11 @@
|
||||||
"description": "If true, wrap lines in the staging view to the width of the view. This\nmakes it much easier to work with diffs that have long lines, e.g.\nparagraphs of markdown text.",
|
"description": "If true, wrap lines in the staging view to the width of the view. This\nmakes it much easier to work with diffs that have long lines, e.g.\nparagraphs of markdown text.",
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
|
"showSelectionInFocusedMainView": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If true, show a selection when the main view is focused.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
|
10
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
10
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
|
@ -92,6 +92,12 @@ type ViewMouseBinding struct {
|
||||||
type ViewMouseBindingOpts struct {
|
type ViewMouseBindingOpts struct {
|
||||||
X int // i.e. origin x + cursor x
|
X int // i.e. origin x + cursor x
|
||||||
Y int // i.e. origin y + cursor y
|
Y int // i.e. origin y + cursor y
|
||||||
|
|
||||||
|
// the previous cursor right before the click; useful because by the time
|
||||||
|
// the event is dispatched to handlers, gocui has already set the cursor to
|
||||||
|
// the new position. This is useful for detecting double clicks.
|
||||||
|
PreviousX int
|
||||||
|
PreviousY int
|
||||||
}
|
}
|
||||||
|
|
||||||
type GuiMutexes struct {
|
type GuiMutexes struct {
|
||||||
|
@ -1375,6 +1381,8 @@ func (g *Gui) onKey(ev *GocuiEvent) error {
|
||||||
newCx = lastCharForLine - v.ox
|
newCx = lastCharForLine - v.ox
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
previousX := v.cx + v.ox
|
||||||
|
previousY := v.cy + v.oy
|
||||||
if !IsMouseScrollKey(ev.Key) {
|
if !IsMouseScrollKey(ev.Key) {
|
||||||
v.SetCursor(newCx, newCy)
|
v.SetCursor(newCx, newCy)
|
||||||
if v.Editable {
|
if v.Editable {
|
||||||
|
@ -1397,7 +1405,7 @@ func (g *Gui) onKey(ev *GocuiEvent) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if IsMouseKey(ev.Key) {
|
if IsMouseKey(ev.Key) {
|
||||||
opts := ViewMouseBindingOpts{X: newX, Y: newY}
|
opts := ViewMouseBindingOpts{X: newX, Y: newY, PreviousX: previousX, PreviousY: previousY}
|
||||||
matched, err := g.execMouseKeybindings(v, ev, opts)
|
matched, err := g.execMouseKeybindings(v, ev, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
20
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
20
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
|
@ -326,7 +326,11 @@ func (v *View) IsSearching() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *View) FocusPoint(cx int, cy int) {
|
func (v *View) FocusPoint(cx int, cy int) {
|
||||||
lineCount := len(v.lines)
|
v.writeMutex.Lock()
|
||||||
|
defer v.writeMutex.Unlock()
|
||||||
|
|
||||||
|
v.refreshViewLinesIfNeeded()
|
||||||
|
lineCount := len(v.viewLines)
|
||||||
if cy < 0 || cy > lineCount {
|
if cy < 0 || cy > lineCount {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1466,6 +1470,20 @@ func (v *View) Word(x, y int) (string, bool) {
|
||||||
return str[nl:nr], true
|
return str[nl:nr], true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *View) HyperLinkInLine(y int, urlScheme string) (string, bool) {
|
||||||
|
if y < 0 || y >= len(v.viewLines) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range v.lines[v.viewLines[y].linesY] {
|
||||||
|
if strings.HasPrefix(c.hyperlink, urlScheme) {
|
||||||
|
return c.hyperlink, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
// indexFunc allows to split lines by words taking into account spaces
|
// indexFunc allows to split lines by words taking into account spaces
|
||||||
// and 0.
|
// and 0.
|
||||||
func indexFunc(r rune) bool {
|
func indexFunc(r rune) bool {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue