From 0508e70f9bf941e3429668fcef955919a71e03c2 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Wed, 14 Dec 2016 12:07:27 +0530 Subject: [PATCH] Overhaul fish functions (#759) Replace the "temp file" workaround with the "read" function: it's simpler and faster. Use proper escaping, remove the custom function. The "file" widget uses last token as root for the "find" command. This replaces the equivalent of '**' completion in bash/zsh. The "$dir" non-expanded variable can be used in FZF_CTRL_T_COMMAND to set the root. --- README.md | 31 ++++++++++++++++-- shell/key-bindings.fish | 69 +++++++++++++++++++++++------------------ 2 files changed, 66 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 4535a48c..63812794 100644 --- a/README.md +++ b/README.md @@ -434,11 +434,36 @@ export FZF_DEFAULT_COMMAND=' It's [a known bug of fish](https://github.com/fish-shell/fish-shell/issues/1362) that it doesn't allow reading from STDIN in command substitution, which means -simple `vim (fzf)` won't work as expected. The workaround is to store the result -of fzf to a temporary file. +simple `vim (fzf)` won't work as expected. The workaround is to use the `read` +fish command: ```sh -fzf > $TMPDIR/fzf.result; and vim (cat $TMPDIR/fzf.result) +fzf | read -l result; and vim $result +``` + +or, for multiple results: + +```sh +fzf -m | while read -l r; set result $result $r; end; and vim $result +``` + +The globbing system is different in fish and thus `**` completion will not work. +However, the `CTRL-T` command will use the last token on the commandline as the +root folder for the recursive search. For instance, hitting `CTRL-T` at the end +of the following commandline + +```sh +ls /var/ +``` + +will list all files and folders under `/var/`. + +When using a custom `FZF_CTRL_T_COMMAND`, use the unexpanded `$dir` variable to +make use of this feature. `$dir` defaults to `.` when the last token is not a +valid directory. Example: + +```sh +set -l FZF_CTRL_T_COMMAND "command find -L \$dir -type f 2> /dev/null | sed '1d; s#^\./##'" ``` License diff --git a/shell/key-bindings.fish b/shell/key-bindings.fish index b63ee89b..b520d8b8 100644 --- a/shell/key-bindings.fish +++ b/shell/key-bindings.fish @@ -1,52 +1,60 @@ # Key bindings # ------------ function fzf_key_bindings - # Due to a bug of fish, we cannot use command substitution, - # so we use temporary file instead - if [ -z "$TMPDIR" ] - set -g TMPDIR /tmp - end - function __fzf_escape - while read item - echo -n (echo -n "$item" | sed -E 's/([ "$~'\''([{<>})])/\\\\\\1/g')' ' + # Store last token in $dir as root for the 'find' command + function fzf-file-widget -d "List files and folders" + set -l dir (commandline -t) + # The commandline token might be escaped, we need to unescape it. + set dir (eval "printf '%s' $dir") + if [ ! -d "$dir" ] + set dir . end - end + # Some 'find' versions print undesired duplicated slashes if the path ends with slashes. + set dir (string replace --regex '(.)/+$' '$1' "$dir") - function fzf-file-widget + # "-path \$dir'*/\\.*'" matches hidden files/folders inside $dir but not + # $dir itself, even if hidden. set -q FZF_CTRL_T_COMMAND; or set -l FZF_CTRL_T_COMMAND " - command find -L . \\( -path '*/\\.*' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \ - -o -type f -print \ - -o -type d -print \ - -o -type l -print 2> /dev/null | sed 1d | cut -b3-" - eval "$FZF_CTRL_T_COMMAND | "(__fzfcmd)" -m $FZF_CTRL_T_OPTS > $TMPDIR/fzf.result" - and for i in (seq 20); commandline -i (cat $TMPDIR/fzf.result | __fzf_escape) 2> /dev/null; and break; sleep 0.1; end + command find -L \$dir \\( -path \$dir'*/\\.*' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \ + -o -type f -print \ + -o -type d -print \ + -o -type l -print 2> /dev/null | sed '1d; s#^\./##'" + + eval "$FZF_CTRL_T_COMMAND | "(__fzfcmd)" -m $FZF_CTRL_T_OPTS" | while read -l r; set result $result $r; end + if [ -z "$result" ] + commandline -f repaint + return + end + + if [ "$dir" != . ] + # Remove last token from commandline. + commandline -t "" + end + for i in $result + commandline -it -- (string escape $i) + commandline -it -- ' ' + end commandline -f repaint - rm -f $TMPDIR/fzf.result end - function fzf-history-widget - history | eval (__fzfcmd) +s +m --tiebreak=index --toggle-sort=ctrl-r $FZF_CTRL_R_OPTS -q '(commandline)' > $TMPDIR/fzf.result - and commandline -- (cat $TMPDIR/fzf.result) + function fzf-history-widget -d "Show command history" + history | eval (__fzfcmd) +s +m --tiebreak=index $FZF_CTRL_R_OPTS -q '(commandline)' | read -l result + and commandline -- $result commandline -f repaint - rm -f $TMPDIR/fzf.result end - function fzf-cd-widget + function fzf-cd-widget -d "Change directory" set -q FZF_ALT_C_COMMAND; or set -l FZF_ALT_C_COMMAND " - command find -L . \\( -path '*/\\.*' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \ - -o -type d -print 2> /dev/null | sed 1d | cut -b3-" - # Fish hangs if the command before pipe redirects (2> /dev/null) - eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)" +m $FZF_ALT_C_OPTS > $TMPDIR/fzf.result" - [ (cat $TMPDIR/fzf.result | wc -l) -gt 0 ] - and cd (cat $TMPDIR/fzf.result) + command find -L . \\( -path '*/\\.*' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \ + -o -type d -print 2> /dev/null | sed 1d | cut -b3-" + eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)" +m $FZF_ALT_C_OPTS" | read -l result + [ "$result" ]; and cd $result commandline -f repaint - rm -f $TMPDIR/fzf.result end function __fzfcmd set -q FZF_TMUX; or set FZF_TMUX 1 - if [ $FZF_TMUX -eq 1 ] if set -q FZF_TMUX_HEIGHT echo "fzf-tmux -d$FZF_TMUX_HEIGHT" @@ -68,4 +76,3 @@ function fzf_key_bindings bind -M insert \ec fzf-cd-widget end end -