Skip to content

Commit

Permalink
prompt: improve escaping of prompt sequences
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed Apr 2, 2022
1 parent 4dbf16f commit a9551e5
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 37 deletions.
2 changes: 1 addition & 1 deletion contrib
Submodule contrib updated 1 files
+2 −2 config/execmark.bash
6 changes: 3 additions & 3 deletions lib/core-complete.sh
Expand Up @@ -5925,16 +5925,16 @@ function ble/complete/menu-complete.class/render-item {

# 基本色の初期化 (Note: 高速化の為、直接 _ble_color_g2sgr を参照する)
local sgrN0= sgrN1= sgrB0= sgrB1=
[[ :$opts: == *:selected:* ]] && ((g0|=_ble_color_gflags_Revert))
[[ :$opts: == *:selected:* ]] && ((g0^=_ble_color_gflags_Revert))
ret=${_ble_color_g2sgr[g=g0]}
[[ $ret ]] || ble/color/g2sgr "$g"; sgrN0=$ret
ret=${_ble_color_g2sgr[g=g0|_ble_color_gflags_Revert]}
ret=${_ble_color_g2sgr[g=g0^_ble_color_gflags_Revert]}
[[ $ret ]] || ble/color/g2sgr "$g"; sgrN1=$ret
if ((${#m[@]})); then
# 一致色の初期化
ret=${_ble_color_g2sgr[g=g0|_ble_color_gflags_Bold]}
[[ $ret ]] || ble/color/g2sgr "$g"; sgrB0=$ret
ret=${_ble_color_g2sgr[g=g0|_ble_color_gflags_Bold|_ble_color_gflags_Revert]}
ret=${_ble_color_g2sgr[g=(g0|_ble_color_gflags_Bold)^_ble_color_gflags_Revert]}
[[ $ret ]] || ble/color/g2sgr "$g"; sgrB1=$ret
fi

Expand Down
109 changes: 109 additions & 0 deletions note.txt
Expand Up @@ -1809,6 +1809,22 @@ bash_tips
ToDo
-------------------------------------------------------------------------------

2022-03-19

* BUG bash-5.2 で \q{...} が動かなくなっている

* 編集文字列に含まれる制御文字の反転表示の可能性

現在は見た目には区別がつかない。プロンプトシーケンス \w, \W, etc に含まれる
制御文字については反転の toggle で対処している。

一方で、現在の実装だと単純に編集文字列に含まれる制御文字の反転状態を変更す
る事はできない。着色に関係なく制御文字の表現を決めている、つまり地の反転状
態に関係ない escape sequence を決める必要があるが、地の反転状態が分からなけ
れば制御文字が終わった位置で正しく反転状態を復元することができなくなる。

これについてはまた今後必要性を感じた時に実装すれば良い気がする。

2022-03-03

* 起動時の fork について
Expand Down Expand Up @@ -6177,6 +6193,99 @@ bash_tips
Done (実装ログ)
-------------------------------------------------------------------------------

2022-03-19

* menu-complete: 補完候補に含まれる制御文字の反転は xor で行う [#D1802]

prompt seq \w, \W, \s で制御文字の反転を toggle する様にしたが、補完候補の
表示でも同様に toggle した方が良い気がする。#D1798, #D1801 のテストの最中に
実際に制御文字を含むディレクトリを作成して補完を実行した時にやはり気になっ
た。当初は #D1801 の一部として対応したが別項目に分けた方が良い様に感じるの
で分ける事にした。

* done: menu-complete: 特殊文字を含む候補の反転と選択時の反転が重なって見に
くい。反転は重ね書きではなくて toggle にするべきなのではないか。

新しい \e[9807m で対応できるかと思って実装を確認したが trace は使っていな
くて直接画面に出力するのに使うシーケンスを sgr を直接指定することで構築し
ていたので使えなかった。その代わりに反転に使う sgr などをそもそもその場で
g|_ble_color_Revert 等として構築していたのでその | を ^ に置き換える事で
簡単に対応する事ができた。

一方で編集文字列に含まれる制御文字についても気になったがそもそも反転してい
ないので関係ないのであった。

* ok: 更に編集文字列本体についても同様に toggle するべきなのではないか? と
思ったがそもそも現在の実装だと編集文字列に含まれる制御文字は特に反転など
していないので関係ないのであった。

今から新しく対応しようにも現状の実装だと反転の toggle を実装するのは簡単
ではない様なので取り敢えずは対応しない事にする。もし今後必要性を感じた時
に改めて対応する事にすれば良い気がする。一応項目として残しては置く。

2022-03-12

* prompt: bash-5.2 では \w, \W に加えて \s についても escape する様だ [#D1801]
Ref #D1798

bash がなかなか実装しないと思ったらこちらが \w, \W に対する実装を push した
4日後に対応が入った。どうも \s についても escape が入ったという様に commit
message には書かれている。

\s は "bash" という文字列の様だ。これに制御文字が含まれるとは一体どういう状
況だろうか。これを気にするのであれば \h, \H, \u も気にするべきなのではない
か。取り敢えず全てに大して対策を加える事にした。

? bash-5.2 では ${PS1@P} でもちゃんと \w, \W を escape してくれるのか→ちゃ
んと escape してくれている。

* done: bash-4.4 以上では escape が起こった時に @P を使わない自前実装に切り
替える必要がある。

* bash-5.2 の実装についても改めて確認する。\t はそのまま特殊文字のまま出力
する様だ。その他については zsh の様に \n だとか \t の様な表現には置き換え
ず一貫して ^X という形で表現している。改行は ^J になっている。

\s 以外にも対応している物がないか確認する。

bash-5.2 の対応では sh_backslash_quote_for_double_quotes に手を入れる事に
よって対応が行われている。この関数は本来は eval する時に変な事が起こらな
い様に $`"\ 等を escape するのが目的だったのだと思われるが、同時にエスケー
プまで実行してしまうという算段である。

特にこの機能が有効になるのは第二引数に bit 1 を指定した時で以下が該当する。

./parse.y:5745: temp = sh_backslash_quote_for_double_quotes (temp, 1);
./parse.y:5822: temp = sh_backslash_quote_for_double_quotes (t_string, 1);

そしてこれは正に commit msg にある様にそれぞれ \s と \w\W に対する処理で
あった。なので \s, \w, \W 以外に関しては対策は実装されていない。

? うーん。 ^\ に大して何か変な事が起こったりするのではないか…。例えば
^\$(echo hello) が実行されてしまう可能性はないのだろうか。試してみたら
問題を再現できてしまった。

$ mkdir $'\034$(echo hello)'
$ cd !$

? これは shopt -u promptvars でもちゃんと実行されるのだろうか? →試してみ
たがちゃんと動いている様だ。OK

2022-03-19 これは bash に PATCH を送ったら今日適用されていた。OK

* bash-5.2 では META_CHAR に対しても escape を実装している様だ。うーん。
ble.sh でも対応するべきだろうか。と思ったがよく考えてみたら ble.sh ではそ
もそも編集文字列であっても 80..A0 に対して特別な文字は割り当てていない様
な気もする。これはそれも含めて再考する必要がある。

→と思ったが改めて確認してみたところ
_ble_unicode_GraphemeCluster_ControlRepresentation という配列にちゃんと登
録しているのだった。この新しい実装でもこの配列を参照するべきである。

と思ったが _ble_unicode_GraphemeCluster_ControlRepresentation は単にキャッ
シュしているだけなので代わりに ble/unicode/GraphemeCluster/.get-ascii-rep
を呼び出すべきである。その様に実装し直した。

2022-03-04

* 2021-10-26 ble/builtin/read: status が表示されている気がする [#D1800]
Expand Down
117 changes: 84 additions & 33 deletions src/edit.sh
Expand Up @@ -465,31 +465,79 @@ function ble/prompt/status#collapse {
_ble_prompt_hash=
_ble_prompt_version=0

function ble/prompt/.escape-control-characters {
ret=$1
local glob_ctrl=$'[\001-\037\177-\237]'
[[ $ret == *$glob_ctrl* ]] || return 0

local out= head tail=$ret cs
while head=${tail%%$glob_ctrl*}; [[ $head != "$tail" ]]; do
ble/util/s2c "${tail:${#head}:1}"
ble/unicode/GraphemeCluster/.get-ascii-rep "$ret" # -> cs
out=$out$head$'\e[9807m'$cs$'\e[9807m'
tail=${tail#*$glob_ctrl}
done
ret=$out$tail
}

## @fn ble/prompt/.initialize-constant ps defeval [opts]
## @param ps
## 初期化に使用する prompt シーケンスを指定します。
## @param defeval
## 初期化に使用するコマンドを指定します。ret に結果を格納します。
## @param[opt] opts
## コロン区切りのオプションリストです。escape が指定されている時、
## 展開結果に含まれる制御文字をエスケープします。
function ble/prompt/.initialize-constant {
local __ps=$1 __defeval=$2 __opts=$3
if ((_ble_bash>=40400)); then
ret=${__ps@P}
else
builtin eval -- "$__defeval"
fi

if [[ $__opts == *:escape:* ]]; then
if ((_ble_bash>=50200)); then
# bash-5.2 以上では bash が escape を行うが、反転などの処理が実
# 装されていないので、制御文字が含まれている場合には ble.sh の側
# で処理を行う。
if [[ $ret == *\^['A'-'Z[\]^_?']* ]]; then
builtin eval -- "$__defeval"
ble/prompt/.escape-control-characters "$ret"
elif [[ $ret == *$'\t'* ]]; then
ble/prompt/.escape-control-characters "$ret"
fi
else
ble/prompt/.escape-control-characters "$_ble_prompt_const_s"
fi
fi
}

## called by ble-edit/initialize
function ble/prompt/initialize {
local ret

# hostname
_ble_prompt_const_H=${HOSTNAME}
ble/prompt/.initialize-constant '\H' 'ret=$HOSTNAME' escape
_ble_prompt_const_H=$ret
if local rex='^[0-9]+(\.[0-9]){3}$'; [[ $HOSTNAME =~ $rex ]]; then
# IPv4 の形式の場合には省略しない
_ble_prompt_const_h=$HOSTNAME
_ble_prompt_const_h=$_ble_prompt_const_H
else
_ble_prompt_const_h=${HOSTNAME%%.*}
_ble_prompt_const_h=${_ble_prompt_const_H%%.*}
fi

# tty basename
local tmp
if ((_ble_bash>=40400)); then
tmp='\l' _ble_prompt_const_l=${tmp@P}
else
ble/util/assign tmp 'ble/bin/tty 2>/dev/null'
_ble_prompt_const_l=${tmp##*/}
fi
ble/prompt/.initialize-constant '\l' 'ble/util/assign ret "ble/bin/tty 2>/dev/null";ret=${ret##*/}'
_ble_prompt_const_l=$ret

# command name
_ble_prompt_const_s=${0##*/}
ble/prompt/.initialize-constant '\s' 'ret=${0##*/}' escape
_ble_prompt_const_s=$ret

# user
_ble_prompt_const_u=${USER}
ble/prompt/.initialize-constant '\s' 'ret=$USER' escape
_ble_prompt_const_u=$ret

# bash versions
ble/util/sprintf _ble_prompt_const_v '%d.%d' "${BASH_VERSINFO[0]}" "${BASH_VERSINFO[1]}"
Expand Down Expand Up @@ -897,26 +945,11 @@ function ble/prompt/backslash:V { # = bash version %d.%d.%d
ble/prompt/print "$_ble_prompt_const_V"
return 0
}
function ble/prompt/backslash/.escape-control-characters {
ret=$1
local glob_ctrl=$'[\001-\037\177]'
[[ $ret == *$glob_ctrl* ]] || return 0

local out= head tail=$ret
while head=${tail%%$glob_ctrl*}; [[ $head != "$tail" ]]; do
out=$out$head
ble/util/s2c "${tail:${#head}:1}"
ble/util/c2s $((ret<32?ret+64:63))
out=$out$'\e[9807m'^$ret$'\e[9807m'
tail=${tail#*$glob_ctrl}
done
ret=$out$tail
}
function ble/prompt/backslash:w { # PWD
ble/prompt/unit/add-hash '$PWD'
ble/prompt/.update-working-directory
local ret
ble/prompt/backslash/.escape-control-characters "$prompt_cache_wd"
ble/prompt/.escape-control-characters "$prompt_cache_wd"
ble/prompt/print "$ret"
return 0
}
Expand All @@ -927,7 +960,7 @@ function ble/prompt/backslash:W { # PWD短縮
else
ble/prompt/.update-working-directory
local ret
ble/prompt/backslash/.escape-control-characters "${prompt_cache_wd##*/}"
ble/prompt/.escape-control-characters "${prompt_cache_wd##*/}"
ble/prompt/print "$ret"
fi
return 0
Expand Down Expand Up @@ -1178,6 +1211,27 @@ function ble/prompt/.get-keymap-for-current-mode {
keymap=${_ble_decode_keymap_stack[index]}
done
}

function ble/prompt/.uses-builtin-prompt-expansion {
((_ble_bash>=40400)) || return 1

local ps=$1
local chars_safe_esc='][0-7aenrdtAT@DhHjlsuvV!$\wW'
[[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $_ble_prompt_const_root == '#' ]] &&
chars_safe_esc=${chars_safe_esc//'$'} # Note: cygwin では ble.sh 独自の方法で \$ を処理する。

[[ $ps == *'\'[!"$chars_safe_esc"]* ]] && return 1

local glob_ctrl=$'[\001-\037\177]'
[[ $ps == *'\'[wW]* && $PWD == *$glob_ctrl* ]] && return 1
[[ $ps == *'\s'* && $_ble_prompt_const_s == *$'\e'* ]] && return 1
[[ $ps == *'\u'* && $_ble_prompt_const_u == *$'\e'* ]] && return 1
[[ $ps == *'\h'* && $_ble_prompt_const_h == *$'\e'* ]] && return 1
[[ $ps == *'\H'* && $_ble_prompt_const_H == *$'\e'* ]] && return 1

return 0
}

## @fn ble/prompt/.instantiate ps opts [x0 y0 g0 lc0 lg0 esc0 trace_hash0]
##
## @var[out] x y g
Expand All @@ -1202,10 +1256,7 @@ function ble/prompt/.instantiate {
[[ ! $ps ]] && return 0

local expanded=
local chars_safe_esc='][0-7aenrdtAT@DhHjlsuvV!$\wW'
[[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $_ble_prompt_const_root == '#' ]] &&
chars_safe_esc=${chars_safe_esc//'$'} # Note: cygwin では ble.sh 独自の方法で \$ を処理する。
if ((_ble_bash>=40400)) && [[ $ps != *'\'[!"$chars_safe_esc"]* && ! ( $ps == *'\'[wW]* && $PWD == *[$'\001'-$'\037\177']* ) ]]; then
if ble/prompt/.uses-builtin-prompt-expansion "$ps"; then
[[ $ps == *'\'[wW]* ]] && ble/prompt/unit/add-hash '$PWD'
ble-edit/exec/.setexit "$_ble_edit_exec_lastarg"
BASH_COMMAND=$_ble_edit_exec_BASH_COMMAND \
Expand Down

0 comments on commit a9551e5

Please sign in to comment.