Skip to content

Commit

Permalink
[fish] Accept starting dir for <M-c> key binding (#944)
Browse files Browse the repository at this point in the history
This also modifies <C-t> behaviour.
The longest file path in the input is used as root directory for `find`
command. The remainder of the input is passed to fzf's --query as a
initial search parameters.
  • Loading branch information
ipwnponies authored and junegunn committed Jun 25, 2017
1 parent dbcaec5 commit 70cfa6a
Showing 1 changed file with 62 additions and 13 deletions.
75 changes: 62 additions & 13 deletions shell/key-bindings.fish
Expand Up @@ -4,14 +4,9 @@ function fzf_key_bindings

# Store current 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
# Some 'find' versions print undesired duplicated slashes if the path ends with slashes.
set dir (string replace --regex '(.)/+$' '$1' "$dir")
set -l commandline (__fzf_parse_commandline)
set -l dir $commandline[1]
set -l fzf_query $commandline[2]

# "-path \$dir'*/\\.*'" matches hidden files/folders inside $dir but not
# $dir itself, even if hidden.
Expand All @@ -24,7 +19,7 @@ function fzf_key_bindings
set -q FZF_TMUX_HEIGHT; or set FZF_TMUX_HEIGHT 40%
begin
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS"
eval "$FZF_CTRL_T_COMMAND | "(__fzfcmd)" -m" | while read -l r; set result $result $r; end
eval "$FZF_CTRL_T_COMMAND | "(__fzfcmd)' -m --query "'$fzf_query'"' | while read -l r; set result $result $r; end

This comment has been minimized.

Copy link
@kansaichris

kansaichris Nov 21, 2021

Just out of curiosity, why are you using double nested quotes here? Couldn't ' -m --query "'$fzf_query'"' be simplified to " -m --query $fzf_query"?

I ask because although the Fish shell normally does not expand variables in single quotes, eval seems to expand variables in both single and double quotes as follows:

~> set -lx TEST "Hello, world"
~> echo "$TEST"
Hello, world
~> echo '$TEST'
$TEST
~> echo "'$TEST'"
'Hello, world'
~> echo '"$TEST"'
"$TEST"
~> eval "echo $TEST"
Hello, world
~> eval 'echo $TEST'
Hello, world
~> eval 'echo "$TEST"'
Hello, world
~> eval 'echo "'$TEST'"'
Hello, world

Does this make sense, or am I missing something?

end
if [ -z "$result" ]
commandline -f repaint
Expand All @@ -51,15 +46,26 @@ function fzf_key_bindings
end

function fzf-cd-widget -d "Change directory"
set -l commandline (__fzf_parse_commandline)
set -l dir $commandline[1]
set -l fzf_query $commandline[2]

set -q FZF_ALT_C_COMMAND; or set -l FZF_ALT_C_COMMAND "
command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \
-o -type d -print 2> /dev/null | cut -b3-"
command find -L \$dir -mindepth 1 \\( -path \$dir'*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \
-o -type d -print 2> /dev/null | sed 's@^\./@@'"
set -q FZF_TMUX_HEIGHT; or set FZF_TMUX_HEIGHT 40%
begin
set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS"
eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)" +m" | read -l result
[ "$result" ]; and cd $result
eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)' +m --query "'$fzf_query'"' | read -l result

if [ -n "$result" ]
cd $result

# Remove last token from commandline.
commandline -t ""
end
end

commandline -f repaint
end

Expand All @@ -82,4 +88,47 @@ function fzf_key_bindings
bind -M insert \cr fzf-history-widget
bind -M insert \ec fzf-cd-widget
end

function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath and rest of token'
# eval is used to do shell expansion on paths
set -l commandline (eval "printf '%s' "(commandline -t))

if [ -z $commandline ]
# Default to current directory with no --query
set dir '.'
set fzf_query ''
else
set dir (__fzf_get_dir $commandline)

if [ "$dir" = "." -a (string sub -l 1 $commandline) != '.' ]
# if $dir is "." but commandline is not a relative path, this means no file path found
set fzf_query $commandline
else
# Also remove trailing slash after dir, to "split" input properly
set fzf_query (string replace -r "^$dir/?" '' "$commandline")
end
end

echo $dir
echo $fzf_query
end

function __fzf_get_dir -d 'Find the longest existing filepath from input string'
set dir $argv

# Strip all trailing slashes. Ignore if $dir is root dir (/)
if [ (string length $dir) -gt 1 ]
set dir (string replace -r '/*$' '' $dir)
end

# Iteratively check if dir exists and strip tail end of path
while [ ! -d "$dir" ]
# If path is absolute, this can keep going until ends up at /
# If path is relative, this can keep going until entire input is consumed, dirname returns "."
set dir (dirname "$dir")
end

echo $dir
end

end

0 comments on commit 70cfa6a

Please sign in to comment.