diff --git a/docs/ChangeLog.md b/docs/ChangeLog.md index aa28e069..7533d1e3 100644 --- a/docs/ChangeLog.md +++ b/docs/ChangeLog.md @@ -55,6 +55,7 @@ - edit(gexec): fix a bug that `LINENO` is vanishing `#D2108` b5776596 - mandb: fix extraction of option description in format 5 (reported by bkerin) `#D2110` 90a992cc - decode: fix handling of @ESC in quoted-insert `#D2119` 0bbc3639 +- syntax: save stat after command name for consistent completion-context `#D2126` xxxxxxxx ## Compatibility diff --git a/lib/core-syntax.sh b/lib/core-syntax.sh index 69b97c04..6a1062f5 100644 --- a/lib/core-syntax.sh +++ b/lib/core-syntax.sh @@ -688,7 +688,9 @@ function ble/syntax/parse/serialize-stat { ## @fn ble/syntax/parse/set-lookahead count ## ## @param[in] count -## 現在位置の何文字先まで参照して動作を決定したかを指定します。 +## 現在位置の何文字先まで参照して動作を決定したかを指定します。文字列が終端 +## している事を確認した時、そこに可能性として存在し得た1文字を参照した事に +## なるので、その文字列終端も1文字に数える必要があります。 ## @var[out] i ## @var[out] ilook ## @@ -3867,7 +3869,12 @@ function ble/syntax:bash/ctx-command/check-word-end { i+=${#rematch2})) else # case: /hoge */ 恐らくコマンド - ((_ble_syntax_attr[i]=CTX_ARGX,i+=${#rematch1})) + + # Note (#D2126): 実装当初 e1e87c2c (2015-02-26) から長らく解析中断点を + # 置かずに一気に読み取っていたが、補完文脈生成で正しい文脈を再構築する + # のが困難になるので、やはり解析中断点を単語末尾に置く事にする。正しく + # lookahead を設定している限りは問題にならない筈。 + ble/syntax/parse/set-lookahead "$((${#rematch1}+1))" fi fi diff --git a/note.txt b/note.txt index ac63d9b9..66185b8c 100644 --- a/note.txt +++ b/note.txt @@ -7073,6 +7073,142 @@ bash_tips 2024-01-28 + * complete: "'bin'/cmd [TAB]" で単語が補完されない [#D2126] + + 補完設定取得の為のコマンド名を外部から設定する様にして bin/cmd などに対して + は動く様になったが 'bin'/cmd に対しては結局動かない。更に一文字でも引数が入 + 力されていれば動く。何故だろう。quote がなければ一文字も入力していなくても + 動く。 + + $ 'bin'/test1 [TAB] 動かない + $ 'bin'/test1 a[TAB] 動く + $ bin/test1 [TAB] 動く + + そもそも ble/complete/progcomp が呼び出されていない。うーん。 + source:argument の時点で呼び出されていない。completion-context の生成で失敗 + しているという事。 + + ? ok: 振る舞いを見ると何だかよく分からない。そもそも "echo [TAB]" の時点で + completion context のチェックに於いて next-command が生成されている。 + + 何故 next-argument でないのか? "echo hello [TAB]" の時にはちゃんと + next-argument が生成されている。そして何故 next-command であるのにも関わ + らずちゃんと補完を実行できている様に見えるのか。うーん。check-prefix は一 + 旦候補を生成した後にそれが現在のカーソル位置まで継続しているかチェックす + る。単語の先頭の場合には check-prefix で生成した物は結局使われないという + 事の気がする。 + + →これは check-prefix の候補生成なので問題ない。この文脈では本来の処理は + check-here の方から生成される。 + + どうも "echo [TAB]" の場合には check-prefix による source が一つも生成され + ないので check-here の方に処理が流れてくるが、"'bin'/test1 [TAB]" の時には + 何故か "command 0" というのが source として生成される為に check-here まで処 + 理が流れてこない様だ。 + + では何故 "command 0" 等という source が生成されてしまうのか。 + + a 何と inside-command の方では単語が終わっていない事をチェックするのを忘れ + ている。と思ったが単語が終わっているかどうかをどうやって判定するのか? + + 単純な単語であれば良いが、複雑な構造を持つ単語の場合には simple-word では + 判定できない。check-prefix で simple-word を使って判定できていたのは、単 + 語の文脈を保ったまま一回の解析中断点で進めるのは単純単語に限られたからな + のではないか? と思ったが $((...)) の場合にはどうだろうか。 + + ? ok: inside-argument の方でも同様のチェックが必要になるのではないか? + + と思ったが引数の場合には check-prefix は単語の終端を当てる気がする。→ + 実際に確認してみた所、引数の場合には終端にはちゃんと解析中断点が設置さ + れるので問題ない。 + + b 或いは内部構造が存在している時点で関数名とはならないのだから解析中断点を + 置けば良いのではないか? + + x ok: と思ったが編集の途中でたまたま解析状態が一致して更新がされない等の + 事は発生するだろうか? うーん。例えば内部構造がある状態から内部構造がな + い場合に移行する場合には何が起こるかというと単語の最初から解析し直す。 + その場合には一気に単語の末尾及び続く空白まで読み取る事になるので、解析 + 中断点が予期せず残るという状況にはならない。或いは、内部構造がない状態 + から内部構造が発生した場合にはどうなるかというと、それまでは空白の終わ + りまで一気に読んでいたので空白の終わりに到達する迄は解析状態が一致する + 事もなく、ちゃんと空白の終わりまで解析がやり直される。 + + なので、内部状態の有無によって (或いはもっと正確には関数名候補になるかど + うかに応じて) 解析中断点を設置するというのは一つの手である。 + + ? 然し本当に有効な関数名は単純単語である事が保証されるのだろうか? + + 例えば履歴展開。現在の実装だと a!b の様な関数名も許容している。これによっ + て履歴展開が発動するとしても (実際に試してみたが履歴展開があるとやはり + 関数名の位置にあっても履歴展開が発生して変な事になる)。うーんでも、実際 + に例えば前のコマンドの最後の単語を関数名にしたい等という時には関数名の + 位置にそういった物を含ませる事を許容して良い気がする。 + + うーん。対応する履歴が見つからない場合には結局 ! を含む関数をそのまま定 + 義できてしまうみたいである。なので、やはり履歴展開があっても関数として + の解釈を中断する訳には行かない。 + + 一方で $((...)) の様な物は関数の中には含められない。有効な識別子ではあ + りませんというエラーになる。 + + c 或いはそもそもコマンド名に関しては単純単語でない物は補完しない可能性 + + 例えば安全に評価できるより複雑なものがあるのだとしたらそれを単純単語に取 + り入れてしまえば良い。と思ったがユーザーがユーザー固有の安全な単語を考え + ている場合などを考えるとそういった物も許しておいてユーザーの側でユーザー + の好みに応じた拡張的な補完を定義できても原理的に良い。或いは、既存のコマ + ンド名を遡って消して新しい物を挿入する可能性だってある。なので、単純単語 + ベースの判定はやはり不完全な気がする。 + + それに構文解析しているのだから文法構造を反映して補完文脈は抽出したいもの + である。という事を考えるとやはり b の方針になる気がする。 + + d 或いは更にもう一つの可能性はコマンド名の終端ではやはり解析中断点を設置す + る事にして但し lookahead を設定しておくという事。そうすれば "func ()" が + "func x" に書き換わった場合などでもちゃんと再処理される。 + + 然しこの方法について過去にさんざん考察したはずで、過去の考察で何が問題だっ + たのかもう忘れている。改めて過去の議論を調べるのは大変である。 + + [これまでの実装] + + 関数定義の実装は歴史的にどうなっていたか。e1e87c2c (記録なし) で func() + が導入されたがこの時点で既に空白を余分に読み取っていた。8717767e (#D0225) + で function func が導入された。多少コードに変更があるが本質的には変わらな + い。26219e4f (#D0590) は単なる refactoring である。然し、関数の解析に関連 + する問題が度々発生して修正した筈である。今検索して見つかるのは #D1848 と + #D0210 である ("関数定義" で検索した)。 + + * 前者は function aaa bbb ... の着色についてで、文法エラー着色がされてい + るのにも関わらず関数定義およびコマンド名の単語着色でエラー着色が上書き + されているという物。 + + * 後者は shift の時に単語が stat と一緒にあると仮定していた所為で起こった + バグについてであって、これに関しては今回は気にしなくて良い。 + + 他にもっと問題が継続して発生した事があった様な気がするけれど議論が見つか + らない。/syntax\(\.sh\)?:.*関数/ で検索してみたが見つからない。或いは、最 + 初に実装した時に暫く使ってそれで色々修正したというだけかもしれない。だい + ぶ初期に実装したのでその時は未だ全てを記録していたわけではないのである。 + + [実装] + + 然しこれまでの実装は実装として、そもそもの設計理念にさえ従っていれば本当 + は問題は発生しないはずである。という事を考えれば、今回の場合も別に途中に + 解析中断点を置いたからと言って本来は問題は起こらない筈なのではないか。昔 + 実装した時には未だ lookahead の機能がなかっただけである。とは言いつつ、 + completion-context だとかその他の機能で stat を参照している物が影響を受け + る可能性は残っている。うーん。 + + もしかしたら問題が発生するかもしれないが取り敢えず lookahead を設定する方 + 向に変更する事にした。簡単に試した限りでは問題は起こっていない。これで暫 + く様子見する。 + + 結局 d の方針で解析中断点を設置する事にした。ちゃんと補完が動くようになった + か確かめた。最初は動いていないと思ったが make し直したら動く様になった。 + make し忘れてテストしていたのかもしれない。 + * complete: ./manage.py で呼び出した補完が動かない (reported by REmerald) [#D2125] どうも補完設定的には complete manage.py で登録しているけれども、受け取った