diff --git a/docs/Custom_Command_Keybindings.md b/docs/Custom_Command_Keybindings.md index dd7c11af7..432625693 100644 --- a/docs/Custom_Command_Keybindings.md +++ b/docs/Custom_Command_Keybindings.md @@ -296,9 +296,7 @@ Here's an example using a command but not specifying anything else: so each line Your commands can contain placeholder strings using Go's [template syntax](https://jan.newmarch.name/golang/template/chapter-template.html). The template syntax is pretty powerful, letting you do things like conditionals if you want, but for the most part you'll simply want to be accessing the fields on the following objects: ``` -SelectedLocalCommit -SelectedReflogCommit -SelectedSubCommit +SelectedCommit SelectedFile SelectedPath SelectedLocalBranch @@ -311,6 +309,9 @@ SelectedWorktree CheckedOutBranch ``` +(For legacy reasons, `SelectedLocalCommit`, `SelectedReflogCommit`, and `SelectedSubCommit` are also available, but they are deprecated.) + + To see what fields are available on e.g. the `SelectedFile`, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/gui/services/custom_commands/models.go) (all the modelling lives in the same file). ## Keybinding collisions diff --git a/pkg/gui/context.go b/pkg/gui/context.go index abb7cb6ee..3ac1425fb 100644 --- a/pkg/gui/context.go +++ b/pkg/gui/context.go @@ -300,6 +300,18 @@ func (self *ContextMgr) IsCurrent(c types.Context) bool { return self.Current().GetKey() == c.GetKey() } +func (self *ContextMgr) IsCurrentOrParent(c types.Context) bool { + current := self.Current() + for current != nil { + if current.GetKey() == c.GetKey() { + return true + } + current = current.GetParentContext() + } + + return false +} + func (self *ContextMgr) AllFilterable() []types.IFilterableContext { var result []types.IFilterableContext diff --git a/pkg/gui/services/custom_commands/session_state_loader.go b/pkg/gui/services/custom_commands/session_state_loader.go index 6f39c5f8c..a0e486f13 100644 --- a/pkg/gui/services/custom_commands/session_state_loader.go +++ b/pkg/gui/services/custom_commands/session_state_loader.go @@ -164,9 +164,10 @@ func worktreeShimFromModelRemote(worktree *models.Worktree) *Worktree { // SessionState captures the current state of the application for use in custom commands type SessionState struct { - SelectedLocalCommit *Commit - SelectedReflogCommit *Commit - SelectedSubCommit *Commit + SelectedLocalCommit *Commit // deprecated, use SelectedCommit + SelectedReflogCommit *Commit // deprecated, use SelectedCommit + SelectedSubCommit *Commit // deprecated, use SelectedCommit + SelectedCommit *Commit SelectedFile *File SelectedPath string SelectedLocalBranch *Branch @@ -181,11 +182,24 @@ type SessionState struct { } func (self *SessionStateLoader) call() *SessionState { + selectedLocalCommit := commitShimFromModelCommit(self.c.Contexts().LocalCommits.GetSelected()) + selectedReflogCommit := commitShimFromModelCommit(self.c.Contexts().ReflogCommits.GetSelected()) + selectedSubCommit := commitShimFromModelCommit(self.c.Contexts().SubCommits.GetSelected()) + + selectedCommit := selectedLocalCommit + if self.c.Context().IsCurrentOrParent(self.c.Contexts().ReflogCommits) { + selectedCommit = selectedReflogCommit + } else if self.c.Context().IsCurrentOrParent(self.c.Contexts().SubCommits) { + selectedCommit = selectedSubCommit + } + return &SessionState{ SelectedFile: fileShimFromModelFile(self.c.Contexts().Files.GetSelectedFile()), SelectedPath: self.c.Contexts().Files.GetSelectedPath(), - SelectedLocalCommit: commitShimFromModelCommit(self.c.Contexts().LocalCommits.GetSelected()), - SelectedReflogCommit: commitShimFromModelCommit(self.c.Contexts().ReflogCommits.GetSelected()), + SelectedLocalCommit: selectedLocalCommit, + SelectedReflogCommit: selectedReflogCommit, + SelectedSubCommit: selectedSubCommit, + SelectedCommit: selectedCommit, SelectedLocalBranch: branchShimFromModelBranch(self.c.Contexts().Branches.GetSelected()), SelectedRemoteBranch: remoteBranchShimFromModelRemoteBranch(self.c.Contexts().RemoteBranches.GetSelected()), SelectedRemote: remoteShimFromModelRemote(self.c.Contexts().Remotes.GetSelected()), @@ -193,7 +207,6 @@ func (self *SessionStateLoader) call() *SessionState { SelectedStashEntry: stashEntryShimFromModelRemote(self.c.Contexts().Stash.GetSelected()), SelectedCommitFile: commitFileShimFromModelRemote(self.c.Contexts().CommitFiles.GetSelectedFile()), SelectedCommitFilePath: self.c.Contexts().CommitFiles.GetSelectedPath(), - SelectedSubCommit: commitShimFromModelCommit(self.c.Contexts().SubCommits.GetSelected()), SelectedWorktree: worktreeShimFromModelRemote(self.c.Contexts().Worktrees.GetSelected()), CheckedOutBranch: branchShimFromModelBranch(self.refsHelper.GetCheckedOutRef()), } diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go index 49a6e6e35..2948f2eda 100644 --- a/pkg/gui/types/context.go +++ b/pkg/gui/types/context.go @@ -284,6 +284,7 @@ type IContextMgr interface { CurrentSide() Context CurrentPopup() []Context IsCurrent(c Context) bool + IsCurrentOrParent(c Context) bool ForEach(func(Context)) AllList() []IListContext AllFilterable() []IFilterableContext diff --git a/pkg/integration/tests/custom_commands/selected_commit.go b/pkg/integration/tests/custom_commands/selected_commit.go new file mode 100644 index 000000000..0dda19546 --- /dev/null +++ b/pkg/integration/tests/custom_commands/selected_commit.go @@ -0,0 +1,67 @@ +package custom_commands + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var SelectedCommit = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Use the {{ .SelectedCommit }} template variable in different contexts", + ExtraCmdArgs: []string{}, + Skip: false, + SetupRepo: func(shell *Shell) { + shell.CreateNCommits(3) + }, + SetupConfig: func(cfg *config.AppConfig) { + cfg.UserConfig.CustomCommands = []config.CustomCommand{ + { + Key: "X", + Context: "global", + Command: "printf '%s' '{{ .SelectedCommit.Name }}' > file.txt", + }, + } + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + // Select different commits in each of the commit views + t.Views().Commits().Focus(). + NavigateToLine(Contains("commit 01")) + t.Views().ReflogCommits().Focus(). + NavigateToLine(Contains("commit 02")) + t.Views().Branches().Focus(). + Lines(Contains("master").IsSelected()). + PressEnter() + t.Views().SubCommits().IsFocused(). + NavigateToLine(Contains("commit 03")) + + // SubCommits + t.GlobalPress("X") + t.FileSystem().FileContent("file.txt", Equals("commit 03")) + + t.Views().SubCommits().PressEnter() + t.GlobalPress("X") + t.FileSystem().FileContent("file.txt", Equals("commit 03")) + + // ReflogCommits + t.Views().ReflogCommits().Focus() + t.GlobalPress("X") + t.FileSystem().FileContent("file.txt", Equals("commit: commit 02")) + + t.Views().ReflogCommits().PressEnter() + t.GlobalPress("X") + t.FileSystem().FileContent("file.txt", Equals("commit: commit 02")) + + // LocalCommits + t.Views().Commits().Focus() + t.GlobalPress("X") + t.FileSystem().FileContent("file.txt", Equals("commit 01")) + + t.Views().Commits().PressEnter() + t.GlobalPress("X") + t.FileSystem().FileContent("file.txt", Equals("commit 01")) + + // None of these + t.Views().Files().Focus() + t.GlobalPress("X") + t.FileSystem().FileContent("file.txt", Equals("commit 01")) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index c1038ee2a..64123ff05 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -124,6 +124,7 @@ var tests = []*components.IntegrationTest{ custom_commands.MenuFromCommandsOutput, custom_commands.MultipleContexts, custom_commands.MultiplePrompts, + custom_commands.SelectedCommit, custom_commands.ShowOutputInPanel, custom_commands.SuggestionsCommand, custom_commands.SuggestionsPreset,