Skip to content

Commit

Permalink
progcomp: support quoted commands and better "progcomp_alias"
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed May 29, 2021
1 parent 301d40f commit dbe87c3
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 35 deletions.
133 changes: 116 additions & 17 deletions lib/core-complete.sh
Expand Up @@ -2498,6 +2498,9 @@ function ble/complete/progcomp/.compgen {
if [[ $is_special_completion ]]; then
# -I, -D, etc.
ble/util/assign compdef 'builtin complete -p "$compcmd" 2>/dev/null'
elif ble/syntax:bash/simple-word/is-simple "$compcmd"; then
# 既に呼び出し元で quote されている想定
ble/util/assign compdef "builtin complete -p -- $compcmd 2>/dev/null"
else
ble/util/assign compdef 'builtin complete -p -- "$compcmd" 2>/dev/null'
fi
Expand Down Expand Up @@ -2636,12 +2639,72 @@ function ble/complete/progcomp/.compline-rewrite-command {
[[ $1 != "$ocmd" ]] || (($#>=2)) || return 1
local IFS=$_ble_term_IFS
local ins="$*"
comp_line=$ins${comp_line:${#ocmd}}
((comp_point-=${#ocmd},comp_point<0&&(comp_point=0),comp_point+=${#ins}))
if (($#==0)); then
# コマンド除去 (aliasで空に展開された時)
local ret; ble/string#ltrim "${comp_line:${#ocmd}}"
((comp_point-=${#comp_line}-${#ret}))
comp_line=$ret
else
comp_line=$ins${comp_line:${#ocmd}}
((comp_point-=${#ocmd}))
fi
((comp_point<0&&(comp_point=0),comp_point+=${#ins}))
comp_words=("$@" "${comp_words[@]:1}")
((comp_cword&&(comp_cword+=$#-1)))
}

function ble/complete/progcomp/.split-alias-words {
local tail=$1
local rex_redir='^'$_ble_syntax_bash_RexRedirect
local rex_word='^'$_ble_syntax_bash_simple_rex_element'+'
local rex_delim=$'^[\n;|&]'
local rex_spaces=$'^[ \t]+'
local rex_misc='^[<>()]+'

local -a words=()
while [[ $tail ]]; do
if [[ $tail =~ $rex_redir && $tail != ['<>']'('* ]]; then
ble/array#push words "$BASH_REMATCH"
tail=${tail:${#BASH_REMATCH}}
elif [[ $tail =~ $rex_word ]]; then
local w=$BASH_REMATCH
tail=${tail:${#w}}
if [[ $tail && $tail != ["$_ble_term_IFS;|&<>()"]* ]]; then
local s=${tail%%["$_ble_term_IFS"]*}
tail=${tail:${#s}}
w=$w$s
fi
ble/array#push words "$w"
elif [[ $tail =~ $rex_delim ]]; then
words=()
tail=${tail:${#BASH_REMATCH}}
elif [[ $tail =~ $rex_spaces ]]; then
tail=${tail:${#BASH_REMATCH}}
elif [[ $tail =~ $rex_misc ]]; then
ble/array#push words "$BASH_REMATCH"
tail=${tail:${#BASH_REMATCH}}
else
local w=${tail%%["$_ble_term_IFS"]*}
ble/array#push words "$w"
tail=${tail:${#w}}
fi
done

# skip assignments/redirections
local i=0 rex_assign='^[a-zA-Z_0-9]+(\['$_ble_syntax_bash_simple_rex_element'*\])?\+?='
while ((i<${#words[@]})); do
if [[ ${words[i]} =~ $rex_assign ]]; then
((i++))
elif [[ ${words[i]} =~ $rex_redir && ${words[i]} != ['<>']'('* ]]; then
((i+=2))
else
break
fi
done

ret=("${words[@]:i}")
}

## @fn ble/complete/progcomp cmd opts
## 補完指定を検索して対応する補完関数を呼び出します。
## @var[in] comp_line comp_words comp_point comp_cword
Expand All @@ -2654,30 +2717,52 @@ function ble/complete/progcomp {
comp_words=("${tmp[@]}")

local -a alias_args=()
local alias_checked=' '
[[ :$opts: == *:__recursive__:* ]] ||
local alias_checked=' '
while :; do
if ble/is-function "ble/cmdinfo/complete:$cmd"; then
ble/complete/progcomp/.compline-rewrite-command "$cmd" "${alias_args[@]}"
"ble/cmdinfo/complete:$cmd" "$opts"

# @var cmd ... 元のコマンド名
# @var ucmd ... simple-word/eval したコマンド名
# @var qcmds ... simple-word/eval x quote-word したコマンド
local ret ucmd qcmds
ucmd=$cmd qcmds=("$cmd")
if ble/syntax:bash/simple-word/is-simple "$cmd" &&
ble/syntax:bash/simple-word/eval "$cmd" noglob &&
[[ $ret != "$cmd" || ${#ret[0]} -ne 1 ]]; then

ucmd=${ret[0]} qcmds=()
local word
for word in "${ret[@]}"; do
ble/string#quote-word "$word" quote-empty
ble/array#push qcmds "$ret"
done
fi

if ble/is-function "ble/cmdinfo/complete:$ucmd"; then
ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}"
"ble/cmdinfo/complete:$ucmd" "$opts"
return "$?"
elif [[ $cmd == */?* ]] && ble/is-function "ble/cmdinfo/complete:${cmd##*/}"; then
ble/complete/progcomp/.compline-rewrite-command "${cmd##*/}" "${alias_args[@]}"
"ble/cmdinfo/complete:${cmd##*/}" "$opts"
elif [[ $ucmd == */?* ]] && ble/is-function "ble/cmdinfo/complete:${ucmd##*/}"; then
ble/string#quote-word "${ucmd##*/}"; qcmds[0]=$ret
ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}"
"ble/cmdinfo/complete:${ucmd##*/}" "$opts"
return "$?"
elif builtin complete -p "$cmd" &>/dev/null; then
ble/complete/progcomp/.compline-rewrite-command "$cmd" "${alias_args[@]}"
elif builtin complete -p "$ucmd" &>/dev/null; then
ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}"
ble/complete/progcomp/.compgen "$opts"
return "$?"
elif [[ $cmd == */?* ]] && builtin complete -p "${cmd##*/}" &>/dev/null; then
ble/complete/progcomp/.compline-rewrite-command "${cmd##*/}" "${alias_args[@]}"
elif [[ $ucmd == */?* ]] && builtin complete -p "${ucmd##*/}" &>/dev/null; then
ble/string#quote-word "${ucmd##*/}"; qcmds[0]=$ret
ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}"
ble/complete/progcomp/.compgen "$opts"
return "$?"
elif
# bash-completion の loader を呼び出して遅延補完設定をチェックする。
ble/function#try __load_completion "${cmd##*/}" &>/dev/null &&
builtin complete -p "${cmd##*/}" &>/dev/null
ble/function#try __load_completion "${ucmd##*/}" &>/dev/null &&
builtin complete -p "${ucmd##*/}" &>/dev/null
then
ble/complete/progcomp/.compline-rewrite-command "${cmd##*/}" "${alias_args[@]}"
ble/string#quote-word "${ucmd##*/}"; qcmds[0]=$ret
ble/complete/progcomp/.compline-rewrite-command "${qcmds[@]}" "${alias_args[@]}"
ble/complete/progcomp/.compgen "$opts"
return "$?"
fi
Expand All @@ -2688,7 +2773,21 @@ function ble/complete/progcomp {

local ret
ble/util/expand-alias "$cmd"
ble/string#split-words ret "$ret"
[[ $ret == "$cmd" ]] && break
ble/complete/progcomp/.split-alias-words "$ret"
if ((${#ret[@]}==0)); then
# alias 展開により内容が消滅した時は次の単語をコマンドとして再度展開を繰り返す
ble/complete/progcomp/.compline-rewrite-command "${alias_args[@]}"
if ((${#comp_words[@]})); then
if ((comp_cword==0)); then
ble/complete/source:command
else
ble/complete/progcomp "${comp_words[0]}" "__recursive__:$opts"
fi
fi
return $?
fi

[[ $alias_checked != *" $ret "* ]] || break
cmd=$ret
((${#ret[@]}>=2)) &&
Expand Down
2 changes: 1 addition & 1 deletion lib/core-syntax.sh
Expand Up @@ -1484,7 +1484,7 @@ function ble/syntax:bash/simple-word/eval/.cache-load {
## cached
## 展開結果をキャッシュします。
##
## 以下はパス名展開の起こる可能性にある単語に大して有効です
## 以下はパス名展開の起こる可能性にある単語に対して有効です
##
## stopcheck
## ユーザー入力があった場合に中断します。
Expand Down
7 changes: 4 additions & 3 deletions memo/ChangeLog.md
Expand Up @@ -46,6 +46,7 @@
- util (`bleopt`): support option `-r` and `-u` and wildcards in option names
- util (`blehook`): hide internal hooks by default and support option `-a`
- util, color: fix argument analysis of `bleopt`, `blehook`, and `ble-face` (fixup c94d292) `#D1571` bb53271
- progcomp: support quoted commands and better `progcomp_alias` `#D1581` `#D1583` 0000000

## Changes

Expand Down Expand Up @@ -74,7 +75,7 @@
- main: preserve user-space overridden builtins `#D1519` 0860be0
- util (`ble/util/type`): fix a bug that aliases are not properly highlighted (reported by 3ximus) `#D1526` 45b30a7
- main: preserve user's `expand_aliases` and allow aliases in internal space (fixup 0860be0) `#D1574` afc4112
- main: main: fix expand_aliases unset on ble-reload (fixup afc4112) `#D1577` 0000000
- main: main: fix expand_aliases unset on ble-reload (fixup afc4112) `#D1577` 3417388
- syntax (`ble/syntax:bash/simple-word/eval`): optimize large array passing (motivated by timjrd) `#D1522` c89aa23
- main: accept non-regular files as `blerc` and add option `--norc` `#D1530` 7244e2f
- prompt: let `stderr` pass through to tty in evaluating `PS0` (reported by tycho-kirchner) `#D1541` 24a88ce
Expand Down Expand Up @@ -124,7 +125,7 @@
- sabbrev (`ble-sabbrev`): fix delayed output before the initialization `#D1573` 5d85238
- main: fix the workaround for `set -u` `#D1575` 76073a9
- history: fix the workaround for bash-3.0 bug of reducing histories `#D1576` 15c9133
- syntax: fix a bug that argument completion is attempted in nested commands (reported by huresche) `#D1579` 0000000
- syntax: fix a bug that argument completion is attempted in nested commands (reported by huresche) `#D1579` 301d40f

## Compatibility

Expand Down Expand Up @@ -157,7 +158,7 @@
- tui, edit: add a new render mode for full-screen applications 817889d
- main: prefer `nawk` over `mawk` and `gawk` `#D1523` `#D1524` c89aa23
- main (`ble/bin/.freeze-utility-path`): fix unupdated temporary implementations `#D1528` c70a3b4
- util (`ble/util/assign`): work around subshell conflits `#D1578` 0000000
- util (`ble/util/assign`): work around subshell conflicts `#D1578` 6e4bb12
- test (`test-canvas`): fix dependency on `ext/contra` `#D1525` c89aa23
- util: inherit special file descriptors `#D1552` 98835b5
- global: use `_ble_term_IFS` `#D1557` d23ad3c
Expand Down
94 changes: 80 additions & 14 deletions note.txt
Expand Up @@ -1442,20 +1442,6 @@ bash_tips

2021-05-29

* complete: そもそも -F に指定する事のできる文字列には制限がある筈である。こ
れを満たさない場合には強制的に其処で終端させる等の対策が必要なのではないか。

然し、;&| 等が含まれていた場合にどの様に取り扱うべきかは不明である。明らか
に何かが間違っているけれども、だからと言ってその直前までを関数名と捉えるの
にも無理がある様な気がする。然し、それ以外に解釈のしようがないとも言える。

* complete: quote していると補完関数が認識されない。例えば git co とすると OK
だが 'git' co とすると反応しない。然し実の所、これは普通の bash でも同様で
ある。普通の bash でも 'git' としている時には git の補完関数は呼び出されな
い。

* complete: alias の展開結果で変数代入を除去するべきではないか

* syntax: \q を着色したい

2021-05-28
Expand Down Expand Up @@ -4706,6 +4692,86 @@ bash_tips

2021-05-29

* complete: quote されているコマンド名に対しても補完関数を適切に探索 [#D1583]

quote していると補完関数が認識されない。例えば git co とすると OK だが
'git' co とすると反応しない。然し実の所、これは普通の bash でも同様である。
普通の bash でも 'git' としている時には git の補完関数は呼び出されない。

unquote した上で補完を探索するべきである気がする。但し、alias 展開などに関
しては quote された物で実行するという事。comp_line, comp_words に入っている
のは展開前の quote された物であるので余計な事は考えなくて良い。

* [reject] complete: そもそも -F に指定する事のできる文字列には制限がある筈である [#D1582]

これを満たさない場合には強制的に其処で終端させる等の対策が必要なのではない
か。

然し、;&| 等が含まれていた場合にどの様に取り扱うべきかは不明である。明らか
に何かが間違っているけれども、だからと言ってその直前までを関数名と捉えるの
にも無理がある様な気がする。然し、それ以外に解釈のしようがないとも言える。

或いは途中に空白がある場合には ble.sh の拡張として関数に余分の引数を渡す事
にする? 然し、これは最新の bash-5.1 では使えない事なので古い Bash だけで使
える機能として提供しても仕方がない。結局これは失敗するべきなのではないか。
そして失敗するのだとしたら現状の動作のままで良いという事の気がする。

* complete: alias の展開結果で変数代入を除去するべきではないか [#D1581]

というより実の所 simple-word でできるだけ解析していくべきの気がする。と思っ
たが、 ; や変数代入があった時の振る舞い、文法エラーがある場合の振る舞いなど
は用途によってまちまちなのでこんk内は core-complete.sh の側で実装する事に
した。

simple-word の終端しない版というのはあっただろうか。なかったが
simple_rex_element をそのまま使えば良い。

* __load_completion を呼び出すと -D に相当する処理が勝手に入ってしまう。
complete が駄目な気がする。自前で補完定義を探してロードする関数を作ってみ
たが、その後で気づいたのは __load_completion は別に見つからなかった時に勝
手に既定の定義を使う訳ではないのだという事。

以下の自前の定義は結局使われる事はないのだった。

| _ble_complete_progcomp_bashcomp_initialized=
| _ble_complete_progcomp_bashcomp_dirs=()
| function ble/complete/progcomp/.bashcomp-initialize {
| if [[ ! $_ble_complete_progcomp_bashcomp_initialized ]]; then
| _ble_complete_progcomp_bashcomp_initialized=1
|
| local user repo bin
| user=${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions
| [[ $BASH_COMPLETION_USER_DIR ]] && user=$BASH_COMPLETION_USER_DIR/completions
| ble/string#split repo : "${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
| bin=./completions
| [[ $BASH_SOURCE == */* ]] && user=${BASH_SOURCE%/*}/completions
|
| local path
| for path in "$user" "${repo[@]}" "$bin"; do
| [[ -d $path ]] && ble/array#push _ble_complete_progcomp_bashcomp_dirs "$path"
| done
| fi
| }
| function ble/complete/progcomp/.load-bash-completion {
| local cmd=$1
| ble/is-function __load_completion || return 1
| ble/complete/progcomp/.bashcomp-initialize
|
| local dir file
| for dir in "${dirs[@]}"; do
| for file in "$dir"/{"$cmd","$cmd.bash","_$cmd"}; do
| [[ -s $file ]] || continue
| source "$file"
| return 0
| done
| done
|
| ((_ble_base>=40000)) || return 1
| ble/is-function _filedir_xspec &&
| [[ ${_xspecs[$cmd]+set} ]] &&
| complete -F _filedir_xspec "$cmd"
| }

* complete: "\a" 等のコマンド名の補完で問題が起こる (reported by huresche) [#D1579]
https://github.com/akinomyoga/ble.sh/issues/116

Expand Down

0 comments on commit dbe87c3

Please sign in to comment.