From 5a3049485c66ac8367d4d59d9f941b2d99cbcf6e Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 3 Aug 2024 11:03:16 +0200 Subject: [PATCH] Use an interactive shell for running custom commands Also, use the user's shell (from the SHELL env variable) instead of bash. Both of these together allow users to use their shell aliases or shell functions in the interactive command prompt. --- pkg/commands/git_cmd_obj_builder.go | 4 ++++ pkg/commands/oscommands/cmd_obj_builder.go | 9 ++++++++ pkg/commands/oscommands/dummies.go | 12 ++++++----- pkg/commands/oscommands/os.go | 12 ++++++----- .../oscommands/os_default_platform.go | 21 ++++++++++++++----- pkg/commands/oscommands/os_windows.go | 8 ++++--- pkg/gui/controllers/custom_command_action.go | 2 +- 7 files changed, 49 insertions(+), 19 deletions(-) diff --git a/pkg/commands/git_cmd_obj_builder.go b/pkg/commands/git_cmd_obj_builder.go index 21f300bb3..7cb4f1496 100644 --- a/pkg/commands/git_cmd_obj_builder.go +++ b/pkg/commands/git_cmd_obj_builder.go @@ -38,6 +38,10 @@ func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj { return self.innerBuilder.NewShell(cmdStr).AddEnvVars(defaultEnvVar) } +func (self *gitCmdObjBuilder) NewInteractiveShell(cmdStr string) oscommands.ICmdObj { + return self.innerBuilder.NewInteractiveShell(cmdStr).AddEnvVars(defaultEnvVar) +} + func (self *gitCmdObjBuilder) Quote(str string) string { return self.innerBuilder.Quote(str) } diff --git a/pkg/commands/oscommands/cmd_obj_builder.go b/pkg/commands/oscommands/cmd_obj_builder.go index c72f3c187..3e89ce102 100644 --- a/pkg/commands/oscommands/cmd_obj_builder.go +++ b/pkg/commands/oscommands/cmd_obj_builder.go @@ -14,6 +14,8 @@ type ICmdObjBuilder interface { New(args []string) ICmdObj // NewShell takes a string like `git commit` and returns an executable shell command for it e.g. `sh -c 'git commit'` NewShell(commandStr string) ICmdObj + // Like NewShell, but uses the user's shell rather than "bash", and passes -i to it + NewInteractiveShell(commandStr string) ICmdObj // Quote wraps a string in quotes with any necessary escaping applied. The reason for bundling this up with the other methods in this interface is that we basically always need to make use of this when creating new command objects. Quote(str string) string } @@ -49,6 +51,13 @@ func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj { return self.New(cmdArgs) } +func (self *CmdObjBuilder) NewInteractiveShell(commandStr string) ICmdObj { + quotedCommand := self.quotedCommandString(commandStr) + cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s %s", self.platform.InteractiveShell, self.platform.InteractiveShellArg, self.platform.ShellArg, quotedCommand)) + + return self.New(cmdArgs) +} + func (self *CmdObjBuilder) quotedCommandString(commandStr string) string { // Windows does not seem to like quotes around the command if self.platform.OS == "windows" { diff --git a/pkg/commands/oscommands/dummies.go b/pkg/commands/oscommands/dummies.go index 3f72a6f06..ab528782f 100644 --- a/pkg/commands/oscommands/dummies.go +++ b/pkg/commands/oscommands/dummies.go @@ -51,11 +51,13 @@ func NewDummyCmdObjBuilder(runner ICmdObjRunner) *CmdObjBuilder { } var dummyPlatform = &Platform{ - OS: "darwin", - Shell: "bash", - ShellArg: "-c", - OpenCommand: "open {{filename}}", - OpenLinkCommand: "open {{link}}", + OS: "darwin", + Shell: "bash", + InteractiveShell: "bash", + ShellArg: "-c", + InteractiveShellArg: "-i", + OpenCommand: "open {{filename}}", + OpenLinkCommand: "open {{link}}", } func NewDummyOSCommandWithRunner(runner *FakeCmdObjRunner) *OSCommand { diff --git a/pkg/commands/oscommands/os.go b/pkg/commands/oscommands/os.go index 7771dffba..7f31b5332 100644 --- a/pkg/commands/oscommands/os.go +++ b/pkg/commands/oscommands/os.go @@ -35,11 +35,13 @@ type OSCommand struct { // Platform stores the os state type Platform struct { - OS string - Shell string - ShellArg string - OpenCommand string - OpenLinkCommand string + OS string + Shell string + InteractiveShell string + ShellArg string + InteractiveShellArg string + OpenCommand string + OpenLinkCommand string } // NewOSCommand os command runner diff --git a/pkg/commands/oscommands/os_default_platform.go b/pkg/commands/oscommands/os_default_platform.go index fd4967d95..196e4d9f6 100644 --- a/pkg/commands/oscommands/os_default_platform.go +++ b/pkg/commands/oscommands/os_default_platform.go @@ -4,15 +4,26 @@ package oscommands import ( + "os" "runtime" ) func GetPlatform() *Platform { return &Platform{ - OS: runtime.GOOS, - Shell: "bash", - ShellArg: "-c", - OpenCommand: "open {{filename}}", - OpenLinkCommand: "open {{link}}", + OS: runtime.GOOS, + Shell: "bash", + InteractiveShell: getUserShell(), + ShellArg: "-c", + InteractiveShellArg: "-i", + OpenCommand: "open {{filename}}", + OpenLinkCommand: "open {{link}}", } } + +func getUserShell() string { + if shell := os.Getenv("SHELL"); shell != "" { + return shell + } + + return "bash" +} diff --git a/pkg/commands/oscommands/os_windows.go b/pkg/commands/oscommands/os_windows.go index 783f50518..32cd59edb 100644 --- a/pkg/commands/oscommands/os_windows.go +++ b/pkg/commands/oscommands/os_windows.go @@ -2,8 +2,10 @@ package oscommands func GetPlatform() *Platform { return &Platform{ - OS: "windows", - Shell: "cmd", - ShellArg: "/c", + OS: "windows", + Shell: "cmd", + InteractiveShell: "cmd", + ShellArg: "/c", + InteractiveShellArg: "", } } diff --git a/pkg/gui/controllers/custom_command_action.go b/pkg/gui/controllers/custom_command_action.go index 39777e70a..7b8cbc641 100644 --- a/pkg/gui/controllers/custom_command_action.go +++ b/pkg/gui/controllers/custom_command_action.go @@ -31,7 +31,7 @@ func (self *CustomCommandAction) Call() error { self.c.LogAction(self.c.Tr.Actions.CustomCommand) return self.c.RunSubprocessAndRefresh( - self.c.OS().Cmd.NewShell(command), + self.c.OS().Cmd.NewInteractiveShell(command), ) }, HandleDeleteSuggestion: func(index int) error {