├── utils.mksh ├── key-bindings.mksh ├── README.md ├── completion.mksh └── fzf-tmux.mksh /utils.mksh: -------------------------------------------------------------------------------- 1 | function __mksh_escape { 2 | print -nr -- "${1@/[\"-\$\&-*:-?[\\\`\{-\~${IFS-$' \t\n'}]/\\$KSH_MATCH}" 3 | } 4 | 5 | function __fzf_cmd { 6 | [ -n "$TMUX_PANE" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "$FZF_TMUX_OPTS" ]; } \ 7 | && print "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || print "fzf" 8 | } 9 | -------------------------------------------------------------------------------- /key-bindings.mksh: -------------------------------------------------------------------------------- 1 | fzf_mksh_abs="$(readlink -f $_)" 2 | fzf_mksh_dir="$(dirname $fzf_mksh_abs)" 3 | . "$fzf_mksh_dir/utils.mksh" 4 | unset fzf_mksh_abs fzf_mksh_dir 5 | 6 | if [[ $- = *i* ]]; then 7 | # >> 8 | 9 | function __fzf_select { 10 | local cmd 11 | cmd="${FZF_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) \ 12 | -prune \ 13 | -o -type f -print \ 14 | -o -type d -print \ 15 | -o -type l -print \ 16 | 2> /dev/null | cut -b3-"}" 17 | eval "$cmd" \ 18 | | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS --reverse --bind=ctrl-z:ignore $FZF_CTRL_T_OPTS -m" $(__fzf_cmd) "$@" \ 19 | | while read -r item; do 20 | print -nr -- "$(__mksh_escape "$item") " 21 | done 22 | } 23 | 24 | function __fzf_cd { 25 | local cmd dir 26 | cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) \ 27 | -prune \ 28 | -o -type d -print \ 29 | 2> /dev/null | cut -b3-"}" 30 | dir=$( 31 | eval "$cmd" \ 32 | | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS --reverse --bind=ctrl-z:ignore $FZF_ALT_C_OPTS +m" $(__fzf_cmd) 33 | ) \ 34 | && print -nr -- "cd $(__mksh_escape "$dir")" 35 | } 36 | 37 | function __fzf_history { 38 | local line cmd 39 | line=$( 40 | history 1 \ 41 | | sed '/^[0-9]/ s/^/\x00/; s/^ //' \ 42 | | tr '\n' $'\x01' \ 43 | | sed -z 's/\x01$//g; s/\x01/^J\x01/g' \ 44 | | grep -z -v "^[0-9]*\s*$" \ 45 | | LC_ALL=C sort -z -k2 -k1,1nr \ 46 | | uniq -z -f 1 \ 47 | | LC_ALL=C sort -z -nr \ 48 | | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort,ctrl-z:ignore $FZF_CTRL_R_OPTS +m --read0" $(__fzf_cmd) \ 49 | ) \ 50 | && cmd=$(print -nr -- "$line" | sed 's/^ *\([0-9]*\) //; s/\^J\x01/\x0A/g') \ 51 | && print -nr "$cmd" 52 | } 53 | 54 | if [[ ! -o vi ]]; then 55 | # CTRL-T - Paste the selected file path into the command line 56 | bind -m '^t'=' __fzf_select^e' 57 | 58 | # CTRL-R - Paste the selected command from history into the command line 59 | bind -m '^r'='^u__fzf_history^e' 60 | 61 | # ALT-C - cd into the selected directory 62 | bind -m 'c'='^u__fzf_cd^e^m' 63 | else 64 | # We'd usually use "\e" to enter vi-movement-mode so we can do our magic, 65 | # but this incurs a very noticeable delay of a half second or so, 66 | # because many other commands start with "\e". 67 | # Instead, we bind an unused key, "\C-x\C-a", 68 | # to also enter vi-movement-mode, 69 | # and then use that thereafter. 70 | # (We imagine that "\C-x\C-a" is relatively unlikely to be in use.) 71 | bind '"\C-x\C-a": vi-movement-mode' 72 | 73 | bind '"\C-x\C-e": shell-expand-line' 74 | bind '"\C-x\C-r": redraw-current-line' 75 | bind '"\C-x^": history-expand-line' 76 | 77 | # CTRL-T - Paste the selected file path into the command line 78 | # - FIXME: Selected items are attached to the end regardless of cursor position 79 | bind '"\C-t": "\C-x\C-a$a \C-x\C-addi$(__fzf_select__)\C-x\C-e\C-x\C-a0Px$a \C-x\C-r\C-x\C-axa "' 80 | bind -m vi-command '"\C-t": "i\C-t"' 81 | 82 | # CTRL-R - Paste the selected command from history into the command line 83 | bind '"\C-r": "\C-x\C-addi$(__fzf_history__)\C-x\C-e\C-x^\C-x\C-a$a\C-x\C-r"' 84 | bind -m vi-command '"\C-r": "i\C-r"' 85 | 86 | # ALT-C - cd into the selected directory 87 | bind '"\ec": "\C-x\C-addi$(__fzf_cd__)\C-x\C-e\C-x\C-r\C-m"' 88 | bind -m vi-command '"\ec": "ddi$(__fzf_cd__)\C-x\C-e\C-x\C-r\C-m"' 89 | fi 90 | 91 | # << 92 | fi 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fzf-mksh 2 | *fzf's key-bindings and tab completion for mksh* 3 | 4 | ## What it is 5 | 6 | [`fzf`](https://github.com/junegunn/fzf) provides `CTRL-T` (file selection), `ALT-C` (change to directory), `CTRL-R` (history search), and some bespoke tab completion support for bash and zsh (and partially for fish). This repo ports the three key-bindings to pure mksh. It also provides tab completion, but instead of doing a bespoke form of it meant to work alongside the builtin completion, this repo provides the ability to select candidates from mksh's built-in tab completion capabilities, instead of displaying them in a columnized form as mksh normally does. 7 | 8 | ## Installation 9 | 10 | You'll need mksh R53a or later for key-bindings support (requires the `evaluate-region` editing command), and a current development snapshot for tab completion (requires a functional `quote-region` editing command). 11 | 12 | Any reasonably modern version of `fzf` will do. 13 | 14 | 1. Clone this repo wherever you like, and optionally copy or symlink the `completion.mksh` and `key-bindings.mksh` files to any location of your choice. (I have mine symlinked at `~/.fzf/shell/`, which is where the analogous bash, zsh, and fish files were placed when I first installed `fzf` through the provided directions.) 15 | 2. Source the files in your current shell, or add them to your `~/.mkshrc` (what you write is the same either way). The first provides the three key-bindings, and the second provides tab completion. You can pick just one or the other if you like: 16 | ```sh 17 | . /path/to/key-bindings.mksh 18 | . /path/to/completion.mksh 19 | ``` 20 | 3. Set any of the fzf-specific environment variables that you'd like (see the `fzf` README [here](https://github.com/junegunn/fzf#environment-variables) and [here](https://github.com/junegunn/fzf#key-bindings-for-command-line)). The following are respected for now: 21 | ```sh 22 | $FZF_DEFAULT_OPTS 23 | $FZF_TMUX 24 | $FZF_TMUX_OPTS 25 | $FZF_TMUX_HEIGHT 26 | $FZF_CTRL_T_COMMAND 27 | $FZF_CTRL_T_OPTS 28 | $FZF_ALT_C_COMMAND 29 | $FZF_ALT_C_OPTS 30 | $FZF_CTRL_R_OPTS 31 | ``` 32 | 4. In the shell where these files are sourced, press `CTRL-T`, `ALT-C`, `CTRL-R`, or `` to your heart's content! All four key-bindings are already made for you in the relevant files. 33 | 34 | 4. Optionally, install `fzf-tmux.mksh` to replace the otherwise-used `fzf-tmux` script written in bash. In any case, you're good to go as long as it's on your `$PATH` and named `fzf-tmux`. You can do this either linking, moving, or copying `fzf-tmux.mksh` to you a directory on your `$PATH` that has higher precedence than the existing executable: 35 | ```sh 36 | ln -s /path/to/fzf-tmux.mksh /path/to/higher-precedence/directory/on/PATH/fzf-tmux 37 | ``` 38 | or by replacing the `fzf-tmux` executable you were previously using: 39 | ```sh 40 | mv /path/to/fzf-bindir/fzf-tmux /path/to/fzf-bindir/fzf-tmux.bash 41 | ln -s /path/to/fzf-tmux.mksh /path/to/fzf-bindir/fzf-tmux 42 | ``` 43 | 44 | ## Details 45 | 46 | ### Design choices 47 | The initial target of this project is to a) provide a pure-mksh experience for mksh+fzf users, b) provide the three key-bindings that the main repo provides for bash, and c) for any features that interact with built-in mksh capabilities, to replicate mksh's behavior as much as possible while simply providing the completion candidates via `fzf` instead of via the built-in interface. 48 | 49 | Porting `fzf-tmux` to mksh done, but hasn't been tested much, though it should work identically to the bash version provided by `fzf`. 50 | 51 | The three key-bindings are mostly feature-complete, although there are some small additions and optimizations to be made (see below). 52 | 53 | Completion should be working, but hasn't been tested much, and has some known bugs or incomplete features. For anyone not familiar with mksh, it provides two forms of completion: command completion and file completion. Command completion will provide executables in your `$PATH`, as well as some extras like builtins and a few other things: basically, anything that's valid at the start of a line. File completion will provide any type of file (directories, sockets, etc) that match the current word. Any unambiguous completion will automatically be entered, and at that point if there are mulitple candidates, a list will be displayed. This project, as said before, simply sends the candidates in that list to `fzf`. While mksh doesn't provide complex command completion e.g. for command arguments, it provides a deterministic, predictable, and comprehensible set of candidates. In the future, I plan to extend the completion capabilities of mksh through some of these mechanisms, but for now, work will focus on reaching feature parity and accuracy against built-in mksh completion. 54 | 55 | ### TODO 56 | - [ ] fix vim edit mode keybindings (currently untested and haven't been looked at in years) 57 | - [ ] tab completion is missing some candidates for command completion (i.e., things not found on your `$PATH`) 58 | - [ ] tab completion breaks in some cases if the file/folder in the path you're completing has spaces in it 59 | - [ ] tab completion isn't aware of `` ` ``, `$(`, or `./`, to trigger either command completion (first two cases) or file completion (last case) when it otherwise would trigger the other 60 | - [ ] add tab completion for some of the special term lists that are present in `fzf`, like environment variables & aliases, ssh/telnet hostnames, PIDs, etc. 61 | - [x] fix port of `fzf-tmux` to mksh (the `fzf-tmux.mksh` file in this repo has a race condition or some other error) 62 | - [ ] more options for completion, to deviate in possibly preferable ways from mksh's built-in behavior 63 | -------------------------------------------------------------------------------- /completion.mksh: -------------------------------------------------------------------------------- 1 | # TODO: 2 | # - something is up with escaping/quoting and the glob to get candidates 3 | # ls Tournament\ Machu/test<__fzf_complete> -> nothing, should be test.txt^ntest2.txt 4 | # - ./ at beginning of line (i.e. for the first word) 5 | # 6 | # CASES: 7 | # - {unambig_complete, ambig_complete, no_complete} x 8 | # {start_of_word, middle_of_word, end_of_word, empty_word} x 9 | # {first_word, middle_word, last_word} 10 | # - various numbers of spaces before and after the above cases 11 | # - spaces in various parts of input, as well as returned text 12 | # - "./" as first word (should match files, never commands) 13 | # - "$(" or "`" before cursor (should match commands, never files) 14 | # - other symbols: ', ", >, <, (, ), ./, shell syntax..., possibly &, etc. 15 | # - mksh's completion is kinda broken from within quotes... 16 | # - globbing -- handle it all! 17 | 18 | fzf_mksh_abs="$(readlink -f $_)" 19 | fzf_mksh_dir="$(dirname $fzf_mksh_abs)" 20 | . "$fzf_mksh_dir/utils.mksh" 21 | unset fzf_mksh_abs fzf_mksh_dir 22 | 23 | function __fzf_get_completion_candidates { 24 | local args 25 | set -A args -- "$@" 26 | local matches 27 | # file completion 28 | if [ ${#args[@]} -gt 1 ]; then 29 | last_arg="${args[${#args[@]}-1]}" 30 | set +o markdirs 31 | if [[ $last_arg = *[*?]* ]]; then 32 | eval "set -A matches -- $last_arg" 33 | else 34 | set -A matches -- "$last_arg"* 35 | fi 36 | for match in "${matches[@]}"; do 37 | if [[ "${match}" != "$last_arg*" ]]; then 38 | if [[ -d "${match}" ]]; then 39 | print -r -- "${match##*/}/" 40 | else 41 | print -r -- "${match##*/}" 42 | fi 43 | fi 44 | done 45 | # command completion 46 | elif [ "${#args[@]}" -eq 1 ] || [ "${#args[@]}" -eq 0 ]; then 47 | local paths 48 | set -o noglob; IFS=:; set -A paths -- $PATH; IFS=$' \t\n'; set +o noglob 49 | for path in "${paths[@]}"; do 50 | matches+=(${path:-.}/"$@"*) 51 | done 52 | for match in "${matches[@]}"; do 53 | if [[ "${match##*/}" != "$@*" ]]; then 54 | print -r -- "${match##*/}" 55 | fi 56 | done | LC_ALL=C sort -u 57 | fi 58 | } 59 | 60 | function __fzf_complete { 61 | local comp 62 | local curr_0 63 | local curr_1 64 | local comp_y 65 | local comp_n 66 | set -A comp -- $1 67 | if [ "${1: -1}" = ' ' ]; then 68 | comp+=('') 69 | fi 70 | curr_0="$2" 71 | curr_1="$3" 72 | # comp_y is the current word post-completion 73 | comp_y="${comp[${#comp[@]}-1]}" 74 | # comp_n is the current word sans-completion 75 | comp_n="${curr_0}${curr_1}" 76 | if [ ${#comp_y} -ne ${#comp_n} ]; then 77 | print -r -- "$1" 78 | else 79 | local sel 80 | local res 81 | local fzf_prompt_query_args 82 | fzf_prompt_query_args="--prompt=\"${FZF_COMPLETION_PROMPT_CHAR:->} $1\"" 83 | sel=$(__fzf_get_completion_candidates "${comp[@]}") 84 | if [[ "$sel" = '' ]]; then 85 | print -rn -- "${1%${comp_y}}$2$3" 86 | else 87 | local fzf_compl_args 88 | fzf_compl_args="--reverse --bind=ctrl-z:ignore --no-info -0 -1 +m" 89 | res=$( 90 | print -n "$sel" | 91 | FZF_DEFAULT_OPTS="$FZF_DEFAULT_OPTS $fzf_compl_args $fzf_prompt_query_args" $(__fzf_cmd) 92 | ) 93 | if [[ $res = *[![:space:]]* ]]; then 94 | print -rn -- "${1%${comp_y##*/}}$(__mksh_escape "$res")" 95 | if [[ ! "${res: -1}" = '/' ]]; then 96 | print -rn -- ' ' 97 | fi 98 | else 99 | print -rn -- "$1" 100 | fi 101 | print -r -- '' 102 | fi 103 | fi 104 | } 105 | 106 | if [[ ! -o vi ]]; then 107 | # we need kill-region to be bound to something 108 | bind '^W'='kill-region' 109 | # '^v^a' : special mark ^A 110 | # '^xD F^w^y ^y' : move to left of mark, copy "current word, right of cursor", paste 111 | # '^x^x^xC^h' : go back to beginning of word and delete the pasted special mark 112 | # '^x^xQ' : quote the "current word, right of cursor" pasted text ($3) 113 | # '^v^b' : special mark ^B 114 | # '^a' : go back to the original cursor position (special mark ^A) 115 | # '^xC B^x^x^w^y' : go to right of special mark, copy "current word, left of cursor, paste 116 | # '^xDF  ^y^hQ' : paste a quoted copy of the "current word, left of cursor" ($2) 117 | # '^a^xC^h' : go to original cursor position and try to complete 118 | # '^v^a' : add the ^A mark we just deleted back; had to remove it to do completion 119 | # '^xDF ' : go to end of current word; if word was '', the ^A mark is necessary 120 | # '^a^xC^h^x^x ' : go back to the ^A mark, remove it, go back again, and set a mark 121 | # '^a^x^xQ' : quote from the beginning of the line to the end of the current word ($1) 122 | # '^a __fzf_complete ': go to beginning of line, set a mark, type func name 123 | # '^b^xC^h^e' : go back to ^B mark and delete it; evaluate 124 | # '^a^xC^h' : __fzf_complete adds a mark indicating where the cursor needs to be 125 | bind -m '^I'='^v^a^xD F^w^y ^y^x^x^xC^h^x^xQ^v^b^a^xC B^x^x^w^y^xDF  ^y^hQ^a^xC^h^v^a^xDF ^a^xC^h^x^x ^a^x^xQ^a __fzf_complete ^b^xC^h^e^a^xC^h' 126 | fi 127 | -------------------------------------------------------------------------------- /fzf-tmux.mksh: -------------------------------------------------------------------------------- 1 | #!/bin/mksh 2 | # fzf-tmux: starts fzf in a tmux pane 3 | # usage: fzf-tmux [LAYOUT OPTIONS] [--] [FZF OPTIONS] 4 | 5 | fail() { 6 | echo >&2 "$1" 7 | exit 2 8 | } 9 | 10 | fzf="$(command -v fzf 2> /dev/null)" || fzf="$(dirname "$0")/fzf" 11 | [[ -x "$fzf" ]] || fail 'fzf executable not found' 12 | 13 | tmux_args=() 14 | args=() 15 | opt="" 16 | skip="" 17 | close="" 18 | term="" 19 | [[ -n "$LINES" ]] && lines=$LINES || lines=$(tput lines) || lines=$(tmux display-message -p "#{pane_height}") 20 | [[ -n "$COLUMNS" ]] && columns=$COLUMNS || columns=$(tput cols) || columns=$(tmux display-message -p "#{pane_width}") 21 | 22 | help() { 23 | echo >&2 'usage: fzf-tmux [LAYOUT OPTIONS] [--] [FZF OPTIONS] 24 | 25 | LAYOUT OPTIONS: 26 | (default layout: -d 50%) 27 | 28 | Popup window (requires tmux 3.2 or above): 29 | -p [WIDTH[%][,HEIGHT[%]]] (default: 50%) 30 | -w WIDTH[%] 31 | -h HEIGHT[%] 32 | -x COL 33 | -y ROW 34 | 35 | Split pane: 36 | -u [HEIGHT[%]] Split above (up) 37 | -d [HEIGHT[%]] Split below (down) 38 | -l [WIDTH[%]] Split left 39 | -r [WIDTH[%]] Split right 40 | ' 41 | exit 42 | } 43 | 44 | while [[ $# -gt 0 ]]; do 45 | arg="$1" 46 | shift 47 | [[ -z "$skip" ]] && case "$arg" in 48 | -) 49 | term=1 50 | ;; 51 | --help) 52 | help 53 | ;; 54 | --version) 55 | echo "fzf-tmux (with fzf $("$fzf" --version))" 56 | exit 57 | ;; 58 | -p* | -w* | -h* | -x* | -y* | -d* | -u* | -r* | -l*) 59 | if [[ "$arg" = -@([pwhxy])* ]]; then 60 | [[ "$opt" = *'-K -E'* ]] || opt="-K -E" 61 | elif [[ "$arg" = ?@([lr])* ]]; then 62 | opt="-h" 63 | if [[ "$arg" = ?'l'* ]]; then 64 | opt="$opt -b" 65 | fi 66 | else 67 | opt="" 68 | if [[ "$arg" = ?'u'* ]]; then 69 | opt="$opt -b" 70 | fi 71 | fi 72 | if [[ ${#arg} -gt 2 ]]; then 73 | size="${arg:2}" 74 | else 75 | if [[ "$1" = +([0-9%,]) ]] || [[ "$1" = [[:upper:]] ]]; then 76 | size="$1" 77 | shift 78 | else 79 | continue 80 | fi 81 | fi 82 | if [[ "$arg" = '-p'* ]]; then 83 | if [[ -n "$size" ]]; then 84 | w=${size%%,*} 85 | h=${size##*,} 86 | opt="$opt -w$w -h$h" 87 | fi 88 | elif [[ "$arg" = -@([whxy])* ]]; then 89 | opt="$opt ${arg:0:2}$size" 90 | elif [[ "$size" = *'%' ]]; then 91 | size=${size:0:((${#size} - 1))} 92 | opt="$opt -p $size" 93 | else 94 | opt="$opt -l $size" 95 | fi 96 | ;; 97 | --) 98 | # "--" can be used to separate fzf-tmux options from fzf options to 99 | # avoid conflicts 100 | skip=1 101 | tmux_args=("${args[@]}") 102 | args=() 103 | continue 104 | ;; 105 | *) 106 | args+=("$arg") 107 | ;; 108 | esac 109 | [[ -n "$skip" ]] && args+=("$arg") 110 | done 111 | 112 | if [[ -z "$TMUX" ]]; then 113 | "$fzf" "${args[@]}" 114 | exit $? 115 | fi 116 | 117 | # --height option is not allowed. CTRL-Z is also disabled. 118 | args=("${args[@]}" "--no-height" "--bind=ctrl-z:ignore") 119 | 120 | # Handle zoomed tmux pane without popup options by moving it to a temp window 121 | if [[ ! "$opt" = *'-K -E'* ]] && tmux list-panes -F '#F' | grep -q Z; then 122 | zoomed_without_popup=1 123 | original_window=$(tmux display-message -p "#{window_id}") 124 | tmp_window=$(tmux new-window -d -P -F "#{window_id}" "mksh -c 'while :; do for c in \\| / - '\\;' do sleep 0.2; printf \"\\r\$c fzf-tmux is running\\r\"; done; done'") 125 | tmux swap-pane -t $tmp_window \; select-window -t $tmp_window 126 | fi 127 | 128 | set -e 129 | 130 | # Clean up named pipes on exit 131 | id=$RANDOM 132 | argsf="${TMPDIR:-/tmp}/fzf-args-$id" 133 | fifo1="${TMPDIR:-/tmp}/fzf-fifo1-$id" 134 | fifo2="${TMPDIR:-/tmp}/fzf-fifo2-$id" 135 | fifo3="${TMPDIR:-/tmp}/fzf-fifo3-$id" 136 | tmux_win_opts=($(tmux show-window-options remain-on-exit \; show-window-options synchronize-panes | sed '/ off/d; s/^/set-window-option /; s/$/ \\;/')) 137 | cleanup() { 138 | \rm -f $argsf $fifo1 $fifo2 $fifo3 139 | 140 | # Restore tmux window options 141 | if [[ "${#tmux_win_opts[@]}" -gt 0 ]]; then 142 | eval "tmux ${tmux_win_opts[*]}" 143 | fi 144 | 145 | # Remove temp window if we were zoomed without popup options 146 | if [[ -n "$zoomed_without_popup" ]]; then 147 | tmux display-message -p "#{window_id}" > /dev/null 148 | tmux swap-pane -t $original_window \; \ 149 | select-window -t $original_window \; \ 150 | kill-window -t $tmp_window \; \ 151 | resize-pane -Z 152 | fi 153 | 154 | if [ $# -gt 0 ]; then 155 | trap - EXIT 156 | exit 130 157 | fi 158 | } 159 | trap 'cleanup 1' SIGUSR1 160 | trap 'cleanup' EXIT 161 | 162 | envs="export TERM=$TERM " 163 | [[ "$opt" = *'-K -E'* ]] && FZF_DEFAULT_OPTS="--margin 0,1 $FZF_DEFAULT_OPTS" 164 | [[ -n "$FZF_DEFAULT_OPTS" ]] && envs="$envs FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS")" 165 | [[ -n "$FZF_DEFAULT_COMMAND" ]] && envs="$envs FZF_DEFAULT_COMMAND=$(printf %q "$FZF_DEFAULT_COMMAND")" 166 | echo "$envs;" > "$argsf" 167 | 168 | # Build arguments to fzf 169 | opts=$(printf "%q " "${args[@]}") 170 | 171 | pppid=$$ 172 | echo -n "trap 'kill -SIGUSR1 -$pppid' EXIT SIGINT SIGTERM;" >> $argsf 173 | close="; trap - EXIT SIGINT SIGTERM" 174 | 175 | export TMUX=$(print "$TMUX" | cut -d , -f 1,2) 176 | mkfifo -m o+w $fifo2 177 | if [[ "$opt" = *'-K -E'* ]]; then 178 | if [[ -n "$term" ]] || [[ -t 0 ]]; then 179 | print -- "\"$fzf\" $opts > $fifo2; out=\$? $close; exit \$out" >> $argsf 180 | else 181 | # use a named pipe to get the stdin to the command 182 | mkfifo $fifo1 183 | print -- "\"$fzf\" $opts < $fifo1 > $fifo2; out=\$? $close; exit \$out" >> $argsf 184 | fi 185 | # tmux dropped the support for `-K`, `-R` to popup command 186 | # TODO: We can remove this once tmux 3.2 is released 187 | if [[ ! "$(tmux popup --help 2>&1)" = *'-R shell-command'* ]]; then 188 | opt="${opt/-K/}" 189 | else 190 | opt="${opt} -R" 191 | fi 192 | tmux popup -d "$PWD" $opt "${tmux_args[@]}" "mksh $argsf" > /dev/null 2>&1 & 193 | if [[ -z "$term" ]] && [[ ! -t 0 ]]; then 194 | exec 3<&0 195 | cat > $fifo1 <&3 & 196 | fi 197 | cat $fifo2 198 | exit $? 199 | fi 200 | 201 | mkfifo -m o+w $fifo3 202 | if [[ -n "$term" ]] || [[ -t 0 ]]; then 203 | print -- "\"$fzf\" $opts > $fifo2; print \$? > $fifo3 $close" >> $argsf 204 | else 205 | mkfifo $fifo1 206 | print -- "\"$fzf\" $opts < $fifo1 > $fifo2; print \$? > $fifo3 $close" >> $argsf 207 | fi 208 | tmux set-window-option synchronize-panes off \;\ 209 | set-window-option remain-on-exit off \;\ 210 | split-window -c "$PWD" $opt "${tmux_args[@]}" "mksh -c 'exec -a fzf mksh $argsf'" \ 211 | > /dev/null 2>&1 212 | if [[ -z "$term" ]] && [[ ! -t 0 ]]; then 213 | exec 3<&0 214 | cat > $fifo1 <&3 & 215 | fi 216 | cat $fifo2 217 | exit "$(cat $fifo3)" 218 | --------------------------------------------------------------------------------