Skip to content

Commit

Permalink
edit: work around problems with "mc"
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed Sep 26, 2020
1 parent 5bed6e6 commit e97aa07
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 3 deletions.
5 changes: 5 additions & 0 deletions make_command.sh
Expand Up @@ -128,6 +128,10 @@ function sub:scan/builtin {
sed -E 'h;s/'"$esc"'//g;\Z(\.awk|push|load|==) \b'"$command"'\bZd;g'
}

function sub:scan/check-todo-mark {
echo "--- $FUNCNAME ---"
grc --color --exclude=./make_command.sh '@@@'
}
function sub:scan/a.txt {
echo "--- $FUNCNAME ---"
grc --color --exclude=./test --exclude=./lib/test-*.sh --exclude=./make_command.sh --exclude=\*.md 'a\.txt|/dev/(pts/|pty)[0-9]*' |
Expand Down Expand Up @@ -280,6 +284,7 @@ function sub:scan {
g'

sub:scan/a.txt
sub:scan/check-todo-mark
sub:scan/bash300bug
sub:scan/bash301bug-array-element-length
sub:scan/array-count-in-arithmetic-expression
Expand Down
2 changes: 2 additions & 0 deletions memo/ChangeLog.md
Expand Up @@ -151,6 +151,7 @@
- decode: fix a bug of broken cmap cache found in ble-0.3 `#D1327` 16b56bf
- util (strftime): fix a bug not working with `-v var` option in Bash <= 4.1 (test-util) f1a2818
- complete: work around slow `compgen -c` in Cygwin `#D1329` 5327f5d
- edit: work around problems with `mc` (reported by onelittlehope) `#D1392` 0000000

## Internal changes and fixes

Expand Down Expand Up @@ -181,6 +182,7 @@
- main: unset `BLE_VERSION`, `_ble_bash`, etc. on `ble-unload` `#D1382` 6b615b6
- util: revisit `ble/variable#is-global` implementation `#D1383` 6b5468f
- cmap: recognize <kbd>SS3 O</kbd> as <kbd>blur</kbd> `#D1384` 445a5ad
- edit (`ble/widget/{accept-line,newline}`): automatically switch widgets by the keymap `#D1391` 0000000

<!---------------------------------------------------------------------------->
# ble-0.4.0-devel1
Expand Down
211 changes: 211 additions & 0 deletions note.txt
Expand Up @@ -3571,6 +3571,217 @@ bash_tips

2020-09-26

* mc と一緒に使うと mc が固まる (reported by onelittlehope) [#D1392]
https://github.com/akinomyoga/ble.sh/issues/62

報告した症状は起動時に 10s の delay が入るということ。
これは自分の手許でも再現する。

| 何故だろうか。取り敢えず実行してみると mc は新しく PTY を作って、その中で
| bash を interactive に起動する様子である。
|
| というか mc はどういう言語で書かれているんだ? PTY を生成しているという事は
| bash という事はなかろう。何のために bash を起動している? 不思議なのは実際に
| コマンドを実行する場合にはnon-interactive の bash が使われているということ。
| 或いは、ble.sh がロードされていなければ interactive になるのか。fallback と
| して non-intarcive の bash になっているのか。
| →どうやらその様である。
| ble.sh なしで起動した時には interactive mode になっている。

* mc はコマンドを受け取る為に PTY を開いて中で bash を interactive mode で起動する。
然し、ble.sh が有効の時には何らかの timeout により non-interactive bash に fallback する。

| prompt-attach をしない様にすればOK? と思ったが実際に attach するとやはり動かない。
| うーん。mc は一体何を期待しているのだろうか。或いは… C-q 等の状態になっている可能性?
| 或いは stty の状態を ble.sh が変更しようとするのがいけない可能性?
|
| もしかして mc はプロンプトが表示されるのを待っている可能性?
| そして PS1= を設定している。と思ったが、vi モードの時には -- INSERT -- を表示するから
| それを prompt だと判定しても良いのではないかという気がする。
| 実際に一回起動してから source ble.sh すると -- INSERT -- をプロンプトだと思う様である。
| それに PS1= を設定するのにプロンプトが表示されるのを待つというのは変である。

mc の中にいる事を判定できる方法はあるだろうか。例えば環境変数が設定されているかなど。
調べてみると以下の変数が設定されているので検出する事は可能である。
そもそも mc は自身の line editor を使っている様なので、
ble.sh が使えなくても特に問題はない。
なので基本的には mc の内部では ble.sh を無効にするという方法で良い筈。

declare -x MC_SID="30814"
declare -x MC_TMPDIR="/var/tmp/mc-murase"

一方で何故この様な振る舞いになるのか調べておく必要はある。
ソースコードは以下にある。C言語で書かれている。
https://github.com/MidnightCommander/mc

| ソースコードを検索してみたが特に bind を使って bash と交信しているという事はない気がする。
|
| * TERM も別に mc の物を設定しているという訳ではない。
| * ble-0.1 でも同様に問題は発生する。
| * PS1 に変な物が指定されているという事もない。
| * PS1='\$ ' として見ても問題は変わらない。
| * bleopt_internal_suppress_bash_output= を試したがやはり振る舞いは変わらない。
|
| mc は一体何を待っているのだろうか。エラーメッセージもないので分からない。
| mc のソースコードの何処かに non-interactive で起動するのと
| interactive で起動するのの二種類が存在する筈である。
| 或いは PTY ありとなしで起動しているだけの可能性もあるが。
| うーん。grantpt で検索したら subshell/common.c に init_subshell という関数があって、
| 其処で色々と初期化している様に見える。
|
| mc をコンパイルした。試してみる。確かに init_subshell の中で 10s 過ごしている。
|
| どうも PROMPT_COMMAND に pwd >&XXX を設定していて、
| これが実行されるのを待っている様だ。
| と思ったが、もしそうならば --noattach & attach でちゃんと動く筈なのでは。
| 実際に試してみると PROMPT_COMMAND は ble-attach 時には空になっている。
| →PROMPT_COMMAND を何処で設定しているのかと思ったら一回起動した後に、
| PROMPT_COMMAND=... というコマンドを pty 経由で入力している。
|
| ble.sh はコマンド履歴を受け取っていない気がする?
| それなのに .bash_history にはちゃんと文字列が追加されている。
| これは一体どういう事なのだろうか。別の経路で bash 本体がコマンドを受信している?
| と思ったが分からない。それは考えにくいという気がする。
| 或いは単に見落としだろうか→見落としだった。
| ちゃんと実行されていた。然し問題はコマンドの途中の改行で分割されて実行されているという事。
| 多分 syntax が初期化される前なので文法チェックがなされずに直接コマンド実行されているという事?

状況をまとめると。mc は bash に対して 'PROMPT_COMMAND=云々' というコマンドを送信する。
そして次のプロンプトが表示される時に PROMPT_COMMAND に設定した pwd >&15 というコマンドを経由して
現在のディレクトリを受信する。現在のディレクトリを受信する迄待ち 10s で timeout すると失敗と見做す。

ble.sh は最初のコマンドを受信するが受信した文字列を、
途中の改行で分割して個別に実行しようとする為、PROMPT_COMMAND の設定に失敗する。
従って pwd >&15 がいつまでも実行されないのでブロックされる事になる。

syntax.sh がロードされていない状態でもちゃんと
文法チェックをする様にした方が良いのかもしれない。

うーん。分かった。何故効かないのかというと \n (C-j) を使って改行を入力しているからだ。
一方で ble.sh では C-j を強制実行に割り当てている。

% 取り敢えず ble-bind -s C-j $'\r' 等としておけば問題は発生しない様だ。
% 報告されているもう一つの問題もこれで一緒に解決する。
% 対策としては MC の中にいる時には既定で上記の設定に切り替える。
% もしくは最初のコマンド実行迄は C-j を強制的に C-m に読み替える様にする。
% MC の中にいるかどうかをより正確に判定するにはどうすれば良いか。
% MC_SID の有無だと更に subshell の中に入った時にも対策が起動してしまう。
%
% * 強制的に attach している筈なのに ble-detach 状態になっている。何故か。
% と思ったらそもそも ble-attach していなかった。そしてble-bind で解決したかに見えたのも、
% 単に ble-attach していなかったからである。

改めて回避方法について考える。ble-bind -f C-j nop とすると、
ごみの bash_history すら追加されなくなる事を見ると、
ちゃんと ble-bind は効果を持っている。然し、反応がなくなってしまう。

とここで分かった。先ず ble.sh の C-m は文法構造に関係なく、
続きに何か入力がある時には改行挿入と見做す。
そして一旦改行がコマンドラインに入ると以降は multiline mode になって
以降どんなに改行が現れても改行挿入にしかならない。

つまり ble.sh の C-m でも C-j でもない振る舞いにしなければならない。
うーん。accept-line でも文法チェックを行う?
→結局 accept-line に syntax という引数を与えるか、
或いは $$MC_SID == $$ 且つ LINENO == 0 の時に文法的に完全かどうかをチェックする事にした。

x fixed: 実際にコマンド履歴に妙な物が大量に出力されている。
これについてもどうにかする必要がある。
或いは特に問題が発生しない限りはコマンド履歴に変な物が登録される事はないのだろうか。

というか普通に bash が起動できた場合にもこのごみが出力されるのだろうか。
→確かにごみが .bash_history に残ってしまう。
cd の記録程度であればまあ残っていても良い気がするが、
PROMPT_COMMAND 云々は .bash_history に残す理由もないし消したい所である。

うーん。ble.sh だと PROMPT_COMMAND= と PS1= の二種類のコマンドが履歴に書き
込まれてしまって、最初の物は空白が前置されているので HISTCONTROL に
ignorespace が入っている distro では何も起こらないが、二番目の PS1= は空白
が前置されていないので本当に履歴に書き込まれてしまう。所が bash の中で実行
すると PROMPT_COMMAND しか設定されない。この違いは何処から来るのだろうか。
或いは何かのエラーが起こった時に fallback として PS1 を設定している?

ソースコードを見るとちゃんと PROMPT_COMMAND と共に PS1= が送信される様になっている。
然し実際に起動した物を見ると PS1 は設定されていない様に見える。何故。
うーん。どうもこれは kill -STOP $$ がどの時点で発動するかという問題の様だ。
元の bash では \n が来た時にコマンドの解釈が途切れるので、
その時点で SIGSTOP が来て bash が取り敢えず動作を停止する?
一方で、ble.sh では kill -STOP をしてもお構いなく入力されたコマンドを全て実行する。
結果として後に続く PS1= も一緒に実行されるという事である。

これは意図的にその様に動作する様にしているのだと思われるが何故かはよくわからない。
取り敢えず現状のままに ble.sh の中では PS1= も実行してしまうという振る舞いのままにする。
cd に関してはユーザの設定で HISTCONTROL=ignorespace でも追加してもらう事にする。

x fixed: プロンプトが消滅している。これは rps1 やその他の特別なプロンプトを
$MC_SID == $$ の時には表示しない様にすれば良い気がする。
と思ってその様に対策したがそれでもプロンプトが消滅したままである。

明示的にプロンプトの設定を全て削除してもやはりプロンプトは消滅したままである。
というかそもそもプロンプトが食われてしまって何も表示されない。
という事はプロンプトを抽出するコードで何か問題が生じているという事。

調べると正に read_subshell_prompt という関数が存在している。
うーん。というかもしかして kill -STOP を用いて区切れを判定している?
そして kill -STOP のタイミングが変なのでプロンプトではなくコマンドの出力と思われている可能性。
然し、実際にはプロンプトが消滅してしまっているという事を考えるとやはり違うのだろうか。
或いはプロンプトが消滅してしまっているのは単に \r\e[K して現在行を消去しているからという可能性もある。
それにテストしている最中に -- INSERT -- をプロンプトと認識してしまう事もあったので、
恐らく kill -STOP のタイミングによる問題ではないのではないか。

* 改めて read_subshell_prompt の動作について確認する必要がある。

read_subshell_prompt の呼び出しを見てみると通常の bash では一回しか呼び出
されていないのに、ble.sh の中ではコマンドを実行する度に 3 回呼び出されてい
る。最初の呼び出しでは空文字列が読み取られ、二回目の呼び出しでちゃんと内容
が読み取られる。最後の呼び出しで \e[m が読み取られて終わる。
どうして呼び出しの回数が異なるのか。

何処から呼び出されているのか。filemanager/layout.c の do_load_prompt から
呼び出されている。そして do_load_prompt は更に load_prompt から呼び出され
ている。呼び出し経路を調べてみると、

bash (1) do_load_prompt -> read_subshell_prompt (有限)
ble.sh (1) do_load_prompt -> read_subshell_prompt (空)
ble.sh (2) load_prompt -> do_load_prompt -> read_subshell_prompt (有限)
ble.sh (3) load_prompt -> do_load_prompt -> read_subshell_prompt (sgr0)

の様になっている。ble.sh では load_prompt 経由の読み取りが余分にある。
そして load_prompt は filemanager/midnight.c の初期化時に

add_select_channel (mc_global.tty.subshell_pty, load_prompt, NULL);

の様にして登録されている以外は使われていない。要するに pty で何か受け取っ
た時に load_prompt が呼び出されるという事の様に思われる。それにしても何を
trigger にして呼び出されるのだろうか。

* 3回目の read_subshell_prompt の呼び出しで sgr0 を読み取ってしまうのが原因に思われる。
この3回目の read_subshell_prompt が起こらない様にすればよいのではないか。
その為にはこの sgr0 の出どころを調べる必要がある。

何が起こっているのか分かった。ble/textarea#render を実行すると、
特に更新の必要がない場合には ble/textarea#focus が呼び出され、
カーソル位置を ble/textarea#render の現在位置に移動する。
この時に sgr0 を出力してから移動しようとするのである。

もう一つの疑問は何故二つに分けて出力されているのかという事。
うーん。分かった。ble/textarea#render -> ble/util/idle.do
-> ble/textarea#render の順に呼び出されている。従って、
ble/textarea#render が二回呼び出されるのである。

うーん。goto しても移動がない場合には _ble_term_sgr0 は出力しない事にする?
他に影響があるかどうか分からないが多分大丈夫だろう。
→これでちゃんと動くようになった。実は set -o vi でもちゃんと動いている。
-- INSERT -- を表示するタイミングとプロンプトを表示するタイミングが偶々
良い感じになっていたからだと思われる。

それでも C-o の画面に戻るとプロンプトが消滅してしまってはいる。これは画面
の一番下にいる時に発生している。どうも mc はコマンドを実行する時に画面の一
番下の行にカーソルを持ってくる様である。ble.sh の canvas は行を全て管理で
きていると思っているから、消去したい panel に普通に CUD で移動して削除しよ
うとする。然し、実際には CUD で移動するのに失敗して誤って現在の行を削除し
てしまうという事。これは対策できる気がする。対策した。

* widget: keymap による自動的な振る舞いの切り替えの仕組みを作る [#D1391]

うーん。何だか面倒になってきた。そもそも vi_imap の時にだけ振る舞いが変わる
Expand Down
15 changes: 14 additions & 1 deletion src/canvas.sh
Expand Up @@ -452,7 +452,13 @@ function ble/canvas/put-move-y.draw {
local dy=$1
((dy)) || return 1
if ((dy>0)); then
ble/canvas/put-cud.draw "$dy"
if [[ $MC_SID == $$ ]]; then
# Note #D1392: mc (midnight commander) の中だと layout が破壊されるので、
# 必ずしも CUD で想定した行だけ移動できると限らない。
ble/canvas/put-ind.draw "$dy"
else
ble/canvas/put-cud.draw "$dy"
fi
else
ble/canvas/put-cuu.draw $((-dy))
fi
Expand Down Expand Up @@ -1669,6 +1675,13 @@ _ble_canvas_x=0 _ble_canvas_y=0
## プロンプト原点が x=0 y=0 に対応します。
function ble/canvas/goto.draw {
local x=$1 y=$2

# Note #D1392: mc (midnight commander) は
# sgr0 単体でもプロンプトと勘違いするので、
# プロンプト更新もカーソル移動も不要の時は、
# sgr0 も含めて何も出力しない。
((x==_ble_canvas_x&&y==_ble_canvas_y)) && return 0

ble/canvas/put.draw "$_ble_term_sgr0"

ble/canvas/put-move-y.draw $((y-_ble_canvas_y))
Expand Down
13 changes: 13 additions & 0 deletions src/edit.sh
Expand Up @@ -847,6 +847,9 @@ function ble/prompt/update {
_ble_edit_prompt=("$version" "$x" "$y" "$g" "$lc" "$lg" "$esc" "$trace_hash")
ret=$esc

# Note #D1392: mc (midnight commander) の中では補助プロンプトは全て off
[[ $MC_SID == $$ ]] && return 0

# update edit_rps1
if [[ $bleopt_prompt_rps1 ]]; then
local ps1_height=$((y+1))
Expand Down Expand Up @@ -4839,6 +4842,16 @@ function ble/widget/accept-line {
ble/decode/widget/keymap-dispatch "$@"
}
function ble/widget/default/accept-line {
# 文法的に不完全の時は改行挿入
# Note: mc (midnight commander) が改行を含むコマンドを書き込んでくる #D1392
if [[ :$1: == *:syntax:* || $MC_SID == $$ && $LINENO == 0 ]]; then
ble-edit/content/update-syntax
if ! ble/syntax:bash/is-complete; then
ble/widget/newline
return "$?"
fi
fi

ble-edit/content/clear-arg
local BASH_COMMAND=$_ble_edit_str

Expand Down

0 comments on commit e97aa07

Please sign in to comment.