Add bash line editor.

This commit is contained in:
2026-02-14 12:43:31 -05:00
parent c7a300cd58
commit 10251dd1cb
290 changed files with 49346 additions and 0 deletions

View File

@@ -0,0 +1,334 @@
# ble/contrib/integration/bash-completion.bash (C) 2024, akinomyoga
## @fn ble/string#hash-pjw text [size shift]
## @var[out] ret
function ble/string#hash-pjw {
local size=${2:-32}
local S=${3:-$(((size+7)/8))} # shift 4
local C=$((size-2*S)) # co-shift 24
local M=$(((1<<size-S)-1)) # mask 0x0FFFFFFF
local N=$(((1<<S)-1<<S)) # mask2 0x000000F0
ble/util/s2bytes "$1"
local c h=0
for c in "${ret[@]}"; do
((h=(h<<S)+c,h=(h^h>>C&N)&M))
done
ret=$h
}
#------------------------------------------------------------------------------
# Hooks for completion loaders
## @fn ble/contrib/integration:bash-completion/loader/.adjust-progcomp cmd
## @param[in] cmd
## The command name whose completion setting should be parsed and adjusted.
function ble/contrib/integration:bash-completion/loader/.adjust-progcomp {
local ret
ble/syntax:bash/simple-word/safe-eval "$1" nonull || return 0
local cmd=$ret
local compdef
ble/util/assign compdef 'builtin complete -p -- "$cmd"' 2>/dev/null ||
{ cmd=${cmd##*/}; ble/util/assign compdef 'builtin complete -p -- "$cmd"' 2>/dev/null; } ||
return 0
compdef=${compdef%"${cmd:-''}"}
compdef=${compdef%' '}' '
local comp_opts=$comp_opts comp_prog comp_func compoptions flag_noquote
ble/complete/progcomp/parse-complete "$compdef"
# Call adjustments based on the values of "comp_prog" and "comp_func"
ble/complete/progcomp/adjust-third-party-completions
}
function ble/contrib/integration:bash-completion/loader/_comp_load.advice {
[[ ${BLE_ATTACHED-} ]] || return 0
((ADVICE_EXIT==0)) || return 0
# command-line argument parsing taken from _comp_load (bash_completion).
local flag_fallback_default="" IFS=$' \t\n'
local OPTIND=1 OPTARG="" OPTERR=0 opt
set -- "${ADVICE_WORDS[@]:1}"
while getopts ':D' opt "$@"; do
case $opt in
D) flag_fallback_default=set ;;
*) return 2 ;;
esac
done
shift "$((OPTIND - 1))"
ble/contrib/integration:bash-completion/loader/.adjust-progcomp "$@"
}
function ble/contrib/integration:bash-completion/loader/__load_completion.advice {
[[ ${BLE_ATTACHED-} ]] || return 0
((ADVICE_EXIT==0)) || return 0
ble/contrib/integration:bash-completion/loader/.adjust-progcomp "$@"
}
function ble/contrib/integration:bash-completion/loader/adjust {
if ble/is-function _comp_load; then
ble/function#advice after _comp_load ble/contrib/integration:bash-completion/loader/_comp_load.advice
elif ble/is-function __load_completion; then
ble/function#advice after __load_completion ble/contrib/integration:bash-completion/loader/__load_completion.advice
fi
}
#------------------------------------------------------------------------------
# Hooks for mandb
## @fn ble/contrib/integration:bash-completion/mandb/.alloc-subcache command hash [opts]
## @var[out] ret
function ble/contrib/integration:bash-completion/mandb/.alloc-subcache {
ret=
[[ $_ble_attached ]] || return 1
local command=$1 hash=$2 opts=$3
if [[ :$opts: == *:dequote:* ]]; then
ble/syntax:bash/simple-word/safe-eval "$command" noglob:nonull &&
command=$ret
fi
[[ $command ]] || return 1
[[ $command == ble*/* ]] || command=${1##*/}
ble/string#hash-pjw "$args" 64; local hash=$ret
local lc_messages=${LC_ALL:-${LC_MESSAGES:-${LANG:-C}}}
local mandb_cache_dir=$_ble_base_cache/complete.mandb/${lc_messages//'/'/%}
ble/util/sprintf ret '%s.%014x' "$mandb_cache_dir/_parse_help.d/$command" "$hash"
[[ -s $ret && $ret -nt $_ble_base/lib/core-complete.sh ]] && return 1
ble/util/mkd "${ret%/*}"
}
## @fn ble/contrib/integration:bash-completion/mandb/_parse_help.advice command args
function ble/contrib/integration:bash-completion/mandb/_parse_help.advice {
[[ ${BLE_ATTACHED-} ]] || return 0
local cmd=$1 args=$2 func=$ADVICE_FUNCNAME
# 現在のコマンド名。 Note: ADVICE_WORDS には実際に現在補完しようとしているコ
# マンドとは異なるものが指定される場合があるので (例えば help や - 等) 信用で
# きない。
local command=${COMP_WORDS[0]-} hash="${ADVICE_WORDS[*]}" ret
ble/contrib/integration:bash-completion/mandb/.alloc-subcache "$command" "$hash" dequote || return 0
local subcache=$ret
local default_option=--help help_opts=
[[ $func == _parse_usage ]] &&
default_option=--usage help_opts=mandb-usage
if [[ ( $func == _parse_help || $func == _parse_usage ) && $cmd == - ]]; then
# 標準入力からの読み取り
ble/complete/mandb:help/generate-cache "$help_opts" >| "$subcache"
# Note: _parse_help が読み取る筈だった内容を横取りしたので抽出した内容を標
# 準出力に出力する。但し、対応する long option がある short option は除外す
# る。
LC_ALL= LC_COLLATE=C ble/bin/awk -F "$_ble_term_FS" '
BEGIN { entry_count = 0; }
{
entries[entry_count++] = $1;
# Assumption: the descriptions of long options have the form
# "[short_opt] desc". The format is defined by
# ble/complete/mandb:help/generate-cache.
desc = $4;
gsub(/\033\[[ -?]*[@-~]/, "", desc); # disable=#D1440 (LC_COLLATE=C is set)
if (match(desc, /^\[[^]'"$_ble_term_blank"'[]*\] /) > 0) { # #D1709 safe
short_opt = substr(desc, 2, RLENGTH - 3);
excludes[short_opt] =1;
}
}
END {
for (i = 0; i < entry_count; i++)
if (!excludes[entries[i]])
print entries[i];
}
' "$subcache" 2>/dev/null # suppress locale error #D1440
else
local cmd_args
ble/string#split-words cmd_args "${args:-$default_option}"
"$cmd" "${cmd_args[@]}" 2>&1 | ble/complete/mandb:help/generate-cache "$help_opts" >| "$subcache"
fi
}
function ble/contrib/integration:bash-completion/mandb/_get_help_lines.advice {
[[ ${BLE_ATTACHED-} ]] || return 0
((${#_lines[@]})) || return 0
# @var cmd
# 現在のコマンド名。Note: _comp_command_offset 等によって別のコマンドの補完
# を呼び出している場合があるので ble.sh の用意する comp_words は信用できな
# い。bash-completion の使っている _comp_args[0] または bash-completion が
# 上書きしている COMP_WORDS を参照する。
local cmd=${_comp_args[0]-${COMP_WORDS[0]-}} hash="${ADVICE_WORDS[*]}"
ble/contrib/integration:bash-completion/mandb/.alloc-subcache "$cmd" "$hash" dequote || return 0
local subcache=$ret
local help_opts=
[[ ${ADVICE_FUNCNAME[1]} == *_usage ]] && help_opts=mandb-usage
printf '%s\n' "${_lines[@]}" | ble/complete/mandb:help/generate-cache "$help_opts" >| "$subcache"
}
## @fn ble/contrib/integration:bash-completion/mandb/_comp_command_offset.yield
## process generated completions stored in COMPREPLY.
function ble/contrib/integration:bash-completion/mandb/_comp_command_offset.yield {
local word_offset=$1
# Other variables for ble/complete/progcomp/process-compgen-output
local comp_words comp_cword comp_line comp_point IFS=$' \t\n'
comp_cword=$((COMP_CWORD-word_offset))
((comp_cword==0)) && return 0
comp_words=("${COMP_WORDS[@]:word_offset}")
comp_line="${comp_words[*]}"
comp_point="${comp_words[*]::comp_cword+1}"
comp_point=${#comp_point}
if ((${#COMPREPLY[@]}==0)); then
ble/complete/source:argument/fallback reuse-comp_words; local ext=$?
compopt +o ble/default +o default +o dirnames
return "$ext"
fi
# Input
local compgen parse_compgen_opts=array
compgen=("${COMPREPLY[@]}")
COMPREPLY=()
# Arguments for ble/complete/progcomp/process-compgen-output
local cmd=${comp_words[0]} ret
ble/syntax:bash/simple-word/safe-eval "$cmd" nonull && cmd=$ret
[[ ${cmd##*/} == git ]] && parse_compgen_opts=work-around-git
ble/complete/progcomp/process-compgen-output "$cmd" "$parse_compgen_opts"
}
function ble/contrib/integration:bash-completion/mandb/_comp_command_offset.advice {
[[ ${BLE_ATTACHED-} ]] || return 0
local REPLY
_comp__find_original_word "${ADVICE_WORDS[1]}"
ble/contrib/integration:bash-completion/mandb/_comp_command_offset.yield "$REPLY"
}
function ble/contrib/integration:bash-completion/mandb/_command_offset.advice {
[[ ${BLE_ATTACHED-} ]] || return 0
local word_offset=${ADVICE_WORDS[1]} i j
for ((i=0;i<word_offset;i++)); do
for ((j=0;j<=${#COMP_LINE};j++)); do
[[ $COMP_LINE == "${COMP_WORDS[i]}"* ]] && break
COMP_LINE=${COMP_LINE:1}
((COMP_POINT--))
done
COMP_LINE=${COMP_LINE#"${COMP_WORDS[i]}"}
((COMP_POINT-=${#COMP_WORDS[i]}))
done
ble/contrib/integration:bash-completion/mandb/_comp_command_offset.yield "$word_offset"
}
function ble/contrib/integration:bash-completion/mandb/adjust {
local fail=
if ble/is-function _comp_compgen_help; then
# bash-completion 2.12
ble/function#advice after _comp_compgen_help__get_help_lines 'ble/contrib/integration:bash-completion/mandb/_get_help_lines.advice' || fail=1
ble/function#advice before _comp_complete_longopt 'ble/contrib/integration:bash-completion/mandb/_parse_help.advice "${ADVICE_WORDS[1]}"' ||
ble/function#advice before _comp_longopt 'ble/contrib/integration:bash-completion/mandb/_parse_help.advice "${ADVICE_WORDS[1]}"' || fail=1
ble/function#advice after _comp_command_offset 'ble/contrib/integration:bash-completion/mandb/_comp_command_offset.advice' || fail=1
elif ble/is-function _parse_help; then
# bash-completion <= 2.11
ble/function#advice before _parse_help 'ble/contrib/integration:bash-completion/mandb/_parse_help.advice "${ADVICE_WORDS[1]}" "${ADVICE_WORDS[2]}"' || fail=1
ble/function#advice before _longopt 'ble/contrib/integration:bash-completion/mandb/_parse_help.advice "${ADVICE_WORDS[1]}"' || fail=1
ble/function#advice before _parse_usage 'ble/contrib/integration:bash-completion/mandb/_parse_help.advice "${ADVICE_WORDS[1]}" "${ADVICE_WORDS[2]}"' || fail=1
ble/function#advice after _command_offset 'ble/contrib/integration:bash-completion/mandb/_command_offset.advice' || fail=1
else
fail=1
fi
if [[ ! $fail ]]; then
function ble/contrib/integration:bash-completion/mandb/adjust { return 0; }
fi
} 2>/dev/null # _parse_help が別の枠組みで定義されている事がある? #D1900
#------------------------------------------------------------------------------
function ble/contrib/integration:bash-completion/cmd-with-conditional-sync.advice {
if [[ ${BLE_ATTACHED-} ]]; then
ble/function#push "${ADVICE_WORDS[1]}" '
local -a cmd_args; cmd_args=("${ADVICE_WORDS[1]}" "$@")
ble/util/conditional-sync \
'\''command "${cmd_args[@]}"'\'' \
'\''! ble/complete/check-cancel'\'' 128 progressive-weight:killall'
ble/function#advice/do
ble/function#pop "${ADVICE_WORDS[1]}"
else
ble/function#advice/do
fi
}
function ble/contrib/integration:bash-completion/_do_dnf5_completion.advice {
ble/contrib/integration:bash-completion/cmd-with-conditional-sync.advice "$@"
[[ ${BLE_ATTACHED-} ]] || return 0
if ((${#COMPREPLY[@]}>=2)); then
local i has_desc=
for i in "${!COMPREPLY[@]}"; do
if ble/string#match "${COMPREPLY[i]}" '[[:blank:]]+\((.*)\)$'; then
local cand=${COMPREPLY[i]%"$BASH_REMATCH"} desc=${BASH_REMATCH[1]}
if [[ $cand && $cand != "${COMPREPLY[i]}" ]]; then
ble/complete/cand/yield word "$cand" "$desc"
has_desc=1
builtin unset -v 'COMPREPLY[i]'
continue
fi
fi
if [[ ! -e ${COMPREPLY[i]} ]]; then
ble/complete/cand/yield word "$cand"
builtin unset -v 'COMPREPLY[i]'
fi
done
COMPREPLY=("${COMPREPLY[@]}")
[[ $has_desc ]] && bleopt complete_menu_style=desc
fi
}
function ble/contrib/integration:bash-completion/adjust {
ble/is-function _comp_initialize || ble/is-function _quote_readline_by_ref || return 0
ble/contrib/integration:bash-completion/loader/adjust
_ble_contrib_integration_bash_completion_cmd_conditional_sync=(_comp_cmd_make _make _do_dnf5_completion)
if [[ $comp_func == _comp_cmd_make || $comp_func == _make ]]; then
ble/is-function "$comp_func" &&
ble/function#advice around "$comp_func" ble/contrib/integration:bash-completion/cmd-with-conditional-sync.advice
elif [[ $comp_func == _do_dnf5_completion ]]; then
ble/is-function "$comp_func" &&
ble/function#advice around "$comp_func" ble/contrib/integration:bash-completion/_do_dnf5_completion.advice
fi
if ((BASH_COMPLETION_VERSINFO[0]<2||BASH_COMPLETION_VERSINFO[0]==2&&BASH_COMPLETION_VERSINFO[1]<12)); then
# Fix issues with bash-completion <= 2.11
# https://github.com/scop/bash-completion/pull/492 (fixed in bash-completion 2.12)
function _quote_readline_by_ref {
if [[ $1 == \'* ]]; then
printf -v "$2" %s "${1:1}"
else
printf -v "$2" %q "$1"
[[ ${!2} == \$* ]] && builtin eval "$2=${!2}"
fi
}
ble/function#suppress-stderr _filedir 2>/dev/null
# https://github.com/scop/bash-completion/issues/509 (fixed in bash-completion 2.12)
ble/function#suppress-stderr _find 2>/dev/null
# https://github.com/scop/bash-completion/pull/556 (fixed in bash-completion 2.12)
ble/function#suppress-stderr _scp_remote_files 2>/dev/null
# https://github.com/scop/bash-completion/pull/773 (fixed in bash-completion 2.12)
ble/function#suppress-stderr _function 2>/dev/null
fi
ble/contrib/integration:bash-completion/mandb/adjust
}

View File

@@ -0,0 +1,180 @@
# ble/contrib/integration/bash-preexec.bash (C) 2022-2023, akinomyoga
## @arr[in] precmd_functions
## @arr[in] preexec_functions
## The functions registered to these arrays are executed on PRECMD and
## PREEXEC hooks, respectively.
##
## @fn[in] precmd
## @fn[in] preexec
## If these functions are defined, they are executed on PRECMD and PREEXEC
## hooks, respectively, through "precmd_functions" and "preexec_functions".
##
##
## * Integration with bash-preexec.sh (https://github.com/rcaloras/bash-preexec)
##
## Although this script works without bash-preexec, it provides a better
## integration with bash-preexec when bash-preexec is loaded. The
## integration relies on the following public API of bash-preexec.
##
## @fn __bp_precmd_invoke_functions lastexit lastarg
## @fn __bp_preexec_invoke_functions lastexit lastarg this_command
## @fn __bp_uninstall
## @var __bp_trapdebug_string
## @var __bp_install_string
function ble/contrib/integration:bash-preexec/add-convenience-functions {
ble/array#remove precmd_functions precmd
ble/array#remove preexec_functions preexec
ble/array#unshift precmd_functions precmd
ble/array#unshift preexec_functions preexec
}
function ble/contrib/integration:bash-preexec/precmd.hook {
local _ble_local_lastexit=$? _ble_local_lastarg=$_
# Emulate bash-preexec variables
__bp_last_ret_value=$_ble_local_lastexit
__bp_last_argument_prev_command=$_ble_local_lastarg
BP_PIPESTATUS=("${BLE_PIPESTATUS[@]}")
local __bp_inside_precmd=1
# local __bp_blesh_invoking_through_blesh=1 # XXX
if ble/is-function __bp_precmd_invoke_functions; then
__bp_precmd_invoke_functions "$_ble_local_lastexit" "$_ble_local_lastarg"
else
# For older bash-preexec.sh / without bash-preexec.sh
local _ble_local_hook
for _ble_local_hook in "${precmd_functions[@]}"; do
if ble/bin#has "$_ble_local_hook"; then
ble/util/setexit "$_ble_local_lastexit" "$_ble_local_lastarg"
"$_ble_local_hook"
fi
done
fi
}
function ble/contrib/integration:bash-preexec/preexec.hook {
local _ble_local_lastexit=$? _ble_local_lastarg=$_ _ble_local_command=$1
# Emulate bash-preexec variables
local __bp_inside_preexec=1
# local __bp_blesh_invoking_through_blesh=1 # XXX
if ble/is-function __bp_preexec_invoke_functions; then
__bp_preexec_invoke_functions "$_ble_local_lastexit" "$_ble_local_lastarg"
else
# For older bash-preexec.sh / without bash-preexec.sh
local _ble_local_hook
for _ble_local_hook in "${preexec_functions[@]}"; do
if ble/bin#has "$_ble_local_hook"; then
ble/util/setexit "$_ble_local_lastexit" "$_ble_local_lastarg"
"$_ble_local_hook" "$_ble_local_command"
fi
done
fi
}
## @fn ble/contrib/integration:bash-preexec/attach.hook
## Remove bash-preexec hooks
function ble/contrib/integration:bash-preexec/attach.hook {
local BP_TRAPDEBUG_STRING=${__bp_trapdebug_string:-'__bp_preexec_invoke_exec "$_"'}
# Remove bash-preexec preexec hook in builtin DEBUG trap.
local trap_string q="'" Q="'\''"
ble/util/assign trap_string 'builtin trap -p DEBUG'
if [[ $trap_string == "trap -- '${BP_TRAPDEBUG_STRING//$q/$Q}' DEBUG" ]]; then
if [[ ${__bp_trap_string-} ]]; then
builtin eval -- "builtin $__bp_trap_string"
else
builtin trap - DEBUG
fi
fi
if ble/is-function __bp_uninstall; then
__bp_uninstall
else
# For older bash-preexec.sh
local BP_INSTALL_STRING=${__bp_install_string:-$'__bp_trap_string="$(trap -p DEBUG)"\ntrap - DEBUG\n__bp_install'}
local BP_PROMPT_COMMAND_PREFIX=__bp_precmd_invoke_cmd
local BP_PROMPT_COMMAND_SUFFIX=__bp_interactive_mode
# Remove __bp_install hook from PROMPT_COMMAND
if [[ ${PROMPT_COMMAND-} == *"$__bp_install_string"* ]]; then
PROMPT_COMMAND="${PROMPT_COMMAND//$BP_INSTALL_STRING[;$'\n']}" # Edge case of appending to PROMPT_COMMAND
PROMPT_COMMAND="${PROMPT_COMMAND//$BP_INSTALL_STRING}"
fi
# Remove precmd hook from PROMPT_COMMAND
local i prompt_command
for i in "${!PROMPT_COMMAND[@]}"; do
prompt_command=${PROMPT_COMMAND[i]}
case $prompt_command in
("$BP_PROMPT_COMMAND_PREFIX"|"$BP_PROMPT_COMMAND_SUFFIX")
prompt_command= ;;
(*)
prompt_command=${prompt_command/#"$BP_PROMPT_COMMAND_PREFIX"$'\n'/$'\n'}
prompt_command=${prompt_command%$'\n'"$BP_PROMPT_COMMAND_SUFFIX"}
prompt_command=${prompt_command#$'\n'}
esac
PROMPT_COMMAND[i]=$prompt_command
done
# Remove preexec hook in the DEBUG trap
local q="'" Q="'\''" trap_string
ble/util/assign trap_string 'trap -p DEBUG'
if [[ $trap_string == "trap -- '${BP_TRAPDEBUG_STRING//$q/$Q}' DEBUG" ]]; then
if [[ ${__bp_trap_string-} ]]; then
builtin eval -- "$__bp_trap_string"
else
trap - DEBUG
fi
fi
fi
}
ble/function#trace ble/contrib/integration:bash-preexec/attach.hook
## @fn ble/contrib/integration:bash-preexec/detach.hook
function ble/contrib/integration:bash-preexec/detach.hook {
# Reinstall bash-preexec hooks
local BP_INSTALL_STRING=${__bp_install_string-}
[[ ! $BP_INSTALL_STRING ]] && ble/is-function __bp_install &&
BP_INSTALL_STRING=$'__bp_trap_string="$(trap -p DEBUG)"\ntrap - DEBUG\n__bp_install'
builtin eval -- "$__bp_install_string"
# Note: 重複して登録される (古い bash-preexec.sh) かもしれないし、全
# く登録されない (bash-preexec.sh をロードしていない時) かもしれない
# ので、ble.sh 側で末尾で一回呼び出す形に修正する。
ble/contrib/integration:bash-preexec/add-convenience-functions
}
ble/contrib/integration:bash-preexec/add-convenience-functions
blehook PRECMD!=ble/contrib/integration:bash-preexec/precmd.hook
blehook PREEXEC!=ble/contrib/integration:bash-preexec/preexec.hook
blehook ATTACH!=ble/contrib/integration:bash-preexec/attach.hook
blehook DETACH!=ble/contrib/integration:bash-preexec/detach.hook
if [[ ${bash_preexec_imported-${__bp_imported-}} ]]; then
ble/contrib/integration:bash-preexec/attach.hook
fi
# prevent bash-preexec.sh to be loaded
blehook ATTACH-=ble/contrib/integration:bash-preexec/loader
blehook POSTEXEC-=ble/contrib/integration:bash-preexec/loader
bash_preexec_imported=defined
__bp_imported=defined
# XXX: 以下は uninstall で削除しきれなかった時の為の保険。今の所不要に思われる。
# __bp_blesh_check() {
# if [[ $BLE_ATTACHED && ! ${__bp_blesh_invoking_through_blesh-} ]]; then
# ble/contrib/integration:bash-preexec/attach.hook
# fi
# }
# precmd_function+=(__bp_blesh_check)
# preexec_function+=(__bp_blesh_check)
# Some settings rely on the internal APIs of bash-preexec. For example, iTerm2
# shell integration uses "__bp_set_ret_value" and
# "$__bp_last_argument_prev_command".
function __bp_set_ret_value { return ${1:+"$1"}; }

View File

@@ -0,0 +1,101 @@
# ble/contrib/integration/fzf-completion.bash (C) 2020-2024, akinomyoga
[[ $- == *i* ]] || return 0
# fzf/shell/completion.bash を未ロードの時のみロードする
if ! ble/is-function _fzf_complete; then
ble-import contrib/integration/fzf-initialize || return 1
if [[ -f $_ble_contrib_fzf_base/completion.bash ]]; then
source -- "$_ble_contrib_fzf_base/completion.bash"
elif [[ -f $_ble_contrib_fzf_base/shell/completion.bash ]]; then
source -- "$_ble_contrib_fzf_base/shell/completion.bash"
elif [[ $_ble_contrib_fzf_base == */share/fzf && -f /etc/bash_completion.d/fzf ]]; then
source /etc/bash_completion.d/fzf
elif [[ $_ble_contrib_fzf_base == __eval_fzf_bash__ ]]; then
ble/util/eval-stdout '"$_ble_contrib_fzf_path" --bash | sed -n "/### completion/,/### end/p"'
fi
fi
# clear blesh completer for cd
blehook/eval-after-load complete 'builtin unset -f ble/cmdinfo/complete:cd'
# patch fzf functions
ble-import contrib/integration/fzf.common
ble/function#advice -f around __fzf_generic_path_completion 'ble/contrib/integration:fzf/complete.advice'
ble/function#advice -f around _fzf_complete 'ble/contrib/integration:fzf/complete.advice keep-stdin'
ble/function#advice -f around _fzf_complete_kill 'ble/contrib/integration:fzf/complete.advice'
ble/function#advice -f around _fzf_handle_dynamic_completion 'ble/contrib/integration:fzf/handle_dynamic_completion.advice'
if ble/is-function __fzf_orig_completion_get_orig_func; then
## @fn ble/contrib/integration:fzf/orig_completion_get_orig_func.advice
## @var[ref] comp_opts
function ble/contrib/integration:fzf/orig_completion_get_orig_func.advice {
if ((ADVICE_EXIT==0)); then
local comp_func=$REPLY comp_prog=
ble/complete/progcomp/adjust-third-party-completions
fi
}
ble/function#advice after __fzf_orig_completion_get_orig_func 'ble/contrib/integration:fzf/orig_completion_get_orig_func.advice'
elif ble/is-function _fzf_handle_dynamic_completion; then
_ble_contrib_fzf_adjust_dynamic_completion=1
fi
#------------------------------------------------------------------------------
# Extensions
# This widget can be used to trigger fzf's '**' completion from a keybinding.
function ble/widget/fzf-complete {
local handler=_fzf_${1:-path}_completion
if ! ble/is-function "$handler"; then
ble/widget/.bell "unrecognized fzf-complete type '$1' (function '$handler' not found)"
return 1
fi
((_ble_edit_ind==0)) && return 0
# If the width of the box-drawing characters in the current terminal is not
# 1, we specify --no-unicode to fzf to suppress the use of the box-drawing
# characters.
if [[ " ${FZF_COMPLETION_OPTS-} " != *' --no-unicode '* ]]; then
local ret
ble/util/c2w 0x2500
((ret==1)) ||
local FZF_COMPLETION_OPTS="--no-unicode${FZF_COMPLETION_OPTS:+ $FZF_COMPLETION_OPTS}"
fi
# Mask completion settings cached by fzf
local -a fzf_orig_completions
fzf_orig_completions=("${!_fzf_orig_completion_@}")
local "${fzf_orig_completions[@]/%/=}" # disable=#D1570
# Replace the programmable-completion setting
local completion_save
ble/util/assign completion_save 'complete -p'
complete -r
if ((_ble_bash>=40100)); then
complete -F "$handler" -D
complete -F "$handler" -E
else
complete -F "$handler" _DefaultCmD_
complete -F "$handler" _EmptycmD_
fi
if ((_ble_bash>=50000)); then
complete -F "$handler" -I
else
complete -F "$handler" _InitialWorD_
fi
# Disable completion auto-loader
ble/function#push _comp_load 'return 1'
# Trigger fzf-completion without '**'
local FZF_COMPLETION_TRIGGER=
ble/widget/complete; local ext=$?
# restore the settings
ble/function#pop _comp_load
complete -r
builtin eval -- "$completion_save"
return "$ext"
}

View File

@@ -0,0 +1,322 @@
# Copyright (c) 2016, 2022 Junegunn Choi
# ble/contrib/integration/fzf-git.bash (C) 2020, 2023, akinomyoga
#
# 2020-04-16 https://gist.github.com/junegunn/8b572b8d4b5eddd8b85e5f4d40f17236 (Revision 2019-03-14)
# 2023-06-30 https://gist.github.com/junegunn/8b572b8d4b5eddd8b85e5f4d40f17236 (Revision 2022-08-16)
# 2023-06-30 https://github.com/junegunn/fzf-git.sh/commit/4bc0323b4822b3426989863996cc266c52c7f25a
# 2023-06-30 https://github.com/junegunn/fzf-git.sh/commit/b6192ec86609afea761c7d3954f9b539a512dc80
# 2023-11-09 https://github.com/junegunn/fzf-git.sh/blob/aacab4ae557657e0f9de288d688f312a28b86d21/fzf-git.sh
if [[ $- != *i* ]]; then
if (($# == 1)); then
function ble/contrib/integration:fzf-git/sub:branches {
git branch "$@" --sort=-committerdate --sort=-HEAD --format=$'%(HEAD) %(color:yellow)%(refname:short) %(color:green)(%(committerdate:relative))\t%(color:blue)%(subject)%(color:reset)' --color=always | column -ts$'\t'
}
function ble/contrib/integration:fzf-git/sub:refs {
git for-each-ref --sort=-creatordate --sort=-HEAD --color=always --format=$'%(refname) %(color:green)(%(creatordate:relative))\t%(color:blue)%(subject)%(color:reset)' |
builtin eval -- "$1" |
sed 's#^refs/remotes/#\x1b[95mremote-branch\t\x1b[33m#; s#^refs/heads/#\x1b[92mbranch\t\x1b[33m#; s#^refs/tags/#\x1b[96mtag\t\x1b[33m#; s#refs/stash#\x1b[91mstash\t\x1b[33mrefs/stash#' |
column -ts$'\t'
}
case $1 in
(branches)
printf '%s\n' $'CTRL-O (open in browser) ALT-A (show all branches)\n'
ble/contrib/integration:fzf-git/sub:branches
;;
(all-branches)
printf '%s\n' $'CTRL-O (open in browser)\n'
ble/contrib/integration:fzf-git/sub:branches -a
;;
(refs)
printf '%s\n' $'CTRL-O (open in browser) ALT-E (examine in editor) ALT-A (show all refs)\n'
ble/contrib/integration:fzf-git/sub:refs 'grep -v ^refs/remotes'
;;
(all-refs)
printf '%s\n' $'CTRL-O (open in browser) ALT-E (examine in editor)\n'
ble/contrib/integration:fzf-git/sub:refs 'cat'
;;
(nobeep) ;;
(*) exit 1 ;;
esac
elif (($# > 1)); then
set -e
branch=$(git rev-parse --abbrev-ref HEAD 2> /dev/null)
if [[ $branch = HEAD ]]; then
branch=$(git describe --exact-match --tags 2> /dev/null || git rev-parse --short HEAD)
fi
# Only supports GitHub for now
case $1 in
(commit)
hash=$(grep -o "[a-f0-9]\{7,\}" <<< "$2")
path=/commit/$hash
;;
(branch|remote-branch)
branch=$(sed 's/^[* ]*//' <<< "$2" | cut -d' ' -f1)
remote=$(git config branch."${branch}".remote || printf 'origin\n')
branch=${branch#$remote/}
path=/tree/$branch
;;
(remote)
remote=$2
path=/tree/$branch
;;
(file) path=/blob/$branch/$(git rev-parse --show-prefix)$2 ;;
(tag) path=/releases/tag/$2 ;;
(*) exit 1 ;;
esac
remote=${remote:-$(git config branch."${branch}".remote || printf 'origin\n')}
remote_url=$(git remote get-url "$remote" 2> /dev/null || printf '%s\n' "$remote")
if [[ $remote_url =~ ^git@ ]]; then
url=${remote_url%.git}
url=${url#git@}
url=https://${url/://}
elif [[ $remote_url =~ ^http ]]; then
url=${remote_url%.git}
fi
case $(uname -s) in
(Darwin) open "$url$path" ;;
(*) xdg-open "$url$path" ;;
esac
fi
exit 0
fi
#------------------------------------------------------------------------------
ble-import contrib/integration/fzf-initialize
[[ $- == *i* ]] || return 0
## @fn ble/contrib/integration:fzf-git/initialize bash_source
## @param[in] bash_source
## @var[out] __fzf_git
function ble/contrib/integration:fzf-git/initialize {
local ret
ble/util/readlink "$1"
__fzf_git=$ret
}
ble/contrib/integration:fzf-git/initialize "${BASH_SOURCE[0]:-}"
# GIT heart FZF
# -------------
#------------------------------------------------------------------------------
# Redefine this function to change the options
_fzf_git_fzf() {
fzf-tmux -p80%,60% -- \
--layout=reverse --multi --height=50% --min-height=20 --border \
--border-label-pos=2 \
--color='header:italic:underline,label:blue' \
--preview-window='right,50%,border-left' \
--bind='ctrl-/:change-preview-window(down,50%,border-top|hidden|)' "$@"
}
function ble/contrib/integration:fzf-git/fzf {
[[ $_ble_term_state == internal ]] && ble/term/leave-for-widget
_fzf_git_fzf "$@"
local ext=$?
[[ $_ble_term_state == internal ]] && ble/term/enter-for-widget
return "$ext"
}
_fzf_git_check() {
git rev-parse HEAD > /dev/null 2>&1 && return 0
[[ -n $TMUX ]] && tmux display-message "Not in a git repository"
return 1
}
if [[ -z $_fzf_git_cat ]]; then
# Sometimes bat is installed as batcat
export _fzf_git_cat="cat"
_fzf_git_bat_options="--style='${BAT_STYLE:-full}' --color=always --pager=never"
if command -v batcat > /dev/null; then
_fzf_git_cat="batcat $_fzf_git_bat_options"
elif command -v bat > /dev/null; then
_fzf_git_cat="bat $_fzf_git_bat_options"
fi
fi
_fzf_git_files() {
_fzf_git_check || return "$?"
(git -c color.status=always status --short --no-branch
git ls-files | grep -vxFf <(git status -s | grep '^[^?]' | cut -c4-; ble/util/print :) | sed 's/^/ /') |
ble/contrib/integration:fzf-git/fzf -m --ansi --nth 2..,.. \
--border-label '📁 Files' \
--header $'CTRL-O (open in browser) ALT-E (open in editor)\n\n' \
--bind "ctrl-o:execute-silent:bash $__fzf_git file {-1}" \
--bind "alt-e:execute:${EDITOR:-vim} {-1} > /dev/tty" \
--preview "git diff --no-ext-diff --color=always -- {-1} | sed 1,4d; $_fzf_git_cat {-1}" "$@" |
cut -c4- | sed 's/.* -> //'
}
_fzf_git_branches() {
_fzf_git_check || return "$?"
bash "$__fzf_git" branches |
ble/contrib/integration:fzf-git/fzf --ansi \
--border-label '🌲 Branches' \
--header-lines 2 \
--tiebreak begin \
--preview-window down,border-top,40% \
--color hl:underline,hl+:underline \
--no-hscroll \
--bind 'ctrl-/:change-preview-window(down,70%|hidden|)' \
--bind "ctrl-o:execute-silent:bash $__fzf_git branch {}" \
--bind "alt-a:change-prompt(🌳 All branches> )+reload:bash \"$__fzf_git\" all-branches" \
--preview 'git log --oneline --graph --date=short --color=always --pretty="format:%C(auto)%cd %h%d %s" $(sed s/^..// <<< {} | cut -d" " -f1)' "$@" |
sed 's/^..//' | cut -d' ' -f1
}
_fzf_git_tags() {
_fzf_git_check || return "$?"
git tag --sort -version:refname |
ble/contrib/integration:fzf-git/fzf --preview-window right,70% \
--border-label '📛 Tags' \
--header $'CTRL-O (open in browser)\n\n' \
--bind "ctrl-o:execute-silent:bash $__fzf_git tag {}" \
--preview 'git show --color=always {}' "$@"
}
_fzf_git_hashes() {
_fzf_git_check || return "$?"
git log --date=short --format="%C(green)%C(bold)%cd %C(auto)%h%d %s (%an)" --graph --color=always |
ble/contrib/integration:fzf-git/fzf --ansi --no-sort --bind 'ctrl-s:toggle-sort' \
--border-label '🍡 Hashes' \
--header $'CTRL-O (open in browser) CTRL-D (diff) CTRL-S (toggle sort)\n\n' \
--bind "ctrl-o:execute-silent:bash $__fzf_git commit {}" \
--bind 'ctrl-d:execute:grep -o "[a-f0-9]\{7,\}" <<< {} | head -n 1 | xargs git diff > /dev/tty' \
--color hl:underline,hl+:underline \
--preview 'grep -o "[a-f0-9]\{7,\}" <<< {} | head -n 1 | xargs git show --color=always' "$@" |
awk 'match($0, /[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]*/) { print substr($0, RSTART, RLENGTH) }'
}
_fzf_git_remotes() {
_fzf_git_check || return "$?"
git remote -v | awk '{print $1 "\t" $2}' | uniq |
ble/contrib/integration:fzf-git/fzf --tac \
--border-label '📡 Remotes' \
--header $'CTRL-O (open in browser)\n\n' \
--bind "ctrl-o:execute-silent:bash $__fzf_git remote {1}" \
--preview-window right,70% \
--preview 'git log --oneline --graph --date=short --color=always --pretty="format:%C(auto)%cd %h%d %s" {1}/"$(git rev-parse --abbrev-ref HEAD)"' "$@" |
cut -d$'\t' -f1
}
_fzf_git_stashes() {
_fzf_git_check || return "$?"
git stash list | ble/contrib/integration:fzf-git/fzf \
--border-label '🥡 Stashes' \
--header $'CTRL-X (drop stash)\n\n' \
--bind 'ctrl-x:execute-silent(git stash drop {1})+reload(git stash list)' \
-d: --preview 'git show --color=always {1}' "$@" |
cut -d: -f1
}
_fzf_git_lreflogs() {
_fzf_git_check || return "$?"
git reflog --color=always --format="%C(blue)%gD %C(yellow)%h%C(auto)%d %gs" | ble/contrib/integration:fzf-git/fzf --ansi \
--border-label '📒 Reflogs' \
--preview 'git show --color=always {1}' "$@" |
awk '{print $1}'
}
_fzf_git_each_ref() {
_fzf_git_check || return "$?"
bash "$__fzf_git" refs | ble/contrib/integration:fzf-git/fzf --ansi \
--nth 2,2.. \
--tiebreak begin \
--border-label '☘️ Each ref' \
--header-lines 2 \
--preview-window down,border-top,40% \
--color hl:underline,hl+:underline \
--no-hscroll \
--bind 'ctrl-/:change-preview-window(down,70%|hidden|)' \
--bind "ctrl-o:execute-silent:bash $__fzf_git {1} {2}" \
--bind "alt-e:execute:${EDITOR:-vim} <(git show {2}) > /dev/tty" \
--bind "alt-a:change-prompt(🍀 Every ref> )+reload:bash \"$__fzf_git\" all-refs" \
--preview 'git log --oneline --graph --date=short --color=always --pretty="format:%C(auto)%cd %h%d %s" {2}' "$@" |
awk '{print $2}'
}
#------------------------------------------------------------------------------
# export FZF_DEFAULT_OPTS=--no-unicode
: "${_ble_contrib_fzf_git_config=key-binding}"
# original
function ble/contrib:integration/fzf-git/type:original/init {
ble/builtin/bind '"\er": redraw-current-line'
}
function ble/contrib:integration/fzf-git/type:original {
local binding='"\C-g\C-'$1'": "$(_fzf_git_'$2')\e\C-e\er"'
ble/builtin/bind "$binding"
}
# function ble/contrib:integration/fzf-git/type:original {
# local binding='"\C-g\C-'$1'": "$(_fzf_git_'$2')\M-\C-e\M-\C-l"'
# bind "$binding"
# }
function ble/widget/fzf-git {
ble/widget/insert-string "$(_fzf_git_$1)"
ble/textarea#invalidate
}
# key-binding
function ble/contrib:integration/fzf-git/type:key-binding {
ble-bind -f "C-g C-$1" "fzf-git $2"
}
# sabbrev
function ble/contrib:integration/fzf-git/type:sabbrev/init {
function fzf-git.sabbrev {
COMPREPLY=$(_fzf_git_$1)
ble/textarea#invalidate
}
}
function ble/contrib:integration/fzf-git/type:sabbrev {
ble-sabbrev -m "g$1"="fzf-git.sabbrev $2"
}
# arpeggio
function ble/contrib:integration/fzf-git/type:arpeggio/init {
ble-import 'lib/vim-arpeggio.sh'
}
function ble/contrib:integration/fzf-git/type:arpeggio {
ble/lib/vim-arpeggio.sh/bind -f "g$1" "fzf-git $2"
}
# old-functions
function ble/contrib:integration/fzf-git/type:old-functions/init {
function is_in_git_repo { _fzf_git_check "$@"; }
function fzf-down { ble/contrib/integration:fzf-git/fzf "$@"; }
}
function ble/contrib:integration/fzf-git/type:old-functions {
# Note: To suppress duplicate adjustment of the terminal states, we override
# "_ble_term_state" with the temporary environment. I.e., when fzf is called
# through these old function names, we never adjust the terminal states.
builtin eval "function g$1 { _ble_term_state= _fzf_git_$2 \"\$@\"; }"
}
function ble/contrib:integration/fzf-git/initialize {
local type
for type in original key-binding sabbrev arpeggio old-functions; do
[[ :$_ble_contrib_fzf_git_config: == *:"$type":* ]] || continue
ble/function#try ble/contrib:integration/fzf-git/type:"$type"/init
ble/contrib:integration/fzf-git/type:"$type" f files
ble/contrib:integration/fzf-git/type:"$type" b branches
ble/contrib:integration/fzf-git/type:"$type" t tags
ble/contrib:integration/fzf-git/type:"$type" h hashes
ble/contrib:integration/fzf-git/type:"$type" r remotes
ble/contrib:integration/fzf-git/type:"$type" s stashes
ble/contrib:integration/fzf-git/type:"$type" l lreflogs
ble/contrib:integration/fzf-git/type:"$type" e each_ref
done
builtin unset -f "$FUNCNAME"
}
ble/contrib:integration/fzf-git/initialize

View File

@@ -0,0 +1,11 @@
# ble/contrib/integration/fzf-initialize.bash (C) 2020-2024, akinomyoga
# Usage: Please write the following lines in blerc
#
# ```bash
# _ble_contrib_fzf_base=/path/to/fzf-base-directory
# ble-import -d integration/fzf-initialize
# ```
ble-import contrib/integration/fzf.common
ble/contrib/integration:fzf/locate-shell-settings _ble_contrib_fzf fzf fzf || return 1

View File

@@ -0,0 +1,44 @@
# ble/contrib/integration/fzf-key-bindings.bash (C) 2020-2024, akinomyoga
[[ $- == *i* ]] || return 0
if ! ble/is-function __fzf_history__; then
ble-import contrib/integration/fzf-initialize || return 1
ble/function#push bind :
if [[ -f $_ble_contrib_fzf_base/key-bindings.bash ]]; then
source -- "$_ble_contrib_fzf_base/key-bindings.bash"
elif [[ -f $_ble_contrib_fzf_base/shell/key-bindings.bash ]]; then
source -- "$_ble_contrib_fzf_base/shell/key-bindings.bash"
elif [[ $_ble_contrib_fzf_base == __eval_fzf_bash__ ]]; then
ble/util/eval-stdout '"$_ble_contrib_fzf_path" --bash | sed -n "/### key-bindings/,/### end/p"'
fi
ble/function#pop bind
fi
function ble/contrib/integration:fzf-key-bindings/is-fzf-above-7c447bbd {
local def; ble/function#getdef __fzf_history__
[[ $def == *READLINE_LINE=* ]]
}
# CTRL-T - Paste the selected file path into the command line
ble-bind -m emacs -x C-t fzf-file-widget
ble-bind -m vi_imap -x C-t fzf-file-widget
ble-bind -m vi_nmap -s C-t 'i\C-t'
# CTRL-R - Paste the selected command from history into the command line
ble-bind -m emacs -x C-r fzf-history-widget
ble-bind -m vi_imap -x C-r fzf-history-widget
ble-bind -m vi_nmap -s C-r 'i\C-r'
function fzf-history-widget {
ble/util/assign READLINE_LINE '__fzf_history__'
ble/util/assign READLINE_LINE 'history -p "$READLINE_LINE"'
READLINE_POINT=${#READLINE_LINE}
}
((_ble_bash>=40000)) &&
ble/contrib/integration:fzf-key-bindings/is-fzf-above-7c447bbd &&
function fzf-history-widget { __fzf_history__; }
# ALT-C - cd into the selected directory
ble-bind -m emacs -c M-c 'ble/util/eval-stdout "__fzf_cd__"'
ble-bind -m vi_imap -c M-c 'ble/util/eval-stdout "__fzf_cd__"'
ble-bind -m vi_nmap -c M-c 'ble/util/eval-stdout "__fzf_cd__"'

View File

@@ -0,0 +1,125 @@
# ble/contrib/integration/fzf-menu.bash (C) 2024, akinomyoga
#
# https://github.com/akinomyoga/ble.sh/issues/479
## @fn ble/contrib/integration:fzf-menu/SELECTOR args...
## Returns the index of the selected candidate. Users can override this
## function to adjust the detailed behavior.
##
## @param[in] args...
## The list of candidates. Each argument takes the form
## "<index><SEP><word><SEP><desc>". <SEP> is specified by the shell
## variable sep.
## @var[in] common_prefix
## The common prefix of the filter target
## @var[in] sep
## The separator used to separate the index, word, and description in an
## argument
## @stdout
## Output the selected index.
function ble/contrib/integration:fzf-menu/SELECTOR {
local fzf_options
fzf_options=(--ansi --query "$common_prefix")
# Use Unicode box-drawing characters only when the width of the box-drawing
# characters in the current terminal is 1. The width of the box-drawing
# characters is specified to be East_Asian_Width=A (Ambiguous), and the
# actual width depends on the terminal. However, fzf assumes the width of
# the box-drawing characters to be always 1. This breaks the layout of the
# terminal content in the terminals where the box-drawing characters have
# double width. We check the width of the box-drawing characters (U+2500 as
# a representative) and disable the use of the box-drawing characters by fzf
# when it is not 1.
local ret
ble/util/c2w 0x2500
((ret==1)) || ble/array#push fzf_options --no-unicode
# When the current completion is performed by case-insensitive search (i.e.,
# when the readline variable is set to "set completion-ignore-case on"), we
# let fzf filter items by case-insensitive matching.
[[ :$comp_type: == *:i:* ]] &&
ble/array#push fzf_options --ignore-case
local formatter="printf '%s\n' \"\$@\""
ble/bin#has column &&
formatter=$formatter' | column -ts "$sep" -o "$sep" -c unlimited'
ble/array#push fzf_options -d "$sep" --with-nth=2.. --nth=1
builtin eval -- "$formatter" | fzf "${fzf_options[@]}" | cut -d "$sep" -f 1
}
function ble/contrib/integration:fzf-menu/.get-common-prefix {
local cand_word ret
cand_word=("$@")
ble/complete/candidates/determine-common-prefix
common_prefix=$ret
}
function ble/contrib/integration:fzf-menu/.select-and-insert {
if ((cand_count>1)); then
local common_prefix=
ble/contrib/integration:fzf-menu/.get-common-prefix "${cand_cand[@]}"
(($?==148)) && return 148
local desc_sgr0=$_ble_term_sgr0
ble/color/face2sgr menu_desc_quote; local desc_sgrq=$ret
ble/color/face2sgr menu_desc_type; local desc_sgrt=$ret
# Create a list of "<index>^\<word>^\- <desc>" where ^\ represents the ANSI
# control character FS (U+001C).
local sep=$_ble_term_FS
local -a list=()
local i
for ((i=0;i<cand_count;i++)); do
local ACTION=${cand_pack[i]%%:*}
local "${_ble_complete_cand_varnames[@]/%/=}" # disable=#D1570
ble/complete/cand/unpack "${cand_pack[i]}"
local g=0 prefix= suffix= desc=
ble/function#try ble/complete/action:"$ACTION"/init-menu-item
(($?==148)) && return 148
ble/function#try ble/complete/action:"$ACTION"/get-desc
(($?==148)) && return 148
ble/color/g2sgr "$g"; local sgr=$ret
local item=$prefix$sgr$CAND$_ble_term_sgr0$suffix
item=${item//"$sep"/'^\'}
desc=${desc//"$sep"/'^\'}
ble/array#push list "$i$sep$item$_ble_term_sgr0$sep - $desc$_ble_term_sgr0"
done
ble/term/leave-for-widget
local index
ble/util/assign index 'ble/contrib/integration:fzf-menu/SELECTOR "${list[@]}"'
ble/term/enter-for-widget
ble/textarea#invalidate
if [[ $index ]]; then
cand_count=1
cand_cand=("${cand_cand[index]}")
cand_word=("${cand_word[index]}")
cand_pack=("${cand_pack[index]}")
fi
fi
if ((cand_count==1)); then
ble/complete/insert-common
fi
# To suppress the post-processing for ble.sh's menu interface, we return 148.
return 148
}
ble-import -C 'ble/function#push ble/complete/menu/show "ble/contrib/integration:fzf-menu/.select-and-insert"' core-complete
# function ble/contrib/integration:fzf-menu/complete.after {
# [[ $_ble_complete_menu_active ]] || return 0
# local COMP1 COMP2 COMPS COMPV
# local comp_type comps_flags comps_fixed
# local cand_count cand_cand cand_word cand_pack
# ble/complete/candidates/clear
# ble/complete/menu/generate-candidates-from-menu
# ble/complete/menu/clear
# ble/contrib/integration:fzf-menu/.select-and-insert
# }
# ble-import -C 'ble/function#advice after ble/widget/complete "ble/contrib/integration:fzf-menu/compelte.after"' core-complete

View File

@@ -0,0 +1,164 @@
# ble/contrib/integration/fzf.common.bash (C) 2020-2024, akinomyoga
## @fn ble/contrib/integration:fzf/locate-shell-settings prefix pkgname cmd
## @param[in] prefix pkgname cmd
##
## @var[out] PREFIX_base
## @var[out] PREFIX_path
## @var[out] PREFIX_version
##
function ble/contrib/integration:fzf/locate-shell-settings {
local prefix=${1:-_ble_contrib_fzf} pkgname=${2:-fzf} cmd=${3:-fzf}
local base_ref=${prefix}_base
local path_ref=${prefix}_path
local version_ref=${prefix}_version
local path=$cmd
local version=-1
if local base=${!base_ref-}; [[ -d $base ]]; then
if [[ -d $base/bin && :$PATH: != *:"$base/bin":* ]]; then
export PATH="${PATH:+${PATH}:}$base/bin"
fi
local path
if ! ble/bin#get-path "$cmd"; then
ble/util/print "ble/contrib/integration: '$cmd' not found." >&2
return 1
fi
ble/util/set "$path_ref" "$path"
else
local path
if ! ble/bin#get-path "$cmd"; then
ble/util/print "ble/contrib/integration: '$cmd' not found." >&2
return 1
fi
local base= ret
ble/util/readlink "$path"
ret=${ret%/*} # fzf, fzf-linux_amd64, etc.
ret=${ret%/bin} # repo/bin/
ret=${ret%/target} # repo/target (compile directory)
ret=${ret%/target/debug} # repo/target/debug (cargo compile directory)
ret=${ret%/target/release} # repo/target/release (cargo compile directory)
if [[ -s $ret/shell/key-bindings.bash ]]; then
base=$ret
elif [[ -d $ret/share/$pkgname/shell ]]; then
base=$ret/share/$pkgname
elif [[ -s $ret/share/$pkgname/key-bindings.bash ]]; then
# NixOS package (https://github.com/akinomyoga/blesh-contrib/pull/5#issuecomment-1019394821)
base=$ret/share/$pkgname
elif [[ -s $ret/share/doc/$pkgname/examples/key-bindings.bash ]]; then
# Ubuntu $pkgname package (https://github.com/akinomyoga/blesh-contrib/pull/5#issuecomment-1019394821)
base=$ret/share/doc/$pkgname/examples
elif [[ -d /usr/share/$pkgname/shell ]]; then
base=/usr/share/$pkgname
elif [[ -d /usr/share/doc/$pkgname/examples/key-bindings.bash ]]; then
# Ubuntu fzf package (https://github.com/akinomyoga/blesh-contrib/pull/5#issuecomment-1019394821)
base=/usr/share/doc/$pkgname/examples
else
if [[ $cmd == fzf ]]; then
ble/util/assign version '"$path" --version 2>/dev/null'
ble/string#match "$version" '^[[:blank:]]*([0-9]+)\.([0-9]+)\.([0-9]+)?' &&
((version=10#0${BASH_REMATCH[1]}*10000+10#0${BASH_REMATCH[2]}*100+10#0${BASH_REMATCH[3]}))
# fzf >= 0.48.0 has started to offer « eval "$(fzf --bash)" », so
# careless package maintainers may drop the shell integration files. It
# shouldn't actually be dropped, but we would suppress the error message
# because we can work around such a package using "fzf --bash".
((version>=4800)) && base=__eval_fzf_bash__
fi
fi
if [[ ! $base ]]; then
ble/util/print "ble/contrib/integration: failed to find '$cmd' base directory" >&2
return 1
fi
ble/util/set "$base_ref" "$base"
fi
ble/util/set "$path_ref" "$path"
ble/util/set "$version_ref" "$version"
return 0
}
function ble/contrib/integration:fzf/.is-bind-dsr0 {
[[ $1 == '"\e[0n"'* ]] &&
ble/string#match "${FUNCNAME[2]#ble/function#advice/original:}" '^__?(fzf|skim)_'
}
function ble/contrib/integration:fzf/.is-printf-dsr5 {
[[ $1 == '\e[5n' ]] &&
ble/string#match "${FUNCNAME[2]#ble/function#advice/original:}" '^__?(fzf|skim)_'
}
function ble/contrib/integration:fzf/complete.advice {
local opts=${1-}
if [[ ! ${_ble_attached-} ]]; then
ble/function#push caller 'builtin caller ${1+"$(($1+6))"}'
ble/function#advice/do
ble/function#pop caller
return 0
fi
[[ :$comp_type: == *:auto:* || :$comp_type: == *:[maA]:* ]] && return 0
if [[ ! ${_ble_contrib_fzf_comp_words_raw-} ]]; then
local val_COMP_LINE val_COMP_POINT val_COMP_WORDS val_COMP_CWORD
ble/util/save-vars val_ COMP_LINE COMP_POINT COMP_WORDS COMP_CWORD
compopt -o ble/syntax-raw
local _ble_contrib_fzf_comp_words_raw=1
local COMP_WORDS; COMP_WORDS=("${comp_words[@]}")
local COMP_CWORD=$comp_cword
local COMP_LINE=$comp_line COMP_POINT=$comp_point
fi
ble/function#push bind 'ble/contrib/integration:fzf/.is-bind-dsr0 "$@" || ble/builtin/bind "$@"'
ble/function#push printf 'ble/contrib/integration:fzf/.is-printf-dsr5 "$@" || builtin printf "$@"'
ble/function#push caller 'builtin caller ${1+"$(($1+6))"}'
ble/term/leave-for-widget
if [[ :$opts: == *:keep-stdin:* ]]; then
ble/function#advice/do >&"${_ble_util_fd_tty_stdout:-1}" 2>&"${_ble_util_fd_tty_stderr:-2}"
else
ble/function#advice/do >&"${_ble_util_fd_tty_stdout:-1}" 2>&"${_ble_util_fd_tty_stderr:-2}" <&"${_ble_util_fd_tty_stdin:-0}"
fi
ble/term/enter-for-widget
ble/function#pop caller
ble/function#pop printf
ble/function#pop bind
ble/textarea#invalidate
# 単一候補生成の場合は他の候補 (sabbrev 等) を消去して単一確定させる
if ((ADVICE_EXIT==0&&${#COMPREPLY[@]}==1)); then
compopt -o ble/no-default
ble/complete/candidates/clear
[[ $old_cand_count ]] &&
! ble/variable#is-global old_cand_count &&
old_cand_count=0
fi
}
_ble_contrib_fzf_adjust_dynamic_completion=
function ble/contrib/integration:fzf/handle_dynamic_completion.advice {
if [[ ${_ble_attached-} && ${_ble_contrib_fzf_comp_words_raw-} ]]; then
compopt +o ble/syntax-raw
local _ble_contrib_fzf_comp_words_raw=
local COMP_LINE COMP_POINT COMP_WORDS COMP_CWORD
ble/util/restore-vars val_ COMP_LINE COMP_POINT COMP_WORDS COMP_CWORD
fi
if [[ ${_ble_attached-} && $_ble_contrib_fzf_adjust_dynamic_completion ]]; then
local comp_func=_fzf_orig_completion_${ADVICE_WORDS[1]} comp_prog=
local comp_func=${!comp_func}
local comp_func=${comp_func##*#}
if ble/bin#has "$comp_func"; then
ble/complete/progcomp/adjust-third-party-completions
fi
fi
ble/function#advice/do
}

View File

@@ -0,0 +1,27 @@
# ble/contrib/integration/nix-completion.bash (C) 2023, akinomyoga
[[ $- == *i* ]] || return 0
function ble/contrib/integration:nix-completion/_complete_nix.advice {
if [[ ${_ble_attached-} && " ${FUNCNAME[*]} " == *" ble/complete/progcomp/.compgen "* && ${COMP_WORDS[0]-} != *[\'\"\\]* ]]; then
local _ble_nix_cmd=${COMP_WORDS[0]-nix} ret
ble/function#push "$_ble_nix_cmd" '
local IFS=$_ble_term_IFS
local -a args; args=("$@")
ble/util/conditional-sync "exec $_ble_nix_cmd \"\${args[@]}\"" \
'\''! ble/complete/check-cancel'\'' 128 progressive-weight:killall'
ble/function#advice/do
ble/function#pop "$_ble_nix_cmd"
else
ble/function#advice/do
return 0
fi
}
function ble/contrib/integration:nix-completion/adjust {
if ble/is-function _complete_nix; then
ble/function#advice around _complete_nix ble/contrib/integration:nix-completion/_complete_nix.advice
fi
}
ble/contrib/integration:nix-completion/adjust

View File

@@ -0,0 +1,29 @@
# ble/contrib/integration/skim-completion.bash (C) 2024, akinomyoga
[[ $- == *i* ]] || return 0
# skim/shell/completion.bash を未ロードの時のみロードする
if ! ble/is-function _skim_complete; then
ble-import contrib/integration/skim-initialize || return 1
if [[ -f $_ble_contrib_skim_base/completion.bash ]]; then
source -- "$_ble_contrib_skim_base/completion.bash"
elif [[ -f $_ble_contrib_skim_base/shell/completion.bash ]]; then
source -- "$_ble_contrib_skim_base/shell/completion.bash"
elif [[ $_ble_contrib_skim_base == */share/skim && -f /etc/bash_completion.d/skim ]]; then
source /etc/bash_completion.d/skim
fi
fi
# clear blesh completer for cd
blehook/eval-after-load complete 'builtin unset -f ble/cmdinfo/complete:cd'
# Note: completion integration has been moved from "skim/shell/completion.bash"
# to "skim/shell/key-bindings.bash". See "skim-key-bindings.bash" for details.
if ble/is-function _skim_handle_dynamic_completion; then
ble-import contrib/integration/fzf.common
ble/function#advice -f around __skim_generic_path_completion 'ble/contrib/integration:fzf/complete.advice'
ble/function#advice -f around _skim_complete 'ble/contrib/integration:fzf/complete.advice keep-stdin'
ble/function#advice -f around _skim_complete_kill 'ble/contrib/integration:fzf/complete.advice'
ble/function#advice -f around _skim_handle_dynamic_completion 'ble/contrib/integration:fzf/handle_dynamic_completion.advice'
_ble_contrib_fzf_adjust_dynamic_completion=1
fi

View File

@@ -0,0 +1,4 @@
# ble/contrib/integration/skim-initialize.bash (C) 2024, akinomyoga
ble-import contrib/integration/fzf.common
ble/contrib/integration:fzf/locate-shell-settings _ble_contrib_skim skim sk || return 1

View File

@@ -0,0 +1,50 @@
# ble/contrib/integration/skim-key-bindings.bash (C) 2024, akinomyoga
[[ $- == *i* ]] || return 0
if ! ble/is-function __skim_history__; then
ble-import contrib/integration/skim-initialize || return 1
ble/function#push bind :
if [[ -f $_ble_contrib_skim_base/key-bindings.bash ]]; then
source -- "$_ble_contrib_skim_base/key-bindings.bash"
elif [[ -f $_ble_contrib_skim_base/shell/key-bindings.bash ]]; then
source -- "$_ble_contrib_skim_base/shell/key-bindings.bash"
fi
ble/function#pop bind
fi
# CTRL-T - Paste the selected file path into the command line
ble-bind -m emacs -x C-t skim-file-widget
ble-bind -m vi_imap -x C-t skim-file-widget
ble-bind -m vi_nmap -s C-t 'i\C-t'
# CTRL-R - Paste the selected command from history into the command line
ble-bind -m emacs -x C-r skim-history-widget
ble-bind -m vi_imap -x C-r skim-history-widget
ble-bind -m vi_nmap -s C-r 'i\C-r'
function skim-history-widget { __skim_history__; }
# ALT-C - cd into the selected directory
ble-bind -m emacs -c M-c 'ble/util/eval-stdout "__skim_cd__"'
ble-bind -m vi_imap -c M-c 'ble/util/eval-stdout "__skim_cd__"'
ble-bind -m vi_nmap -c M-c 'ble/util/eval-stdout "__skim_cd__"'
# Note: completion integration has been moved from "skim/shell/completion.bash"
# to "skim/shell/key-bindings.bash". When the argument completion for "sk" is
# implemented using Clap in Ref. [1], somehow the completion integration (which
# is unrelated to the argument completion for "sk") was completely removed.
# Later, this was pointed out in Ref. [2], and the completion integration code
# was restored in "key-bindings.bash" instead of "completion.bash".
#
# [1] https://github.com/skim-rs/skim/pull/586
# [2] https://github.com/skim-rs/skim/issues/726
# [3] https://github.com/skim-rs/skim/commit/f87ff6740b20794eaf6288b901f85b7737a28bcf
if ble/is-function _skim_handle_dynamic_completion; then
# patch skim completion functions
ble-import contrib/integration/fzf.common
ble/function#advice -f around __skim_generic_path_completion 'ble/contrib/integration:fzf/complete.advice'
ble/function#advice -f around _skim_complete 'ble/contrib/integration:fzf/complete.advice keep-stdin'
ble/function#advice -f around _skim_complete_kill 'ble/contrib/integration:fzf/complete.advice'
ble/function#advice -f around _skim_handle_dynamic_completion 'ble/contrib/integration:fzf/handle_dynamic_completion.advice'
_ble_contrib_fzf_adjust_dynamic_completion=1
fi

View File

@@ -0,0 +1,60 @@
# ble/contrib/integration/zoxide.bash (C) 2022, akinomyoga
function ble/contrib/integration:zoxide/completion.advice {
if [[ ! ${_ble_attached-} ]]; then
ble/function#advice/do
return 0
fi
[[ :$comp_type: == *:auto:* || :$comp_type: == *:[maA]:* ]] && return 0
ble/term/leave-for-widget
ble/function#advice/do >/dev/null
ble/term/enter-for-widget
ble/textarea#invalidate
# 単一候補生成の場合は他の候補 (sabbrev 等) を消去して単一確定させる
if ((ADVICE_EXIT==0&&${#COMPREPLY[@]}==1)); then
ble/complete/candidates/clear
[[ $old_cand_count ]] &&
! ble/variable#is-global old_cand_count &&
old_cand_count=0
fi
}
function ble/contrib/integration:zoxide/command.advice {
if [[ ${_ble_attached-} && ${READLINE_MARK+set} ]]; then
ble/bin/stty icanon
ble/function#advice/do
ble/bin/stty -icanon
else
ble/function#advice/do
fi
}
function ble/contrib/integration:zoxide/adjust {
local found=
if ble/is-function _z; then
ble/function#advice around _z ble/contrib/integration:zoxide/completion.advice
found=1
fi
if ble/is-function __zoxide_z_complete; then
ble/function#advice around __zoxide_z_complete ble/contrib/integration:zoxide/completion.advice
found=1
fi
if ble/is-function __zoxide_z; then
ble/function#advice around __zoxide_z ble/contrib/integration:zoxide/command.advice
found=1
fi
if ble/is-function __zoxide_zi; then
ble/function#advice around __zoxide_zi ble/contrib/integration:zoxide/command.advice
found=1
fi
[[ $found ]]
}
if ! ble/contrib/integration:zoxide/adjust; then
ble/bin#has zoxide || return 1
ble/util/eval-stdout 'zoxide init bash'
ble/contrib/integration:zoxide/adjust
fi