Skip to content

Commit c72be6c

Browse files
authored
Allow using shell aliases in interactive custom commands (jesseduffield#3793)
- **PR Description** When executing an interactive custom command, use the user's shell rather than "bash", and pass the -i flag. This makes it possible to use shell aliases or shell functions which are not available in non-interactive shells. In previous attempts to solve this, concerns were brought up: [this one](jesseduffield#2096 (comment)) is addressed by using the interactive shell only for custom commands but not anything else. [This one](jesseduffield#2096 (comment)) is a little dubious and unconfirmed, so I'm not very worried about it. Supersedes jesseduffield#2096 and jesseduffield#3299. Fixes jesseduffield#770, jesseduffield#899, and jesseduffield#1642.
2 parents da94ee7 + 5a30494 commit c72be6c

File tree

7 files changed

+58
-26
lines changed

7 files changed

+58
-26
lines changed

pkg/commands/git_cmd_obj_builder.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj {
3838
return self.innerBuilder.NewShell(cmdStr).AddEnvVars(defaultEnvVar)
3939
}
4040

41+
func (self *gitCmdObjBuilder) NewInteractiveShell(cmdStr string) oscommands.ICmdObj {
42+
return self.innerBuilder.NewInteractiveShell(cmdStr).AddEnvVars(defaultEnvVar)
43+
}
44+
4145
func (self *gitCmdObjBuilder) Quote(str string) string {
4246
return self.innerBuilder.Quote(str)
4347
}

pkg/commands/oscommands/cmd_obj_builder.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ type ICmdObjBuilder interface {
1414
New(args []string) ICmdObj
1515
// NewShell takes a string like `git commit` and returns an executable shell command for it e.g. `sh -c 'git commit'`
1616
NewShell(commandStr string) ICmdObj
17+
// Like NewShell, but uses the user's shell rather than "bash", and passes -i to it
18+
NewInteractiveShell(commandStr string) ICmdObj
1719
// 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.
1820
Quote(str string) string
1921
}
@@ -43,24 +45,33 @@ func (self *CmdObjBuilder) NewWithEnviron(args []string, env []string) ICmdObj {
4345
}
4446

4547
func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj {
46-
var quotedCommand string
48+
quotedCommand := self.quotedCommandString(commandStr)
49+
cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand))
50+
51+
return self.New(cmdArgs)
52+
}
53+
54+
func (self *CmdObjBuilder) NewInteractiveShell(commandStr string) ICmdObj {
55+
quotedCommand := self.quotedCommandString(commandStr)
56+
cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s %s", self.platform.InteractiveShell, self.platform.InteractiveShellArg, self.platform.ShellArg, quotedCommand))
57+
58+
return self.New(cmdArgs)
59+
}
60+
61+
func (self *CmdObjBuilder) quotedCommandString(commandStr string) string {
4762
// Windows does not seem to like quotes around the command
4863
if self.platform.OS == "windows" {
49-
quotedCommand = strings.NewReplacer(
64+
return strings.NewReplacer(
5065
"^", "^^",
5166
"&", "^&",
5267
"|", "^|",
5368
"<", "^<",
5469
">", "^>",
5570
"%", "^%",
5671
).Replace(commandStr)
57-
} else {
58-
quotedCommand = self.Quote(commandStr)
5972
}
6073

61-
cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand))
62-
63-
return self.New(cmdArgs)
74+
return self.Quote(commandStr)
6475
}
6576

6677
func (self *CmdObjBuilder) CloneWithNewRunner(decorate func(ICmdObjRunner) ICmdObjRunner) *CmdObjBuilder {

pkg/commands/oscommands/dummies.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,13 @@ func NewDummyCmdObjBuilder(runner ICmdObjRunner) *CmdObjBuilder {
5151
}
5252

5353
var dummyPlatform = &Platform{
54-
OS: "darwin",
55-
Shell: "bash",
56-
ShellArg: "-c",
57-
OpenCommand: "open {{filename}}",
58-
OpenLinkCommand: "open {{link}}",
54+
OS: "darwin",
55+
Shell: "bash",
56+
InteractiveShell: "bash",
57+
ShellArg: "-c",
58+
InteractiveShellArg: "-i",
59+
OpenCommand: "open {{filename}}",
60+
OpenLinkCommand: "open {{link}}",
5961
}
6062

6163
func NewDummyOSCommandWithRunner(runner *FakeCmdObjRunner) *OSCommand {

pkg/commands/oscommands/os.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@ type OSCommand struct {
3535

3636
// Platform stores the os state
3737
type Platform struct {
38-
OS string
39-
Shell string
40-
ShellArg string
41-
OpenCommand string
42-
OpenLinkCommand string
38+
OS string
39+
Shell string
40+
InteractiveShell string
41+
ShellArg string
42+
InteractiveShellArg string
43+
OpenCommand string
44+
OpenLinkCommand string
4345
}
4446

4547
// NewOSCommand os command runner

pkg/commands/oscommands/os_default_platform.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,26 @@
44
package oscommands
55

66
import (
7+
"os"
78
"runtime"
89
)
910

1011
func GetPlatform() *Platform {
1112
return &Platform{
12-
OS: runtime.GOOS,
13-
Shell: "bash",
14-
ShellArg: "-c",
15-
OpenCommand: "open {{filename}}",
16-
OpenLinkCommand: "open {{link}}",
13+
OS: runtime.GOOS,
14+
Shell: "bash",
15+
InteractiveShell: getUserShell(),
16+
ShellArg: "-c",
17+
InteractiveShellArg: "-i",
18+
OpenCommand: "open {{filename}}",
19+
OpenLinkCommand: "open {{link}}",
1720
}
1821
}
22+
23+
func getUserShell() string {
24+
if shell := os.Getenv("SHELL"); shell != "" {
25+
return shell
26+
}
27+
28+
return "bash"
29+
}

pkg/commands/oscommands/os_windows.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package oscommands
22

33
func GetPlatform() *Platform {
44
return &Platform{
5-
OS: "windows",
6-
Shell: "cmd",
7-
ShellArg: "/c",
5+
OS: "windows",
6+
Shell: "cmd",
7+
InteractiveShell: "cmd",
8+
ShellArg: "/c",
9+
InteractiveShellArg: "",
810
}
911
}

pkg/gui/controllers/custom_command_action.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func (self *CustomCommandAction) Call() error {
3131

3232
self.c.LogAction(self.c.Tr.Actions.CustomCommand)
3333
return self.c.RunSubprocessAndRefresh(
34-
self.c.OS().Cmd.NewShell(command),
34+
self.c.OS().Cmd.NewInteractiveShell(command),
3535
)
3636
},
3737
HandleDeleteSuggestion: func(index int) error {

0 commit comments

Comments
 (0)