diff --git a/docs/Custom_Command_Keybindings.md b/docs/Custom_Command_Keybindings.md index dd7c11af78d..432625693ec 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 abb7cb6eebe..3ac1425fbdb 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 6f39c5f8cd2..a0e486f13dd 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 49a6e6e356e..2948f2eda5e 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 00000000000..0dda1954604 --- /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 c1038ee2a0a..64123ff05be 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,