diff --git a/src/constants.go b/src/constants.go index dd2e870e2e3..980e158318e 100644 --- a/src/constants.go +++ b/src/constants.go @@ -67,9 +67,9 @@ const ( ) const ( - ExitCancel = -1 ExitOk = 0 ExitNoMatch = 1 ExitError = 2 + ExitBecome = 126 ExitInterrupt = 130 ) diff --git a/src/options.go b/src/options.go index 0fc19ead909..13ccef9bda0 100644 --- a/src/options.go +++ b/src/options.go @@ -381,6 +381,7 @@ type Options struct { Input chan string Output chan string Tmux *tmuxOptions + TmuxScript string Bash bool Zsh bool Fish bool @@ -1882,6 +1883,10 @@ func parseOptions(opts *Options, allArgs []string) error { } case "--no-tmux": opts.Tmux = nil + case "--tmux-script": + if opts.TmuxScript, err = nextString(allArgs, &i, ""); err != nil { + return err + } case "-x", "--extended": opts.Extended = true case "-e", "--exact": diff --git a/src/terminal.go b/src/terminal.go index 5179b0c9b7e..418830c6efb 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -311,6 +311,7 @@ type Terminal struct { forcePreview bool clickHeaderLine int clickHeaderColumn int + tmuxScript string } type selectedItem struct { @@ -350,6 +351,7 @@ const ( reqPreviewDisplay reqPreviewRefresh reqPreviewDelayed + reqBecome reqQuit reqFatal ) @@ -781,6 +783,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor jumpLabels: opts.JumpLabels, printer: opts.Printer, printsep: opts.PrintSep, + tmuxScript: opts.TmuxScript, merger: EmptyMerger(0), selected: make(map[int32]selectedItem), reqBox: util.NewEventBox(), @@ -3297,6 +3300,9 @@ func (t *Terminal) Loop() error { return ExitOk }) return + case reqBecome: + exit(func() int { return ExitBecome }) + return case reqQuit: exit(func() int { return ExitInterrupt }) return @@ -3471,7 +3477,14 @@ func (t *Terminal) Loop() error { if t.history != nil { t.history.append(string(t.input)) } - t.executor.Become(tui.TtyIn(), t.environ(), command) + + if len(t.tmuxScript) > 0 { + data := strings.Join(append([]string{command}, t.environ()...), "\x00") + os.WriteFile(t.tmuxScript, []byte(data), 0600) + req(reqBecome) + } else { + t.executor.Become(tui.TtyIn(), t.environ(), command) + } } case actExecute, actExecuteSilent: t.executeCommand(a.a, false, a.t == actExecuteSilent, false, false) diff --git a/src/tmux.go b/src/tmux.go index ea1816a5647..5cc970faa37 100644 --- a/src/tmux.go +++ b/src/tmux.go @@ -2,6 +2,7 @@ package fzf import ( "bufio" + "errors" "fmt" "io" "os" @@ -45,6 +46,7 @@ func runTmux(args []string, opts *Options) (int, error) { // %q formatting escapes $'foo\nbar' to "foo\nbar" argStr += " " + escapeSingleQuote(arg) } + argStr += ` --tmux-script "$0"` // Build command var command string @@ -141,7 +143,26 @@ func runTmux(args []string, opts *Options) (int, error) { cmd := exec.Command("tmux", tmuxArgs...) if err := cmd.Run(); err != nil { if exitError, ok := err.(*exec.ExitError); ok { - return exitError.ExitCode(), err + code := exitError.ExitCode() + if code == ExitBecome { + data, err := os.ReadFile(temp) + if err != nil { + return ExitError, err + } + elems := strings.Split(string(data), "\x00") + if len(elems) < 1 { + return ExitError, errors.New("invalid become command") + } + command := elems[0] + env := []string{} + if len(elems) > 1 { + env = elems[1:] + } + os.Remove(temp) + executor := util.NewExecutor(opts.WithShell) + executor.Become(tui.TtyIn(), env, command) + } + return code, err } }