Add --with-shell for shelling out with different command and flags (#3746)

Close #3732
This commit is contained in:
Junegunn Choi
2024-04-27 18:36:37 +09:00
committed by GitHub
parent b86a967ee2
commit a4391aeedd
13 changed files with 198 additions and 132 deletions

View File

@@ -7,7 +7,6 @@ import (
"io"
"math"
"os"
"os/exec"
"os/signal"
"regexp"
"sort"
@@ -245,6 +244,7 @@ type Terminal struct {
listenUnsafe bool
borderShape tui.BorderShape
cleanExit bool
executor *util.Executor
paused bool
border tui.Window
window tui.Window
@@ -640,7 +640,7 @@ func evaluateHeight(opts *Options, termHeight int) int {
}
// NewTerminal returns new Terminal object
func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor) *Terminal {
input := trimQuery(opts.Query)
var delay time.Duration
if opts.Tac {
@@ -736,6 +736,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
previewLabel: nil,
previewLabelOpts: opts.PreviewLabel,
cleanExit: opts.ClearOnExit,
executor: executor,
paused: opts.Phony,
cycle: opts.Cycle,
headerVisible: true,
@@ -2522,6 +2523,7 @@ type replacePlaceholderParams struct {
allItems []*Item
lastAction actionType
prompt string
executor *util.Executor
}
func (t *Terminal) replacePlaceholder(template string, forcePlus bool, input string, list []*Item) string {
@@ -2535,6 +2537,7 @@ func (t *Terminal) replacePlaceholder(template string, forcePlus bool, input str
allItems: list,
lastAction: t.lastAction,
prompt: t.promptString,
executor: t.executor,
})
}
@@ -2595,7 +2598,7 @@ func replacePlaceholder(params replacePlaceholderParams) string {
case escaped:
return match
case match == "{q}" || match == "{fzf:query}":
return quoteEntry(params.query)
return params.executor.QuoteEntry(params.query)
case match == "{}":
replace = func(item *Item) string {
switch {
@@ -2608,13 +2611,13 @@ func replacePlaceholder(params replacePlaceholderParams) string {
case flags.file:
return item.AsString(params.stripAnsi)
default:
return quoteEntry(item.AsString(params.stripAnsi))
return params.executor.QuoteEntry(item.AsString(params.stripAnsi))
}
}
case match == "{fzf:action}":
return params.lastAction.Name()
case match == "{fzf:prompt}":
return quoteEntry(params.prompt)
return params.executor.QuoteEntry(params.prompt)
default:
// token type and also failover (below)
rangeExpressions := strings.Split(match[1:len(match)-1], ",")
@@ -2648,7 +2651,7 @@ func replacePlaceholder(params replacePlaceholderParams) string {
str = strings.TrimSpace(str)
}
if !flags.file {
str = quoteEntry(str)
str = params.executor.QuoteEntry(str)
}
return str
}
@@ -2688,7 +2691,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
return line
}
command := t.replacePlaceholder(template, forcePlus, string(t.input), list)
cmd := util.ExecCommand(command, false)
cmd := t.executor.ExecCommand(command, false)
cmd.Env = t.environ()
t.executing.Set(true)
if !background {
@@ -2965,7 +2968,7 @@ func (t *Terminal) Loop() {
if items[0] != nil {
_, query := t.Input()
command := t.replacePlaceholder(commandTemplate, false, string(query), items)
cmd := util.ExecCommand(command, true)
cmd := t.executor.ExecCommand(command, true)
env := t.environ()
if pwindowSize.Lines > 0 {
lines := fmt.Sprintf("LINES=%d", pwindowSize.Lines)
@@ -3372,27 +3375,21 @@ func (t *Terminal) Loop() {
valid, list := t.buildPlusList(a.a, false)
if valid {
command := t.replacePlaceholder(a.a, false, string(t.input), list)
shell := os.Getenv("SHELL")
if len(shell) == 0 {
shell = "sh"
}
shellPath, err := exec.LookPath(shell)
if err == nil {
t.tui.Close()
if t.history != nil {
t.history.append(string(t.input))
}
/*
FIXME: It is not at all clear why this is required.
The following command will report 'not a tty', unless we open
/dev/tty *twice* after closing the standard input for 'reload'
in Reader.terminate().
: | fzf --bind 'start:reload:ls' --bind 'enter:become:tty'
*/
tui.TtyIn()
util.SetStdin(tui.TtyIn())
syscall.Exec(shellPath, []string{shell, "-c", command}, os.Environ())
t.tui.Close()
if t.history != nil {
t.history.append(string(t.input))
}
/*
FIXME: It is not at all clear why this is required.
The following command will report 'not a tty', unless we open
/dev/tty *twice* after closing the standard input for 'reload'
in Reader.terminate().
while : | fzf --bind 'start:reload:ls' --bind 'load:become:tty'; do echo; done
*/
tui.TtyIn()
t.executor.Become(tui.TtyIn(), t.environ(), command)
}
case actExecute, actExecuteSilent:
t.executeCommand(a.a, false, a.t == actExecuteSilent, false, false)