Skip to content

Commit

Permalink
complete: preserve original path segments as long as possible
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed Jan 24, 2023
1 parent 8a716ad commit e3cdb9d
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 61 deletions.
3 changes: 2 additions & 1 deletion docs/ChangeLog.md
Expand Up @@ -115,7 +115,8 @@
- menu-complete: add `bleopt complete_menu_complete_opts` (requested by DUOLabs333) `#D1911` 6a21ebb
- edit (`magic-space`): support `bleopt edit_magic_expand=...:alias` (requested by telometto) `#D1912` 63da2ac
- auto-complete: cancel auto-complete for `magic-space` `#D1913` 01b4f67
- complete: support ambiguous completion for command paths `#D1922` xxxxxxx
- complete: support ambiguous completion for command paths `#D1922` 8a716ad
- complete: preserve original path segments as long as possible `#D1923` `#D1924` xxxxxxx

## Changes

Expand Down
91 changes: 67 additions & 24 deletions lib/core-complete.sh
Expand Up @@ -1267,16 +1267,27 @@ function ble/complete/action/quote-insert.initialize {
esac

# 遡って書き換える時に comps_fixed には注意する。
quote_fixed_comps=
quote_fixed_compv=
quote_fixed_comps_len=
quote_fixed_compv_len=
quote_fixed_comps=('')
quote_fixed_compv=('')
quote_fixed_comps_len=('')
quote_fixed_compv_len=('')
if [[ $comps_fixed ]]; then
quote_fixed_compv=${comps_fixed#*:}
quote_fixed_compv_len=${#quote_fixed_compv}
quote_fixed_comps_len=${comps_fixed%%:*}
quote_fixed_comps=${COMPS::quote_fixed_comps_len}
fi

# 遡って書き換える時に '/' 区切りでできるだけ元の展開を保持する。
# comps_fixed[1] 以降に '/' 区切りで展開した結果を短い順に格納する。
local i v
for ((i=1;i<${#comps_fixed[@]};i++)); do
v=${comps_fixed[i]#*:}
quote_fixed_compv[i]=$v
quote_fixed_compv_len[i]=${#v}
quote_fixed_comps_len[i]=${comps_fixed[i]%%:*}
quote_fixed_comps[i]=${COMPS::quote_fixed_comps_len[i]}
done
}

function ble/complete/action/quote-insert {
Expand All @@ -1301,6 +1312,7 @@ function ble/complete/action/quote-insert {
escape_flags=T$escape_flags
fi

# 入力済み文字列への追記の場合、元の単語を保持する。
if [[ $comps_flags == *v* && $CAND == "$COMPV"* ]]; then
local ins ret
ble/complete/string#escape-for-completion-context "${CAND:${#COMPV}}" "$escape_flags"; ins=$ret
Expand All @@ -1310,13 +1322,22 @@ function ble/complete/action/quote-insert {
[[ $quote_cont_cutbackslash ]] && ins=${ins#'\'}
INSERT=$COMPS$ins;
fi
elif [[ $quote_fixed_comps && $CAND == "$quote_fixed_compv"* ]]; then
local ret; ble/complete/string#escape-for-completion-context "${CAND:quote_fixed_compv_len}" "$escape_flags"
INSERT=$quote_fixed_comps$quote_trav_prefix$ret
else
local ret; ble/complete/string#escape-for-completion-context "$CAND" "$escape_flags"
INSERT=$quote_trav_prefix$ret
return 0
fi

# 遡って書き換わる場合には単語内のできるだけ長い部分パスを保持する。
local i=${#quote_fixed_comps[@]}
while ((--i>=0)); do
if [[ ${quote_fixed_comps[i]} && $CAND == "${quote_fixed_compv[i]}"* ]]; then
local ret; ble/complete/string#escape-for-completion-context "${CAND:quote_fixed_compv_len[i]}" "$escape_flags"
INSERT=${quote_fixed_comps[i]}$quote_trav_prefix$ret
return 0
fi
done

# 既存の物に一致しない場合、完全に書き換える。
local ret; ble/complete/string#escape-for-completion-context "$CAND" "$escape_flags"
INSERT=$quote_trav_prefix$ret
}

function ble/complete/action/quote-insert.batch/awk {
Expand All @@ -1330,13 +1351,19 @@ function ble/complete/action/quote-insert.batch/awk {
local -x quote_paramx_comps=$quote_paramx_comps
local -x quote_cont_cutbackslash=$quote_cont_cutbackslash
local -x quote_trav_prefix=$quote_trav_prefix
local -x quote_fixed_comps=$quote_fixed_comps
local -x quote_fixed_compv=$quote_fixed_compv

local -x quote_fixed_count=${#quote_fixed_comps[@]}
local i
for ((i=0;i<quote_fixed_count;i++)); do
local -x "quote_fixed_comps$i=${quote_fixed_comps[i]}"
local -x "quote_fixed_compv$i=${quote_fixed_compv[i]}"
done

"$quote_batch_awk" -v quote_batch_nulsep="$quote_batch_nulsep" -v q="$q" '
function exists(filename) { return substr($0, 1, 1) == "1"; }
function is_file(filename) { return substr($0, 2, 1) == "1"; }
function initialize(_, flags, comp_opts, tmp) {
function initialize(_, flags, comp_opts, tmp, i) {
IS_XPG4 = AWKTYPE == "xpg4";
REP_SL = "\\";
if (IS_XPG4) REP_SL = "\\\\";
Expand Down Expand Up @@ -1389,10 +1416,14 @@ function ble/complete/action/quote-insert.batch/awk {
quote_cont_cutbackslash = ENVIRON["quote_cont_cutbackslash"] != "";
quote_paramx_comps = ENVIRON["quote_paramx_comps"];
quote_trav_prefix = ENVIRON["quote_trav_prefix"];
quote_fixed_comps = ENVIRON["quote_fixed_comps"];
quote_fixed_compv = ENVIRON["quote_fixed_compv"];
quote_fixed_comps_len = length(quote_fixed_comps);
quote_fixed_compv_len = length(quote_fixed_compv);
quote_fixed_count = ENVIRON["quote_fixed_count"];
for (i = 0; i < quote_fixed_count; i++) {
quote_fixed_comps[i] = ENVIRON["quote_fixed_comps" i];
quote_fixed_compv[i] = ENVIRON["quote_fixed_compv" i];
quote_fixed_comps_len[i] = length(quote_fixed_comps[i]);
quote_fixed_compv_len[i] = length(quote_fixed_compv[i]);
}
}
BEGIN { initialize(); }
Expand Down Expand Up @@ -1430,7 +1461,7 @@ function ble/complete/action/quote-insert.batch/awk {
return text;
}
function quote_insert(cand) {
function quote_insert(cand, _, i) {
# progcomp 特有
if (quote_action == "command") {
if (comps == compv && cand ~ /^(\[\[|]]|!)$/) return cand;
Expand All @@ -1447,12 +1478,16 @@ function ble/complete/action/quote-insert.batch/awk {
if (quote_cont_cutbackslash) sub(/^\\/, "", ins);
return comps ins;
}
} else if (quote_fixed_comps_len && substr(cand, 1, quote_fixed_compv_len) == quote_fixed_compv) {
ins = substr(cand, quote_fixed_compv_len + 1);
return quote_fixed_comps quote_trav_prefix escape_for_completion_context(ins);
} else {
return quote_trav_prefix escape_for_completion_context(cand);
}
for (i = quote_fixed_count; --i >= 0; ) {
if (quote_fixed_comps_len[i] && substr(cand, 1, quote_fixed_compv_len[i]) == quote_fixed_compv[i]) {
ins = substr(cand, quote_fixed_compv_len[i] + 1);
return quote_fixed_comps[i] quote_trav_prefix escape_for_completion_context(ins);
}
}
return quote_trav_prefix escape_for_completion_context(cand);
}
{
Expand Down Expand Up @@ -5529,7 +5564,7 @@ function ble/complete/candidates/.pick-nearest-sources {

COMPS=${comp_text:COMP1:COMP2-COMP1}
comps_flags=
comps_fixed=
comps_fixed=('')

if [[ ! $COMPS ]]; then
comps_flags=${comps_flags}v COMPV=
Expand All @@ -5553,6 +5588,14 @@ function ble/complete/candidates/.pick-nearest-sources {
comps_fixed=${simple_ibrace%:*}:$ret
comps_flags=${comps_flags}x
fi

local path spec i s
ble/syntax:bash/simple-word/evaluate-path-spec "$reconstructed" '' noglob:fixlen="${simple_ibrace#*:}"
for ((i=0;i<${#spec[@]};i++)); do
s=${spec[i]}
[[ $s == "$comps_fixed" || $s == "$reconstructed" ]] && continue
ble/array#push comps_fixed "${#s}:${path[i]}"
done
else
# Note: failglob により simple-word/eval が失敗した時にここに来る。
COMPV=
Expand Down
11 changes: 10 additions & 1 deletion lib/core-syntax.sh
Expand Up @@ -1571,8 +1571,15 @@ function ble/syntax:bash/simple-word/.get-rex_element {
## timeout=*
## timeout-carry
## cached
## これらは simple-word/eval に対するオプションです。
##
## notilde
## 評価時にチルダ展開を抑制します。
## after-sep
## 分割位置を分割子の前ではなく後に変更します。
## fixlen=LEN
## 分割の対象とならない固定接頭辞の長さを指定します。
##
## @arr[out] spec
## @arr[out] path
## @arr[out] ret
Expand All @@ -1595,14 +1602,16 @@ function ble/syntax:bash/simple-word/evaluate-path-spec {
# read options
local eval_opts=$opts notilde=
[[ :$opts: == *:notilde:* ]] && notilde=\'\' # チルダ展開の抑制
local fixlen
ble/opts#extract-last-optarg "$opts" fixlen 0

# compose regular expressions
local rex_element; ble/syntax:bash/simple-word/.get-rex_element "$sep"
local rex='^['$sep']?'$rex_element'|^['$sep']'
[[ :$opts: == *:after-sep:* ]] &&
local rex='^'$rex_element'['$sep']?|^['$sep']'

local tail=$word s= p= ext=0
local tail=${word:fixlen} s=${word::fixlen} p= ext=0
while [[ $tail =~ $rex ]]; do
local rematch=$BASH_REMATCH
s=$s$rematch
Expand Down
99 changes: 64 additions & 35 deletions note.txt
Expand Up @@ -1861,25 +1861,6 @@ bash_tips
- leakvars
- keymap の移動 (これは別 commit にする?)

2023-01-24

* complete: パス名の曖昧補間でできるだけ各種展開を保持したい

現在の実装では quote-insert で生成候補が COMPV に文字列を追加した物の場合に
は COMPV の部分については COMPS で置き換える様になっている。しかし、遡った
置換がある場合には問答無用で全体が展開されてしまうので、曖昧補間が起こった
時には必ず全体が展開されてしまう。

COMPS を unquoted / 毎に切って、eval して対応する部分 compv を生成して、最
長一致するものについて部分 comps で置き換える。unquote / 毎に切るのは個別の
quote-insert でやっていたら大変なので、事前に処理しておく事にする。

quote-insert.batch で awk で処理する場合にどうするのかについては微妙。awk
の内部で unquote / 毎に切るのを実装するか或いは外で切ったものを何とかして
awk に渡すかする。外で定義したものを渡す方が見通しが良いと思われる。

ToDo 2022-02-03 に関連項目がある。

2022-12-09

* edit,complete: alias expansion で alias sudo='sudo ' 等による引数の展開に対応?
Expand Down Expand Up @@ -2410,22 +2391,6 @@ bash_tips

うーん。load time で time stamp を書き換える様にすれば良いのだろうか。

* complete: 補完している時に勝手に変数の中身が展開されてしまう事がある。展開
する条件をより制限する事はできないか。或いは、展開せずに置換できるパターン
を増やす。

最たる場合が echo $_ble_base_cache/mandb[TAB] である。曖昧一致によって遡っ
て置換が起こるが、それによって変数展開の中身まで展開されてしまう。少なくと
も / で区切ってできるだけ展開せずに一致する様にできないだろうか。

? 曖昧一致に於いて変数展開から得られた文字列は塊で取り扱う?

というかよく考えたら変数の中身に対してまで曖昧一致で一致させるのは変な気
がする。曖昧一致は元の原始的な構成要素に対しては塊で一致する様にするべき
なのではないか。と思ったが 'aaa' の中身の様にユーザーが手で入力した内容に
ついてはやはり曖昧一致であって欲しい。という事を考えると曖昧一致で塊とす
るとしてもその対象は変数展開だけに限られてくるのではないか。

2022-01-23

* コマンドのログ機能を作成する。
Expand Down Expand Up @@ -6691,6 +6656,70 @@ bash_tips

2023-01-24

* [自然解消] 2022-02-03 complete: 補完している時に勝手に変数の中身が展開されてしまう事がある [#D1924]

展開する条件をより制限する事はできないか。或いは、展開せずに置換できるパター
ンを増やす。

最たる場合が echo $_ble_base_cache/mandb[TAB] である。曖昧一致によって遡っ
て置換が起こるが、それによって変数展開の中身まで展開されてしまう。少なくと
も / で区切ってできるだけ展開せずに一致する様にできないだろうか。

? 曖昧一致に於いて変数展開から得られた文字列は塊で取り扱う?

というかよく考えたら変数の中身に対してまで曖昧一致で一致させるのは変な気
がする。曖昧一致は元の原始的な構成要素に対しては塊で一致する様にするべき
なのではないか。と思ったが 'aaa' の中身の様にユーザーが手で入力した内容に
ついてはやはり曖昧一致であって欲しい。という事を考えると曖昧一致で塊とす
るとしてもその対象は変数展開だけに限られてくるのではないか。

2023-01-24 #D1923 で曖昧一致によって遡って書き換わって展開される場合につい
て対応した。勝手に変数の中身が展開されるパターンがこれだけなのかは分からな
いが、取り敢えずは解決した事にする。暫く使って勝手に展開されてしまう他のケー
スに気づいたらその時に新しく項目を立てて対応する事にする。

* complete: パス名の曖昧補間でできるだけ各種展開を保持したい [#D1923]

現在の実装では quote-insert で生成候補が COMPV に文字列を追加した物の場合に
は COMPV の部分については COMPS で置き換える様になっている。しかし、遡った
置換がある場合には問答無用で全体が展開されてしまうので、曖昧補間が起こった
時には必ず全体が展開されてしまう。

COMPS を unquoted / 毎に切って、eval して対応する部分 compv を生成して、最
長一致するものについて部分 comps で置き換える。unquote / 毎に切るのは個別の
quote-insert でやっていたら大変なので、事前に処理しておく事にする。

quote-insert.batch で awk で処理する場合にどうするのかについては微妙。awk
の内部で unquote / 毎に切るのを実装するか或いは外で切ったものを何とかして
awk に渡すかする。外で定義したものを渡す方が見通しが良いと思われる。

ToDo 2022-02-03 に関連項目がある → #D1924

unquoted / で区切るのは既存の
ble/syntax:bash/simple-word/evaluate-path-spec をそのまま使えば良い。

* reject: あらゆる単語について判定を行うと大変なので CAND が既存のファイル
名に一致する時にだけチェックをおこなう? 未だ存在しないファイル名を候補と
して生成する可能性はあるだろうか。例えば Makefile target 等。うー
ん。-o$HOME/ 等の様に short option の optarg として指定している場合等も考
えたら例え存在していなかったとしても一致するかのチェックはするべきの気が
する。

うーん。fixed part との兼ね合いがどうなるのか分からない。取り敢えず
evaluate-path-spec は noglob で呼び出す事にしてチルダ展開・パラメータ展開等
文脈に依存しない物のみを実行する事にする (コマンド置換、算術展開などは元よ
り simple-word ではないので対象ではない)。

? quote-insert.initialize で実行するべきか或いは COMPV を評価する時点で
evaluate-path-spec で展開しておくべきか → COMPV を評価する時点で同時に評
価してしまうべきの気がしてきた。

結局 fixed part を拡張する形で実装する事にした。既存の変数である
comps_fixed 及び quote_fixed_comp{s,v}{,_len} 等を配列に拡張して第1要素以降
に部分パスの展開前・展開後を格納する事にする。

実装した。取り敢えずは動いている気がする。

* 2023-01-02 complete: コマンド名(パス名)の曖昧補間・部分一致など [#D1922]

現在の実装だと PATH に見つかっているコマンドについては曖昧補間が有効である
Expand Down

0 comments on commit e3cdb9d

Please sign in to comment.