Skip to content

Commit

Permalink
complete: fix a bug that "complete -D" settings did not work
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed Oct 11, 2017
1 parent 1f483e2 commit 4df15e1
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 46 deletions.
8 changes: 4 additions & 4 deletions ble-edit.sh
Expand Up @@ -4786,7 +4786,7 @@ function ble-edit/isearch/.shift-backward-references {
## @exit
## 見つかったときに 0 を返します。
## 見つからなかったときに 1 を返します。
## 中断された時に 5 を返します。
## 中断された時に 27 を返します。
##
function ble-edit/isearch/backward-search-history-blockwise {
local opts=$1
Expand Down Expand Up @@ -4823,7 +4823,7 @@ function ble-edit/isearch/backward-search-history-blockwise {

((i-=block))
if ((has_stop_check&&isearch_time%NSTPCHK==0)) && ble/util/is-stdin-ready; then
return 5
return 27
elif ((has_progress&&isearch_time%NPROGRESS==0)); then
ble-edit/isearch/.draw-line-with-progress "$i"
fi
Expand All @@ -4846,7 +4846,7 @@ function ble-edit/isearch/next-history/forward-search-history.impl {
for ((;expr_cond;expr_incr)); do
((isearch_time++))
if ((has_stop_check&&isearch_time%100==0)) && ble/util/is-stdin-ready; then
return 5
return 27
fi

if
Expand Down Expand Up @@ -4954,7 +4954,7 @@ function ble-edit/isearch/next-history.fib {
local beg=${#prefix} end=$((${#prefix}+${#needle}))

ble-edit/isearch/.goto-match "$index" "$beg" "$end" "$needle"
elif ((r==5)); then
elif ((r==27)); then
# 中断した場合
isearch_suspend="index=$index start=$start:$needle"
return
Expand Down
77 changes: 53 additions & 24 deletions complete.sh
Expand Up @@ -271,9 +271,10 @@ function ble-complete/source/argument/.compgen-helper-func {
[[ $comp_func ]] || return
local -a COMP_WORDS
local COMP_LINE COMP_POINT COMP_CWORD COMP_TYPE COMP_KEY
ble-complete/source/argument/.compgen-helper-vars
ble-complete/source/argument/.compgen-helper-varsz

# compopt に介入して -o/+o option を読み取る。
local fDefault=
function compopt {
builtin compopt "$@"; local ret="$?"

Expand Down Expand Up @@ -301,19 +302,21 @@ function ble-complete/source/argument/.compgen-helper-func {
local s
for s in "${ospec[@]}"; do
case "$s" in
(-*) comp_opts="$comp_opts${s:1}:" ;;
(+*) comp_opts="${comp_opts//:${s:1}/:}" ;;
(-*) comp_opts=${comp_opts//:"${s:1}":/:}${s:1}: ;;
(+*) comp_opts=${comp_opts//:"${s:1}":/:} ;;
esac
done

return "$ret"
}


local cmd="${comp_words[0]}" cur="${comp_words[comp_cword]}" prev="${comp_words[comp_cword-1]}"
"$comp_func" "$cmd" "$cur" "$prev"

"$comp_func" "$cmd" "$cur" "$prev"; local ret=$?
unset -f compopt

if [[ $is_default_completion && $ret == 124 ]]; then
is_default_completion=retry
fi
}

## 関数 ble-complete/source/argument/.compgen
Expand All @@ -329,20 +332,21 @@ function ble-complete/source/argument/.compgen {
local comp_prog= comp_func=
ble-syntax:bash/extract-command "$index" || return 1

local cmd="${comp_words[0]}" compcmd=
local cmd="${comp_words[0]}" compcmd= is_default_completion=
if complete -p "$cmd" &>/dev/null; then
compcmd="$cmd"
elif [[ ${cmd##*/} != $cmd ]] && complete -p "${cmd##*/}" &>/dev/null; then
compcmd="${cmd##*/}"
elif complete -p -D &>/dev/null; then
is_default_completion=1
compcmd='-D'
fi

[[ $compcmd ]] || return 1

local -a compargs compoptions
local ret iarg=1
ble/util/assign ret 'complete -p "$cmd" 2>/dev/null'
ble/util/assign ret 'complete -p "$compcmd" 2>/dev/null'
eval "compargs=($ret)"
while ((iarg<${#compargs[@]})); do
local arg="${compargs[iarg++]}"
Expand All @@ -352,15 +356,15 @@ function ble-complete/source/argument/.compgen {
for ((ic=1;ic<${#arg};ic++)); do
c="${arg:ic:1}"
case "$c" in
([abcdefgjksuvDE])
([abcdefgjksuvE])
ble/array#push compoptions "-$c" ;;
([pr])
;; # 無視 (-p 表示 -r 削除)
([AGWXPS])
ble/array#push compoptions "-$c" "${compargs[iarg++]}" ;;
(o)
local o="${compargs[iarg++]}"
comp_opts="$comp_opts$o:"
comp_opts=${comp_opts//:"$o":/:}$o:
ble/array#push compoptions "-$c" "$o" ;;
(F)
comp_func="${compargs[iarg++]}"
Expand All @@ -369,7 +373,7 @@ function ble-complete/source/argument/.compgen {
comp_prog="${compargs[iarg++]}"
ble/array#push compoptions "-$c" ble-complete/source/argument/.compgen-helper-prog ;;
(*)
# just discard
# -D, etc. just discard
esac
done ;;
(*)
Expand All @@ -379,22 +383,47 @@ function ble-complete/source/argument/.compgen {

ble/util/is-stdin-ready && return 27

local rex_compv arr compgen
ble-complete/util/escape-regexchars -v rex_compv "$COMPV"
ble/util/assign compgen 'compgen "${compoptions[@]}" -- "$COMPV" 2>/dev/null'
ble/util/assign compgen 'command sed -n "/^$rex_compv/{s/[[:space:]]\{1,\}\$//;p;}" <<< "$compgen" | command sort -u' 2>/dev/null
ble/string#split arr $'\n' "$compgen"
# * 一旦 compgen だけで ble/util/assign するのは、compgen をサブシェルではなく元のシェルで評価する為である。
# Note: 一旦 compgen だけで ble/util/assign するのは、compgen をサブシェルではなく元のシェルで評価する為である。
# 補完関数が遅延読込になっている場合などに、読み込まれた補完関数が次回から使える様にする為に必要である。
# * "$COMPV" で始まる単語だけを候補として列挙する為に sed /^$rex_compv/ でフィルタする。
local compgen
ble/util/assign compgen 'compgen "${compoptions[@]}" -- "$COMPV" 2>/dev/null'

# Note: complete -D 補完仕様に従った補完関数が 124 を返したとき再度始めから補完を行う。
# ble-complete/source/argument/.compgen-helper-func 関数内で補間関数の終了ステータスを確認し、
# もし 124 だった場合には is_default_completion に retry を設定する。
if [[ $is_default_completion == retry && ! $_ble_complete_retry_guard ]]; then
local _ble_complete_retry_guard=1
ble-complete/source/argument/.compgen
return
fi

# Note: git の補完関数など勝手に末尾に space をつけ -o nospace を指定する物が存在する。
# 単語の後にスペースを挿入する事を意図していると思われるが、
# 通常 compgen (例: compgen -f) で生成される候補に含まれるスペースは、
# 挿入時のエスケープ対象であるので末尾の space もエスケープされてしまう。
#
# 仕方がないので sed で各候補の末端の [[:space:]]+ を除去する。
# これだとスペースで終わるファイル名を挿入できないという実害が発生するが、
# そのような変な補完関数を作るのが悪いのである。
local use_workaround_for_git=
if [[ $comp_func == __git* && $comp_opts == *:nospace:* ]]; then
use_workaround_for_git=1
comp_opts=${comp_opts//:nospace:/:}
fi

# Note: "$COMPV" で始まる単語だけを候補として列挙する為に sed /^$rex_compv/ でフィルタする。
# compgen に -- "$COMPV" を渡しても何故か思うようにフィルタしてくれない為である。
# (compgen -W "$(compgen ...)" -- "$COMPV" の様にしないと駄目なのか?)
# * sed で末端の [[:space:]]+ を除去する。
# git の補完関数など勝手に末尾に space をつける物が存在する為である。
# 単語の後にスペースを挿入する事を意図していると思われるが、
# 通常 compgen (例: compgen -f) で生成される候補に含まれるスペースは、挿入時のエスケープ対象である。
# →これだとスペースで終わるファイル名を挿入できない…。
# * arr=($(...)) としないのは IFS=$'\n' の影響を $(...) の中に持ち込まないためである。
local rex_compv
ble-complete/util/escape-regexchars -v rex_compv "$COMPV"
if [[ $use_workaround_for_git ]]; then
ble/util/assign compgen 'command sed -n "/^$rex_compv/{s/[[:space:]]\{1,\}\$//;p;}" <<< "$compgen" | command sort -u' 2>/dev/null
else
ble/util/assign compgen 'command sed -n "/^$rex_compv/p" <<< "$compgen" | command sort -u' 2>/dev/null
fi

local arr
ble/string#split arr $'\n' "$compgen"

local action=argument
[[ $comp_opts == *:nospace:* ]] && action=argument-nospace
Expand Down
126 changes: 108 additions & 18 deletions memo.txt
Expand Up @@ -407,13 +407,15 @@ tech
この動作は分かりにくいし更に言うと現状の ble.sh の描画コードでは対応していない。
これには取り敢えず対応しないことにする。

以下は積極的に対応する予定はないが、
将来的に対応する場合には注意をしなければならないもの
以下は積極的に対応する予定はない。
将来的に対応する場合の注意点がある場合も含む

* 2017-10-11 M ( ) [[ ]] { } :s :tag
これらのコマンドは "ジャンプ" なので、$flag なしで実際にジャンプに成功する場合には
set-local-mark 96 をする必要がある。

* 2017-10-09 取り敢えず今の所はスクロール (C-b C-d C-e など) には対応しない

2017-09-08

* vi-mode: 以下のリンクで重要そうなコマンドの一覧が見られる [#M0005]
Expand Down Expand Up @@ -497,23 +499,23 @@ tech
ToDo
-------------------------------------------------------------------------------

2017-10-09
2017-10-11

* vi-mode: 取り敢えず今の所はスクロール (C-b C-d C-e など) には対応しない
* isearch が動かなくなっている。

* vi-mode change

新機能
- vi-mode: nmap `<C-d>` 空文字列のとき exit
- vi-mode (nmap `<C-d>`): 空文字列のとき exit
- タブ・インデントの制御
- bleopt tab_width= (タブの表示幅)
- bleopt indent_offset=4 (`>` や `<` のインデントの幅)
- bleopt indent_tabs=1 (`>` や `<` のインデントにタブを用いるかどうか)
- 既定のインデントの幅は 8 から 4 に変更
- vi-mode: mark `mx` <code>`x</code> <code>'x</code>
- vi-mode: `gi` および <code>`^</code> <code>'^</code>
- vi-mode: mark `mx` <code>`x</code> <code>'x</code> (`x` = <code>[][<>`'a-zA-Z"^.]</code>)
- vi-mode: nmap `gi`
- vi-mode: xmap `I` `A`
- vim-surround.sh: nmap `yS` `ySS` `ySs` `cS`, xmap `S` `gS`
- vi-mode: xmap I A

バグ修正
- vi-mode: 挿入モードに繰り返し回数を指定したとき `ESC ?` も一緒に繰り返されていたバグの修正
Expand All @@ -536,7 +538,7 @@ tech
2017-10-05

* vi-mode: 最終行付近で + _ g_ などを呼び出したときの振る舞いが異なる。
この当たりの振る舞いについては色々調べる価値はある
この辺りの振る舞いについては色々調べる価値はある

* vi-mode (visual): p P [#tmp003]

Expand Down Expand Up @@ -654,6 +656,7 @@ tech
* cmpltstofB: ビジュアルモード・選択モード?

* 選択モードは範囲に対する挿入モードのような気がする。
選択モードは取り敢えず対応しないことにする。

* テキストオブジェクトで範囲を選択し、また範囲を拡大する。

Expand Down Expand Up @@ -1293,14 +1296,6 @@ tech

* vi bind

2015-11-23

* プログラム補完: 末尾空白の問題点

現在 git 補完関数に合わせる形で勝手に末尾の空白を除去している。
しかしこれで良いのか? これだと空白で終わるファイル名などがあった場合に問題になる。
(その様なファイル名は滅多にないとは思われるが…。)

2015-11-21

* 公開までに追加であった方が良いかも知れない物
Expand Down Expand Up @@ -2248,7 +2243,102 @@ tech

2017-10-11

* 2017-10-10 vi-mode (mark): < > ` ' " . などの設定をそれぞれ実装する必要がある。
* complete: complete -p -D が働いていない気がするが… → 124 に対応 [#D0534]

% complete.sh を見ると対応しているように見えるし、
% 過去の記録を見ても動いていた様子がある。

どうも前から動いていなかった疑いがある。
先ず初めに compgen に -D オプションが混入するとエラーになって、
そもそも補完関数も呼び出されない。

さて、無事に補完関数は呼び出されるようになったが、何も候補が生成されていない。
どうやら __load_completion では定義をロードするだけで候補生成は行わない様だ。
一方で、素の bash でやるとちゃんと候補が生成できている。
これが意味するところは素の bash は、もし候補生成がされなかった場合は、
新しく追加された定義を用いて再度候補生成を行うということである。

何と。マニュアルに書かれていた。
-D による補完関数が 124 を返したとき、
再度補完が試みられるのだと。対応した。

* git 補完関数の末尾空白の問題

| * 2015-11-23 プログラム補完: 末尾空白の問題点
|
| 現在 git 補完関数に合わせる形で勝手に末尾の空白を除去している。
| しかしこれで良いのか? これだと空白で終わるファイル名などがあった場合に問題になる。
| (その様なファイル名は滅多にないとは思われるが…。)

% git の補完関数は変なことをする。候補の末尾に空白を付加し compopt -o nospace を指定する。
% ble.sh の complete は末尾の空白をエスケープする仕様になっている。
% これは compgen -A file の空白を含むファイルとの一貫性のためである。
% 従って git の補完関数の場合には sed で末端の [[:space:]]+ を除去する必要がある。

git の時にだけ work around を追加しようと思ったが、
どうやら bash-completion は一般にそのような設計になっている疑いがある。
分からないので取り敢えず現在の仕様を保持することにする。
と思ったが、やはり末端に空白のあるファイル名を補完するときに問題になる。

うーん。やはり git だけ特別扱いすることにする。

そういえば、やはり git は以前問題になったのだから、
以前は complete -D が動いていたという事になる。
恐らく bash の version によって compgen に -D があった時に
エラーになるかどうかが違うということなのだろう。

* しかし、不思議なのは素の bash ではこの問題が発生していないということである。
つまりファイル名の末尾の空白はちゃんとエスケープされるが、
git subcommand の末尾の空白はエスケープされないというようになっている。

もしかして git の補完関数はファイル名を補完しているときには
nospace を指定せずに末尾空白も付加せずに候補を生成するのではないかという説を思いついた。
つまり nospace は末尾空白をエスケープしないというサインになっているのではないかという説である。
しかし試してみると、常に nospace を指定している。

うーん。しかし /usr/share/bash-completion/completions/git に
compopt -o filenames +o nospace という行がある。この行が実行されているときには、
ファイル名末尾の空白がちゃんとエスケープされるようになるということではあるまいか。
そして自前のシェル関数の compopt は実行されないということなのではないだろうか。

そう思って適当なコマンドを作って試してみた所、
ちゃんと自前のシェル関数が呼び出されるということが分かった。

function _alpha { compopt +o nospace; }
function alpha { echo "$@"; }
complete -F _alpha alpha
alpha hello.txt

ということはやはり git の補完では実際には compopt は呼び出されていないのだろう。
やはり素の bash で正しく動作できている理由が不明である。

更に素の bash の補完の振る舞いについても調べてみたが、

function _alpha { compopt -o nospace; local w=${COMP_WORDS[COMP_CWORD]}; for a in hello world 'abc efg'; do [[ $a == "$w"* ]] && COMPREPLY=("$a "); done; }
function _alpha { compopt +o nospace; local w=${COMP_WORDS[COMP_CWORD]}; for a in hello world 'abc efg'; do [[ $a == "$w"* ]] && COMPREPLY=("$a "); done; }

特に -o nospace / +o nospace でエスケープをしたりしなかったりという事はないようだ。
というか、基本的に補完関数によって生成された候補はエスケープしないようである。

ということはスペースを含むファイル名を補完した時にエスケープしているのは誰なのかという謎が生じる。
実のところ、bash は直接 compgen を呼んでいるのではなくて、
内部的な処理の一環としてファイル名を候補に生成しているので、
それがファイル名による候補かどうかを知っていて、
その内部的にしか知り得ない情報を用いてエスケープするかしないかを決めている可能性がある。

% 面倒なのでここまでにして深入りしない事にする。

と思って ble.sh でやってみたらいつの間にかに正しく動くようになっている。
つまり __git* の生成するものについてはエスケープせず、
ファイル名候補の場合にはエスケープする。
どうやら git はサブコマンドしか候補を生成しない様だ。
そして一致するものが見つからない場合には自前でファイル名候補を生成するなどのことはしない。
これによって ble.sh の通常のファイル名候補にフォールバックする。
結果としてファイル名候補の場合には正しくエスケープされるという動作になる。

取り敢えず OK ということにする。

* 2017-10-10 vi-mode (mark): < > ` ' " . などの設定をそれぞれ実装する必要がある。 [#D0533]

- done: [] は直前の変更または yank 範囲 → #D0529
- done: <> は前回のビジュアルモードの選択範囲 → #D0531 で同時に対応
Expand Down

0 comments on commit 4df15e1

Please sign in to comment.