summaryrefslogtreecommitdiff
path: root/src/tmux.go
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2024-05-10 01:40:56 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2024-05-18 17:08:36 +0900
commit83b603390683d49ff75b72d142b4dba4b5186d73 (patch)
tree103394f2ffab559d08528ed50a342f80c61d392b /src/tmux.go
parent01e7668915c4e3cf8c9eeca283d41beac924fe1f (diff)
downloadfzf-83b603390683d49ff75b72d142b4dba4b5186d73.tar.gz
Add --tmux option to replace fzf-tmux script
Diffstat (limited to 'src/tmux.go')
-rw-r--r--src/tmux.go149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/tmux.go b/src/tmux.go
new file mode 100644
index 00000000..ea1816a5
--- /dev/null
+++ b/src/tmux.go
@@ -0,0 +1,149 @@
+package fzf
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "github.com/junegunn/fzf/src/tui"
+ "github.com/junegunn/fzf/src/util"
+)
+
+func escapeSingleQuote(str string) string {
+ return "'" + strings.ReplaceAll(str, "'", "'\\''") + "'"
+}
+
+func runTmux(args []string, opts *Options) (int, error) {
+ ns := time.Now().UnixNano()
+
+ output := filepath.Join(os.TempDir(), fmt.Sprintf("fzf-tmux-output-%d", ns))
+ if err := mkfifo(output, 0666); err != nil {
+ return ExitError, err
+ }
+ defer os.Remove(output)
+
+ // Find fzf executable
+ fzf := "fzf"
+ if found, err := os.Executable(); err == nil {
+ fzf = found
+ }
+
+ // Prepare arguments
+ args = append([]string{"--bind=ctrl-z:ignore"}, args...)
+ if opts.BorderShape == tui.BorderUndefined {
+ args = append(args, "--border")
+ }
+ args = append(args, "--no-height")
+ args = append(args, "--no-tmux")
+ argStr := ""
+ for _, arg := range args {
+ // %q formatting escapes $'foo\nbar' to "foo\nbar"
+ argStr += " " + escapeSingleQuote(arg)
+ }
+
+ // Build command
+ var command string
+ if opts.Input == nil && util.IsTty() {
+ command = fmt.Sprintf(`%q%s > %q`, fzf, argStr, output)
+ } else {
+ input := filepath.Join(os.TempDir(), fmt.Sprintf("fzf-tmux-input-%d", ns))
+ if err := mkfifo(input, 0644); err != nil {
+ return ExitError, err
+ }
+ defer os.Remove(input)
+
+ go func() {
+ inputFile, err := os.OpenFile(input, os.O_WRONLY, 0)
+ if err != nil {
+ return
+ }
+ if opts.Input == nil {
+ io.Copy(inputFile, os.Stdin)
+ } else {
+ for item := range opts.Input {
+ fmt.Fprint(inputFile, item+opts.PrintSep)
+ }
+ }
+ inputFile.Close()
+ }()
+
+ command = fmt.Sprintf(`%q%s < %q > %q`, fzf, argStr, input, output)
+ }
+
+ // Get current directory
+ dir, err := os.Getwd()
+ if err != nil {
+ dir = "."
+ }
+
+ // Set tmux options for popup placement
+ // C Both The centre of the terminal
+ // R -x The right side of the terminal
+ // P Both The bottom left of the pane
+ // M Both The mouse position
+ // W Both The window position on the status line
+ // S -y The line above or below the status line
+ tmuxArgs := []string{"display-popup", "-E", "-B", "-d", dir}
+ switch opts.Tmux.position {
+ case posUp:
+ tmuxArgs = append(tmuxArgs, "-xC", "-y0")
+ case posDown:
+ tmuxArgs = append(tmuxArgs, "-xC", "-yS")
+ case posLeft:
+ tmuxArgs = append(tmuxArgs, "-x0", "-yC")
+ case posRight:
+ tmuxArgs = append(tmuxArgs, "-xR", "-yC")
+ case posCenter:
+ tmuxArgs = append(tmuxArgs, "-xC", "-yC")
+ }
+ tmuxArgs = append(tmuxArgs, "-w"+opts.Tmux.width.String())
+ tmuxArgs = append(tmuxArgs, "-h"+opts.Tmux.height.String())
+
+ // To ensure that the options are processed by a POSIX-compliant shell,
+ // we need to write the command to a temporary file and execute it with sh.
+ exports := os.Environ()
+ for idx, pairStr := range exports {
+ pair := strings.SplitN(pairStr, "=", 2)
+ exports[idx] = fmt.Sprintf("export %s=%s", pair[0], escapeSingleQuote(pair[1]))
+ }
+ temp := writeTemporaryFile(append(exports, command), "\n")
+ defer os.Remove(temp)
+ tmuxArgs = append(tmuxArgs, "sh", temp)
+
+ // Take the output
+ go func() {
+ outputFile, err := os.OpenFile(output, os.O_RDONLY, 0)
+ if err != nil {
+ return
+ }
+ if opts.Output == nil {
+ io.Copy(os.Stdout, outputFile)
+ } else {
+ reader := bufio.NewReader(outputFile)
+ sep := opts.PrintSep[0]
+ for {
+ item, err := reader.ReadString(sep)
+ if err != nil {
+ break
+ }
+ opts.Output <- item
+ }
+ }
+
+ outputFile.Close()
+ }()
+
+ cmd := exec.Command("tmux", tmuxArgs...)
+ if err := cmd.Run(); err != nil {
+ if exitError, ok := err.(*exec.ExitError); ok {
+ return exitError.ExitCode(), err
+ }
+ }
+
+ return ExitOk, nil
+}