mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-11 08:06:25 -04:00
Collapse the focused-main-view handler channels into one
Each command a side panel handles in the focused main view needed its own delegation channel: a HasKeybindings getter, an IBaseContext Add method, a BaseContext field + getter, a baseController nil default, and an attach.go registration — roughly five touch points per command. With three commands (click, stage, toggle-patch) that was already a lot of boilerplate, and it doesn't scale to the discard / copy commands coming next. Replace the three channels with one: a side panel exposes a single FocusedMainViewActions interface via GetFocusedMainViewActions (nil when its diff offers no actions), and the controllers implement that interface directly. The stage and toggle-patch handlers, already unified to a plain error return, become one PrimaryAction method whose meaning is the panel's business (stage for the files panel, patch toggle for the commit panels); the click handler becomes OnClick. MainViewController is now a thin dispatcher: fetch the actions from the panel beneath, call the method. Adding a command is now one interface method, an implementation in the two or three controllers, and a keybinding — no plumbing. The toggle-patch channel did double duty as the "this panel builds a custom patch" signal for the inclusion gutter (shown only beneath a patch-building panel, not the staging files panel). Collapsing the channels removes that proxy, so make the classification explicit on DiffMainViewContext, whose marker method now returns a DiffMainViewType (none / staging / patch-building) instead of being a bare marker. The gutter shows only beneath a panel whose type is patch-building (commit files, local commits, sub-commits, stash). Behavior-preserving. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
96b09bb41d
commit
88dae2f2c6
|
|
@ -13,17 +13,15 @@ type BaseContext struct {
|
|||
windowName string
|
||||
onGetOptionsMap func() map[string]string
|
||||
|
||||
keybindingsFns []types.KeybindingsFn
|
||||
mouseKeybindingsFns []types.MouseKeybindingsFn
|
||||
onDoubleClickFn func() error
|
||||
onClickFn func(opts gocui.ViewMouseBindingOpts) error
|
||||
onClickFocusedMainViewFn onClickFocusedMainViewFn
|
||||
onStageFocusedMainViewFn onStageFocusedMainViewFn
|
||||
onTogglePatchFocusedMainViewFn onTogglePatchFocusedMainViewFn
|
||||
onRenderToMainFn func()
|
||||
onFocusFns []onFocusFn
|
||||
onFocusLostFns []onFocusLostFn
|
||||
onQuitFns []func()
|
||||
keybindingsFns []types.KeybindingsFn
|
||||
mouseKeybindingsFns []types.MouseKeybindingsFn
|
||||
onDoubleClickFn func() error
|
||||
onClickFn func(opts gocui.ViewMouseBindingOpts) error
|
||||
focusedMainViewActions types.FocusedMainViewActions
|
||||
onRenderToMainFn func()
|
||||
onFocusFns []onFocusFn
|
||||
onFocusLostFns []onFocusLostFn
|
||||
onQuitFns []func()
|
||||
|
||||
focusable bool
|
||||
transient bool
|
||||
|
|
@ -36,11 +34,8 @@ type BaseContext struct {
|
|||
}
|
||||
|
||||
type (
|
||||
onFocusFn = func(types.OnFocusOpts)
|
||||
onFocusLostFn = func(types.OnFocusLostOpts)
|
||||
onClickFocusedMainViewFn = func(mainViewName string, clickedLineIdx int) error
|
||||
onStageFocusedMainViewFn = func(mainViewName string, firstLineIdx int, lastLineIdx int) error
|
||||
onTogglePatchFocusedMainViewFn = func(mainViewName string, firstLineIdx int, lastLineIdx int) error
|
||||
onFocusFn = func(types.OnFocusOpts)
|
||||
onFocusLostFn = func(types.OnFocusLostOpts)
|
||||
)
|
||||
|
||||
var _ types.IBaseContext = &BaseContext{}
|
||||
|
|
@ -149,9 +144,7 @@ func (self *BaseContext) ClearAllAttachedControllerFunctions() {
|
|||
self.onQuitFns = nil
|
||||
self.onDoubleClickFn = nil
|
||||
self.onClickFn = nil
|
||||
self.onClickFocusedMainViewFn = nil
|
||||
self.onStageFocusedMainViewFn = nil
|
||||
self.onTogglePatchFocusedMainViewFn = nil
|
||||
self.focusedMainViewActions = nil
|
||||
self.onRenderToMainFn = nil
|
||||
}
|
||||
|
||||
|
|
@ -173,12 +166,12 @@ func (self *BaseContext) AddOnClickFn(fn func(opts gocui.ViewMouseBindingOpts) e
|
|||
}
|
||||
}
|
||||
|
||||
func (self *BaseContext) AddOnClickFocusedMainViewFn(fn onClickFocusedMainViewFn) {
|
||||
if fn != nil {
|
||||
if self.onClickFocusedMainViewFn != nil {
|
||||
panic("only one controller is allowed to set an onClickFocusedMainViewFn")
|
||||
func (self *BaseContext) AddFocusedMainViewActions(actions types.FocusedMainViewActions) {
|
||||
if actions != nil {
|
||||
if self.focusedMainViewActions != nil {
|
||||
panic("only one controller is allowed to set the focused main view actions")
|
||||
}
|
||||
self.onClickFocusedMainViewFn = fn
|
||||
self.focusedMainViewActions = actions
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,34 +183,8 @@ func (self *BaseContext) GetOnClick() func(opts gocui.ViewMouseBindingOpts) erro
|
|||
return self.onClickFn
|
||||
}
|
||||
|
||||
func (self *BaseContext) GetOnClickFocusedMainView() onClickFocusedMainViewFn {
|
||||
return self.onClickFocusedMainViewFn
|
||||
}
|
||||
|
||||
func (self *BaseContext) AddOnStageFocusedMainViewFn(fn onStageFocusedMainViewFn) {
|
||||
if fn != nil {
|
||||
if self.onStageFocusedMainViewFn != nil {
|
||||
panic("only one controller is allowed to set an onStageFocusedMainViewFn")
|
||||
}
|
||||
self.onStageFocusedMainViewFn = fn
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BaseContext) GetOnStageFocusedMainView() onStageFocusedMainViewFn {
|
||||
return self.onStageFocusedMainViewFn
|
||||
}
|
||||
|
||||
func (self *BaseContext) AddOnTogglePatchFocusedMainViewFn(fn onTogglePatchFocusedMainViewFn) {
|
||||
if fn != nil {
|
||||
if self.onTogglePatchFocusedMainViewFn != nil {
|
||||
panic("only one controller is allowed to set an onTogglePatchFocusedMainViewFn")
|
||||
}
|
||||
self.onTogglePatchFocusedMainViewFn = fn
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BaseContext) GetOnTogglePatchFocusedMainView() onTogglePatchFocusedMainViewFn {
|
||||
return self.onTogglePatchFocusedMainViewFn
|
||||
func (self *BaseContext) GetFocusedMainViewActions() types.FocusedMainViewActions {
|
||||
return self.focusedMainViewActions
|
||||
}
|
||||
|
||||
func (self *BaseContext) AddOnRenderToMainFn(fn func()) {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ var (
|
|||
_ types.DiffMainViewContext = (*CommitFilesContext)(nil)
|
||||
)
|
||||
|
||||
func (self *CommitFilesContext) IsDiffMainViewContext() {}
|
||||
func (self *CommitFilesContext) GetDiffMainViewType() types.DiffMainViewType {
|
||||
return types.DiffMainViewTypePatchBuilding
|
||||
}
|
||||
|
||||
func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
||||
viewModel := filetree.NewCommitFileTreeViewModel(
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@ var (
|
|||
_ types.DiffMainViewContext = (*LocalCommitsContext)(nil)
|
||||
)
|
||||
|
||||
func (self *LocalCommitsContext) IsDiffMainViewContext() {}
|
||||
func (self *LocalCommitsContext) GetDiffMainViewType() types.DiffMainViewType {
|
||||
return types.DiffMainViewTypePatchBuilding
|
||||
}
|
||||
|
||||
func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||
dropIndicator := &commitDropIndicator{insertionIndex: -1}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,11 @@ var (
|
|||
_ types.DiffMainViewContext = (*ReflogCommitsContext)(nil)
|
||||
)
|
||||
|
||||
func (self *ReflogCommitsContext) IsDiffMainViewContext() {}
|
||||
// Reflog shows a commit's diff but has no primary action (building a patch from it is
|
||||
// a deferred gap), so the focused main view has a selection but space does nothing.
|
||||
func (self *ReflogCommitsContext) GetDiffMainViewType() types.DiffMainViewType {
|
||||
return types.DiffMainViewTypeNone
|
||||
}
|
||||
|
||||
func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
|
||||
viewModel := NewFilteredListViewModel(
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ var (
|
|||
_ types.DiffMainViewContext = (*StashContext)(nil)
|
||||
)
|
||||
|
||||
func (self *StashContext) IsDiffMainViewContext() {}
|
||||
func (self *StashContext) GetDiffMainViewType() types.DiffMainViewType {
|
||||
return types.DiffMainViewTypePatchBuilding
|
||||
}
|
||||
|
||||
func NewStashContext(
|
||||
c *ContextCommon,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ var (
|
|||
_ types.DiffMainViewContext = (*SubCommitsContext)(nil)
|
||||
)
|
||||
|
||||
func (self *SubCommitsContext) IsDiffMainViewContext() {}
|
||||
func (self *SubCommitsContext) GetDiffMainViewType() types.DiffMainViewType {
|
||||
return types.DiffMainViewTypePatchBuilding
|
||||
}
|
||||
|
||||
func NewSubCommitsContext(
|
||||
c *ContextCommon,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ var (
|
|||
_ types.DiffMainViewContext = (*WorkingTreeContext)(nil)
|
||||
)
|
||||
|
||||
func (self *WorkingTreeContext) IsDiffMainViewContext() {}
|
||||
func (self *WorkingTreeContext) GetDiffMainViewType() types.DiffMainViewType {
|
||||
return types.DiffMainViewTypeStaging
|
||||
}
|
||||
|
||||
func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
|
||||
viewModel := filetree.NewFileTreeViewModel(
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ func AttachControllers(context types.Context, controllers ...types.IController)
|
|||
context.AddMouseKeybindingsFn(controller.GetMouseKeybindings)
|
||||
context.AddOnDoubleClickFn(controller.GetOnDoubleClick())
|
||||
context.AddOnClickFn(controller.GetOnClick())
|
||||
context.AddOnClickFocusedMainViewFn(controller.GetOnClickFocusedMainView())
|
||||
context.AddOnStageFocusedMainViewFn(controller.GetOnStageFocusedMainView())
|
||||
context.AddOnTogglePatchFocusedMainViewFn(controller.GetOnTogglePatchFocusedMainView())
|
||||
context.AddFocusedMainViewActions(controller.GetFocusedMainViewActions())
|
||||
context.AddOnRenderToMainFn(controller.GetOnRenderToMain())
|
||||
context.AddOnFocusFn(controller.GetOnFocus())
|
||||
context.AddOnFocusLostFn(controller.GetOnFocusLost())
|
||||
|
|
|
|||
|
|
@ -19,15 +19,7 @@ func (self *baseController) GetOnDoubleClick() func() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (self *baseController) GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *baseController) GetOnStageFocusedMainView() func(mainViewName string, firstLineIdx int, lastLineIdx int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *baseController) GetOnTogglePatchFocusedMainView() func(mainViewName string, firstLineIdx int, lastLineIdx int) error {
|
||||
func (self *baseController) GetFocusedMainViewActions() types.FocusedMainViewActions {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -552,62 +552,62 @@ func (self *CommitFilesController) expandAll() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error {
|
||||
return func(mainViewName string, clickedLineIdx int) error {
|
||||
// Capture before any mutation below that might re-render the main view.
|
||||
snapshot := focusedMainViewSnapshot(self.c, mainViewName, self.context())
|
||||
|
||||
info, ok := self.c.Helpers().Staging.GetDiffLineInfo(mainViewName, clickedLineIdx)
|
||||
line := -1
|
||||
isDeletion := false
|
||||
if ok {
|
||||
line, isDeletion = info.PatchSelectLine()
|
||||
}
|
||||
|
||||
node := self.getSelectedItem()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !node.IsFile() && ok {
|
||||
relativePath, err := filepath.Rel(self.c.Git().RepoPaths.WorktreePath(), info.Path)
|
||||
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), false)
|
||||
node = self.context().GetSelected()
|
||||
}
|
||||
}
|
||||
|
||||
// Entered from the focused main view, so escaping returns there.
|
||||
return self.c.Helpers().CommitFiles.EnterCommitFile(node, snapshot, types.OnFocusOpts{ClickedWindowName: "main", ClickedViewLineIdx: line, ClickedViewRealLineIdx: line, ClickedViewRealLineIsDeletion: isDeletion, SelectLineInDefaultMode: true})
|
||||
}
|
||||
func (self *CommitFilesController) GetFocusedMainViewActions() types.FocusedMainViewActions {
|
||||
return self
|
||||
}
|
||||
|
||||
// GetOnTogglePatchFocusedMainView toggles the selected diff line(s) into or out of the
|
||||
// custom patch when space is pressed in the focused main view of a commit's files. The
|
||||
// per-file diff's patch target comes from the commit files context. It refreshes
|
||||
// normally afterwards so the file's patch-status indicator in the browser updates along
|
||||
// with the secondary patch view (the commits / sub-commits / stash panels, which build
|
||||
// from the whole-commit diff, have no such indicator and refresh more cheaply).
|
||||
func (self *CommitFilesController) GetOnTogglePatchFocusedMainView() func(mainViewName string, firstLineIdx int, lastLineIdx int) error {
|
||||
return func(mainViewName string, firstLineIdx int, lastLineIdx int) error {
|
||||
from, to, reverse := self.c.Helpers().CommitFiles.CurrentFromToReverseForPatchBuilding()
|
||||
canRebase := self.context().GetCanRebase()
|
||||
return togglePatchFromFocusedMainView(self.c, mainViewName, firstLineIdx, lastLineIdx,
|
||||
from, to, reverse, canRebase,
|
||||
func() {
|
||||
self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.COMMIT_FILES}})
|
||||
})
|
||||
func (self *CommitFilesController) OnClick(mainViewName string, clickedLineIdx int) error {
|
||||
// Capture before any mutation below that might re-render the main view.
|
||||
snapshot := focusedMainViewSnapshot(self.c, mainViewName, self.context())
|
||||
|
||||
info, ok := self.c.Helpers().Staging.GetDiffLineInfo(mainViewName, clickedLineIdx)
|
||||
line := -1
|
||||
isDeletion := false
|
||||
if ok {
|
||||
line, isDeletion = info.PatchSelectLine()
|
||||
}
|
||||
|
||||
node := self.getSelectedItem()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !node.IsFile() && ok {
|
||||
relativePath, err := filepath.Rel(self.c.Git().RepoPaths.WorktreePath(), info.Path)
|
||||
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), false)
|
||||
node = self.context().GetSelected()
|
||||
}
|
||||
}
|
||||
|
||||
// Entered from the focused main view, so escaping returns there.
|
||||
return self.c.Helpers().CommitFiles.EnterCommitFile(node, snapshot, types.OnFocusOpts{ClickedWindowName: "main", ClickedViewLineIdx: line, ClickedViewRealLineIdx: line, ClickedViewRealLineIsDeletion: isDeletion, SelectLineInDefaultMode: true})
|
||||
}
|
||||
|
||||
// PrimaryAction toggles the selected diff line(s) into or out of the custom patch when
|
||||
// space is pressed in the focused main view of a commit's files. The per-file diff's
|
||||
// patch target comes from the commit files context. It refreshes normally afterwards so
|
||||
// the file's patch-status indicator in the browser updates along with the secondary patch
|
||||
// view (the commits / sub-commits / stash panels, which build from the whole-commit diff,
|
||||
// have no such indicator and refresh more cheaply).
|
||||
func (self *CommitFilesController) PrimaryAction(mainViewName string, firstLineIdx int, lastLineIdx int) error {
|
||||
from, to, reverse := self.c.Helpers().CommitFiles.CurrentFromToReverseForPatchBuilding()
|
||||
canRebase := self.context().GetCanRebase()
|
||||
return togglePatchFromFocusedMainView(self.c, mainViewName, firstLineIdx, lastLineIdx,
|
||||
from, to, reverse, canRebase,
|
||||
func() {
|
||||
self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.COMMIT_FILES}})
|
||||
})
|
||||
}
|
||||
|
||||
// pathsForDiff returns the file paths to use for a diff command. When a text
|
||||
|
|
|
|||
|
|
@ -407,122 +407,124 @@ func (self *FilesController) GetOnDoubleClick() func() error {
|
|||
})
|
||||
}
|
||||
|
||||
func (self *FilesController) GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error {
|
||||
return func(mainViewName string, clickedLineIdx int) error {
|
||||
// Capture before any mutation below that might re-render the main view.
|
||||
snapshot := focusedMainViewSnapshot(self.c, mainViewName, self.context())
|
||||
func (self *FilesController) GetFocusedMainViewActions() types.FocusedMainViewActions {
|
||||
return self
|
||||
}
|
||||
|
||||
info, ok := self.c.Helpers().Staging.GetDiffLineInfo(mainViewName, clickedLineIdx)
|
||||
line := -1
|
||||
isDeletion := false
|
||||
if ok {
|
||||
line, isDeletion = info.PatchSelectLine()
|
||||
}
|
||||
func (self *FilesController) OnClick(mainViewName string, clickedLineIdx int) error {
|
||||
// Capture before any mutation below that might re-render the main view.
|
||||
snapshot := focusedMainViewSnapshot(self.c, mainViewName, self.context())
|
||||
|
||||
node := self.context().GetSelected()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !node.IsFile() && ok {
|
||||
relativePath, err := filepath.Rel(self.c.Git().RepoPaths.WorktreePath(), info.Path)
|
||||
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), false)
|
||||
}
|
||||
}
|
||||
|
||||
return self.EnterFile(snapshot, types.OnFocusOpts{ClickedWindowName: mainViewName, ClickedViewLineIdx: line, ClickedViewRealLineIdx: line, ClickedViewRealLineIsDeletion: isDeletion, SelectLineInDefaultMode: true})
|
||||
info, ok := self.c.Helpers().Staging.GetDiffLineInfo(mainViewName, clickedLineIdx)
|
||||
line := -1
|
||||
isDeletion := false
|
||||
if ok {
|
||||
line, isDeletion = info.PatchSelectLine()
|
||||
}
|
||||
|
||||
node := self.context().GetSelected()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !node.IsFile() && ok {
|
||||
relativePath, err := filepath.Rel(self.c.Git().RepoPaths.WorktreePath(), info.Path)
|
||||
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), false)
|
||||
}
|
||||
}
|
||||
|
||||
return self.EnterFile(snapshot, types.OnFocusOpts{ClickedWindowName: mainViewName, ClickedViewLineIdx: line, ClickedViewRealLineIdx: line, ClickedViewRealLineIsDeletion: isDeletion, SelectLineInDefaultMode: true})
|
||||
}
|
||||
|
||||
// diffSplitState reports, for the given file node, how the focused main view lays
|
||||
// out its diff: whether it's split into unstaged (Normal) and staged
|
||||
// (NormalSecondary) halves, and — when not split — whether the single Normal view
|
||||
// shows the staged diff (which happens when the file has only staged changes).
|
||||
// GetOnRenderToMain and GetOnStageFocusedMainView share this so the staging
|
||||
// direction can't drift from what's on screen.
|
||||
// GetOnRenderToMain and PrimaryAction share this so the staging direction
|
||||
// can't drift from what's on screen.
|
||||
func (self *FilesController) diffSplitState(node *filetree.FileNode) (split bool, mainShowsStaged bool) {
|
||||
split = self.c.UserConfig().Gui.SplitDiff == "always" || (node.GetHasUnstagedChanges() && node.GetHasStagedChanges())
|
||||
mainShowsStaged = !split && node.GetHasStagedChanges()
|
||||
return split, mainShowsStaged
|
||||
}
|
||||
|
||||
func (self *FilesController) GetOnStageFocusedMainView() func(mainViewName string, firstLineIdx int, lastLineIdx int) error {
|
||||
return func(mainViewName string, firstLineIdx int, lastLineIdx int) error {
|
||||
if self.c.UserConfig().Git.DiffContextSize == 0 {
|
||||
return fmt.Errorf(self.c.Tr.Actions.NotEnoughContextToStage,
|
||||
self.c.UserConfig().Keybinding.Universal.IncreaseContextInDiffView)
|
||||
}
|
||||
// PrimaryAction stages (or unstages) the selected diff line(s) when space is pressed in
|
||||
// the focused main view of the working-tree files panel.
|
||||
func (self *FilesController) PrimaryAction(mainViewName string, firstLineIdx int, lastLineIdx int) error {
|
||||
if self.c.UserConfig().Git.DiffContextSize == 0 {
|
||||
return fmt.Errorf(self.c.Tr.Actions.NotEnoughContextToStage,
|
||||
self.c.UserConfig().Keybinding.Universal.IncreaseContextInDiffView)
|
||||
}
|
||||
|
||||
node := self.context().GetSelected()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
infos := self.c.Helpers().Staging.ChangeLinesInViewRange(mainViewName, firstLineIdx, lastLineIdx)
|
||||
if len(infos) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The whole diff shown in the main view is on one side — the staged diff in
|
||||
// the secondary half of a split, and in the main half when there are only
|
||||
// staged changes; in those cases space unstages, otherwise it stages. The
|
||||
// direction is the same for every file in a multi-file (directory) diff.
|
||||
_, mainShowsStaged := self.diffSplitState(node)
|
||||
reverse := mainShowsStaged || mainViewName == self.c.Contexts().NormalSecondary.GetViewName()
|
||||
|
||||
// A directory diff spans several files; group the selected change lines by
|
||||
// file and apply one patch per file.
|
||||
infosByFile := lo.GroupBy(infos, func(info types.DiffLineInfo) string { return info.Path })
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.ApplyPatch)
|
||||
for path, fileInfos := range infosByFile {
|
||||
file := self.fileForDiffLinePath(path)
|
||||
if file == nil {
|
||||
continue
|
||||
}
|
||||
if err := self.stageDiffLines(file, fileInfos, reverse); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES, types.STAGING}})
|
||||
|
||||
// Focus follows the side that was acted on. Staging keeps it in the main half
|
||||
// (which always holds the unstaged side, or the staged side once the file has
|
||||
// only staged changes). Unstaging keeps it on the staged side, which lives in
|
||||
// the secondary half once the file is split into staged + unstaged, and moves
|
||||
// back to the main half when the staged side empties and the split collapses.
|
||||
// The model is up to date now (Refresh above is synchronous), so the post-op
|
||||
// split is read from the freshly selected node.
|
||||
focusViewName := self.c.Contexts().Normal.GetViewName()
|
||||
if reverse {
|
||||
if node := self.context().GetSelected(); node != nil {
|
||||
if split, _ := self.diffSplitState(node); split {
|
||||
focusViewName = self.c.Contexts().NormalSecondary.GetViewName()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The staging Refresh above queued the main-view re-render; re-establish the
|
||||
// selection in whichever pane now holds the acted-on side once that render lands,
|
||||
// and focus that pane if staging moved it there.
|
||||
revealSelectionAfterPrimaryAction(self.c, mainViewName, focusViewName, firstLineIdx)
|
||||
if focusViewName != mainViewName {
|
||||
self.c.Context().Push(mainContextForViewName(self.c, focusViewName), types.OnFocusOpts{})
|
||||
}
|
||||
node := self.context().GetSelected()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
infos := self.c.Helpers().Staging.ChangeLinesInViewRange(mainViewName, firstLineIdx, lastLineIdx)
|
||||
if len(infos) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The whole diff shown in the main view is on one side — the staged diff in
|
||||
// the secondary half of a split, and in the main half when there are only
|
||||
// staged changes; in those cases space unstages, otherwise it stages. The
|
||||
// direction is the same for every file in a multi-file (directory) diff.
|
||||
_, mainShowsStaged := self.diffSplitState(node)
|
||||
reverse := mainShowsStaged || mainViewName == self.c.Contexts().NormalSecondary.GetViewName()
|
||||
|
||||
// A directory diff spans several files; group the selected change lines by
|
||||
// file and apply one patch per file.
|
||||
infosByFile := lo.GroupBy(infos, func(info types.DiffLineInfo) string { return info.Path })
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.ApplyPatch)
|
||||
for path, fileInfos := range infosByFile {
|
||||
file := self.fileForDiffLinePath(path)
|
||||
if file == nil {
|
||||
continue
|
||||
}
|
||||
if err := self.stageDiffLines(file, fileInfos, reverse); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES, types.STAGING}})
|
||||
|
||||
// Focus follows the side that was acted on. Staging keeps it in the main half
|
||||
// (which always holds the unstaged side, or the staged side once the file has
|
||||
// only staged changes). Unstaging keeps it on the staged side, which lives in
|
||||
// the secondary half once the file is split into staged + unstaged, and moves
|
||||
// back to the main half when the staged side empties and the split collapses.
|
||||
// The model is up to date now (Refresh above is synchronous), so the post-op
|
||||
// split is read from the freshly selected node.
|
||||
focusViewName := self.c.Contexts().Normal.GetViewName()
|
||||
if reverse {
|
||||
if node := self.context().GetSelected(); node != nil {
|
||||
if split, _ := self.diffSplitState(node); split {
|
||||
focusViewName = self.c.Contexts().NormalSecondary.GetViewName()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The staging Refresh above queued the main-view re-render; re-establish the
|
||||
// selection in whichever pane now holds the acted-on side once that render lands,
|
||||
// and focus that pane if staging moved it there.
|
||||
revealSelectionAfterPrimaryAction(self.c, mainViewName, focusViewName, firstLineIdx)
|
||||
if focusViewName != mainViewName {
|
||||
self.c.Context().Push(mainContextForViewName(self.c, focusViewName), types.OnFocusOpts{})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fileForDiffLinePath maps a diff line's absolute file path (as carried by the
|
||||
|
|
|
|||
|
|
@ -732,7 +732,8 @@ func (self *StagingHelper) RefreshInclusionGutter() {
|
|||
}
|
||||
|
||||
sidePanel := self.c.Context().NextInStack(mainContext)
|
||||
if sidePanel == nil || sidePanel.GetOnTogglePatchFocusedMainView() == nil {
|
||||
diffMainView, ok := sidePanel.(types.DiffMainViewContext)
|
||||
if !ok || diffMainView.GetDiffMainViewType() != types.DiffMainViewTypePatchBuilding {
|
||||
v.SetInclusionGutter(false, nil)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*ty
|
|||
},
|
||||
{
|
||||
Keys: opts.GetKeys(opts.Config.Universal.Select),
|
||||
Handler: self.stageSelectedLine,
|
||||
Handler: self.primaryAction,
|
||||
Description: self.c.Tr.Stage,
|
||||
Tooltip: self.c.Tr.StageSelectionTooltip,
|
||||
DisplayOnScreen: selectionShown,
|
||||
|
|
@ -279,27 +279,31 @@ func (self *MainViewController) isDiffView() bool {
|
|||
return sidePanelShowsDiff(self.c.Context().NextInStack(self.context))
|
||||
}
|
||||
|
||||
// stageSelectedLine acts on the selected diff line(s) — a single line, a range, or a
|
||||
// hunk — delegating the primary action to the side panel beneath the focused main view,
|
||||
// since what the action means is the panel's business: the working tree stages, while
|
||||
// commits toggle the selection into a custom patch. Each handler does its own re-render
|
||||
// and re-establishes the selection afterwards (see revealSelectionAfterPrimaryAction), so
|
||||
// the dispatcher just hands over the selected range. Panels whose diff supports neither
|
||||
// register no handler, so this is a no-op there.
|
||||
func (self *MainViewController) stageSelectedLine() error {
|
||||
// focusedMainViewActions returns the actions the side panel beneath the focused main
|
||||
// view offers on its diff (diving in, staging, patch toggling), or nil when there is no
|
||||
// panel beneath or its diff offers none.
|
||||
func (self *MainViewController) focusedMainViewActions() types.FocusedMainViewActions {
|
||||
sidePanelContext := self.c.Context().NextInStack(self.context)
|
||||
if sidePanelContext == nil {
|
||||
return nil
|
||||
}
|
||||
return sidePanelContext.GetFocusedMainViewActions()
|
||||
}
|
||||
|
||||
// primaryAction acts on the selected diff line(s) — a single line, a range, or a hunk —
|
||||
// delegating to the side panel beneath the focused main view, since what the action means
|
||||
// is the panel's business: the working tree stages, while commits toggle the selection
|
||||
// into a custom patch. The handler does its own re-render and re-establishes the selection
|
||||
// afterwards (see revealSelectionAfterPrimaryAction), so the dispatcher just hands over
|
||||
// the selected range. A no-op when the panel beneath offers no actions.
|
||||
func (self *MainViewController) primaryAction() error {
|
||||
actions := self.focusedMainViewActions()
|
||||
if actions == nil {
|
||||
return nil
|
||||
}
|
||||
v := self.context.GetView()
|
||||
first, last := v.SelectedLineRange()
|
||||
if handler := sidePanelContext.GetOnStageFocusedMainView(); handler != nil {
|
||||
return handler(self.context.GetViewName(), first, last)
|
||||
}
|
||||
if handler := sidePanelContext.GetOnTogglePatchFocusedMainView(); handler != nil {
|
||||
return handler(self.context.GetViewName(), first, last)
|
||||
}
|
||||
return nil
|
||||
return actions.PrimaryAction(self.context.GetViewName(), first, last)
|
||||
}
|
||||
|
||||
// revealSelectionAfterPrimaryAction re-establishes the focused-main-view selection after a
|
||||
|
|
@ -343,12 +347,11 @@ func (self *MainViewController) enter() error {
|
|||
}
|
||||
|
||||
// enterForLine dives into staging/patch-building for the given line, by
|
||||
// delegating to the side panel beneath the focused main view (the same handler
|
||||
// used when clicking).
|
||||
// delegating to the side panel beneath the focused main view (the same action
|
||||
// taken when clicking).
|
||||
func (self *MainViewController) enterForLine(lineIdx int) error {
|
||||
sidePanelContext := self.c.Context().NextInStack(self.context)
|
||||
if sidePanelContext != nil && sidePanelContext.GetOnClickFocusedMainView() != nil {
|
||||
return sidePanelContext.GetOnClickFocusedMainView()(self.context.GetViewName(), lineIdx)
|
||||
if actions := self.focusedMainViewActions(); actions != nil {
|
||||
return actions.OnClick(self.context.GetViewName(), lineIdx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,45 +53,47 @@ func (self *SwitchToDiffFilesController) GetKeybindings(opts types.KeybindingsOp
|
|||
return bindings
|
||||
}
|
||||
|
||||
func (self *SwitchToDiffFilesController) GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error {
|
||||
return func(mainViewName string, clickedLineIdx int) error {
|
||||
info, ok := self.c.Helpers().Staging.GetDiffLineInfo(mainViewName, clickedLineIdx)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
line, isDeletion := info.PatchSelectLine()
|
||||
func (self *SwitchToDiffFilesController) GetFocusedMainViewActions() types.FocusedMainViewActions {
|
||||
return self
|
||||
}
|
||||
|
||||
// Capture before self.enter() pushes the commit files panel, which
|
||||
// re-renders the main view. We escape "all the way out" to this side
|
||||
// panel (skipping the commit files panel), then focus the main view.
|
||||
snapshot := focusedMainViewSnapshot(self.c, mainViewName, self.context)
|
||||
|
||||
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.WorktreePath(), info.Path)
|
||||
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), false)
|
||||
node = context.GetSelected()
|
||||
return self.c.Helpers().CommitFiles.EnterCommitFile(node, snapshot, types.OnFocusOpts{ClickedWindowName: "main", ClickedViewLineIdx: line, ClickedViewRealLineIdx: line, ClickedViewRealLineIsDeletion: isDeletion, SelectLineInDefaultMode: true})
|
||||
func (self *SwitchToDiffFilesController) OnClick(mainViewName string, clickedLineIdx int) error {
|
||||
info, ok := self.c.Helpers().Staging.GetDiffLineInfo(mainViewName, clickedLineIdx)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
line, isDeletion := info.PatchSelectLine()
|
||||
|
||||
// Capture before self.enter() pushes the commit files panel, which
|
||||
// re-renders the main view. We escape "all the way out" to this side
|
||||
// panel (skipping the commit files panel), then focus the main view.
|
||||
snapshot := focusedMainViewSnapshot(self.c, mainViewName, self.context)
|
||||
|
||||
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.WorktreePath(), info.Path)
|
||||
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), false)
|
||||
node = context.GetSelected()
|
||||
return self.c.Helpers().CommitFiles.EnterCommitFile(node, snapshot, types.OnFocusOpts{ClickedWindowName: "main", ClickedViewLineIdx: line, ClickedViewRealLineIdx: line, ClickedViewRealLineIsDeletion: isDeletion, SelectLineInDefaultMode: true})
|
||||
}
|
||||
|
||||
func (self *SwitchToDiffFilesController) Context() types.Context {
|
||||
|
|
@ -159,35 +161,33 @@ func (self *SwitchToDiffFilesController) canRebase(ref models.Ref, refsRange *ty
|
|||
return canRebase
|
||||
}
|
||||
|
||||
// GetOnTogglePatchFocusedMainView toggles the selected line(s) of the whole-commit diff
|
||||
// into or out of the custom patch when space is pressed in the focused main view of the
|
||||
// commits / sub-commits / stash panels. The patch target is the panel's selected ref (or
|
||||
// range), matching the diff the main view shows. Unlike the commit files panel there are
|
||||
// no per-file patch indicators to update, so the toggle refreshes cheaply: it re-renders
|
||||
// just this panel's main + secondary views (leaving the commit list untouched, which a
|
||||
// list refresh would needlessly reload on every keystroke), re-running the same diff
|
||||
// command (scroll preserved) and repainting the inclusion gutter.
|
||||
func (self *SwitchToDiffFilesController) GetOnTogglePatchFocusedMainView() func(mainViewName string, firstLineIdx int, lastLineIdx int) error {
|
||||
return func(mainViewName string, firstLineIdx int, lastLineIdx int) error {
|
||||
ref := self.context.GetSelectedRef()
|
||||
if ref == nil {
|
||||
return nil
|
||||
}
|
||||
refsRange := self.context.GetSelectedRefRangeForDiffFiles()
|
||||
|
||||
from, to := context.FromAndToForDiff(ref, refsRange)
|
||||
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from)
|
||||
canRebase := self.canRebase(ref, refsRange)
|
||||
|
||||
return togglePatchFromFocusedMainView(self.c, mainViewName, firstLineIdx, lastLineIdx,
|
||||
from, to, reverse, canRebase,
|
||||
func() {
|
||||
self.c.OnUIThread(func() error {
|
||||
self.c.PostRefreshUpdate(self.context)
|
||||
return nil
|
||||
})
|
||||
})
|
||||
// PrimaryAction toggles the selected line(s) of the whole-commit diff into or out of the
|
||||
// custom patch when space is pressed in the focused main view of the commits / sub-commits
|
||||
// / stash panels. The patch target is the panel's selected ref (or range), matching the
|
||||
// diff the main view shows. Unlike the commit files panel there are no per-file patch
|
||||
// indicators to update, so the toggle refreshes cheaply: it re-renders just this panel's
|
||||
// main + secondary views (leaving the commit list untouched, which a list refresh would
|
||||
// needlessly reload on every keystroke), re-running the same diff command (scroll
|
||||
// preserved) and repainting the inclusion gutter.
|
||||
func (self *SwitchToDiffFilesController) PrimaryAction(mainViewName string, firstLineIdx int, lastLineIdx int) error {
|
||||
ref := self.context.GetSelectedRef()
|
||||
if ref == nil {
|
||||
return nil
|
||||
}
|
||||
refsRange := self.context.GetSelectedRefRangeForDiffFiles()
|
||||
|
||||
from, to := context.FromAndToForDiff(ref, refsRange)
|
||||
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from)
|
||||
canRebase := self.canRebase(ref, refsRange)
|
||||
|
||||
return togglePatchFromFocusedMainView(self.c, mainViewName, firstLineIdx, lastLineIdx,
|
||||
from, to, reverse, canRebase,
|
||||
func() {
|
||||
self.c.OnUIThread(func() error {
|
||||
self.c.PostRefreshUpdate(self.context)
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SwitchToDiffFilesController) canEnter() *types.DisabledReason {
|
||||
|
|
|
|||
|
|
@ -95,18 +95,10 @@ type IBaseContext interface {
|
|||
// that the generic ListController can be specialized by view-specific controllers.
|
||||
// We'll need to think of a better way to do this.
|
||||
AddOnDoubleClickFn(func() error)
|
||||
// Likewise for the focused main view: we need this to communicate between a
|
||||
// side panel controller and the focused main view controller.
|
||||
AddOnClickFocusedMainViewFn(func(mainViewName string, clickedLineIdx int) error)
|
||||
// And for staging the selected line(s) directly from the focused main view
|
||||
// (space), delegated to the side panel that owns the diff being shown. The
|
||||
// inclusive view-line range is the current selection (a single line, a range, or
|
||||
// a hunk).
|
||||
AddOnStageFocusedMainViewFn(func(mainViewName string, firstLineIdx int, lastLineIdx int) error)
|
||||
// And for toggling the selected line(s) into/out of the custom patch from the
|
||||
// focused main view (space), when the panel beneath builds a patch rather than
|
||||
// staging.
|
||||
AddOnTogglePatchFocusedMainViewFn(func(mainViewName string, firstLineIdx int, lastLineIdx int) error)
|
||||
// Likewise for the focused main view: this is how a side panel controller exposes
|
||||
// the actions its diff supports there (diving in, staging, patch toggling, …) to
|
||||
// the focused main view controller. nil for panels with no such actions.
|
||||
AddFocusedMainViewActions(FocusedMainViewActions)
|
||||
// Adding on to the above, this is so that a list-specific handler can register
|
||||
// a hook for doing additional click handling
|
||||
AddOnClickFn(func(opts gocui.ViewMouseBindingOpts) error)
|
||||
|
|
@ -187,16 +179,38 @@ type DiffableContext interface {
|
|||
// main view shows a unified diff — files, local commits, sub-commits, reflog,
|
||||
// stash, and commit files — as opposed to a commit log or other non-diff content
|
||||
// (branches, tags, status, …). It is distinct from DiffableContext, which is
|
||||
// about producing a diff between two refs for the diff menu. This is the signal
|
||||
// for whether to show a selection in the focused main view: a selection is only
|
||||
// meaningful where there are diff lines to act on (stage, edit, jump by hunk,
|
||||
// open in a pull request).
|
||||
// about producing a diff between two refs for the diff menu. Implementing it is
|
||||
// the signal for whether to show a selection in the focused main view: a selection
|
||||
// is only meaningful where there are diff lines to act on (stage, edit, jump by
|
||||
// hunk, open in a pull request). The returned type additionally classifies what the
|
||||
// primary action (space) does there.
|
||||
type DiffMainViewContext interface {
|
||||
Context
|
||||
|
||||
IsDiffMainViewContext()
|
||||
GetDiffMainViewType() DiffMainViewType
|
||||
}
|
||||
|
||||
// DiffMainViewType classifies what the primary action (space) does in a side panel's
|
||||
// focused main view — and, for DiffMainViewTypePatchBuilding, that the focused main
|
||||
// view shows the inclusion gutter (it marks which change lines are in the patch, so
|
||||
// it's meaningful only beneath a patch-building panel, and even then only while a
|
||||
// patch is active).
|
||||
type DiffMainViewType int
|
||||
|
||||
const (
|
||||
// DiffMainViewTypeNone: a diff is shown — so the focused main view still has a
|
||||
// selection to edit, jump by hunk, or open in a pull request — but the primary
|
||||
// action does nothing. Currently the reflog (building a patch from it is a deferred
|
||||
// gap).
|
||||
DiffMainViewTypeNone DiffMainViewType = iota
|
||||
// DiffMainViewTypeStaging: the primary action stages/unstages into the working tree
|
||||
// (the files panel).
|
||||
DiffMainViewTypeStaging
|
||||
// DiffMainViewTypePatchBuilding: the primary action toggles the selection into a
|
||||
// custom patch (the commit files / commits / sub-commits / stash panels).
|
||||
DiffMainViewTypePatchBuilding
|
||||
)
|
||||
|
||||
type IListContext interface {
|
||||
Context
|
||||
|
||||
|
|
@ -334,26 +348,31 @@ type HasKeybindings interface {
|
|||
// decides not to do anything with the click.
|
||||
GetOnClick() func(opts gocui.ViewMouseBindingOpts) error
|
||||
|
||||
// Implement this in a side-panel controller to get called when there's a click in the main view
|
||||
// that belongs to your panel while the main view is already focused.
|
||||
GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error
|
||||
// Implement this in a side-panel controller to expose the actions its diff
|
||||
// supports when shown in the focused main view (see FocusedMainViewActions).
|
||||
// Return nil for a panel whose diff offers none.
|
||||
GetFocusedMainViewActions() FocusedMainViewActions
|
||||
}
|
||||
|
||||
// Implement this in a side-panel controller to stage/unstage the selected diff
|
||||
// line(s) when the user presses space in the focused main view. The inclusive
|
||||
// view-line range is the current selection (a single line, a range, or a hunk).
|
||||
// The handler re-renders the diff and re-establishes the selection itself
|
||||
// (staging/unstaging can move the acted-on side to the other pane, which the
|
||||
// handler then focuses). Return a nil func to do nothing.
|
||||
GetOnStageFocusedMainView() func(mainViewName string, firstLineIdx int, lastLineIdx int) error
|
||||
// FocusedMainViewActions is the set of actions a side panel offers on its diff while
|
||||
// that diff is shown in the focused main view. The focused main view controller owns
|
||||
// the keybindings and the selection mechanics and dispatches to whichever panel is
|
||||
// beneath it; what each action means is the panel's business — the working-tree files
|
||||
// panel stages, the commit panels toggle the selection into a custom patch. The
|
||||
// inclusive view-line range passed to the action methods is the current selection (a
|
||||
// single line, a range, or a hunk). mainViewName identifies which of the two main
|
||||
// panes the user acted in.
|
||||
type FocusedMainViewActions interface {
|
||||
// OnClick dives into staging / patch-building for the clicked line, the same way a
|
||||
// click in the panel's own view does. Also used by enter (on the selected line).
|
||||
OnClick(mainViewName string, clickedLineIdx int) error
|
||||
|
||||
// Implement this in a side-panel controller to toggle the selected diff line(s)
|
||||
// into or out of the custom patch when the user presses space in the focused main
|
||||
// view. It is the patch-building counterpart of GetOnStageFocusedMainView: the
|
||||
// commit's diff is unchanged by the toggle (only the inclusion set changes), so
|
||||
// unlike staging it does its work synchronously and the focused main view does
|
||||
// nothing further. The inclusive view-line range is the current selection. Return
|
||||
// a nil func to do nothing.
|
||||
GetOnTogglePatchFocusedMainView() func(mainViewName string, firstLineIdx int, lastLineIdx int) error
|
||||
// PrimaryAction acts on the selected diff line(s) when the user presses space:
|
||||
// stage/unstage for the working-tree files panel, toggle into/out of the custom
|
||||
// patch for the commit panels. The handler re-renders the diff and re-establishes
|
||||
// the selection itself (staging can move the acted-on side to the other pane,
|
||||
// which the handler then focuses).
|
||||
PrimaryAction(mainViewName string, firstLineIdx int, lastLineIdx int) error
|
||||
}
|
||||
|
||||
type IController interface {
|
||||
|
|
|
|||
Loading…
Reference in a new issue