diff --git a/blerc b/blerc index c78da215..88ee848c 100644 --- a/blerc +++ b/blerc @@ -472,20 +472,36 @@ # bleopt filename_ls_colors="$LS_COLORS" -## The following settings controls enable or disable the syntax highlighting. -## When the setting "highlight_syntax" has a non-empty value, the syntax -## highlighting is enabled. When the setting "highlight_filename" has a -## non-empty value, the highlighting based on the filename and the command name -## is enabled during the process of the syntax highlighting. Similarly, when -## the setting "highlight_variable" has a non-empty value, the highlighting -## based on the variable type is enabled. All of these settings have non-empty -## values by default. +## The following settings enable or disable the syntax highlighting. When the +## setting "highlight_syntax" has a non-empty value, the syntax highlighting is +## enabled. When the setting "highlight_filename" has a non-empty value, the +## highlighting based on the filename and the command name is enabled during +## the process of the syntax highlighting. Similarly, when the setting +## "highlight_variable" has a non-empty value, the highlighting based on the +## variable type is enabled. All of these settings have non-empty values by +## default. # bleopt highlight_syntax= # bleopt highlight_filename= # bleopt highlight_variable= +## The following settings control the timeout for the pathname expansions +## performed in the syntax highlighting. When the word contains a glob pattern +## that takes a long time to evaluate the pathname expansion, the syntax +## highlighting based on the filename is canceled based on the timeouts +## specified by these settings. "highlight_timeout_sync" / +## "highlight_timeout_async" specify the timeout durations in milliseconds to +## be used for the foreground / background syntax highlighting, respectively. +## When the timeout occurred in the foreground, the syntax highlighting will be +## deferred to the background syntax highlighting. When the timeout occurred +## in the background, the syntax highlighting for the filename is canceled. +## When the value is empty, the corresponding timeout is disabled. + +# bleopt highlight_timeout_sync=500 +# bleopt highlight_timeout_async=5000 + + ## The following settings specify graphic styles of each faces. # ble-color-setface region fg=white,bg=60 diff --git a/lib/core-complete.sh b/lib/core-complete.sh index 9c549317..e9faddac 100644 --- a/lib/core-complete.sh +++ b/lib/core-complete.sh @@ -1962,12 +1962,12 @@ function ble/complete/progcomp/.compvar-generate-subwords/impl1 { point=0 words=() # 単語毎に評価 (前半) - local evaluator=eval-noglob - ((${#ret[@]}==1)) && evaluator=eval + local eval_opts=noglob + ((${#ret[@]}==1)) && eval_opts= ble/syntax:bash/simple-word#break-word "$left" local subword for subword in "${ret[@]}"; do - ble/syntax:bash/simple-word/"$evaluator" "$subword" + ble/syntax:bash/simple-word/eval "$subword" "$eval_opts" ble/array#push words "$ret" ((point+=${#ret})) done @@ -1977,7 +1977,7 @@ function ble/complete/progcomp/.compvar-generate-subwords/impl1 { ble/syntax:bash/simple-word#break-word "$right" local subword isfirst=1 for subword in "${ret[@]}"; do - ble/syntax:bash/simple-word/eval-noglob "$subword" + ble/syntax:bash/simple-word/eval "$subword" noglob if [[ $isfirst ]]; then isfirst= local iword=${#words[@]}; ((iword&&iword--)) @@ -3728,9 +3728,9 @@ function ble/complete/candidates/determine-common-prefix { if [[ ! $is_processed ]] && local notilde=\'\' && ble/syntax:bash/simple-word/reconstruct-incomplete-word "$COMPS" && - ble/syntax:bash/simple-word/eval-noglob "$notilde$ret" && + ble/syntax:bash/simple-word/eval "$notilde$ret" noglob && local compv_notilde=$ret && - ble/syntax:bash/simple-word/eval-noglob "$notilde$common_reconstructed" && + ble/syntax:bash/simple-word/eval "$notilde$common_reconstructed" noglob && local commonv_notilde=$ret && COMPV=$compv_notilde ble/complete/candidates/filter:"$filter_type"/count-match-chars "$commonv_notilde" then diff --git a/lib/core-syntax-def.sh b/lib/core-syntax-def.sh index 0b508d51..5fe99696 100644 --- a/lib/core-syntax-def.sh +++ b/lib/core-syntax-def.sh @@ -71,7 +71,6 @@ ble/util/autoload "$_ble_base/lib/core-syntax.sh" \ ble/syntax/highlight/getg-from-filename \ ble/syntax:bash/extract-command \ ble/syntax:bash/simple-word/eval \ - ble/syntax:bash/simple-word/eval-noglob \ ble/syntax:bash/simple-word/evaluate-path-spec \ ble/syntax:bash/simple-word/is-never-word \ ble/syntax:bash/simple-word/is-simple \ @@ -98,6 +97,8 @@ bleopt/declare -v filename_ls_colors '' bleopt/declare -v highlight_syntax 1 bleopt/declare -v highlight_filename 1 bleopt/declare -v highlight_variable 1 +bleopt/declare -v highlight_timeout_sync 50 +bleopt/declare -v highlight_timeout_async 5000 if ((_ble_bash>=40300||_ble_bash>=40000&&!_ble_bash_loaded_in_function)); then builtin unset -v _ble_syntax_highlight_filetype diff --git a/lib/core-syntax.sh b/lib/core-syntax.sh index 0d01ab76..6da3b2b1 100644 --- a/lib/core-syntax.sh +++ b/lib/core-syntax.sh @@ -864,18 +864,15 @@ function ble/syntax/parse/nest-equals { ## word については [_ble_syntax_word_umin, _ble_syntax_word_umax] が範囲である。 _ble_syntax_attr_umin=-1 _ble_syntax_attr_umax=-1 _ble_syntax_word_umin=-1 _ble_syntax_word_umax=-1 +_ble_syntax_word_defer_umin=-1 _ble_syntax_word_defer_umax=-1 function ble/syntax/parse/touch-updated-attr { - (((_ble_syntax_attr_umin<0||_ble_syntax_attr_umin>$1)&&( - _ble_syntax_attr_umin=$1))) + ble/syntax/urange#update _ble_syntax_attr_ "$1" $(($1+1)) } function ble/syntax/parse/touch-updated-word { #%if !release ble/util/assert "(($1>0))" "invalid word position $1" #%end - (((_ble_syntax_word_umin<0||_ble_syntax_word_umin>$1)&&( - _ble_syntax_word_umin=$1))) - (((_ble_syntax_word_umax<0||_ble_syntax_word_umax<$1)&&( - _ble_syntax_word_umax=$1))) + ble/syntax/wrange#update _ble_syntax_word_ "$1" } #============================================================================== @@ -1075,6 +1072,9 @@ _ble_syntax_bash_simple_rex_open_squot= _ble_syntax_bash_simple_rex_incomplete_word1= _ble_syntax_bash_simple_rex_incomplete_word2= +_ble_syntax_bash_simple_rex_noglob_word1= +_ble_syntax_bash_simple_rex_noglob_word2= + function ble/syntax:bash/simple-word/update { local q="'" @@ -1109,6 +1109,11 @@ function ble/syntax:bash/simple-word/update { local letter2='\[[!^]|[^'${_ble_syntax_bashc_simple}']' _ble_syntax_bash_simple_rex_incomplete_word1='^('$bquot'|'$squot'|'$dquot'|'$param'|'$letter1')+' _ble_syntax_bash_simple_rex_incomplete_word2='^(('$bquot'|'$squot'|'$dquot'|'$param'|'$letter2')*)(\\|'$open_squot'|'$open_dquot')?$' + + # @var _ble_syntax_bash_simple_rex_noglob_word{1,2} + local noglob_letter='[^[?*'${_ble_syntax_bashc_simple}']' + _ble_syntax_bash_simple_rex_noglob_word1='^('$bquot'|'$squot'|'$dquot'|'$noglob_letter')+$' + _ble_syntax_bash_simple_rex_noglob_word2='^('$bquot'|'$squot'|'$dquot'|'$param'|'$noglob_letter')+$' } ble/syntax:bash/simple-word/update @@ -1123,6 +1128,15 @@ function ble/syntax:bash/simple-word/is-never-word { local rex=${_ble_syntax_bash_simple_rex_word%'$'}'[ |&;<>()]|^[ |&;<>()]' [[ $1 =~ $rex ]] } +function ble/syntax:bash/simple-word/is-simple-noglob { + [[ $1 =~ $_ble_syntax_bash_simple_rex_noglob_word1 ]] && return 0 + if [[ $1 =~ $_ble_syntax_bash_simple_rex_noglob_word2 ]]; then + builtin eval -- "local expanded=$1" + local rex='[*?]|\[.+\]|[*?@+!]\(.*\)' + [[ $expanded =~ $rex ]] || return 0 + fi + return 1 +} ## @fn ble/syntax:bash/simple-word/evaluate-last-brace-expansion simple_word ## @param[in] simple_word @@ -1309,60 +1323,101 @@ function ble/syntax:bash/simple-word/extract-parameter-names/.process-dquot { } function ble/syntax:bash/simple-word/eval/.set-result { __ble_ret=("$@"); } -function ble/syntax:bash/simple-word/eval-noglob/.impl { - # グローバル変数の復元 - local -a ret - ble/syntax:bash/simple-word/extract-parameter-names "$1" - if ((${#ret[@]})); then - local __ble_defs - ble/util/assign __ble_defs 'ble/util/print-global-definitions --hidden-only "${ret[@]}"' - builtin eval -- "$__ble_defs" &>/dev/null # 読み取り専用の変数のこともある - fi +function ble/syntax:bash/simple-word/eval/.print-result { + local args q=\' Q="'\''" + args=("${@//$q/$Q}") + args=("${args[@]/%/$q}") + args=("${args[@]/#/$q}") + ble/util/print "__ble_ret=(${args[*]})" - local __ble_unset_f= - [[ $- != *f* ]] && { __ble_unset_f=1; set -f; } - builtin eval "ble/syntax:bash/simple-word/eval/.set-result $1"; local ext=$? - [[ $__ble_unset_f ]] && set +f - return "$ext" -} -## @fn ble/syntax:bash/simple-word/eval-noglob str -## @var[out] ret -function ble/syntax:bash/simple-word/eval-noglob { - local __ble_ret - ble/syntax:bash/simple-word/eval-noglob/.impl "$1" - ret=("${__ble_ret[@]}") } +## @fn ble/syntax:bash/simple-word/eval/.impl word opts +## @param[in] word +## @param[in,opt] opts +## @var[out] __ble_ret function ble/syntax:bash/simple-word/eval/.impl { - if ble/util/is-cygwin-slow-glob "$1"; then # Note: #D1168 - if shopt -q failglob &>/dev/null; then - __ble_ret=() - return 1 - elif shopt -q nullglob &>/dev/null; then - __ble_ret=() - return 0 - else - ble/syntax:bash/simple-word/eval-noglob "$1"; return "$?" - fi - fi + local __ble_word=$1 __ble_opts=$2 __ble_flags= # グローバル変数の復元 local -a ret=() - ble/syntax:bash/simple-word/extract-parameter-names "$1" + ble/syntax:bash/simple-word/extract-parameter-names "$__ble_word" if ((${#ret[@]})); then local __ble_defs ble/util/assign __ble_defs 'ble/util/print-global-definitions --hidden-only "${ret[@]}"' builtin eval -- "$__ble_defs" &>/dev/null # 読み取り専用の変数のこともある fi + # glob パターンを含む可能性がある時の処理 (Note: is-simple-noglob の + # 判定で変数を参照するので、グローバル変数の復元よりも後で処理する必 + # 要がある) + if [[ $- != *f* ]] && ! ble/syntax:bash/simple-word/is-simple-noglob "$1"; then + if [[ :$__ble_opts: == *:noglob:* ]]; then + set -f + __ble_flags=f + elif ble/util/is-cygwin-slow-glob "$1"; then # Note: #D1168 + if shopt -q failglob &>/dev/null; then + __ble_ret=() + return 1 + elif shopt -q nullglob &>/dev/null; then + __ble_ret=() + return 0 + else + # noglob で処理する + set -f + __ble_flags=f + fi + elif [[ :$__ble_opts: == *:stopcheck:* ]]; then + ble/decode/has-input && return 148 + if ((_ble_bash>=40000)); then + __ble_flags=s + elif shopt -q globstar &>/dev/null; then + # conditional-sync が使えない場合はせめて globstar だけでも撥ねる + if builtin eval "[[ $__ble_word == *'**'* ]]"; then + [[ :$__ble_opts: == *:timeout=*:* ]] && return 142 + return 148 + fi + fi + fi + fi + # Note: failglob 時に一致がないと実行されないので予め __ble_ret=() をする。 # また、エラーメッセージが生じるので /dev/null に繋ぐ。 __ble_ret=() - builtin eval "ble/syntax:bash/simple-word/eval/.set-result $1" &>/dev/null; local ext=$? - builtin eval : # Note: bash 3.1/3.2 eval バグ対策 (#D1132) + if [[ $__ble_flags == *s* ]]; then + local __ble_sync_command='ble/syntax:bash/simple-word/eval/.print-result $__ble_word' + local __ble_sync_opts=progressive-weight + + # determine timeout + local __ble_sync_timeout= + if local __ble_rex=':timeout=([^:]*):'; [[ :$__ble_opts: =~ $__ble_rex ]]; then + __ble_sync_timeout=${BASH_REMATCH[1]} + elif [[ :$__ble_opts: == *:timeout-highlight:* ]]; then + __ble_sync_timeout=$bleopt_highlight_timeout_sync + [[ $ble_textarea_render_defer_running ]] && + __ble_sync_timeout=$bleopt_highlight_timeout_async + fi + [[ $__ble_sync_timeout ]] && + __ble_sync_opts=$__ble_sync_opts:timeout=$((__ble_sync_timeout)) + + local __ble_def + ble/util/assign __ble_def 'ble/util/conditional-sync "$__ble_sync_command" "" "" "$__ble_sync_opts"' &>/dev/null; local ext=$? + builtin eval -- "$__ble_def" + else + builtin eval "ble/syntax:bash/simple-word/eval/.set-result $__ble_word" &>/dev/null; local ext=$? + builtin eval : # Note: bash 3.1/3.2 eval バグ対策 (#D1132) + fi + + [[ $__ble_flags == *f* ]] && set +f return "$ext" } -## @fn ble/syntax:bash/simple-word/eval word +## @fn ble/syntax:bash/simple-word/eval word opts ## @param[in] word +## @param[in,opt] opts +## stopcheck +## timeout-highlight +## timeout=NUM +## stopcheck を指定している時に有効です。timeout を指定します。 +## ## @var[out] ret ## @exit ## shopt -q failglob の時、パス名展開に失敗すると @@ -1370,7 +1425,7 @@ function ble/syntax:bash/simple-word/eval/.impl { ## function ble/syntax:bash/simple-word/eval { local __ble_ret - ble/syntax:bash/simple-word/eval/.impl "$1"; local ext=$? + ble/syntax:bash/simple-word/eval/.impl "$1" "$2"; local ext=$? ret=("${__ble_ret[@]}") return "$ext" } @@ -1392,6 +1447,9 @@ function ble/syntax:bash/simple-word/.get-rex_element { ## @param[in,opt] sep (default: '/:=') ## @param[in,opt] opts ## noglob notilde after-sep +## stopcheck +## timeout-highlight +## timeout=* ## @arr[out] spec ## @arr[out] path ## @arr[out] ret @@ -1412,8 +1470,11 @@ function ble/syntax:bash/simple-word/evaluate-path-spec { [[ $word ]] || return 0 # read options - local eval_word=ble/syntax:bash/simple-word/eval - [[ :$opts: == *:noglob:* ]] && eval_word=ble/syntax:bash/simple-word/eval-noglob + local eval_opts= rex_timeout=':(timeout=[^:]*):' + [[ :$opts: == *:stopcheck:* ]] && eval_opts=stopcheck + [[ :$opts: == *:timeout-highlight:* ]] && eval_opts=$eval_opts:timeout-highlight + [[ :$opts: =~ $rex_timeout ]] && eval_opts=$eval_opts:${BASH_REMATCH[1]} + [[ :$opts: == *:noglob:* ]] && eval_opts=$eval_opts:noglob local notilde= [[ :$opts: == *:notilde:* ]] && notilde=\'\' # チルダ展開の抑制 @@ -1427,7 +1488,8 @@ function ble/syntax:bash/simple-word/evaluate-path-spec { while [[ $tail =~ $rex ]]; do local rematch=$BASH_REMATCH s=$s$rematch - "$eval_word" "$notilde$s"; ext=$? + ble/syntax:bash/simple-word/eval "$notilde$s" "$eval_opts"; ext=$? + ((ext==148)) && return "$ext" p=$ret tail=${tail:${#rematch}} ble/array#push spec "$s" @@ -1446,6 +1508,9 @@ function ble/syntax:bash/simple-word/evaluate-path-spec { ## url ## noglob ## notilde +## stopcheck +## timeout-highlight +## timeout=* ## @var[out] ret ## 有効な区切り文字の集合を返します。 function ble/syntax:bash/simple-word/detect-separated-path { @@ -1456,8 +1521,11 @@ function ble/syntax:bash/simple-word/detect-separated-path { [[ :$opts: == *:url:* && $word =~ $rex_url ]] && return 1 # read eval options - local eval_word=ble/syntax:bash/simple-word/eval - [[ :$opts: == *:noglob:* ]] && eval_word=ble/syntax:bash/simple-word/eval-noglob + local eval_opts= rex_timeout=':(timeout=[^:]*):' + [[ :$opts: == *:stopcheck:* ]] && eval_opts=stopcheck + [[ :$opts: == *:timeout-highlight:* ]] && eval_opts=$eval_opts:timeout-highlight + [[ :$opts: =~ $rex_timeout ]] && eval_opts=$eval_opts:${BASH_REMATCH[1]} + [[ :$opts: == *:noglob:* ]] && eval_opts=$eval_opts:noglob local notilde= [[ :$opts: == *:notilde:* ]] && notilde=\'\' # チルダ展開の抑制 @@ -1469,7 +1537,9 @@ function ble/syntax:bash/simple-word/detect-separated-path { local tail=$word head= while [[ $tail =~ $rex ]]; do local rematch=$BASH_REMATCH - ble/syntax:bash/simple-word/locate-filename/.exists "$notilde$head$rematch" || break + ble/syntax:bash/simple-word/locate-filename/.exists "$notilde$head$rematch"; local ext=$? + ((ext==148)) && return 148 + ((ext==0)) || break head=$head$rematch tail=${tail:${#rematch}} done @@ -1489,11 +1559,11 @@ function ble/syntax:bash/simple-word/detect-separated-path { ## @fn ble/syntax:bash/simple-word/locate-filename/.exists word opts ## @param[in] word ## @param[in] opts -## @var[in] eval_word +## @var[in] eval_opts ## @var[in] rex_url function ble/syntax:bash/simple-word/locate-filename/.exists { local word=$1 ret - "$eval_word" "$word" || return 1 + ble/syntax:bash/simple-word/eval "$word" "$eval_opts" || return "$?" local path=$ret # Note: #D1168 Cygwin では // で始まるパスの判定は遅いので直接文字列で判定する if [[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $path == //* ]]; then @@ -1512,6 +1582,12 @@ function ble/syntax:bash/simple-word/locate-filename/.exists { ## : を区切りと見做さずに結合したパスが有効であれば、その結合パスを採用します。 ## url ## [a-z]+:// で始まるパスを無条件に有効なパスと判定します。 +## stopcheck +## 時間のかかる可能性のあるパス名展開をユーザ入力により中断します。 +## timeout-highlight +## stopcheck に際して timeout として highlight 用の設定を使用します。 +## timeout=* +## stopcheck に際して timeout を指定します。 ## @arr[out] ret ## 偶数個の要素を含みます。偶数 index (0, 2, 4, ...) が範囲の開始で、 ## 奇数 index (1, 3, 5, ...) が範囲の終了です。 @@ -1522,8 +1598,11 @@ function ble/syntax:bash/simple-word/locate-filename { [[ $word ]] || return 0 # prepare evaluator - local eval_word=ble/syntax:bash/simple-word/eval - [[ :$opts: == *:noglob:* ]] && eval_word=ble/syntax:bash/simple-word/eval-noglob + local eval_opts= rex_timeout=':(timeout=[^:]*):' + [[ :$opts: == *:stopcheck:* ]] && eval_opts=stopcheck + [[ :$opts: == *:timeout-highlight:* ]] && eval_opts=$eval_opts:timeout-highlight + [[ :$opts: =~ $rex_timeout ]] && eval_opts=$eval_opts:${BASH_REMATCH[1]} + [[ :$opts: == *:noglob:* ]] && eval_opts=$eval_opts:noglob # compose regular expressions local rex_element; ble/syntax:bash/simple-word/.get-rex_element "$sep" @@ -1550,15 +1629,19 @@ function ble/syntax:bash/simple-word/locate-filename { if ((j>i)); then # もし繋げて存在するファイル名になるのであればそれを採用 - if ble/syntax:bash/simple-word/locate-filename/.exists "${word:f1:f2-f1}" "$opts"; then + ble/syntax:bash/simple-word/locate-filename/.exists "${word:f1:f2-f1}" "$opts"; local ext=$? + ((ext==148)) && return 148 + if ((ext==0)); then ble/array#push out "$f1" "$f2" ((i=j)) fi else # ファイルが見つからなかった場合は単一区間を登録 - [[ :$opts: != *:exists:* ]] || - ble/syntax:bash/simple-word/locate-filename/.exists "${word:f1:f2-f1}" "$opts" && - ble/array#push out "$f1" "$f2" + if [[ :$opts: != *:exists:* ]] || + { ble/syntax:bash/simple-word/locate-filename/.exists "${word:f1:f2-f1}" "$opts" + local ext=$?; ((ext==148)) && return 148; ((ext==0)); }; then + ble/array#push out "$f1" "$f2" + fi fi done done @@ -4623,6 +4706,7 @@ function ble/syntax/parse/shift { # 更新範囲の shift ble/syntax/urange#shift _ble_syntax_attr_ ble/syntax/wrange#shift _ble_syntax_word_ + ble/syntax/wrange#shift _ble_syntax_word_defer_ ble/syntax/urange#shift _ble_syntax_vanishing_word_ fi } @@ -6221,12 +6305,15 @@ function ble/syntax/progcolor/set-wattr { _ble_syntax_tree[TE_i-1]="${node[*]}" } -## @fn ble/syntax/progcolor/eval-word [iword] +## @fn ble/syntax/progcolor/eval-word [iword] [opts] ## 現在のコマンドの iword 番目の単語を評価した値を返します。 ## iword を省略した場合には現在着色中の単語が使われます。 ## 単語が評価できない場合にはコマンドは失敗します。 ## ## @param[in,opt] iword +## @param[in,opt] opts +## ble/syntax:bash/simple-word/eval に対する opts を指定します。 +## ## @var[in] progcolor_iword ## 現在処理中の単語の番号を指定します。 ## iword を省略した時に使われます。 @@ -6236,7 +6323,7 @@ function ble/syntax/progcolor/set-wattr { ## @var[out] ret ## function ble/syntax/progcolor/eval-word { - local iword=${1:-progcolor_iword} + local iword=${1:-progcolor_iword} opts=$2 if [[ ${progcolor_stats[iword]+set} ]]; then ret=${progcolor_wvals[iword]} return "${progcolor_stats[iword]}" @@ -6252,7 +6339,9 @@ function ble/syntax/progcolor/eval-word { return 2 fi - if ! ble/syntax:bash/simple-word/eval "$ret"; then + ble/syntax:bash/simple-word/eval "$ret" "$opts"; local ext=$? + ((ext==148)) && return 148 + if ((ext!=0)); then # fail glob ret= progcolor_stats[iword]=1 @@ -6321,7 +6410,7 @@ function ble/syntax/progcolor/word:default/.detect-separated-path { local word=$1 ((wtype==CTX_ARGI||wtype==CTX_ARGEI||wtype==CTX_VALI||wtype==ATTR_VAR||wtype==CTX_RDRS)) || return 1 - local detect_opts=url + local detect_opts=stopcheck:timeout-highlight:url ((wtype==CTX_RDRS)) && detect_opts=$detect_opts:noglob [[ $word == '~'* ]] && ((_ble_syntax_attr[p0]!=ATTR_TILDE)) && detect_opts=$detect_opts:notilde ble/syntax:bash/simple-word/detect-separated-path "$word" :, "$detect_opts" @@ -6443,14 +6532,23 @@ function ble/syntax/progcolor/word:default/.highlight-filename { local p0=${1%%:*} p1=${1#*:} local wtxt=${text:p0:p1-p0} - local path_opts=after-sep + local path_opts=stopcheck:timeout-highlight:after-sep # チルダ展開の文脈でない時には抑制 [[ $wtxt == '~'* ]] && ((_ble_syntax_attr[p0]!=ATTR_TILDE)) && path_opts=$path_opts:notilde ((wtype==CTX_RDRS||wtype==ATTR_VAR||wtype==CTX_VALI&&wbeg=2)); then @@ -6478,7 +6576,7 @@ function ble/syntax/progcolor/word:default/.is-option-context { local iword ret for ((iword=1;iword=0)) && ble/syntax/tree-enumerate-children "$proc_children" } -## @var[in,out] _ble_syntax_word_umin,_ble_syntax_word_umax +## @var[in,out] _ble_syntax_word_umin _ble_syntax_word_umax function ble/highlight/layer:syntax/update-word-table { # update table2 (単語の削除に関しては後で考える) # (1) 単語色の計算 @@ -6953,7 +7063,7 @@ function ble/highlight/layer:syntax/update { # ble/string#split-words word "${_ble_syntax_tree[i-1]}" # local wtxt="${text:i-word[1]:word[1]}" value # if ble/syntax:bash/simple-word/is-simple "$wtxt"; then - # local ret; ble/syntax:bash/simple-word/eval "$wtxt"; value=$ret + # local ret; ble/syntax:bash/simple-word/eval "$wtxt" noglob; value=$ret # else # value="? ($wtxt)" # fi @@ -6975,6 +7085,13 @@ function ble/highlight/layer:syntax/getg { fi } +function ble/highlight/layer:syntax/textarea_render_defer.hook { + ble/syntax/wrange#update _ble_syntax_word_ "$_ble_syntax_word_defer_umin" "$_ble_syntax_word_defer_umax" + _ble_syntax_word_defer_umin=-1 + _ble_syntax_word_defer_umax=-1 +} +blehook textarea_render_defer+=ble/highlight/layer:syntax/textarea_render_defer.hook + #%#---------------------------------------------------------------------------- #%# old test samples #%#---------------------------------------------------------------------------- diff --git a/memo/ChangeLog.md b/memo/ChangeLog.md index 93f488e7..0306cdc9 100644 --- a/memo/ChangeLog.md +++ b/memo/ChangeLog.md @@ -19,6 +19,7 @@ - complete: do not quote `:` and `=` in non-filename completions generated by progcomp (reported by 3ximus) `#D1434` d82535e - edit: preserve the state of `READLINE_{LINE,POINT,MARK}` `#D1437` 8379d4a - edit: change default behavior of C-w and M-w to operate on backward words `#D1448` 47a3301 +- syntax (`layer:syntax/word`): perform pathname expansions in background subshells (motivated by 3ximus) `#D1449` 0000000 ## Fixes @@ -32,7 +33,7 @@ - edit (`kill-forward-logical-line`): fix a bug not deleting newline at the end of the line `#D1443` 09cf7f1 - complete (mandb): fix an encoding prpblem of utf8 manuals `#D1446` 7a4a480 - global:work around bash-4.2 bug of `declare -gA` (reported by 0xC0ncord) `#D1470` 8856a04 - - global: fix declaration of associative arrays for `ble-reload` `#D1471` 0000000 + - global: fix declaration of associative arrays for `ble-reload` `#D1471` 3cae6e4 ## Internal changes and fixes diff --git a/note.txt b/note.txt index 5842b387..0788df17 100644 --- a/note.txt +++ b/note.txt @@ -1204,6 +1204,58 @@ bash_tips ToDo ------------------------------------------------------------------------------- +2021-01-24 + + * syntax/simple-word/eval: キャッシュ機能を付ける + + 特に一回の着色 (layer:syntax/update) の中では同じ評価は一回しかしない様に工 + 夫したい。キャッシュは dict に保存したいが二種類の問題がある。 + + 1. eval の展開結果は配列なのでそれをどうにか再評価可能な形式に変換する必要 + がある。bash-4.4 以降であれば ${ret[@]@Q} を用いれば良い。古い bash では + この形式に合わせて記録を行う。 + + 2. bash3 では dict がないので工夫が必要 + + * progcolor: ble/syntax/progcolor/eval-word を着色を跨いでキャッシュできないか + + 特に展開結果を何処かに保存しておきたい。新しい配列を用意するか、或いは hash + にして記録するか。 + + a hash にして記録すると一度評価した単語を再評価する機会が失われる。ファイル + が追加・削除された時に更新されなくなってしまう。 + + やはり、単語単位でやはりキャッシュしたい。そうすると shift にも耐えうる仕 + 組みにしたい、という事で新しい配列を用意するか、或いは既存の配列に格納す + るという方法になる。 + + b 新しい配列を追加する + + また shift 等の操作が増える。面倒である。 + + c 既存の配列に要素を追加する + + 既存の配列に格納する場合には任意の文字列を含める事ができないので、補助配 + 列にデータを格納する事にしてその添字を既存の配列に入れるという手がある。 + 特に単語に id を振っておけば今後の word に関するデータ拡張にも使う事がで + きる。 + + →これに関しては ble/syntax:bash/simple-word/eval の側でキャッシュする様に + したので、今の所はここでは対応しなくて良い気がする。当初は simple-word/eval + では非常に短期のキャッシュしかしない方向を考えていたが、ファイルシステムが + そう頻繁に変わる訳でもないので、取り敢えずは行毎にキャッシュを保持する事に + した。なので、simple-word/eval のキャッシュは単語よりも寿命が長いので単語毎 + のキャッシュは今の所は考えない事にする。 + + * 但し、やはりファイルシステムの変化に追随したいという事であれば、適当にキャッ + シュを更新する必要がある気がする。或いは、globpat を含む様な場合にのみキャッ + シュを行うというのでも良い様な気がしている。 + + * また、ここでの実装手法は例えばエラーメッセージの記録等の場合にも使えるの + ではないだろうか。という気がする。 + + * layer:syntax: 展開結果を一つの呼び出しの中でキャッシュしたい。 + 2021-01-22 * highlight: 引数が沢山あると cygwin で滅茶苦茶遅い @@ -3789,6 +3841,299 @@ bash_tips 拾ってきた物だったが、これは 2017 年の物だったので報告のあった flamingo の絵文字 (2018) は含まれていなかったのだ。 +2021-01-28 + + * highlight: グロブパターンが含まれるファイル名の着色が遅い (reported by 3ximus) [#D1449] + https://github.com/akinomyoga/ble.sh/issues/82 + + glob expansion を subshell で実行してユーザー入力があったら timeout させる + 方針について考察。timeout は必要だろうか。 + + * 或いは、もういきなり中断しても良いかもしれない。着色は次の rendering の時 + に反映させれば良いという発想。然し、その場合には再 rendering を発生させる + 為にどうにかして部分 invalidate しなければならない。或いは再描画の条件に + 着色未完の状態を含めても良いのかもしれない。着色の更新に関しては + ble/textarea#update-text-buffer を呼び出した時に実行される。実は dirty + range の有無に関わらず毎回 layer/update は呼び出される様だ。考えてみれば + region の着色等は dirty range に関係なく変化する可能性があるので、この振 + る舞いも妥当である。 + + 一般に単語着色に関してはユーザーの入力があったら中断してしまって良いので + はないだろうか。background worker の方で処理する事にすれば良い (但し、 + bash3 だと判定できないので難しい。bash3 に関しては loadable builtin が使 + えれば自前でそれを使ってしまうという手もある)。 + + * キャッシュする可能性。同じ単語が繰り返し使われている場合に処理を短縮する + 為。これは特殊な場合にしか効果が現れない。余り効果はないのではないかとい + う気がする。 + + * globpat が含まれる場合にだけ subshell 実行する? + globpat があるかどうかに関しては正規表現で判定すれば良い。 + + "**" が含まれる場合にだけ subshell 実行するという可能性も考えたが、"*" や + "?" だけでも大量のファイル名に展開される可能性もあるので、"*" だけ特別扱 + いしても仕方がない。 + + * reject: ファイル数が多いディレクトリだけで subshell 実行する? + + ディレクトリ移動はそんなに頻繁に行わない筈なのでファイル数をディレクトリ + 移動をする度に確認すれば良いのではないか。と思ったが引数に + dir1/dir2/... と指定する事もできるのでファイル数が多いディレクトリに現在 + いるかどうかという情報は余り当てにならない。 + + "/" が含まれるかどうかを事前に判定する可能性もあるが、変数展開の中に / が + 含まれる可能性もあるので、完全に判定するのは難しい。 + + * そもそも単語が単純な場合には展開の操作すら必要ないのではないか。 + + {} も quote も history expan も param も ~ もない場合。 + + [実装] + + * done: 取り敢えずユーザーからの入力がある場合には着色を中断する。サブシェ + ルの中で実行する。後で着色し直す可能性については取り敢えず考えない。 + + 取り敢えず実装してみたが、微妙。とにかく入力を続ければ応答が全くないとい + う訳ではないが、常に位置文字分だけ遅延している。何故だろうか。一文字だけ + 次の文字が来ている場合に次の文字が来ているという事を検出できていないとい + う事だろうか。うーん。ble/decode/has-input の問題であろう。 + + * done: simple-word/eval-noglob を eval ... noglob に書き換える + + * done: 以下の関数の呼び出し元で適切に stop_check を設定する様にする? + + ble/syntax:bash/simple-word/detect-separated-path + ble/syntax:bash/simple-word/evaluate-path-spec + ble/syntax:bash/simple-word/locate-filename + + 特に bash3 で stop_check を行うかどうか。bash3 で stop_check をしたとして + も常に is-stdin-ready は false になるので、唯単に今まで通りにブロックされ + るだけである。然し、そうであるならば最初から subshell を生成する必要もな + い。やはり bash3 では stop_check を省略する様に工夫する必要があるのではな + いか。と思ったが、それならば simple-word/eval の側で bash3 では + stop_check しないという様にしても良い気がする。 + →その様にする事にした。 + + 結局上記の3関数では常に stop_check を指定して、 + bash3 についての特別な取り扱いは simple-word/eval の側で実装した。 + + * done: 以下の関数について 148 を返した時の振る舞いを正しく実装する。 + + 148 を返した時に現在はエラー着色にしているが、 + そうではなくて着色せずに抜ける事にする。 + + ble/syntax:bash/simple-word/evaluate-path-spec [done] + ble/syntax/progcolor/word:default/.highlight-filename [done] + ble/syntax/progcolor/word:default [ok] + ble/syntax:bash/simple-word/locate-filename/.exists [done] + ble/syntax:bash/simple-word/locate-filename [done] + ble/syntax/progcolor/word:default [ok] + ble/syntax:bash/simple-word/detect-separated-path [done] + ble/syntax/progcolor/word:default/.detect-separated-path [done] + ble/syntax/progcolor/word:default [ok] + + 取り敢えず ble/syntax/progcolor/word:default まで行って抜ければ、 + 単語情報として何か間違った物が登録される事はない。 + + x ok: ble/decode/has-input で次の文字が来ている判定しているが、一文字分だけずれ + がある様に見える。何が起こっているのか調べる必要がある。 + + 調べてみると既に次の文字は受信している様である。 + 問題は未だ処理しきっていないのに描画が実施されているという事である。 + コールスタックを見るとちゃんと EPILOGUE から呼び出されている。 + つまり、ちゃんと文字を処理してからという事になっている筈である。 + + と此処で理由が分かった。入力した文字を表示する為に配色を計算しているのだから、 + 配色を計算している間は未だ文字が描画されないというのは道理である。 + やはり timeout を入れないと変である。 + + conditional-sync に timeout 機能も付ける事にする。 + + * done: 着色せずに抜けた場合にはその事を記録に残す (progcolor_dirty)。 + これは ble/syntax/progcolor/word:default に対して実行すれば良い。 + 或いは idle に bgworker を登録するだけでも良い? + と思ったが、それだと沢山の bgworker が生成されてしまう気がする。 + idle に bgworker を登録するとしても、 + 未着色単語がある事の情報は何処かに記録する必要がある。 + + 実は未着色範囲を管理した方が自然な実装になる気もする。 + 例えば _ble_syntax_word_async_u{min,max} 等の変数に記録する。 + bgworker ではこれを _ble_syntax_word_u{min,max} に反映させて + その上で着色を実行する…と思ったが shift 等の取り扱いがどうなるのか分からない。 + _ble_syntax_word_u{min,max} の場合にはどの様にしていただろうか。 + というより、_ble_syntax_word_u{min,max} が設定されるのはどのタイミングだろうか。 + + % うーん。ble/syntax/parse/touch-updated-word で変更している。そしてこれは + % ble/syntax/parse の中で呼び出される物である。一方で、ble/syntax/parse は + % ble-edit/content/update-syntax から単体で呼び出される事もある。という事を + % 考えると、此処で設定された _ble_syntax_word_u{min,max} は処理される事なく + % 次の ble/syntax/parse に伝播する可能性があるという事。その時に必要になる + % と考えられる shift が実行されていない。 + % + % _ble_syntax_attr_u{min,max} についても同様である。これらもちゃんと shift + % する必要がある。 + % + % →と思ったら、ble/syntax/parse/shift の中でちゃんと shift されていた。 + % _ble_syntax_word_ 及び _ble_syntax_attr_ が接頭辞になっていたのだった。 + + + * done: 着色せずに残っている部分を着色する bgworker を実装する。 + 最後まで完了した時に progcolor_dirty を clear する。 + + * done: highlight_timeout_background: bg で着色している時の timeout は長めに取る。 + + x fixed: 実際に試してみると相変わらず止まってしまう。CPUがぶんぶん回っている。 + + 調べてみると eval が stop_check なしで呼び出されている。 + stackdump してみると以下の様な呼び出しになっている。 + どうやら simple-word/eval の呼び出し元のチェックに漏れがあった様だ。 + + stackdump: + @ /home/murase/.mwg/src/ble.sh/out/lib/core-syntax.sh:27 (ble/syntax:bash/simple-word/eval) + @ /home/murase/.mwg/src/ble.sh/out/lib/core-syntax.sh:17 (ble/syntax/progcolor/eval-word) + @ /home/murase/.mwg/src/ble.sh/out/lib/core-syntax.sh:5 (ble/syntax/progcolor/word:default/.is-option-context) + @ /home/murase/.mwg/src/ble.sh/out/lib/core-syntax.sh:26 (ble/syntax/progcolor/word:default/.impl) + @ /home/murase/.mwg/src/ble.sh/out/lib/core-syntax.sh:3 (ble/syntax/progcolor/word:default) + @ /home/murase/.mwg/src/ble.sh/out/lib/core-syntax.sh:7 (ble/syntax/progcolor/default) + @ /home/murase/.mwg/src/ble.sh/out/lib/core-syntax.sh:31 (ble/syntax/progcolor) + @ /home/murase/.mwg/src/ble.sh/out/lib/core-syntax.sh:8 (ble/highlight/layer:syntax/word/.update-attributes/.proc) + @ /home/murase/.mwg/src/ble.sh/out/lib/core-syntax.sh:12 (ble/syntax/tree-enumerate-in-range) + @ /home/murase/.mwg/src/ble.sh/out/lib/core-syntax.sh:2 (ble/highlight/layer:syntax/word/.update-attributes) + @ /home/murase/.mwg/src/ble.sh/out/lib/core-syntax.sh:4 (ble/highlight/layer:syntax/update-word-table) + @ /home/murase/.mwg/src/ble.sh/out/lib/core-syntax.sh:32 (ble/highlight/layer:syntax/update) + @ /home/murase/.mwg/src/ble.sh/out/ble.sh:12 (ble/highlight/layer/update) + @ /home/murase/.mwg/src/ble.sh/out/ble.sh:9 (ble/textarea#update-text-buffer) + @ /home/murase/.mwg/src/ble.sh/out/ble.sh:75 (ble/textarea#render) + @ /home/murase/.mwg/src/ble.sh/out/ble.sh:2 (ble-edit/bind/.tail) + @ /home/murase/.mwg/src/ble.sh/out/ble.sh:17 (ble-decode/EPILOGUE) + @ /home/murase/.mwg/src/ble.sh/out/ble.sh:78 (ble-decode/.hook) + + 取り敢えずこれについては修正した。 + + x ok: 次におかしいのは、既に着色済みの筈単語も改めて毎回着色されている様な気がするという事。 + + と思ったらやはり気の所為だろうか。。。今度はちゃんと新しい単語に対しての + み処理が行われている様な気がする。と思ったが、どうも word:default 自体は + 新しい単語に対してのみ呼び出されているが、simple-word/eval は何度も呼び + 出されているという事の様である。呼び出し履歴を探ると以下の様になっている。 + + ble/syntax:bash/simple-word/eval + ble/syntax/progcolor/eval-word + ble/syntax/progcolor/word:default/.is-option-context + ble/syntax/progcolor/word:default/.impl + ble/syntax/progcolor/word:default + ble/syntax/progcolor/default + ble/syntax/progcolor + ble/highlight/layer:syntax/word/.update-attributes/.proc + ble/syntax/tree-enumerate-in-range + ble/highlight/layer:syntax/word/.update-attributes + ble/highlight/layer:syntax/update-word-table + ble/highlight/layer:syntax/update + ble/highlight/layer/update + ble/textarea#update-text-buffer + ble/textarea#render + ble-edit/bind/.tail + ble-decode/EPILOGUE + ble-decode/.hook + + うーん。分かった。ble/syntax/progcolor/word:default/.is-option-context で + 全ての引数に対して評価を行っている。ble/syntax/progcolor/eval-word で一応 + キャッシュはしているが、これは一回の着色の内部でのキャッシュであって、着 + 色を跨いだキャッシュは行っていない。これはまた別の箇所で対策するべきの気がする。 + + x fixed: 展開が timeout した時にエラー着色になっている。 + timeout の時には d で削除する事にしてみたが、 + d ではなく 0 になってしまって黒で塗潰されている。 + + どうやら #setattr は g を設定するのであって、 + wattr の指定を行える物ではない様だ。 + + * reject: 実は pathname expansion に限らずユーザー入力がある時には単語着色 + は中断して良いのではないだろうか。 + + うーん。これは様子見。早く着色が終わる物から順に着色して行かないと、一番 + 遅い着色によって他の着色がブロックされてしまう。なので、軽い物は先に処理 + するべき。何段階にも分けるのは現実的でないので、現状は重い単語だけ処理を + 後回しにする事にする。 + + x fixed: ble/util/assign の混線に関連して発生していた様々な問題 + + x "bash: 予期しないトークン `(' 周辺に構文エラーがあります" というエラー + メッセージが出る。何処かにバグが有るという事。background で実行している + 時に発生している気がする。先ずは再現性。 + + x うーん。操作していると突然 hang して暫くして emacs が起動した + + 何やら操作していたら "# reached line_limit_length=10000" に抵触して、そ + の後に emacs が起動した。2500項目の要素を含んだ "ret=(......) fdsa1 + aaaa" と言った感じの内容が入力されている。これが意味する所は何 + か。_ble_edit_str に内容が展開されているのが原因と考えられるが何が起こっ + ているのだろうか。 + + a ble-decode-char に紛れ込むとは考えにくい。 + + b terminal の echo back に引っかかっているとも考えにくい。そもそもその為 + にはそれ専用の escape sequence で囲まなければならないし、それを出力する + ような事が起こるとも思えない。 + + c _ble_edit_str を直接編集している箇所もないだろうし、 + + d 一つの可能性は ble/util/assign の混線である。然し、そうだとしても + ble/util/assign で挿入内容を決定する様な機会があるだろうか。 + + TAB は入力していなかったと思う。従って補完が関係している可能性は取り敢 + えず除外する事にする。 + + →あー。分かった。ble/util/assign の混線である。conditional-sync で + timeout した時に動かしていたプロセスを kill するのを忘れていた為に、遅 + 延して ble/util/assign の一時ファイルに様々な情報が書き込まれて変な事が + 起こっていたという事。一つ前の問題もこれに関連して発生していた現象であ + ろうと推測される。取り敢えず再びいろいろ試して問題が再現しないか確認す + る必要がある。 + + x fixed: failglob が発生する様な状況で failglob にならなくなっている。何 + 故だろうか。これは瞬間的に timeout になっている所為で、failglob で失敗 + するよりも先に timeout 142 を返して、その為に着色が無効化されているのが + 原因だった。 + + * done: glob が有効な場合でも実は最初に noglob で展開を行って、其処に + rex='[*?]|\[.*\]|[@+!]\(.*\)' が含まれている時に限り改めて + subshell で実行するという形式にしても良いのではないだろうか。 + + これに関してはわざわざ noglob で展開を行わなくても、 + 変数代入を実行すれば良いのではないだろうか。 + builtin eval -- "tmp=$word" + + * done: bash3 では globstar を一時的に off にする事も考える。 + + 然し、そうするとユーザに対して嘘の情報を提供する事になる。本来は一致する + 筈なのに failglob して赤色に着色される等の可能性。そういう事を考えるとや + はり時間がかかっても良いから着色するか、或いは着色自体を諦めるか。簡単な + 内容の場合には着色を諦めても良いのではという気がする。 + + 或いは、** が含まれる場合に限って着色を諦めるという手もある。即座に 142 + を出力する様にすれば良い。これで OK + + * done: 変数展開の中まで参照して noglob かどうかを判定する前に、 + 先に extract-parameter-names & 変数内容の復元をするべき。 + + 再び eval の構造を大きく書き換えたが取り敢えずは動作している模様である。 + 取り敢えず conditional-sync を経由した展開も動いている様子だ。OK + + x 別項目: 全く同じ内容で eval を連続して2回試すという事が頻繁に起こっている。 + これは何だろうか。やはり eval-word による呼び出しと、それから本当の着色用 + の呼び出しが混ざっているという事だろうか。これに関しては、eval の呼び出し + をキャッシュする事で対応できる気がする。 + + 但し呼び出しをキャッシュすると言っても、bash3 でどの様に対処するべきかは + 微妙である。やはり汎用の hash 辞書のインターフェイスを準備するべきかもし + れない。 + + これは調べてみた所、ble/syntax:bash/simple-word/detect-separated-path と + ble/syntax:bash/simple-word/evaluate-path-spec の二箇所で発生していれう事 + の様である。これは eval の内容をキャッシュする様にすれば解決する話である。 + 独立項目で取り扱う事にする。 + 2021-01-25 * edit: change default behavior of "C-w" and "M-w" to operate on backward words (reported by 3ximus) [#D1448] diff --git a/src/def.sh b/src/def.sh index a58e5416..1dd1fb7b 100644 --- a/src/def.sh +++ b/src/def.sh @@ -40,6 +40,7 @@ blehook/declare PRECMD blehook/declare PREEXEC blehook/declare POSTEXEC blehook/declare widget_bell +blehook/declare textarea_render_defer # deprecated function function ble-edit/prompt/print { ble/prompt/print "$@"; } diff --git a/src/edit.sh b/src/edit.sh index 960b7836..15ade24c 100644 --- a/src/edit.sh +++ b/src/edit.sh @@ -1702,7 +1702,8 @@ _ble_textarea_VARNAMES=( _ble_textarea_invalidated _ble_textarea_version _ble_textarea_caret_state - _ble_textarea_cache) + _ble_textarea_cache + _ble_textarea_render_defer) _ble_textarea_local_VARNAMES=() @@ -1739,7 +1740,7 @@ _ble_textarea_bufferName= ## @param[in,out] lc lg ## カーソル左の文字のコードと gflag を返します。 ## カーソルが先頭にある場合は、編集文字列開始位置の左(プロンプトの最後の文字)について記述します。 -## @var [ out] umin umax +## @var [in,out] umin umax ## umin,umax は再描画の必要な範囲を文字インデックスで返します。 ## ## @var[in] _ble_textmap_* @@ -2106,6 +2107,8 @@ function ble/textarea#focus { ## @param[in] opts ## leave ## bleopt prompt_rps1_transient が非空文字列の時、rps1 を消去します。 +## update +## 強制的に再描画します。例えば非同期の着色を更新する時に用います。 ## ## @var _ble_textarea_caret_state := inds ':' mark ':' mark_active ':' line_disabled ':' overwrite_mode ## ble/textarea#render で用いる変数です。 @@ -2127,7 +2130,7 @@ function ble/textarea#render { dirty=1 elif [[ $_ble_textarea_scroll != "$_ble_textarea_scroll_new" ]]; then dirty=1 - elif [[ :$opts: == *:leave:* ]]; then + elif [[ :$opts: == *:leave:* || :$opts: == *:update:* ]]; then dirty=1 fi @@ -2481,6 +2484,25 @@ function ble/textarea#clear-state { fi } +# 非同期更新 + +_ble_textarea_render_defer= +function ble/textarea#render-defer.idle { + ble/util/idle.wait-user-input + [[ $_ble_textarea_render_defer ]] || return 0 + + local ble_textarea_render_defer_running=1 + ble/util/buffer.flush >&2 + _ble_textarea_render_defer= + blehook/invoke textarea_render_defer + ble/textarea#render update + + [[ $_ble_textarea_render_defer ]] && + ble/util/idle.continue + return 0 +} +ble/function#try ble/util/idle.push-background ble/textarea#render-defer.idle + # #------------------------------------------------------------------------------ @@ -5248,7 +5270,7 @@ function ble/widget/shell-expand-line.expand-word { # 単語展開 ret=$word; [[ $ret == '~'* ]] && ret='\'$word - ble/syntax:bash/simple-word/eval-noglob "$ret" + ble/syntax:bash/simple-word/eval "$ret" noglob if [[ $word != $ret || ${#ret[@]} -ne 1 ]]; then [[ $opts == *:quote:* ]] && flags=${flags}q return 0 diff --git a/src/util.sh b/src/util.sh index f96c0ff0..0b5659b6 100644 --- a/src/util.sh +++ b/src/util.sh @@ -2515,26 +2515,50 @@ function ble/util/sleep { # ble/util/conditional-sync ## @fn ble/util/conditional-sync command [condition weight opts] +## @param[in] command +## @param[in,opt] condition +## @param[in,opt] weight +## @param[in,opt] opts +## progressive-weight +## function ble/util/conditional-sync { - local command=$1 - local cancel=${2:-'! ble/decode/has-input'} - local weight=$3; ((weight<=0&&(weight=100))) - local opts=$4 - [[ :$opts: == *:progressive-weight:* ]] && - local weight_max=$weight weight=1 + local __command=$1 + local __cancel=${2:-'! ble/decode/has-input'} + local __weight=$3; ((__weight<=0&&(__weight=100))) + local __opts=$4 + + local __timeout= __rex=':timeout=([^:]+):' + [[ :$__opts: =~ $__rex ]] && ((__timeout=BASH_REMATCH[1])) + + [[ :$__opts: == *:progressive-weight:* ]] && + local __weight_max=$__weight __weight=1 + + [[ $__timeout ]] && ((__timeout<=0)) && return 142 + builtin eval -- "$__cancel" || return 148 ( - builtin eval -- "$command" & local pid=$! + builtin eval -- "$__command" & local __pid=$! while - ble/util/msleep "$weight" - [[ :$opts: == *:progressive-weight:* ]] && - ((weight<<=1,weight>weight_max&&(weight=weight_max))) - builtin kill -0 "$pid" &>/dev/null + # check timeout + if [[ $__timeout ]]; then + if ((__timeout<=0)); then + builtin kill "$__pid" &>/dev/null + return 142 + fi + ((__weight>__timeout)) && __weight=$__timeout + ((__timeout-=__weight)) + fi + + ble/util/msleep "$__weight" + [[ :$__opts: == *:progressive-weight:* ]] && + ((__weight<<=1,__weight>__weight_max&&(__weight=__weight_max))) + builtin kill -0 "$__pid" &>/dev/null do - if ! builtin eval -- "$cancel"; then - builtin kill "$pid" &>/dev/null + if ! builtin eval -- "$__cancel"; then + builtin kill "$__pid" &>/dev/null return 148 fi done + wait "$__pid" ) }