chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 11:59:03 +08:00
commit bd73f79ffd
154 changed files with 54688 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
__fzf_defaults() {
# $1: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
# $2: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
builtin printf '%s\n' "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
builtin printf '%s\n' "${FZF_DEFAULT_OPTS-} $2"
}
__fzf_exec_awk() {
# This function performs `exec awk "$@"` safely by working around awk
# compatibility issues.
#
# To reduce an extra fork, this function performs "exec" so is expected to be
# run as the last command in a subshell.
if [[ -z ${__fzf_awk-} ]]; then
__fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
# Note: Solaris awk at /usr/bin/awk is meant for backward compatibility
# with an ancient implementation of 1977 awk in the original UNIX. It
# lacks many features of POSIX awk, so it is essentially useless in the
# modern point of view. To use a standard-conforming version in Solaris,
# one needs to explicitly use /usr/xpg4/bin/awk.
__fzf_awk=/usr/xpg4/bin/awk
elif command -v mawk > /dev/null 2>&1; then
# choose the faster mawk if: it's installed && build date >= 20230322 &&
# version >= 1.3.4
local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] &&
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi
fi
# Note: macOS awk has a quirk that it stops processing at all when it sees
# any data not following UTF-8 in the input stream when the current LC_CTYPE
# specifies the UTF-8 encoding. To work around this quirk, one needs to
# specify LC_ALL=C to change the current encoding to the plain one.
LC_ALL=C exec "$__fzf_awk" "$@"
}
+91
View File
@@ -0,0 +1,91 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ completion-examples.nu
#
# Example custom completers for fzf's Nushell integration.
#
# To use these, add the desired entries to $env.FZF_COMPLETERS in your
# config.nu. Each closure receives two arguments:
# - prefix: the text before the trigger (e.g. "vim" in "vim **<TAB>")
# - spans: the full command as a list of words (e.g. ["pacman", "-S", "vim**"])
#
# A closure can return either:
# - a list of candidate strings (fzf will use default options), or
# - a record with the following optional fields:
# candidates: list<string> # candidates to feed to fzf
# opts: list<string> # custom fzf options (default: ["-m"])
# post: closure (|sel| ...) # post-processing of the selected item
#
# Simple example:
# $env.FZF_COMPLETERS = {
# git: {|prefix, spans| ["branch-main", "branch-dev", "branch-feature"]}
# }
# --- pacman / paru ---
# Completes package names for pacman and paru.
# Uses the spans to distinguish between subcommands:
# -S (sync), -F (files): list available packages from repos
# -Q (query), -R (remove): list installed packages
# Returns a record with custom fzf options for package preview.
$env.FZF_COMPLETERS = {}
$env.FZF_COMPLETERS.pacman = {|prefix, spans|
let sub = $spans | skip 1 | first
let candidates = (if ($sub =~ "-[SF]") {
^pacman -Slq | lines
} else if ($sub =~ "-[QR]") {
^pacman -Qq | lines
} else {
[]
})
{
candidates: $candidates
opts: ["-m", "--preview", "pacman -Si {}", "--prompt", "Package > "]
}
}
$env.FZF_COMPLETERS.paru = {|prefix, spans|
let sub = $spans | skip 1 | first
let candidates = (if ($sub =~ "-[SF]") {
^pacman -Slq | lines
} else if ($sub =~ "-[QR]") {
^pacman -Qq | lines
} else {
[]
})
{
candidates: $candidates
opts: ["-m", "--preview", "pacman -Si {}", "--prompt", "Package > "]
}
}
# --- pass (password-store) ---
# Completes entry names from ~/.password-store.
# Returns a simple list (no custom fzf options needed).
$env.FZF_COMPLETERS.pass = {|prefix, spans|
try {
ls ~/.password-store/**/*.gpg
| get name
| each {$in | str replace -r '^.*?\.password-store/(.*).gpg' '${1}'}
} catch {
[]
}
}
# --- Example with post-processing hook ---
# The "post" closure transforms the selected line after fzf returns.
# This is useful when the displayed line contains more information than
# what you want inserted on the command line (e.g. extracting a PID from
# a full "ps" output line).
#
# $env.FZF_COMPLETERS.mycommand = {|prefix, spans|
# {
# candidates: (^some-command | lines)
# opts: ["+m", "--header-lines=1"]
# post: {|selection| $selection | split row ' ' | get 0}
# }
# }
+740
View File
@@ -0,0 +1,740 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ completion.bash
#
# - $FZF_COMPLETION_TRIGGER (default: '**')
# - $FZF_COMPLETION_OPTS (default: empty)
# - $FZF_COMPLETION_PATH_OPTS (default: empty)
# - $FZF_COMPLETION_DIR_OPTS (default: empty)
if [[ $- =~ i ]]; then
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
#
# _fzf_compgen_path() {
# echo "$1"
# command find -L "$1" \
# -name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
# -a -not -path "$1" -print 2> /dev/null | command sed 's@^\./@@'
# }
#
# _fzf_compgen_dir() {
# command find -L "$1" \
# -name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
# -a -not -path "$1" -print 2> /dev/null | command sed 's@^\./@@'
# }
###########################################################
#----BEGIN shfmt
#----BEGIN INCLUDE common.sh
# NOTE: Do not directly edit this section, which is copied from "common.sh".
# To modify it, one can edit "common.sh" and run "./update.sh" to apply
# the changes. See code comments in "common.sh" for the implementation details.
__fzf_defaults() {
builtin printf '%s\n' "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
builtin printf '%s\n' "${FZF_DEFAULT_OPTS-} $2"
}
__fzf_exec_awk() {
if [[ -z ${__fzf_awk-} ]]; then
__fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
__fzf_awk=/usr/xpg4/bin/awk
elif command -v mawk > /dev/null 2>&1; then
local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] &&
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi
fi
LC_ALL=C exec "$__fzf_awk" "$@"
}
#----END INCLUDE
__fzf_comprun() {
if [[ "$(type -t _fzf_comprun 2>&1)" == function ]]; then
_fzf_comprun "$@"
elif [[ -n ${TMUX_PANE-} ]] && { [[ ${FZF_TMUX:-0} != 0 ]] || [[ -n ${FZF_TMUX_OPTS-} ]]; }; then
shift
fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- "$@"
else
shift
fzf "$@"
fi
}
__fzf_orig_completion() {
local l comp f cmd
while read -r l; do
if [[ $l =~ ^(.*\ -F)\ *([^ ]*).*\ ([^ ]*)$ ]]; then
comp="${BASH_REMATCH[1]}"
f="${BASH_REMATCH[2]}"
cmd="${BASH_REMATCH[3]}"
[[ $f == _fzf_* ]] && continue
builtin printf -v "_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}" "%s" "${comp} %s ${cmd} #${f}"
if [[ $l == *" -o nospace "* ]] && [[ ${__fzf_nospace_commands-} != *" $cmd "* ]]; then
__fzf_nospace_commands="${__fzf_nospace_commands-} $cmd "
fi
fi
done
}
# @param $1 cmd - Command name for which the original completion is searched
# @var[out] REPLY - Original function name is returned
__fzf_orig_completion_get_orig_func() {
local cmd orig_var orig
cmd=$1
orig_var="_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
orig="${!orig_var-}"
REPLY="${orig##*#}"
[[ $REPLY ]] && type "$REPLY" &> /dev/null
}
# @param $1 cmd - Command name for which the original completion is searched
# @param $2 func - Fzf's completion function to replace the original function
# @var[out] REPLY - Completion setting is returned as a string to "eval"
__fzf_orig_completion_instantiate() {
local cmd func orig_var orig
cmd=$1
func=$2
orig_var="_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
orig="${!orig_var-}"
orig="${orig%#*}"
[[ $orig == *' %s '* ]] || return 1
builtin printf -v REPLY "$orig" "$func"
}
_fzf_opts_completion() {
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD - 1]}"
opts="
+c --no-color
+i --no-ignore-case
+s --no-sort
+x --no-extended
--accept-nth
--ansi
--bash
--bind
--border
--border-label
--border-label-pos
--color
--cycle
--disabled
--ellipsis
--expect
--filepath-word
--fish
--footer
--footer-border
--footer-label
--footer-label-pos
--freeze-left
--freeze-right
--gap
--gap-line
--ghost
--gutter
--gutter-raw
--header
--header-border
--header-first
--header-label
--header-label-pos
--header-lines
--header-lines-border
--height
--highlight-line
--history
--history-size
--hscroll-off
--id-nth
--info
--info-command
--input-border
--input-label
--input-label-pos
--jump-labels
--keep-right
--layout
--listen
--listen-unsafe
--list-border
--list-label
--list-label-pos
--literal
--man
--margin
--marker
--marker-multi-line
--min-height
--no-bold
--no-hscroll
--no-input
--no-multi-line
--no-scrollbar
--no-separator
--padding
--pointer
--preview
--preview-border
--preview-label
--preview-label-pos
--preview-window
--print-query
--print0
--prompt
--raw
--read0
--scheme
--scroll-off
--scrollbar
--separator
--smart-case
--style
--sync
--tabstop
--tac
--tail
--tiebreak
--tmux
--track
--version
--walker
--walker-root
--walker-skip
--with-nth
--with-shell
--wrap
--wrap-sign
--preview-wrap-sign
--zsh
-0 --exit-0
-1 --select-1
-d --delimiter
-e --exact
-f --filter
-h --help
-i --ignore-case
-m --multi
-n --nth
-q --query
--"
case "${prev}" in
--scheme)
COMPREPLY=($(compgen -W "default path history" -- "$cur"))
return 0
;;
--tiebreak)
COMPREPLY=($(compgen -W "length chunk pathname begin end index" -- "$cur"))
return 0
;;
--color)
COMPREPLY=($(compgen -W "dark light base16 16 bw no" -- "$cur"))
return 0
;;
--layout)
COMPREPLY=($(compgen -W "default reverse reverse-list" -- "$cur"))
return 0
;;
--info)
COMPREPLY=($(compgen -W "default right hidden inline inline-right" -- "$cur"))
return 0
;;
--wrap)
COMPREPLY=($(compgen -W "char word" -- "$cur"))
return 0
;;
--style)
COMPREPLY=($(compgen -W "default minimal full" -- "$cur"))
return 0
;;
--preview-window)
COMPREPLY=($(compgen -W "
default
hidden
nohidden
wrap
wrap-word
nowrap
cycle
nocycle
up top
down bottom
left
right
rounded border border-rounded
border-line
sharp border-sharp
border-bold
border-block
border-thinblock
border-double
noborder border-none
border-horizontal
border-vertical
border-up border-top
border-down border-bottom
border-left
border-right
follow
nofollow
info
noinfo" -- "$cur"))
return 0
;;
--border | --list-border | --header-border | --header-lines-border | --footer-border | --input-border | --preview-border)
COMPREPLY=($(compgen -W "line rounded sharp bold block thinblock double horizontal vertical top bottom left right none" -- "$cur"))
return 0
;;
--border-label-pos | --preview-label-pos | --list-label-pos | --header-label-pos | --footer-label-pos | --input-label-pos)
COMPREPLY=($(compgen -W "center bottom top" -- "$cur"))
return 0
;;
esac
if [[ $cur =~ ^-|\+ ]]; then
COMPREPLY=($(compgen -W "${opts}" -- "$cur"))
return 0
fi
return 0
}
_fzf_handle_dynamic_completion() {
local cmd ret REPLY orig_cmd orig_complete
cmd="$1"
shift
orig_cmd="$1"
if __fzf_orig_completion_get_orig_func "$cmd"; then
"$REPLY" "$@"
elif [[ -n ${_fzf_completion_loader-} ]]; then
orig_complete=$(complete -p "$orig_cmd" 2> /dev/null)
$_fzf_completion_loader "$@"
ret=$?
# _completion_loader may not have updated completion for the command
if [[ "$(complete -p "$orig_cmd" 2> /dev/null)" != "$orig_complete" ]]; then
__fzf_orig_completion < <(complete -p "$orig_cmd" 2> /dev/null)
__fzf_orig_completion_get_orig_func "$cmd" || ret=1
# Update orig_complete by _fzf_orig_completion entry
[[ $orig_complete =~ ' -F '(_fzf_[^ ]+)' ' ]] &&
__fzf_orig_completion_instantiate "$cmd" "${BASH_REMATCH[1]}" &&
orig_complete=$REPLY
if [[ ${__fzf_nospace_commands-} == *" $orig_cmd "* ]]; then
eval "${orig_complete/ -F / -o nospace -F }"
else
eval "$orig_complete"
fi
fi
[[ $ret -eq 0 ]] && return 124
return $ret
fi
}
__fzf_generic_path_completion() {
local cur base dir leftover matches trigger cmd
cmd="${COMP_WORDS[0]}"
if [[ $cmd == \\* ]]; then
cmd="${cmd:1}"
fi
COMPREPLY=()
trigger=${FZF_COMPLETION_TRIGGER-'**'}
[[ $COMP_CWORD -ge 0 ]] && cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $cur == *"$trigger" ]] && [[ $cur != *'$('* ]] && [[ $cur != *':='* ]] && [[ $cur != *'`'* ]]; then
base=${cur:0:${#cur}-${#trigger}}
eval "base=$base" 2> /dev/null || return
dir=
[[ $base == *"/"* ]] && dir="$base"
while true; do
if [[ -z $dir ]] || [[ -d $dir ]]; then
leftover=${base/#"$dir"/}
leftover=${leftover/#\//}
[[ -z $dir ]] && dir='.'
[[ $dir != "/" ]] && dir="${dir/%\//}"
matches=$(
export FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --scheme=path" "${FZF_COMPLETION_OPTS-} $2")
unset FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS_FILE
if [[ $1 =~ dir ]]; then
eval "rest=(${FZF_COMPLETION_DIR_OPTS-})"
else
eval "rest=(${FZF_COMPLETION_PATH_OPTS-})"
fi
if declare -F "$1" > /dev/null; then
eval "$1 $(builtin printf %q "$dir")" | __fzf_comprun "$4" -q "$leftover" "${rest[@]}"
else
if [[ $1 =~ dir ]]; then
walker=dir,follow
else
walker=file,dir,follow,hidden
fi
__fzf_comprun "$4" -q "$leftover" --walker "$walker" --walker-root="$dir" "${rest[@]}"
fi | while read -r item; do
builtin printf "%q " "${item%$3}$3"
done
)
matches=${matches% }
[[ -z $3 ]] && [[ ${__fzf_nospace_commands-} == *" ${COMP_WORDS[0]} "* ]] && matches="$matches "
if [[ -n $matches ]]; then
COMPREPLY=("$matches")
else
COMPREPLY=("$cur")
fi
# To redraw line after fzf closes (builtin printf '\e[5n')
bind '"\e[0n": redraw-current-line' 2> /dev/null
builtin printf '\e[5n'
return 0
fi
dir=$(command dirname "$dir")
[[ $dir =~ /$ ]] || dir="$dir"/
done
else
shift
shift
shift
_fzf_handle_dynamic_completion "$cmd" "$@"
fi
}
_fzf_complete() {
# Split arguments around --
local args rest str_arg i sep
args=("$@")
sep=
for i in "${!args[@]}"; do
if [[ ${args[$i]} == -- ]]; then
sep=$i
break
fi
done
if [[ -n $sep ]]; then
str_arg=
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
args=("${args[@]:0:sep}")
else
str_arg=$1
args=()
shift
rest=("$@")
fi
local cur selected trigger cmd post
post="$(caller 0 | __fzf_exec_awk '{print $2}')_post"
type -t "$post" > /dev/null 2>&1 || post='command cat'
trigger=${FZF_COMPLETION_TRIGGER-'**'}
cmd="${COMP_WORDS[0]}"
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $cur == *"$trigger" ]] && [[ $cur != *'$('* ]] && [[ $cur != *':='* ]] && [[ $cur != *'`'* ]]; then
cur=${cur:0:${#cur}-${#trigger}}
selected=$(
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse" "${FZF_COMPLETION_OPTS-} $str_arg") \
FZF_DEFAULT_OPTS_FILE='' \
__fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | eval "$post" | command tr '\n' ' '
)
selected=${selected% } # Strip trailing space not to repeat "-o nospace"
if [[ -n $selected ]]; then
COMPREPLY=("$selected")
else
COMPREPLY=("$cur")
fi
bind '"\e[0n": redraw-current-line' 2> /dev/null
builtin printf '\e[5n'
return 0
else
_fzf_handle_dynamic_completion "$cmd" "${rest[@]}"
fi
}
_fzf_path_completion() {
__fzf_generic_path_completion _fzf_compgen_path "-m" "" "$@"
}
# Deprecated. No file only completion.
_fzf_file_completion() {
_fzf_path_completion "$@"
}
_fzf_dir_completion() {
__fzf_generic_path_completion _fzf_compgen_dir "" "/" "$@"
}
_fzf_complete_kill() {
_fzf_proc_completion "$@"
}
_fzf_proc_completion() {
local transformer
transformer='
if [[ $FZF_KEY =~ ctrl|alt|shift ]] && [[ -n $FZF_NTH ]]; then
nths=( ${FZF_NTH//,/ } )
new_nths=()
found=0
for nth in ${nths[@]}; do
if [[ $nth = $FZF_CLICK_HEADER_NTH ]]; then
found=1
else
new_nths+=($nth)
fi
done
[[ $found = 0 ]] && new_nths+=($FZF_CLICK_HEADER_NTH)
new_nths=${new_nths[*]}
new_nths=${new_nths// /,}
echo "change-nth($new_nths)+change-prompt($new_nths> )"
else
if [[ $FZF_NTH = $FZF_CLICK_HEADER_NTH ]]; then
echo "change-nth()+change-prompt(> )"
else
echo "change-nth($FZF_CLICK_HEADER_NTH)+change-prompt($FZF_CLICK_HEADER_WORD> )"
fi
fi
'
_fzf_complete -m --header-lines=1 --no-preview --wrap --color fg:dim,nth:regular \
--bind "click-header:transform:$transformer" -- "$@" < <(
command ps -eo user,pid,ppid,start,time,command 2> /dev/null ||
command ps -eo user,pid,ppid,time,args 2> /dev/null || # For BusyBox
command ps --everyone --full --windows # For cygwin
)
}
_fzf_proc_completion_post() {
__fzf_exec_awk '{print $2}'
}
# To use custom hostname lists, override __fzf_list_hosts.
# The function is expected to print hostnames, one per line as well as in the
# desired sorting and with any duplicates removed, to standard output.
#
# e.g.
# # Use bash-completionss _known_hosts_real() for getting the list of hosts
# __fzf_list_hosts() {
# # Set the local attribute for any non-local variable that is set by _known_hosts_real()
# local COMPREPLY=()
# _known_hosts_real ''
# builtin printf '%s\n' "${COMPREPLY[@]}" | command sort -u --version-sort
# }
if ! declare -F __fzf_list_hosts > /dev/null; then
__fzf_list_hosts() {
command sort -u \
<(
# Note: To make the pathname expansion of "~/.ssh/config.d/*" work
# properly, we need to adjust the related shell options. We need to
# unset "set -f" and "GLOBIGNORE", which disable the pathname expansion
# totally or partially. We need to unset "dotglob" and "nocaseglob" to
# avoid matching unwanted files. We need to unset "failglob" to avoid
# outputting the error messages to the terminal when no matching is
# found. We need to set "nullglob" to avoid attempting to read the
# literal filename '~/.ssh/config.d/*' when no matching is found.
set +f
GLOBIGNORE=
shopt -u dotglob nocaseglob failglob
shopt -s nullglob
__fzf_exec_awk '
# Note: mawk <= 1.3.3-20090705 does not support the POSIX brackets of
# the form [[:blank:]], and Ubuntu 18.04 LTS still uses this
# 16-year-old mawk unfortunately. We need to use [ \t] instead.
match(tolower($0), /^[ \t]*host(name)?[ \t]*[ \t=]/) {
$0 = substr($0, RLENGTH + 1) # Remove "Host(name)?=?"
sub(/#.*/, "")
for (i = 1; i <= NF; i++)
if ($i !~ /[*?%]/)
print $i
}
' ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null
) \
<(
__fzf_exec_awk -F ',' '
match($0, /^[][a-zA-Z0-9.,:-]+/) {
$0 = substr($0, 1, RLENGTH)
gsub(/[][]|:[^,]*/, "")
for (i = 1; i <= NF; i++)
print $i
}
' ~/.ssh/known_hosts 2> /dev/null
) \
<(
__fzf_exec_awk '
{
sub(/#.*/, "")
for (i = 2; i <= NF; i++)
if ($i != "0.0.0.0")
print $i
}
' /etc/hosts 2> /dev/null
)
}
fi
_fzf_host_completion() {
_fzf_complete +m -- "$@" < <(__fzf_list_hosts)
}
# Values for $1 $2 $3 are described here
# https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html
# > the first argument ($1) is the name of the command whose arguments are being completed,
# > the second argument ($2) is the word being completed,
# > and the third argument ($3) is the word preceding the word being completed on the current command line.
_fzf_complete_ssh() {
case $3 in
-i | -F | -E)
_fzf_path_completion "$@"
;;
*)
local user=
[[ $2 =~ '@' ]] && user="${2%%@*}@"
_fzf_complete +m -- "$@" < <(__fzf_list_hosts | __fzf_exec_awk -v user="$user" '{print user $0}')
;;
esac
}
_fzf_var_completion() {
_fzf_complete -m -- "$@" < <(
declare -xp | command sed -En 's|^declare [^ ]+ ([^=]+).*|\1|p'
)
}
_fzf_alias_completion() {
_fzf_complete -m -- "$@" < <(
alias | command sed -En 's|^alias ([^=]+).*|\1|p'
)
}
# fzf options
complete -o default -F _fzf_opts_completion fzf
# fzf-tmux is a thin fzf wrapper that has only a few more options than fzf
# itself. As a quick improvement we take fzf's completion. Adding the few extra
# fzf-tmux specific options (like `-w WIDTH`) are left as a future patch.
complete -o default -F _fzf_opts_completion fzf-tmux
# Default path completion
__fzf_default_completion() {
__fzf_generic_path_completion _fzf_compgen_path "-m" "" "$@"
# Dynamic completion loader has updated the completion for the command
if [[ $? -eq 124 ]]; then
# We trigger _fzf_setup_completion so that fuzzy completion for the command
# still works. However, loader can update the completion for multiple
# commands at once, and fuzzy completion will no longer work for those
# other commands. e.g. pytest -> py.test, pytest-2, pytest-3, etc
_fzf_setup_completion path "$1"
return 124
fi
}
# Set fuzzy path completion as the default completion for all commands.
# We can't set up default completion,
# 1. if it's already set up by another script
# 2. or if the current version of bash doesn't support -D option
complete | command grep -q __fzf_default_completion ||
complete | command grep -- '-D$' | command grep -qv _comp_complete_load ||
complete -D -F __fzf_default_completion -o default -o bashdefault 2> /dev/null
d_cmds="${FZF_COMPLETION_DIR_COMMANDS-cd pushd rmdir}"
# NOTE: $FZF_COMPLETION_PATH_COMMANDS and $FZF_COMPLETION_VAR_COMMANDS are
# undocumented and subject to change in the future.
#
# NOTE: Although we have default completion, we still need to set up completion
# for each command in case they already have completion set up by another script.
a_cmds="${FZF_COMPLETION_PATH_COMMANDS-"
awk bat cat code diff diff3
emacs emacsclient ex file ftp g++ gcc gvim head hg hx java
javac ld less more mvim nvim patch perl python ruby
sed sftp sort source tail tee uniq vi view vim wc xdg-open
basename bunzip2 bzip2 chmod chown curl cp dirname du
find git grep gunzip gzip hg jar
ln ls mv open rm rsync scp
svn tar unzip zip"}"
v_cmds="${FZF_COMPLETION_VAR_COMMANDS-export unset printenv}"
# Preserve existing completion
__fzf_orig_completion < <(complete -p $d_cmds $a_cmds $v_cmds unalias kill ssh 2> /dev/null)
if type _comp_load > /dev/null 2>&1; then
# _comp_load was added in bash-completion 2.12 to replace _completion_loader.
# We use it without -D option so that it does not use _comp_complete_minimal as the fallback.
_fzf_completion_loader=_comp_load
elif type __load_completion > /dev/null 2>&1; then
# In bash-completion 2.11, _completion_loader internally calls __load_completion
# and if it returns a non-zero status, it sets the default 'minimal' completion.
_fzf_completion_loader=__load_completion
elif type _completion_loader > /dev/null 2>&1; then
_fzf_completion_loader=_completion_loader
fi
__fzf_defc() {
local cmd func opts REPLY
cmd="$1"
func="$2"
opts="$3"
if __fzf_orig_completion_instantiate "$cmd" "$func"; then
eval "$REPLY"
else
eval "complete -F \"$func\" $opts \"$cmd\""
fi
}
# Anything
for cmd in $a_cmds; do
__fzf_defc "$cmd" _fzf_path_completion "-o default -o bashdefault"
done
# Directory
for cmd in $d_cmds; do
__fzf_defc "$cmd" _fzf_dir_completion "-o bashdefault -o nospace -o dirnames"
done
# Variables
for cmd in $v_cmds; do
__fzf_defc "$cmd" _fzf_var_completion "-o default -o nospace -v"
done
# Aliases
__fzf_defc unalias _fzf_alias_completion "-a"
# Processes
__fzf_defc kill _fzf_proc_completion "-o default -o bashdefault"
# ssh
__fzf_defc ssh _fzf_complete_ssh "-o default -o bashdefault"
unset cmd d_cmds a_cmds v_cmds
_fzf_setup_completion() {
local kind fn cmd
kind=$1
fn=_fzf_${1}_completion
if [[ $# -lt 2 ]] || ! type -t "$fn" > /dev/null; then
echo "usage: ${FUNCNAME[0]} path|dir|var|alias|host|proc COMMANDS..."
return 1
fi
shift
__fzf_orig_completion < <(complete -p "$@" 2> /dev/null)
for cmd in "$@"; do
case "$kind" in
dir) __fzf_defc "$cmd" "$fn" "-o nospace -o dirnames" ;;
var) __fzf_defc "$cmd" "$fn" "-o default -o nospace -v" ;;
alias) __fzf_defc "$cmd" "$fn" "-a" ;;
*) __fzf_defc "$cmd" "$fn" "-o default -o bashdefault" ;;
esac
done
}
#----END shfmt
fi
+169
View File
@@ -0,0 +1,169 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ completion.fish
#
# - $FZF_COMPLETION_OPTS
# - $FZF_EXPANSION_OPTS
# The oldest supported fish version is 3.4.0. For this message being able to be
# displayed on older versions, the command substitution syntax $() should not
# be used anywhere in the script, otherwise the source command will fail.
if string match -qr -- '^[12]\\.|^3\\.[0-3]' $version
echo "fzf completion script requires fish version 3.4.0 or newer." >&2
return 1
else if not command -q fzf
echo "fzf was not found in path." >&2
return 1
end
function fzf_complete -w fzf -d 'fzf command completion and wildcard expansion search'
# Restore the default shift-tab behavior on tab completions
if commandline --paging-mode
commandline -f complete-and-search
return
end
# Remove any trailing unescaped backslash from token and update command line
set -l -- token (string replace -r -- '(?<!\\\\)(?:\\\\\\\\)*\\K\\\\$' '' (commandline -t | string collect) | string collect)
commandline -rt -- $token
# Remove any line breaks from token
set -- token (string replace -ra -- '\\\\\\n' '' $token | string collect)
# regex: Match token with unescaped/unquoted glob character
set -l -- r_glob '^(?:[^\'"\\\\*]|\\\\[\\S\\s]|\'(?:\\\\[\\S\\s]|[^\'\\\\])*\'|"(?:\\\\[\\S\\s]|[^"\\\\])*")*\\*[\\S\\s]*$'
# regex: Match any unbalanced quote character
set -l -- r_quote '^(?>(?:\\\\[\\s\\S]|"(?:[^"\\\\]|\\\\[\\s\\S])*"|\'(?:[^\'\\\\]|\\\\[\\s\\S])*\'|[^\'"\\\\]+)*)\\K[\'"]'
# The expansion pattern is the token with any open quote closed, or is empty.
set -l -- glob_pattern (string match -r -- $r_glob $token | string collect)(string match -r -- $r_quote $token | string collect -a)
set -l -- cl_tokenize_opt '--tokens-expanded'
string match -q -- '3.*' $version
and set -- cl_tokenize_opt '--tokenize'
# Set command line tokens without any leading variable definitions or launcher
# commands (including their options, but not any option arguments).
set -l -- r_cmd '^(?:(?:builtin|command|doas|env|sudo|\\w+=\\S*|-\\S+)\\s+)*\\K[\\s\\S]+'
set -l -- cmd (commandline $cl_tokenize_opt --input=(commandline -pc | string match -r $r_cmd))
test -z "$token"
and set -a -- cmd ''
# Set fzf options
test -z "$FZF_TMUX_HEIGHT"
and set -l -- FZF_TMUX_HEIGHT 40%
set -lax -- FZF_DEFAULT_OPTS \
"--height=$FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" \
(test -r "$FZF_DEFAULT_OPTS_FILE"; and string join -- ' ' <$FZF_DEFAULT_OPTS_FILE) \
$FZF_DEFAULT_OPTS '--bind=alt-r:toggle-raw --multi --wrap=word --reverse' \
(if test -n "$glob_pattern"; string collect -- $FZF_EXPANSION_OPTS; else;
string collect -- $FZF_COMPLETION_OPTS; end; string escape -n -- $argv) \
--with-shell=(status fish-path)\\ -c
set -lx FZF_DEFAULT_OPTS_FILE
set -l -- fzf_cmd fzf
test "$FZF_TMUX" = 1
and set -- fzf_cmd fzf-tmux $FZF_TMUX_OPTS -d$FZF_TMUX_HEIGHT --
set -l result
# Get the completion list from stdin when it's not a tty
if not isatty stdin
set -l -- custom_post_func _fzf_post_complete_$cmd[1]
functions -q $custom_post_func
or set -- custom_post_func _fzf_complete_$cmd[1]_post
if functions -q $custom_post_func
$fzf_cmd | $custom_post_func $cmd | while read -l r; set -a -- result $r; end
else if string match -q -- '*--print0*' "$FZF_DEFAULT_OPTS"
$fzf_cmd | while read -lz r; set -a -- result $r; end
else
$fzf_cmd | while read -l r; set -a -- result $r; end
end
# Wildcard expansion
else if test -n "$glob_pattern"
# Set the command to be run by fzf, so there is a visual indicator and an
# easy way to abort on long recursive searches.
set -lx -- FZF_DEFAULT_COMMAND "for i in $glob_pattern;" \
'test -d "$i"; and string match -qv -- "*/" $i; and set -- i $i/;' \
'string join0 -- $i; end'
set -- result (string escape -n -- ($fzf_cmd --read0 --print0 --scheme=path --no-multi-line | string split0))
# Command completion
else
# Call custom function if defined
set -l -- custom_func _fzf_complete_$cmd[1]
if functions -q $custom_func; and not set -q __fzf_no_custom_complete
set -lx __fzf_no_custom_complete
$custom_func $cmd
return
end
# Workaround for complete not having newlines in results
if string match -qr -- '\\n' $token
set -- token (string replace -ra -- '(?<!\\\\)(?:\\\\\\\\)*\\K\\\\\$' '\\\\\\\\\$' $token | string collect)
set -- token (string unescape -- $token | string collect)
set -- token (string replace -ra -- '\\n' '\\\\n' $token | string collect)
end
set -- list (complete -C --escape -- (string join -- ' ' (commandline -pc $cl_tokenize_opt) $token | string collect))
if test -n "$list"
# Get the initial tabstop value
if set -l -- tabstop (string match -rga -- '--tabstop[= ](?:0*)([1-9]\\d+|[4-9])' "$FZF_DEFAULT_OPTS")[-1]
set -- tabstop (math $tabstop - 4)
else
set -- tabstop 4
end
# Determine the tabstop length for description alignment
set -l -- max_columns (math $COLUMNS - 40)
for i in $list[1..500]
set -l -- item (string split -f 1 -- \t $i)
and set -l -- len (string length -V -- $item)
and test "$len" -gt "$tabstop" -a "$len" -lt "$max_columns"
and set -- tabstop $len
end
set -- tabstop (math $tabstop + 4)
set -- result (string collect -- $list | $fzf_cmd --delimiter="\t" --tabstop=$tabstop --wrap-sign=\t"↳ " --accept-nth=1)
end
end
# Update command line
if test -n "$result"
# No extra space after single selection that ends with path separator
set -l -- tail ' '
test (count $result) -eq 1
and string match -q -- '*/' "$result"
and set -- tail ''
commandline -rt -- (string join -- ' ' $result)$tail
end
commandline -f repaint
end
function _fzf_complete
set -l fzf_args
for i in $argv
string match -q -- '--' $i; and break
set -a -- fzf_args $i
end
fzf_complete $fzf_args
end
# Bind to shift-tab
if string match -qr -- '^\\d\\d+|^[4-9]' $version
bind shift-tab fzf_complete
bind -M insert shift-tab fzf_complete
else
bind -k btab fzf_complete
bind -M insert -k btab fzf_complete
end
+490
View File
@@ -0,0 +1,490 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ completion.nu
# An implementation of completion.nu
# This loads FZF as a Nushell External Completer
# https://www.nushell.sh/cookbook/external_completers.html
# --- Default Environment Variables ---
# These can be overridden in your config.nu or environment.
# Example: $env.FZF_COMPLETION_TRIGGER = "!<TAB>"
# - $env.FZF_TMUX (default: 0)
# - $env.FZF_TMUX_OPTS (default: empty)
# - $env.FZF_TMUX_HEIGHT (default: 40%)
# - $env.FZF_COMPLETION_TRIGGER (default: '**')
# - $env.FZF_COMPLETION_OPTS (default: empty)
# - $env.FZF_COMPLETION_PATH_OPTS (default: empty)
# - $env.FZF_COMPLETION_DIR_OPTS (default: empty)
$env.FZF_COMPLETION_TRIGGER = $env.FZF_COMPLETION_TRIGGER? | default '**'
# Options for fzf completion in general. e.g. '--border'
$env.FZF_COMPLETION_OPTS = $env.FZF_COMPLETION_OPTS? | default ''
# Options specific to path completion. e.g. '--extended'
$env.FZF_COMPLETION_PATH_OPTS = $env.FZF_COMPLETION_PATH_OPTS? | default ''
# Options specific to directory completion. e.g. '--extended'
$env.FZF_COMPLETION_DIR_OPTS = $env.FZF_COMPLETION_DIR_OPTS? | default ''
$env.FZF_COMPLETION_DIR_COMMANDS = $env.FZF_COMPLETION_DIR_COMMANDS? | default ['cd', 'pushd', 'rmdir']
# --- Helper Functions ---
# Helper to build default fzf options list
def __fzf_defaults_completion [prepend: string, append: string]: nothing -> string {
let base = $"--height ($env.FZF_TMUX_HEIGHT? | default '40%') --min-height 20+ --bind=ctrl-z:ignore ($prepend)"
let opts_file = if ($env.FZF_DEFAULT_OPTS_FILE? | default '' | is-not-empty) {
try { open --raw ($env.FZF_DEFAULT_OPTS_FILE) | str trim } catch { '' }
} else {
''
}
let default_opts = $env.FZF_DEFAULT_OPTS? | default ''
$"($base) ($opts_file) ($default_opts) ($append)" | str trim
}
# Wrapper for running fzf or fzf-tmux
def __fzf_comprun [ context_name: string # e.g., "fzf-completion" , "fzf-helper" - mainly for potential debugging
, query: string # The initial query string for fzf
, fzf_opts_arg: list<string> # Remaining options for fzf/fzf-tmux
] {
let stdin_content = try {
# Collect stdin into a single string. Adjust if structured data is expected.
$in | into string
} catch {
null # Set to null if there's no stdin or an error occurs reading it
}
let fzf_default_opts = (__fzf_defaults_completion "" ($env.FZF_COMPLETION_OPTS | default ''))
let fzf_prefinal_opt = ['--query', $query, '--reverse'] | append $fzf_opts_arg
# Get the configured height, defaulting to '40%'
let height_opt = $env.FZF_TMUX_HEIGHT? | default '40%'
# Determine if fzf should generate its own candidates via walker
let has_walker = ($fzf_prefinal_opt | find '--walker' | is-not-empty)
# Check for custom comprun function (Nu equivalent)
if (which _fzf_comprun | is-not-empty) {
# Note: Nushell doesn't have a direct equivalent to Zsh/Bash `type -t _fzf_comprun`.
# This check assumes a user might define a custom command named `_fzf_comprun`.
_fzf_comprun $context_name $query ...$fzf_prefinal_opt # Pass args correctly to custom function
} else if ($env.TMUX_PANE? | default '' | into string | is-not-empty) and (($env.FZF_TMUX? | default 0) != 0 or ($env.FZF_TMUX_OPTS? | is-not-empty)) {
# Running inside tmux, use fzf-tmux
let final_fzf_opts = if ($env.FZF_TMUX_OPTS? | is-not-empty) {
$env.FZF_TMUX_OPTS | split row ' ' | append ['--'] | append $fzf_prefinal_opt
} else {
# Use the default -d option with the configured height for fzf-tmux
['-d', $height_opt, '--'] | append $fzf_prefinal_opt
}
if $has_walker or ($stdin_content == null) {
with-env { FZF_DEFAULT_OPTS: $fzf_default_opts, FZF_DEFAULT_OPTS_FILE: '' } { fzf-tmux ...$final_fzf_opts }
} else {
$stdin_content | with-env { FZF_DEFAULT_OPTS: $fzf_default_opts, FZF_DEFAULT_OPTS_FILE: '' } { fzf-tmux ...$final_fzf_opts }
}
} else {
# Not in tmux or not configured for fzf-tmux, use fzf directly
let final_fzf_opts = $fzf_prefinal_opt
if $has_walker or ($stdin_content == null) {
with-env { FZF_DEFAULT_OPTS: $fzf_default_opts, FZF_DEFAULT_OPTS_FILE: '' } { fzf ...$final_fzf_opts }
} else {
$stdin_content | with-env { FZF_DEFAULT_OPTS: $fzf_default_opts, FZF_DEFAULT_OPTS_FILE: '' } { fzf ...$final_fzf_opts }
}
}
}
# Generate host list for ssh/telnet
def __fzf_list_hosts [] {
# Translate the Zsh pipeline using Nu commands and external tools
let ssh_configs = try { open ~/.ssh/config | lines } catch { [] }
let ssh_configs_d = try { open ~/.ssh/config.d/* | lines } catch { [] }
let ssh_config_global = try { open /etc/ssh/ssh_config | lines } catch { [] }
let known_hosts = try { open ~/.ssh/known_hosts | lines } catch { [] }
let hosts_file = try { open /etc/hosts | lines } catch { [] }
[
(
# Process ssh config files
$ssh_configs | append $ssh_configs_d | append $ssh_config_global
# NOTE: str lowercase is new from Nushell 0.114.0. Please upgrade to Nushell >=0.114.0 if you are seeing `extra positional argument` error regarding `str lowercase` in this block.
| where {|it| ($it | str lowercase | str starts-with 'host') or ($it | str lowercase | str starts-with 'hostname') }
| parse --regex '^\s*host(?:name)?\s+(?<hosts>.+)' # Extract hosts after keyword
| default { hosts: null } # Handle lines that don't match regex
| get hosts
| where {|it| $it != null }
| split row ' '
| where {|it| not ($it =~ '[*?%]') } # Exclude patterns containing *, ?, or %
)
(
# Process known_hosts file
$known_hosts | parse --regex '^(?:\[)?(?<hosts>[a-z0-9.,:_-]+)' # Extract hostnames (possibly in [], possibly comma-separated) - added underscore
| default { hosts: null }
| get hosts
| where {|it| $it != null }
| each { |it| $it | split row ',' } # Split comma-separated hosts if any
| flatten
)
(
# Process /etc/hosts file
$hosts_file | where { |it| not ($it | str starts-with '#') } # Ignore comments
| where { |it| not ($it | str trim | is-empty) } # Ignore empty lines
| where { |it| not ($it | str contains '0.0.0.0') } # Ignore 0.0.0.0
| str replace --regex '#.*$' '' # Remove trailing comments
| parse --regex '^\s*\S+\s+(?<hosts>.+)' # Extract hosts part (after IP)
| default { hosts: null }
| get hosts
| where {|it| $it != null }
| split row ' ' # Split multiple hosts on the same line
)
]
| flatten # Combine all lists into a single stream
| where {|it| not ($it | is-empty) } # Remove empty entries
| sort | uniq # Sort and remove duplicates
}
# Base function for path/directory completion
def __fzf_generic_path_completion [ prefix: string # The text before the trigger
, fzf_opts_arg: list<string> # Extra options for fzf
, suffix: string # Suffix to add to selection (e.g. , "/")
] {
# --- Determine walker root and initial query from the prefix ---
mut walker_root = "."
mut initial_query = ""
if ($prefix | is-empty) {
# Case: "**"
$walker_root = "."
$initial_query = ""
} else if ($prefix | str contains (char separator)) {
# Case: "dir/subdir/partial**" or "dir/**"
$walker_root = $prefix | path dirname
$initial_query = $prefix | path basename
# Handle edge case where prefix ends with separator, e.g., "dir/"
if ($prefix | str ends-with (char separator)) {
# Remove trailing separator to get the intended directory
$walker_root = $prefix | str substring 0..-2
$initial_query = ""
}
# Ensure walker_root isn't empty if prefix was like "/file**"
# or if path dirname returned empty string for some reason (e.g. prefix="file/")
if ($walker_root | is-empty) {
if ($prefix | str starts-with (char separator)) {
$walker_root = (char separator)
} else if ($prefix | str ends-with (char separator)) {
$walker_root = $prefix | str substring 0..-2
} else { $walker_root = "." } # Fallback if dirname weirdly fails
}
} else {
# Case: "partial**" (no slashes)
$walker_root = "."
$initial_query = $prefix
}
# --- Prepare FZF options ---
let completion_type_opts = if $suffix == '/' {
$env.FZF_COMPLETION_DIR_OPTS? | default '' | split row ' ' | where {not ($in | is-empty)}
} else {
$env.FZF_COMPLETION_PATH_OPTS? | default '' | split row ' ' | where {not ($in | is-empty)}
}
let walker_type = if ($suffix == '/') {
"dir,follow"
} else {
"file,dir,follow,hidden"
}
# Expand tilde so fzf receives a valid absolute path as walker-root
let needs_tilde_rewrite = ($walker_root | str starts-with '~')
let walker_root_expanded = ($walker_root | path expand)
# Use the 'walker_root' calculated at the beginning
let fzf_all_opts = ["--scheme=path", "--walker", $walker_type, "--walker-root", $walker_root_expanded] | append $fzf_opts_arg
| append $completion_type_opts
# Call FZF run
let fzf_selection = ( __fzf_comprun "fzf-path-completion-walker" $initial_query $fzf_all_opts ) | str trim
# --- Return Result ---
if ($fzf_selection | is-not-empty) {
# Restore tilde prefix if the user originally typed ~/
let home = $nu.home-dir | path expand
let result = if $needs_tilde_rewrite {
$fzf_selection | lines | each {|line| $line | str replace $home '~' } | str join ' '
} else {
$fzf_selection | lines | str join ' '
}
[$result]
} else {
[]
}
}
# Specific path completion wrapper
def _fzf_path_completion [prefix: string] {
# Zsh args: base, lbuf, _fzf_compgen_path, "-m", "", " "
# Nu: prefix, empty command name (use find), ["-m"], "", " "
__fzf_generic_path_completion $prefix ["-m"] ""
}
# General completion helper for commands that feed a list to fzf
# This is called by ssh, kill, and user-defined completers.
def _fzf_complete [ query: string # The initial query string for fzf
, data_gen_closure: closure # Closure that generates candidates
, fzf_opts_arg: list<string> # Extra options for fzf (like -m, +m)
, --post_process_closure: closure # Closure to process the selected item (optional)
] {
# Generate candidates using the provided command
let candidates = try {
do $data_gen_closure
} catch {
# Capture the actual error object provided by the catch block
let actual_error = $in
# Print a more informative error message including the actual error details
print -e $"Error executing data_gen closure. Closure code: ($data_gen_closure). Actual error: ($actual_error)"
[]
}
# Run fzf and get selection
let fzf_selection = $candidates | to text
| __fzf_comprun "fzf-helper" $query $fzf_opts_arg
| str trim # Trim potential trailing newline from fzf
# Apply post-processing if closure provided and selection is not empty
let processed_selection = if ($fzf_selection | is-not-empty) and ($post_process_closure | is-not-empty) {
# Call the post-processing closure with the selection
try {
do $post_process_closure $fzf_selection
} catch {
print -e $"Error executing post_process closure: ($post_process_closure)"
$fzf_selection # Return original selection on error
}
} else {
$fzf_selection
}
if not ($processed_selection | is-empty) {
[($processed_selection | lines | str join ' ')]
} else {
[]
}
}
# SSH/Telnet completion
def _fzf_complete_ssh [ prefix: string
, input_line_before_trigger: string
] {
let words = ($input_line_before_trigger | split row ' ')
let word_count = $words | length
# Find the index of the word being completed (which is the prefix)
# If prefix is empty, completion happens after a space, index is word_count
# If prefix is not empty, it's the last word, index is word_count - 1
let completion_index = if ($prefix | is-empty) { $word_count } else { $word_count - 1 }
mut handled = false
mut completion_result = [] # List of completion strings to return
# Check for -i, -F, -E flags immediately preceding the cursor position
if $completion_index > 0 {
let prev_arg = ($words | get ($completion_index - 1))
if ($prev_arg in ['-i', '-F', '-E']) {
$handled = true
# Call path completion with the current prefix
$completion_result = (_fzf_path_completion $prefix)
}
}
# If not handled by path completion, do host completion
if not $handled {
let user_part = if ($prefix | str contains "@") { ($prefix | split row "@" | first) + "@" } else { "" }
# The part after '@' (or the whole prefix if no '@') is the initial query for fzf
let query = if ($prefix | str contains "@") { $prefix | split row "@" | last } else { $prefix }
let host_candidates_gen = {||
__fzf_list_hosts
| each {|host_item| $user_part + $host_item } # Prepend user@ if present in prefix
}
# Zsh options: +m -- ; Nu: pass ["+m"]
# Pass the host part of the prefix to _fzf_complete for the initial query
let selected_host = (_fzf_complete $query $host_candidates_gen ["+m"]) # Pass host_prefix here
if not ($selected_host | is-empty) {
$completion_result = $selected_host # _fzf_complete returns a list
}
}
$completion_result
}
# Kill completion post-processor (extracts PID)
def _fzf_complete_kill_post_get_pid [selected_line: string] {
# Assuming standard ps output where PID is the second column
$selected_line | lines | each { $in | from ssv --noheaders | get 0.column1 } | to text
}
# Kill completion to get process PID
def _fzf_complete_kill [query: string] {
let ps_gen_closure = {|| # Define ps generator as a closure
# Try standard ps, then busybox, then cygwin format approximation
# Use `^ps` to ensure external command execution
try {
^ps -eo user,pid,ppid,start,time,command | complete | if $in.exit_code == 0 { $in.stdout | lines } else { error make {msg: "ps failed"} }
} catch {
try {
^ps -eo user,pid,ppid,time,args | complete | if $in.exit_code == 0 { $in.stdout | lines } else { error make {msg: "ps failed"} }
} catch {
try {
^ps --everyone --full --windows | complete | if $in.exit_code == 0 { $in.stdout | lines } else { error make {msg: "ps failed"} }
} catch {
print -e "Error: ps command failed."
[] # Return empty list on failure
}
}
}
}
# Note: Complex Zsh FZF bindings for kill (click-header transformer) are omitted for simplicity.
# Users can set custom bindings via FZF_DEFAULT_OPTS if needed.
let kill_post_closure = {|selected_line| _fzf_complete_kill_post_get_pid $selected_line }
let fzf_opts = ["-m", "--header-lines=1", "--no-preview", "--wrap", "--color", "fg:dim,nth:regular"]
_fzf_complete $query $ps_gen_closure $fzf_opts --post_process_closure $kill_post_closure
}
# --- Main FZF External Completer ---
# This function is registered with Nushell's external completion system.
# It gets called when Tab is pressed.
let fzf_external_completer = {|spans|
let trigger: string = $env.FZF_COMPLETION_TRIGGER? | default '**'
if ($trigger | is-empty) { return null } # Cannot work with empty trigger
if (($spans | length ) == 0) { return null } # Nothing to complete
let last_span = $spans | last
if ($last_span | str ends-with $trigger) {
# --- Trigger Found ---
# Skip sudo to determine the actual command
let cmd_spans = if ($spans | first) == "sudo" { $spans | skip 1 } else { $spans }
let cmd_word = ($cmd_spans | first | default "")
# Calculate the prefix (part before the trigger in the last span)
let prefix = $last_span | str substring 0..(-1 * ($trigger | str length) - 1)
# Reconstruct the line content *before* the trigger for context
# This is an approximation based on spans
let line_without_trigger = $cmd_spans | take (($cmd_spans | length) - 1) | append $prefix | str join ' '
# --- Dispatch to Completer ---
mut completion_results = [] # Will hold the list of strings from the completer
# Check for user-defined completer in $env.FZF_COMPLETERS first.
# Users can define custom completers in their config.nu as a record of closures:
# $env.FZF_COMPLETERS = { git: {|prefix, spans| ... }, docker: {|prefix, spans| ... } }
# Each closure receives the prefix (text before the trigger) and the full
# command spans (e.g. ["pacman", "-S", "vim**"]), and should return either:
# - a list of candidate strings, or
# - a record { candidates: [...], opts: [...], post: {|sel| ...} } to pass
# custom fzf options and/or a post-processing closure.
# See shell/completion-examples.nu for examples.
let user_completers = ($env.FZF_COMPLETERS? | default {})
if ($cmd_word in $user_completers) {
let user_gen = ($user_completers | get $cmd_word)
let user_result = (do $user_gen $prefix $cmd_spans)
if ($user_result | describe | str starts-with 'record') {
let candidates = ($user_result | get candidates)
let fzf_opts = ($user_result | get opts? | default ["-m"])
let post = ($user_result | get post? | default null)
if ($post != null) {
$completion_results = (_fzf_complete $prefix {|| $candidates} $fzf_opts --post_process_closure $post)
} else {
$completion_results = (_fzf_complete $prefix {|| $candidates} $fzf_opts)
}
} else {
$completion_results = (_fzf_complete $prefix {|| $user_result} ["-m"])
}
} else {
match $cmd_word {
"ssh" | "scp" | "sftp" | "telnet" => { $completion_results = (_fzf_complete_ssh $prefix $line_without_trigger) }
"kill" => { $completion_results = (_fzf_complete_kill $prefix) }
_ if ($cmd_word in $env.FZF_COMPLETION_DIR_COMMANDS) => {
$completion_results = (__fzf_generic_path_completion $prefix [] "/")
}
_ => {
# Default to path completion if no specific command matches
$completion_results = (_fzf_path_completion $prefix)
}
}
}
# --- Return Results ---
# The _fzf_... functions return a list of completion strings.
# Nushell's completer expects the suggestions for the token being completed (prefix + trigger).
# The results from the helper functions should be the final desired strings.
# We don't need to manually add spaces; Nushell handles that.
$completion_results # Return the list directly
} else {
# --- Trigger Not Found ---
# Return null to let Nushell fall back to other completers (e.g., default file completion).
null
}
}
# --- WRAPPER AND REGISTRATION ---
# Guard against re-sourcing: wrapping the completer multiple times would
# nest wrappers and grow the call chain on every reload.
if ($env.__fzf_completer_registered? | default false) != true {
# Get the currently configured external completer, if any exists
let previous_external_completer = $env.config? | get completions? | get external? | get completer?
# Define the new wrapper completer
let fzf_wrapper_completer = {|spans|
# 1. Try the FZF completer logic first
let fzf_result = do $fzf_external_completer $spans
# 2. If FZF returned a result (a list, even an empty one), return it.
# `null` means FZF didn't handle it because the trigger wasn't present.
if $fzf_result != null {
$fzf_result
} else {
# 3. FZF didn't handle it, so call the previous completer (if it exists).
if $previous_external_completer != null {
do $previous_external_completer $spans
} else {
# 4. No previous completer, and FZF didn't handle it. Return null.
null
}
}
}
# Register the new wrapper completer
# This ensures external completions are enabled and sets our wrapper.
$env.config = $env.config | upsert completions {
external: {
enable: true
completer: $fzf_wrapper_completer
}
}
$env.__fzf_completer_registered = true
}
# vim: set sts=2 ts=2 sw=2 tw=120 et :
+476
View File
@@ -0,0 +1,476 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ completion.zsh
#
# - $FZF_COMPLETION_TRIGGER (default: '**')
# - $FZF_COMPLETION_OPTS (default: empty)
# - $FZF_COMPLETION_PATH_OPTS (default: empty)
# - $FZF_COMPLETION_DIR_OPTS (default: empty)
# Both branches of the following `if` do the same thing -- define
# __fzf_completion_options such that `eval $__fzf_completion_options` sets
# all options to the same values they currently have. We'll do just that at
# the bottom of the file after changing options to what we prefer.
#
# IMPORTANT: Until we get to the `emulate` line, all words that *can* be quoted
# *must* be quoted in order to prevent alias expansion. In addition, code must
# be written in a way works with any set of zsh options. This is very tricky, so
# careful when you change it.
#
# Start by loading the builtin zsh/parameter module. It provides `options`
# associative array that stores current shell options.
if 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then
# This is the fast branch and it gets taken on virtually all Zsh installations.
#
# ${(kv)options[@]} expands to array of keys (option names) and values ("on"
# or "off"). The subsequent expansion# with (j: :) flag joins all elements
# together separated by spaces. __fzf_completion_options ends up with a value
# like this: "options=(shwordsplit off aliases on ...)".
__fzf_completion_options="options=(${(j: :)${(kv)options[@]}})"
else
# This branch is much slower because it forks to get the names of all
# zsh options. It's possible to eliminate this fork but it's not worth the
# trouble because this branch gets taken only on very ancient or broken
# zsh installations.
() {
# That `()` above defines an anonymous function. This is essentially a scope
# for local parameters. We use it to avoid polluting global scope.
'local' '__fzf_opt'
__fzf_completion_options="setopt"
# `set -o` prints one line for every zsh option. Each line contains option
# name, some spaces, and then either "on" or "off". We just want option names.
# Expansion with (@f) flag splits a string into lines. The outer expansion
# removes spaces and everything that follow them on every line. __fzf_opt
# ends up iterating over option names: shwordsplit, aliases, etc.
for __fzf_opt in "${(@)${(@f)$(set -o)}%% *}"; do
if [[ -o "$__fzf_opt" ]]; then
# Option $__fzf_opt is currently on, so remember to set it back on.
__fzf_completion_options+=" -o $__fzf_opt"
else
# Option $__fzf_opt is currently off, so remember to set it back off.
__fzf_completion_options+=" +o $__fzf_opt"
fi
done
# The value of __fzf_completion_options here looks like this:
# "setopt +o shwordsplit -o aliases ..."
}
fi
# Enable the default zsh options (those marked with <Z> in `man zshoptions`)
# but without `aliases`. Aliases in functions are expanded when functions are
# defined, so if we disable aliases here, we'll be sure to have no pesky
# aliases in any of our functions. This way we won't need prefix every
# command with `command` or to quote every word to defend against global
# aliases. Note that `aliases` is not the only option that's important to
# control. There are several others that could wreck havoc if they are set
# to values we don't expect. With the following `emulate` command we
# sidestep this issue entirely.
'builtin' 'emulate' 'zsh' && 'builtin' 'setopt' 'no_aliases'
# This brace is the start of try-always block. The `always` part is like
# `finally` in lesser languages. We use it to *always* restore user options.
{
# The 'emulate' command should not be placed inside the interactive if check;
# placing it there fails to disable alias expansion. See #3731.
if [[ -o interactive ]]; then
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
#
# _fzf_compgen_path() {
# echo "$1"
# command find -L "$1" \
# -name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
# -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
# }
#
# _fzf_compgen_dir() {
# command find -L "$1" \
# -name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
# -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
# }
###########################################################
#----BEGIN INCLUDE common.sh
# NOTE: Do not directly edit this section, which is copied from "common.sh".
# To modify it, one can edit "common.sh" and run "./update.sh" to apply
# the changes. See code comments in "common.sh" for the implementation details.
__fzf_defaults() {
builtin printf '%s\n' "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
builtin printf '%s\n' "${FZF_DEFAULT_OPTS-} $2"
}
__fzf_exec_awk() {
if [[ -z ${__fzf_awk-} ]]; then
__fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
__fzf_awk=/usr/xpg4/bin/awk
elif command -v mawk > /dev/null 2>&1; then
local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] &&
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi
fi
LC_ALL=C exec "$__fzf_awk" "$@"
}
#----END INCLUDE
__fzf_comprun() {
if [[ "$(type _fzf_comprun 2>&1)" =~ function ]]; then
_fzf_comprun "$@"
elif [ -n "${TMUX_PANE-}" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "${FZF_TMUX_OPTS-}" ]; }; then
shift
if [ -n "${FZF_TMUX_OPTS-}" ]; then
fzf-tmux ${(Q)${(Z+n+)FZF_TMUX_OPTS}} -- "$@"
else
fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%} -- "$@"
fi
else
shift
fzf "$@"
fi
}
# Extract the name of the command. e.g. ls; foo=1 ssh **<tab>
__fzf_extract_command() {
# Control completion with the "compstate" parameter, insert and list nothing
compstate[insert]=
compstate[list]=
cmd_word="${(Q)words[1]}"
}
__fzf_generic_path_completion() {
local base lbuf compgen fzf_opts suffix tail dir leftover matches
base=$1
lbuf=$2
compgen=$3
fzf_opts=$4
suffix=$5
tail=$6
setopt localoptions nonomatch
if [[ $base = *'$('* ]] || [[ $base = *'<('* ]] || [[ $base = *'>('* ]] || [[ $base = *':='* ]] || [[ $base = *'`'* ]]; then
return
fi
eval "base=$base" 2> /dev/null || return
[[ $base = *"/"* ]] && dir="$base"
while [ 1 ]; do
if [[ -z "$dir" || -d ${dir} ]]; then
leftover=${base/#"$dir"}
leftover=${leftover/#\/}
[ -z "$dir" ] && dir='.'
[ "$dir" != "/" ] && dir="${dir/%\//}"
matches=$(
export FZF_DEFAULT_OPTS
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --scheme=path" "${FZF_COMPLETION_OPTS-}")
unset FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS_FILE
if [[ $compgen =~ dir ]]; then
rest=${FZF_COMPLETION_DIR_OPTS-}
else
rest=${FZF_COMPLETION_PATH_OPTS-}
fi
if declare -f "$compgen" > /dev/null; then
eval "$compgen $(printf %q "$dir")" | __fzf_comprun "$cmd_word" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" ${(Q)${(Z+n+)rest}}
else
if [[ $compgen =~ dir ]]; then
walker=dir,follow
else
walker=file,dir,follow,hidden
fi
__fzf_comprun "$cmd_word" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" --walker "$walker" --walker-root="$dir" ${(Q)${(Z+n+)rest}} < /dev/tty
fi | while read -r item; do
item="${item%$suffix}$suffix"
echo -n -E "${(q)item} "
done
)
matches=${matches% }
if [ -n "$matches" ]; then
LBUFFER="$lbuf$matches$tail"
fi
zle reset-prompt
break
fi
dir=$(dirname "$dir")
dir=${dir%/}/
done
}
_fzf_path_completion() {
__fzf_generic_path_completion "$1" "$2" _fzf_compgen_path \
"-m" "" " "
}
_fzf_dir_completion() {
__fzf_generic_path_completion "$1" "$2" _fzf_compgen_dir \
"" "/" ""
}
_fzf_feed_fifo() {
command rm -f "$1"
mkfifo "$1"
cat <&0 > "$1" &|
}
_fzf_complete() {
setopt localoptions ksh_arrays
# Split arguments around --
local args rest str_arg i sep
args=("$@")
sep=
for i in {0..${#args[@]}}; do
if [[ "${args[$i]-}" = -- ]]; then
sep=$i
break
fi
done
if [[ -n "$sep" ]]; then
str_arg=
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
args=("${args[@]:0:$sep}")
else
str_arg=$1
args=()
shift
rest=("$@")
fi
local fifo lbuf matches post
fifo="${TMPDIR:-/tmp}/fzf-complete-fifo-$$"
lbuf=${rest[0]}
post="${funcstack[1]}_post"
type $post > /dev/null 2>&1 || post=cat
_fzf_feed_fifo "$fifo"
matches=$(
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse" "${FZF_COMPLETION_OPTS-} $str_arg") \
FZF_DEFAULT_OPTS_FILE='' \
__fzf_comprun "$cmd_word" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
if [ -n "$matches" ]; then
LBUFFER="$lbuf$matches"
fi
command rm -f "$fifo"
}
# To use custom hostname lists, override __fzf_list_hosts.
# The function is expected to print hostnames, one per line as well as in the
# desired sorting and with any duplicates removed, to standard output.
if ! declare -f __fzf_list_hosts > /dev/null; then
__fzf_list_hosts() {
command sort -u \
<(
# Note: To make the pathname expansion of "~/.ssh/config.d/*" work
# properly, we need to adjust the related shell options. We need to
# unset "NO_GLOB" (or reset "GLOB"), which disable the pathname
# expansion totally. We need to unset "DOT_GLOB" and set "CASE_GLOB"
# to avoid matching unwanted files. We need to set "NULL_GLOB" to
# avoid attempting to read the literal filename '~/.ssh/config.d/*'
# when no matching is found.
setopt GLOB NO_DOT_GLOB CASE_GLOB NO_NOMATCH NULL_GLOB
__fzf_exec_awk '
# Note: mawk <= 1.3.3-20090705 does not support the POSIX brackets of
# the form [[:blank:]], and Ubuntu 18.04 LTS still uses this
# 16-year-old mawk unfortunately. We need to use [ \t] instead.
match(tolower($0), /^[ \t]*host(name)?[ \t]*[ \t=]/) {
$0 = substr($0, RLENGTH + 1) # Remove "Host(name)?=?"
sub(/#.*/, "")
for (i = 1; i <= NF; i++)
if ($i !~ /[*?%]/)
print $i
}
' ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null
) \
<(
__fzf_exec_awk -F ',' '
match($0, /^[][a-zA-Z0-9.,:-]+/) {
$0 = substr($0, 1, RLENGTH)
gsub(/[][]|:[^,]*/, "")
for (i = 1; i <= NF; i++)
print $i
}
' ~/.ssh/known_hosts 2> /dev/null
) \
<(
__fzf_exec_awk '
{
sub(/#.*/, "")
for (i = 2; i <= NF; i++)
if ($i != "0.0.0.0")
print $i
}
' /etc/hosts 2> /dev/null
)
}
fi
_fzf_complete_telnet() {
_fzf_complete +m -- "$@" < <(__fzf_list_hosts)
}
# The first and the only argument is the LBUFFER without the current word that contains the trigger.
# The current word without the trigger is in the $prefix variable passed from the caller.
_fzf_complete_ssh() {
local -a tokens
tokens=(${(z)1})
case ${tokens[-1]} in
-i|-F|-E)
_fzf_path_completion "$prefix" "$1"
;;
*)
local user
[[ $prefix =~ @ ]] && user="${prefix%%@*}@"
_fzf_complete +m -- "$@" < <(__fzf_list_hosts | __fzf_exec_awk -v user="$user" '{print user $0}')
;;
esac
}
_fzf_complete_export() {
_fzf_complete -m -- "$@" < <(
declare -xp | sed 's/=.*//' | sed 's/.* //'
)
}
_fzf_complete_unset() {
_fzf_complete -m -- "$@" < <(
declare -xp | sed 's/=.*//' | sed 's/.* //'
)
}
_fzf_complete_unalias() {
_fzf_complete +m -- "$@" < <(
alias | sed 's/=.*//'
)
}
_fzf_complete_kill() {
local transformer
transformer='
if [[ $FZF_KEY =~ ctrl|alt|shift ]] && [[ -n $FZF_NTH ]]; then
nths=( ${FZF_NTH//,/ } )
new_nths=()
found=0
for nth in ${nths[@]}; do
if [[ $nth = $FZF_CLICK_HEADER_NTH ]]; then
found=1
else
new_nths+=($nth)
fi
done
[[ $found = 0 ]] && new_nths+=($FZF_CLICK_HEADER_NTH)
new_nths=${new_nths[*]}
new_nths=${new_nths// /,}
echo "change-nth($new_nths)+change-prompt($new_nths> )"
else
if [[ $FZF_NTH = $FZF_CLICK_HEADER_NTH ]]; then
echo "change-nth()+change-prompt(> )"
else
echo "change-nth($FZF_CLICK_HEADER_NTH)+change-prompt($FZF_CLICK_HEADER_WORD> )"
fi
fi
'
_fzf_complete -m --header-lines=1 --no-preview --wrap --color fg:dim,nth:regular \
--bind "click-header:transform:$transformer" -- "$@" < <(
command ps -eo user,pid,ppid,start,time,command 2> /dev/null ||
command ps -eo user,pid,ppid,time,args 2> /dev/null || # For BusyBox
command ps --everyone --full --windows # For cygwin
)
}
_fzf_complete_kill_post() {
__fzf_exec_awk '{print $2}'
}
fzf-completion() {
local tokens prefix trigger tail matches lbuf d_cmds cursor_pos cmd_word
setopt localoptions noshwordsplit noksh_arrays noposixbuiltins
# http://zsh.sourceforge.net/FAQ/zshfaq03.html
# http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
tokens=(${(z)LBUFFER})
if [ ${#tokens} -lt 1 ]; then
zle ${fzf_default_completion:-expand-or-complete}
return
fi
# Explicitly allow for empty trigger.
trigger=${FZF_COMPLETION_TRIGGER-'**'}
[[ -z $trigger && ${LBUFFER[-1]} == ' ' ]] && tokens+=("")
# When the trigger starts with ';', it becomes a separate token
if [[ ${LBUFFER} = *"${tokens[-2]-}${tokens[-1]}" ]]; then
tokens[-2]="${tokens[-2]-}${tokens[-1]}"
tokens=(${tokens[0,-2]})
fi
lbuf=$LBUFFER
tail=${LBUFFER:$(( ${#LBUFFER} - ${#trigger} ))}
# Trigger sequence given
if [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
d_cmds=(${=FZF_COMPLETION_DIR_COMMANDS-cd pushd rmdir})
{
cursor_pos=$CURSOR
# Move the cursor before the trigger to preserve word array elements when
# trigger chars like ';' or '`' would otherwise reset the 'words' array.
CURSOR=$((cursor_pos - ${#trigger} - 1))
# Check if at least one completion system (old or new) is active.
# If at least one user-defined completion widget is detected, nothing will
# be completed if neither the old nor the new completion system is enabled.
# In such cases, the 'zsh/compctl' module is loaded as a fallback.
if ! zmodload -F zsh/parameter p:functions 2>/dev/null || ! (( ${+functions[compdef]} )); then
zmodload -F zsh/compctl 2>/dev/null
fi
# Create a completion widget to access the 'words' array (man zshcompwid)
zle -C __fzf_extract_command .complete-word __fzf_extract_command
zle __fzf_extract_command
} always {
CURSOR=$cursor_pos
# Delete the completion widget
zle -D __fzf_extract_command 2>/dev/null
}
[ -z "$trigger" ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}}
if [[ $prefix = *'$('* ]] || [[ $prefix = *'<('* ]] || [[ $prefix = *'>('* ]] || [[ $prefix = *':='* ]] || [[ $prefix = *'`'* ]]; then
return
fi
[ -n "${tokens[-1]}" ] && lbuf=${lbuf:0:-${#tokens[-1]}}
if eval "noglob type _fzf_complete_${cmd_word} >/dev/null"; then
prefix="$prefix" eval _fzf_complete_${cmd_word} ${(q)lbuf}
zle reset-prompt
elif [ ${d_cmds[(i)$cmd_word]} -le ${#d_cmds} ]; then
_fzf_dir_completion "$prefix" "$lbuf"
else
_fzf_path_completion "$prefix" "$lbuf"
fi
# Fall back to default completion
else
zle ${fzf_default_completion:-expand-or-complete}
fi
}
[ -z "$fzf_default_completion" ] && {
binding=$(bindkey '^I')
[[ $binding =~ 'undefined-key' ]] || fzf_default_completion=$binding[(s: :w)2]
unset binding
}
# Normal widget
zle -N fzf-completion
bindkey '^I' fzf-completion
fi
} always {
# Restore the original options.
eval $__fzf_completion_options
'unset' '__fzf_completion_options'
}
+195
View File
@@ -0,0 +1,195 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ key-bindings.bash
#
# - $FZF_CTRL_T_COMMAND
# - $FZF_CTRL_T_OPTS
# - $FZF_CTRL_R_COMMAND
# - $FZF_CTRL_R_OPTS
# - $FZF_ALT_C_COMMAND
# - $FZF_ALT_C_OPTS
if [[ $- =~ i ]]; then
# Key bindings
# ------------
#----BEGIN shfmt
#----BEGIN INCLUDE common.sh
# NOTE: Do not directly edit this section, which is copied from "common.sh".
# To modify it, one can edit "common.sh" and run "./update.sh" to apply
# the changes. See code comments in "common.sh" for the implementation details.
__fzf_defaults() {
builtin printf '%s\n' "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
builtin printf '%s\n' "${FZF_DEFAULT_OPTS-} $2"
}
__fzf_exec_awk() {
if [[ -z ${__fzf_awk-} ]]; then
__fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
__fzf_awk=/usr/xpg4/bin/awk
elif command -v mawk > /dev/null 2>&1; then
local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] &&
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi
fi
LC_ALL=C exec "$__fzf_awk" "$@"
}
#----END INCLUDE
__fzf_select__() {
FZF_DEFAULT_COMMAND=${FZF_CTRL_T_COMMAND:-} \
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=file,dir,follow,hidden --scheme=path" "${FZF_CTRL_T_OPTS-} -m") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) "$@" |
while read -r item; do
printf '%q ' "$item" # escape special chars
done
}
__fzfcmd() {
[[ -n ${TMUX_PANE-} ]] && { [[ ${FZF_TMUX:-0} != 0 ]] || [[ -n ${FZF_TMUX_OPTS-} ]]; } &&
echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
}
fzf-file-widget() {
local selected="$(__fzf_select__ "$@")"
READLINE_LINE="${READLINE_LINE:0:READLINE_POINT}$selected${READLINE_LINE:READLINE_POINT}"
READLINE_POINT=$((READLINE_POINT + ${#selected}))
}
__fzf_cd__() {
local dir
dir=$(
FZF_DEFAULT_COMMAND=${FZF_ALT_C_COMMAND:-} \
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=dir,follow,hidden --scheme=path" "${FZF_ALT_C_OPTS-} +m") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd)
) && printf 'builtin cd -- %q' "$(builtin unset CDPATH && builtin cd -- "$dir" && builtin pwd)"
}
__fzf_history_delete() {
[[ -s $1 ]] || return
local offsets
offsets=($(sort -rnu "$1"))
for offset in "${offsets[@]}"; do
builtin history -d "$offset"
done
if [[ ${#offsets[@]} -gt 0 ]] && shopt -q histappend; then
builtin history -w
fi
}
if command -v perl > /dev/null; then
__fzf_history__() {
local output script deletefile
deletefile=$(mktemp)
script='BEGIN { getc; $/ = "\n\t"; $HISTCOUNT = $ENV{last_hist} + 1 } s/^[ *]//; s/\n/\n\t/gm; print $HISTCOUNT - $. . "\t$_" if !$seen{$_}++'
output=$(
set +o pipefail
builtin fc -lnr -2147483648 |
last_hist=$(HISTTIMEFORMAT='' builtin history 1) command perl -n -l0 -e "$script" |
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,alt-r:toggle-raw --wrap-sign '"$'\t'"↳ ' --highlight-line --bind 'shift-delete:execute-silent(cat {+f1} >> \"$deletefile\")+exclude-multi' --multi ${FZF_CTRL_R_OPTS-} --read0") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) --query "$READLINE_LINE"
)
__fzf_history_delete "$deletefile"
command rm -f "$deletefile"
[[ -n $output ]] || return
READLINE_LINE=$(command perl -pe 's/^\d*\t//' <<< "$output")
if [[ -z $READLINE_POINT ]]; then
echo "$READLINE_LINE"
else
READLINE_POINT=0x7fffffff
fi
}
else # awk - fallback for POSIX systems
__fzf_history__() {
local output script deletefile
deletefile=$(mktemp)
[[ $(HISTTIMEFORMAT='' builtin history 1) =~ [[:digit:]]+ ]] # how many history entries
script='function P(b) { ++n; sub(/^[ *]/, "", b); if (!seen[b]++) { printf "%d\t%s%c", '$((BASH_REMATCH + 1))' - n, b, 0 } }
NR==1 { b = substr($0, 2); next }
/^\t/ { P(b); b = substr($0, 2); next }
{ b = b RS $0 }
END { if (NR) P(b) }'
output=$(
set +o pipefail
builtin fc -lnr -2147483648 2> /dev/null | # ( $'\t '<lines>$'\n' )* ; <lines> ::= [^\n]* ( $'\n'<lines> )*
__fzf_exec_awk "$script" | # ( <counter>$'\t'<lines>$'\000' )*
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,alt-r:toggle-raw --wrap-sign '"$'\t'"↳ ' --highlight-line --bind 'shift-delete:execute-silent(cat {+f1} >> \"$deletefile\")+exclude-multi' --multi ${FZF_CTRL_R_OPTS-} --read0") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) --query "$READLINE_LINE"
)
__fzf_history_delete "$deletefile"
command rm -f "$deletefile"
[[ -n $output ]] || return
READLINE_LINE=${output#*$'\t'}
if [[ -z $READLINE_POINT ]]; then
echo "$READLINE_LINE"
else
READLINE_POINT=0x7fffffff
fi
}
fi
# Required to refresh the prompt after fzf
bind -m emacs-standard '"\C-\e(": redraw-current-line'
bind -m vi-command '"\C-z": emacs-editing-mode'
bind -m vi-insert '"\C-z": emacs-editing-mode'
bind -m emacs-standard '"\C-z": vi-editing-mode'
if ((BASH_VERSINFO[0] < 4)); then
# CTRL-T - Paste the selected file path into the command line
if [[ ${FZF_CTRL_T_COMMAND-x} != "" ]]; then
bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\C-\e(\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f\C-y\ey\C-_"'
bind -m vi-command '"\C-t": "\C-z\C-t\C-z"'
bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"'
fi
# CTRL-R - Paste the selected command from history into the command line
if [[ ${FZF_CTRL_R_COMMAND-x} != "" ]]; then
if [[ -n ${FZF_CTRL_R_COMMAND-} ]]; then
echo "warning: FZF_CTRL_R_COMMAND is set to a custom command, but custom commands are not yet supported for CTRL-R" >&2
fi
bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u`__fzf_history__`\e\C-e\C-\e("'
bind -m vi-command '"\C-r": "\C-z\C-r\C-z"'
bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
fi
else
# CTRL-T - Paste the selected file path into the command line
if [[ ${FZF_CTRL_T_COMMAND-x} != "" ]]; then
bind -m emacs-standard -x '"\C-t": fzf-file-widget'
bind -m vi-command -x '"\C-t": fzf-file-widget'
bind -m vi-insert -x '"\C-t": fzf-file-widget'
fi
# CTRL-R - Paste the selected command from history into the command line
if [[ ${FZF_CTRL_R_COMMAND-x} != "" ]]; then
if [[ -n ${FZF_CTRL_R_COMMAND-} ]]; then
echo "warning: FZF_CTRL_R_COMMAND is set to a custom command, but custom commands are not yet supported for CTRL-R" >&2
fi
bind -m emacs-standard -x '"\C-r": __fzf_history__'
bind -m vi-command -x '"\C-r": __fzf_history__'
bind -m vi-insert -x '"\C-r": __fzf_history__'
fi
fi
# ALT-C - cd into the selected directory
if [[ ${FZF_ALT_C_COMMAND-x} != "" ]]; then
bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\C-\e(\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d\C-y\ey\C-_"'
bind -m vi-command '"\ec": "\C-z\ec\C-z"'
bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
fi
#----END shfmt
fi
+219
View File
@@ -0,0 +1,219 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ key-bindings.fish
#
# - $FZF_CTRL_T_COMMAND
# - $FZF_CTRL_T_OPTS
# - $FZF_CTRL_R_COMMAND
# - $FZF_CTRL_R_OPTS
# - $FZF_ALT_C_COMMAND
# - $FZF_ALT_C_OPTS
# Key bindings
# ------------
function fzf_key_bindings
# The oldest supported fish version is 3.4.0. For this message being able to be
# displayed on older versions, the command substitution syntax $() should not
# be used anywhere in the script, otherwise the source command will fail.
if string match -qr -- '^[12]\\.|^3\\.[0-3]' $version
echo "fzf key bindings script requires fish version 3.4.0 or newer." >&2
return 1
else if not command -q fzf
echo "fzf was not found in path." >&2
return 1
end
function __fzf_defaults
# $argv[1]: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
# $argv[2..]: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
string join ' ' -- \
"--height $FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" $argv[1] \
(test -r "$FZF_DEFAULT_OPTS_FILE"; and string join -- ' ' <$FZF_DEFAULT_OPTS_FILE) \
$FZF_DEFAULT_OPTS $argv[2..]
end
function __fzfcmd
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
if test -n "$FZF_TMUX_OPTS"
echo "fzf-tmux $FZF_TMUX_OPTS -- "
else if test "$FZF_TMUX" = "1"
echo "fzf-tmux -d$FZF_TMUX_HEIGHT -- "
else
echo "fzf"
end
end
function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
set -l fzf_query ''
set -l prefix ''
set -l dir '.'
set -l -- match_regex '(?<fzf_query>[\\s\\S]*?(?=\\n?$)$)'
set -l -- prefix_regex '^-[^\\s=]+=|^-(?!-)\\S'
# Don't use option prefix if " -- " is preceded.
string match -qv -- '* -- *' (string sub -l (commandline -Cp) -- (commandline -p))
and set -- match_regex "(?<prefix>$prefix_regex)?$match_regex"
# Set $prefix and expanded $fzf_query with preserved trailing newlines.
if string match -qr -- '^\\d\\d+|^[4-9]' $version
# fish v4.0.0 and newer
string match -q -r -- $match_regex (commandline --current-token --tokens-expanded | string collect -N)
else
string match -q -r -- $match_regex (commandline --current-token --tokenize | string collect -N)
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\\(?=~)|\\\\(?=\\$\\w)' '')
end
if test -n "$fzf_query"
# Normalize path in $fzf_query, set $dir to the longest existing directory.
if string match -qr -- '^\\d\\d+|^4|^3\\.[5-9]' $version
# fish v3.5.0 and newer
set -- fzf_query (path normalize -- $fzf_query)
set -- dir $fzf_query
while not path is -d $dir
set -- dir (path dirname $dir)
end
else
string match -q -r -- '(?<fzf_query>^[\\s\\S]*?(?=\\n?$)$)' \
(string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\\n)$' '' $fzf_query | string collect -N)
set -- dir $fzf_query
while not test -d "$dir"
set -- dir (dirname -z -- "$dir" | string split0)
end
end
if not string match -q -- '.' $dir; or string match -qr -- '^\\.(/|$)' $fzf_query
# Strip $dir from $fzf_query - preserve trailing newlines.
if string match -qr -- '^\\d\\d+|^[4-9]' $version
# fish v4.0.0 and newer
string match -q -r -- '^'(string escape --style=regex -- $dir)'/?(?<fzf_query>[\\s\\S]*)' $fzf_query
else
string match -q -r -- '^/?(?<fzf_query>[\\s\\S]*?(?=\\n?$)$)' \
(string replace -- "$dir" '' $fzf_query | string collect -N)
end
end
end
string escape -n -- "$dir" "$fzf_query" "$prefix"
end
# Store current token in $dir as root for the 'find' command
function fzf-file-widget -d "List files and folders"
set -l commandline (__fzf_parse_commandline)
set -lx dir $commandline[1]
set -l fzf_query $commandline[2]
set -l prefix $commandline[3]
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
"--reverse --walker=file,dir,follow,hidden --scheme=path" \
"--multi $FZF_CTRL_T_OPTS --print0")
set -lx FZF_DEFAULT_COMMAND "$FZF_CTRL_T_COMMAND"
set -lx FZF_DEFAULT_OPTS_FILE
set -l result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0)
and commandline -rt -- (string join -- ' ' $prefix(string escape -n -- $result))' '
commandline -f repaint
end
function fzf-history-widget -d "Show command history"
set -l -- command_line (commandline)
set -l -- current_line (commandline -L)
set -l -- total_lines (count $command_line)
set -l -- fzf_query (string escape -- $command_line[$current_line])
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults '' \
'--with-nth=2.. --nth=2..,.. --scheme=history --multi --no-multi-line' \
'--no-wrap --wrap-sign="\t\t\t↳ " --preview-wrap-sign="↳ " --freeze-left=1' \
'--bind="alt-enter:become(set -g fzf_temp {+sf3..}; string join0 -- (string split0 -- <$fzf_temp | fish_indent -i); unlink $fzf_temp &>/dev/null)"' \
'--bind="alt-t:change-with-nth(1,3..|3..|2..)"' \
'--bind="shift-delete:execute-silent(eval builtin history delete -Ce -- (string escape -n -- (string split0 -- <{+sf3..})))+reload(eval $FZF_DEFAULT_COMMAND)"' \
"--bind=ctrl-r:toggle-sort,alt-r:toggle-raw --highlight-line $FZF_CTRL_R_OPTS" \
'--accept-nth=3.. --delimiter="\t" --tabstop=4 --read0 --print0 --with-shell='(status fish-path)\\ -c)
# Add dynamic preview options if preview command isn't already set by user
if string match -qvr -- '--preview[= ]' "$FZF_DEFAULT_OPTS"
# Prepend the options to allow user overrides
set -p -- FZF_DEFAULT_OPTS \
'--bind="focus,multi,resize:bg-transform:if test \\"$FZF_COLUMNS\\" -gt 100 -a \\\\( \\"$FZF_SELECT_COUNT\\" -gt 0 -o \\\\( -z \\"$FZF_WRAP\\" -a (string join0 -- <{f3..} | string length) -gt (math $FZF_COLUMNS - (switch $FZF_WITH_NTH; case 2..; echo 13; case 1,3..; echo 25; case 3..; echo 1; end)) \\\\) -o (string split0 -- <{sf3..} | fish_indent | count) -gt 1 \\\\); echo show-preview; else; echo hide-preview; end"' \
'--preview="test \\"$FZF_SELECT_COUNT\\" -gt 0; and string split0 -- <{+sf3..} | fish_indent (string match -q -- 3.\\\\* $version; or echo -- --only-indent) --ansi; and echo -n \\\\n; string collect -- \\\\#\\\\ {1} (string split0 -- <{sf3..}) | fish_indent --ansi"' \
'--preview-window="right,50%,wrap-word,follow,info,hidden"'
end
set -lx FZF_DEFAULT_OPTS_FILE
set -lx -- FZF_DEFAULT_COMMAND 'builtin history -z'
# Enable syntax highlighting colors on fish v4.3.3 and newer
if string match -qr -- '^\\d\\d+|^4\\.[4-9]|^4\\.3\\.[3-9]' $version
set -a -- FZF_DEFAULT_OPTS '--ansi'
set -a -- FZF_DEFAULT_COMMAND '--color=always --show-time=(set_color $fish_color_comment 2>/dev/null; or set_color normal)"%F %a %T%t%s%t"(set_color normal)'
else
set -a -- FZF_DEFAULT_COMMAND '--show-time="%F %a %T%t%s%t"'
end
# Merge history from other sessions before searching
test -z "$fish_private_mode"; and builtin history merge
if set -l result (eval $FZF_DEFAULT_COMMAND \| (__fzfcmd) --query=$fzf_query | string split0)
if test "$total_lines" -eq 1
commandline -- $result
else
set -l a (math $current_line - 1)
set -l b (math $current_line + 1)
commandline -- $command_line[1..$a] $result
commandline -a -- '' $command_line[$b..-1]
end
end
commandline -f repaint
end
function fzf-cd-widget -d "Change directory"
set -l commandline (__fzf_parse_commandline)
set -lx dir $commandline[1]
set -l fzf_query $commandline[2]
set -l prefix $commandline[3]
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
"--reverse --walker=dir,follow,hidden --scheme=path" \
"$FZF_ALT_C_OPTS --no-multi --print0")
set -lx FZF_DEFAULT_OPTS_FILE
set -lx FZF_DEFAULT_COMMAND "$FZF_ALT_C_COMMAND"
if set -l result (eval (__fzfcmd) --query=$fzf_query --walker-root=$dir | string split0)
cd -- $result
commandline -rt -- $prefix
end
commandline -f repaint
end
if not set -q FZF_CTRL_R_COMMAND; or test -n "$FZF_CTRL_R_COMMAND"
if test -n "$FZF_CTRL_R_COMMAND"
echo "warning: FZF_CTRL_R_COMMAND is set to a custom command, but custom commands are not yet supported for CTRL-R" >&2
end
bind \cr fzf-history-widget
bind -M insert \cr fzf-history-widget
end
if not set -q FZF_CTRL_T_COMMAND; or test -n "$FZF_CTRL_T_COMMAND"
bind \ct fzf-file-widget
bind -M insert \ct fzf-file-widget
end
if not set -q FZF_ALT_C_COMMAND; or test -n "$FZF_ALT_C_COMMAND"
bind \ec fzf-cd-widget
bind -M insert \ec fzf-cd-widget
end
end
# Run setup
fzf_key_bindings
+166
View File
@@ -0,0 +1,166 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ key-bindings.nu
#
# - $FZF_TMUX (default: 0)
# - $FZF_TMUX_OPTS
# - $FZF_TMUX_HEIGHT (default: 40%)
# - $FZF_CTRL_T_COMMAND (set to "" to disable)
# - $FZF_CTRL_T_OPTS
# - $FZF_CTRL_R_COMMAND (set to "" to disable)
# - $FZF_CTRL_R_OPTS
# - $FZF_ALT_C_COMMAND (set to "" to disable)
# - $FZF_ALT_C_OPTS
# Code provided by @igor-ramazanov
# Source: https://github.com/junegunn/fzf/issues/4122#issuecomment-2607368316
# Merge default options in the same order as bash/zsh:
# 1. --height, --min-height, --bind=ctrl-z:ignore, $prepend
# 2. $FZF_DEFAULT_OPTS_FILE contents
# 3. $FZF_DEFAULT_OPTS, $append
def __fzf_defaults [prepend: string, append: string]: nothing -> string {
let base = $"--height ($env.FZF_TMUX_HEIGHT? | default '40%') --min-height 20+ --bind=ctrl-z:ignore ($prepend)"
let opts_file = if ($env.FZF_DEFAULT_OPTS_FILE? | default '' | is-not-empty) {
try { open --raw ($env.FZF_DEFAULT_OPTS_FILE) | str trim } catch { '' }
} else {
''
}
let default_opts = $env.FZF_DEFAULT_OPTS? | default ''
$"($base) ($opts_file) ($default_opts) ($append)" | str trim
}
# Return the fzf command to use: fzf-tmux when inside tmux and
# FZF_TMUX is enabled or FZF_TMUX_OPTS is set, plain fzf otherwise.
def __fzfcmd []: nothing -> list<string> {
let in_tmux = ($env.TMUX_PANE? | default '' | into string | is-not-empty)
if $in_tmux {
let fzf_tmux = ($env.FZF_TMUX? | default 0 | into string)
let fzf_tmux_opts = ($env.FZF_TMUX_OPTS? | default '' | into string)
if ($fzf_tmux != '0') or ($fzf_tmux_opts | is-not-empty) {
let opts = if ($fzf_tmux_opts | is-not-empty) { $fzf_tmux_opts } else { $"-d($env.FZF_TMUX_HEIGHT? | default '40%')" }
return ['fzf-tmux' ...(($opts | split row ' ' | where { $in != '' })) '--']
}
}
['fzf']
}
export-env {
$env.FZF_CTRL_T_OPTS = $env.FZF_CTRL_T_OPTS? | default ""
$env.FZF_CTRL_R_OPTS = $env.FZF_CTRL_R_OPTS? | default ""
$env.FZF_ALT_C_OPTS = $env.FZF_ALT_C_OPTS? | default ""
}
# Directories
const alt_c = {
name: fzf_dirs
modifier: alt
keycode: char_c
mode: [emacs, vi_normal, vi_insert]
event: [
{
send: executehostcommand
cmd: "
let fzf_opts = (__fzf_defaults '--reverse --walker=dir,follow,hidden --scheme=path' $'($env.FZF_ALT_C_OPTS) +m');
let fzfcmd = (__fzfcmd);
let fzf_args = ($fzfcmd | skip 1);
let alt_c_cmd = ($env.FZF_ALT_C_COMMAND? | default null);
let result = if ($alt_c_cmd == null) or ($alt_c_cmd | is-empty) {
with-env { FZF_DEFAULT_OPTS: $fzf_opts, FZF_DEFAULT_OPTS_FILE: '' } { ^($fzfcmd | first) ...$fzf_args }
} else {
let fzf_cmd_str = ($fzfcmd | str join ' ');
let sh_cmd = [$alt_c_cmd '|' $fzf_cmd_str] | str join ' ';
with-env { FZF_DEFAULT_OPTS: $fzf_opts, FZF_DEFAULT_OPTS_FILE: '' } { ^sh -c $sh_cmd }
};
if ($result | is-not-empty) { cd $result };
"
}
]
}
# History
const ctrl_r = {
name: fzf_history
modifier: control
keycode: char_r
mode: [emacs, vi_insert, vi_normal]
event: [
{
send: executehostcommand
cmd: "commandline edit --replace (
let fzf_opts = (__fzf_defaults '' $'--scheme=history --bind=ctrl-r:toggle-sort --wrap-sign \"\t↳ \" --highlight-line ($env.FZF_CTRL_R_OPTS) +m --read0');
let fzfcmd = (__fzfcmd);
let fzf_args = ($fzfcmd | skip 1);
# reverse | uniq: show most recent first, deduplicate keeping the latest.
# Nushell's `history` loads the full history as an in-memory table
# (bounded by $env.config.history.max_size, default 100,000), so
# reverse and uniq run on an already-materialized list. This is O(n)
# but acceptable for typical history sizes; unlike bash/zsh `fc -r`,
# there is no streaming primitive that would let fzf show the latest
# entries before the full list is consumed.
history
| get command
| reverse
| uniq
| str join (char -i 0)
| with-env { FZF_DEFAULT_OPTS: $fzf_opts, FZF_DEFAULT_OPTS_FILE: '' } { ^($fzfcmd | first) ...$fzf_args --query (commandline) }
| decode utf-8
| str trim
)"
}
]
}
# Files
const ctrl_t = {
name: fzf_files
modifier: control
keycode: char_t
mode: [emacs, vi_normal, vi_insert]
event: [
{
send: executehostcommand
cmd: "
let fzf_opts = (__fzf_defaults '--reverse --walker=file,dir,follow,hidden --scheme=path' $'($env.FZF_CTRL_T_OPTS) -m');
let fzfcmd = (__fzfcmd);
let fzf_args = ($fzfcmd | skip 1);
let ctrl_t_cmd = ($env.FZF_CTRL_T_COMMAND? | default null);
let result = if ($ctrl_t_cmd == null) or ($ctrl_t_cmd | is-empty) {
with-env { FZF_DEFAULT_OPTS: $fzf_opts, FZF_DEFAULT_OPTS_FILE: '' } { ^($fzfcmd | first) ...$fzf_args }
} else {
let fzf_cmd_str = ($fzfcmd | str join ' ');
let sh_cmd = [$ctrl_t_cmd '|' $fzf_cmd_str] | str join ' ';
with-env { FZF_DEFAULT_OPTS: $fzf_opts, FZF_DEFAULT_OPTS_FILE: '' } { ^sh -c $sh_cmd }
};
let result = ($result | str replace --all (char newline) ' ' | str trim);
commandline edit --append $result;
commandline set-cursor --end
"
}
]
}
# Helper to check if a binding is enabled. A binding is disabled when
# the corresponding *_COMMAND variable is explicitly set to "".
# When not defined (null), the binding is enabled (using fzf's built-in walker).
def __fzf_binding_enabled [var_name: string]: nothing -> bool {
let val = ($env | get -o $var_name)
# null = not defined = enabled; "" = explicitly disabled
$val == null or ($val | into string | is-not-empty)
}
# Update the $env.config
export-env {
let fzf_names = ['fzf_files', 'fzf_dirs', 'fzf_history']
# Filter out any existing fzf bindings, then re-add the enabled ones.
# This allows re-sourcing to update bindings (e.g. after changing
# FZF_CTRL_T_COMMAND) without creating duplicates.
mut bindings = ($env.config.keybindings | where { |kb| $kb.name not-in $fzf_names })
if (__fzf_binding_enabled 'FZF_ALT_C_COMMAND') { $bindings = ($bindings | append $alt_c) }
if (__fzf_binding_enabled 'FZF_CTRL_R_COMMAND') { $bindings = ($bindings | append $ctrl_r) }
if (__fzf_binding_enabled 'FZF_CTRL_T_COMMAND') { $bindings = ($bindings | append $ctrl_t) }
$env.config.keybindings = $bindings
}
+203
View File
@@ -0,0 +1,203 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ key-bindings.zsh
#
# - $FZF_CTRL_T_COMMAND
# - $FZF_CTRL_T_OPTS
# - $FZF_CTRL_R_COMMAND
# - $FZF_CTRL_R_OPTS
# - $FZF_ALT_C_COMMAND
# - $FZF_ALT_C_OPTS
# Key bindings
# ------------
# The code at the top and the bottom of this file is the same as in completion.zsh.
# Refer to that file for explanation.
if 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then
__fzf_key_bindings_options="options=(${(j: :)${(kv)options[@]}})"
else
() {
__fzf_key_bindings_options="setopt"
'local' '__fzf_opt'
for __fzf_opt in "${(@)${(@f)$(set -o)}%% *}"; do
if [[ -o "$__fzf_opt" ]]; then
__fzf_key_bindings_options+=" -o $__fzf_opt"
else
__fzf_key_bindings_options+=" +o $__fzf_opt"
fi
done
}
fi
'builtin' 'emulate' 'zsh' && 'builtin' 'setopt' 'no_aliases'
{
if [[ -o interactive ]]; then
#----BEGIN INCLUDE common.sh
# NOTE: Do not directly edit this section, which is copied from "common.sh".
# To modify it, one can edit "common.sh" and run "./update.sh" to apply
# the changes. See code comments in "common.sh" for the implementation details.
__fzf_defaults() {
builtin printf '%s\n' "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
builtin printf '%s\n' "${FZF_DEFAULT_OPTS-} $2"
}
__fzf_exec_awk() {
if [[ -z ${__fzf_awk-} ]]; then
__fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
__fzf_awk=/usr/xpg4/bin/awk
elif command -v mawk > /dev/null 2>&1; then
local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] &&
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi
fi
LC_ALL=C exec "$__fzf_awk" "$@"
}
#----END INCLUDE
# CTRL-T - Paste the selected file path(s) into the command line
__fzf_select() {
setopt localoptions pipefail no_aliases 2> /dev/null
local item
FZF_DEFAULT_COMMAND=${FZF_CTRL_T_COMMAND:-} \
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=file,dir,follow,hidden --scheme=path" "${FZF_CTRL_T_OPTS-} -m") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) "$@" < /dev/tty | while read -r item; do
echo -n -E "${(q)item} "
done
local ret=$?
echo
return $ret
}
__fzfcmd() {
[ -n "${TMUX_PANE-}" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "${FZF_TMUX_OPTS-}" ]; } &&
echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
}
fzf-file-widget() {
LBUFFER="${LBUFFER}$(__fzf_select)"
local ret=$?
zle reset-prompt
return $ret
}
if [[ "${FZF_CTRL_T_COMMAND-x}" != "" ]]; then
zle -N fzf-file-widget
bindkey -M emacs '^T' fzf-file-widget
bindkey -M vicmd '^T' fzf-file-widget
bindkey -M viins '^T' fzf-file-widget
fi
# ALT-C - cd into the selected directory
fzf-cd-widget() {
setopt localoptions pipefail no_aliases 2> /dev/null
local dir="$(
FZF_DEFAULT_COMMAND=${FZF_ALT_C_COMMAND:-} \
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=dir,follow,hidden --scheme=path" "${FZF_ALT_C_OPTS-} +m") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) < /dev/tty)"
if [[ -z "$dir" ]]; then
zle redisplay
return 0
fi
# Use subshell expansion to get the absolute PWD of the target dir.
# This allows the recorded shell history to be reused even from a different
# working directory.
# If failed, fallback to the unexpanded path to surface the error to the user.
# NOTE: Don't use the `:a` modifier as it resolves symlinks like `pwd -P`.
dir=$(builtin cd >/dev/null -- "${dir}" && echo "${PWD}" || echo "${dir}")
zle push-line # Clear buffer. Auto-restored on next prompt.
BUFFER="builtin cd -- ${(q)dir}"
zle accept-line
local ret=$?
unset dir # ensure this doesn't end up appearing in prompt expansion
zle reset-prompt
return $ret
}
if [[ "${FZF_ALT_C_COMMAND-x}" != "" ]]; then
zle -N fzf-cd-widget
bindkey -M emacs '\ec' fzf-cd-widget
bindkey -M vicmd '\ec' fzf-cd-widget
bindkey -M viins '\ec' fzf-cd-widget
fi
# CTRL-R - Paste the selected command from history into the command line
fzf-history-widget() {
local selected extracted_with_perl=0
setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases no_glob no_sh_glob no_ksharrays extendedglob 2> /dev/null
# Ensure the module is loaded if not already, and the required features, such
# as the associative 'history' array, which maps event numbers to full history
# lines, are set. Also, make sure Perl is installed for multi-line output.
if zmodload -F zsh/parameter p:{commands,history} 2>/dev/null && (( ${+commands[perl]} )); then
selected="$(printf '%s\t%s\000' "${(kv)history[@]}" |
perl -0 -ne 'if (!$seen{(/^\s*[0-9]+\**\t(.*)/s, $1)}++) { s/\n/\n\t/g; print; }' |
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,alt-r:toggle-raw --wrap-sign '\t↳ ' --highlight-line --multi ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} --read0") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))"
extracted_with_perl=1
else
selected="$(fc -rl 1 | __fzf_exec_awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,alt-r:toggle-raw --wrap-sign '\t↳ ' --highlight-line --multi ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER}") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))"
fi
local ret=$?
local -a cmds
# Avoid leaking auto assigned values when using backreferences '(#b)'
local -a mbegin mend match
if [ -n "$selected" ]; then
# Heuristic to check if the selected value is from history or a custom query
if ((( extracted_with_perl )) && [[ $selected == <->$'\t'* ]]) ||
((( ! extracted_with_perl )) && [[ $selected == [[:blank:]]#<->( |\* )* ]]); then
# Split at newlines
for line in ${(ps:\n:)selected}; do
if (( extracted_with_perl )); then
if [[ $line == (#b)(<->)(#B)$'\t'* ]]; then
(( ${+history[${match[1]}]} )) && cmds+=("${history[${match[1]}]}")
fi
elif [[ $line == [[:blank:]]#(#b)(<->)(#B)( |\* )* ]]; then
# Avoid $history array: lags behind 'fc' on foreign commands (*)
# https://zsh.org/mla/users/2024/msg00692.html
# Push BUFFER onto stack; fetch and save history entry from BUFFER; restore
zle .push-line
zle vi-fetch-history -n ${match[1]}
(( ${#BUFFER} )) && cmds+=("${BUFFER}")
BUFFER=""
zle .get-line
fi
done
if (( ${#cmds[@]} )); then
# Join by newline after stripping trailing newlines from each command
BUFFER="${(pj:\n:)${(@)cmds%%$'\n'#}}"
CURSOR=${#BUFFER}
fi
else # selected is a custom query, not from history
LBUFFER="$selected"
fi
fi
zle reset-prompt
return $ret
}
if [[ ${FZF_CTRL_R_COMMAND-x} != "" ]]; then
if [[ -n ${FZF_CTRL_R_COMMAND-} ]]; then
echo "warning: FZF_CTRL_R_COMMAND is set to a custom command, but custom commands are not yet supported for CTRL-R" >&2
fi
zle -N fzf-history-widget
bindkey -M emacs '^R' fzf-history-widget
bindkey -M vicmd '^R' fzf-history-widget
bindkey -M viins '^R' fzf-history-widget
fi
fi
} always {
eval $__fzf_key_bindings_options
'unset' '__fzf_key_bindings_options'
}
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
# This script applies the contents of "common.sh" to the other files.
set -e
dir=${0%"${0##*/}"}
update() {
{
sed -n '1,/^#----BEGIN INCLUDE common\.sh/p' "$1"
cat << EOF
# NOTE: Do not directly edit this section, which is copied from "common.sh".
# To modify it, one can edit "common.sh" and run "./update.sh" to apply
# the changes. See code comments in "common.sh" for the implementation details.
EOF
echo
grep -v '^[[:blank:]]*#' "$dir/common.sh" # remove code comments in common.sh
sed -n '/^#----END INCLUDE/,$p' "$1"
} > "$1.part"
mv -f "$1.part" "$1"
}
update "$dir/completion.bash"
update "$dir/completion.zsh"
update "$dir/key-bindings.bash"
update "$dir/key-bindings.zsh"
# Check if --check is in ARGV
check=0
rest=()
for arg in "$@"; do
case $arg in
--check) check=1 ;;
*) rest+=("$arg") ;;
esac
done
fmt() {
if ! grep -q "^#----BEGIN shfmt" "$1"; then
if [[ $check == 1 ]]; then
shfmt -d "$1"
return $?
else
shfmt -w "$1"
fi
else
{
sed -n '1,/^#----BEGIN shfmt/p' "$1" | sed '$d'
sed -n '/^#----BEGIN shfmt/,/^#----END shfmt/p' "$1" | shfmt --filename "$1"
sed -n '/^#----END shfmt/,$p' "$1" | sed '1d'
} > "$1.part"
if [[ $check == 1 ]]; then
diff -q "$1" "$1.part"
ret=$?
rm -f "$1.part"
return $ret
fi
mv -f "$1.part" "$1"
fi
}
for file in "${rest[@]}"; do
fmt "$file" || exit $?
done