diff --git a/ble.pp b/ble.pp index 3bfccdfc..7c4c3a75 100644 --- a/ble.pp +++ b/ble.pp @@ -1212,9 +1212,9 @@ function ble-update { #%x inc.r|@|src/canvas| #%x inc.r|@|src/history| #%x inc.r|@|src/edit| +#%x inc.r|@|lib/core-cmdspec-def| #%x inc.r|@|lib/core-syntax-def| #%x inc.r|@|lib/core-complete-def| -#%x inc.r|@|lib/core-cmdspec-def| bleopt -I #------------------------------------------------------------------------------ diff --git a/docs/ChangeLog.md b/docs/ChangeLog.md index 5057e9da..00a27148 100644 --- a/docs/ChangeLog.md +++ b/docs/ChangeLog.md @@ -84,6 +84,7 @@ - complete: requote for more compact representations on full completions `#D1700` 0000000 - complete: improve support for `declare` and `[[ ... ]]` `#D1701` 0000000 - syntax: fix completion and highlighting of `declare` with assignment arguments `#D1704` `#D1705` 0000000 + - cmdspec: refactor `{mandb => cmdspec}_opts` `#D1706` `#D1707` 0000000 ## Changes diff --git a/lib/core-cmdspec-def.sh b/lib/core-cmdspec-def.sh index 70c8500e..1c871af0 100644 --- a/lib/core-cmdspec-def.sh +++ b/lib/core-cmdspec-def.sh @@ -2,3 +2,67 @@ function ble/cmdspec/initialize { ble-import "$_ble_base/lib/core-cmdspec.sh"; } ble/is-function ble/util/idle.push && ble-import -d "$_ble_base/lib/core-cmdspec.sh" + + +## @type cmdspec_opts +## 各コマンドのコマンドライン引数解釈に関する情報を記述します。 +## コロン区切りのオプションの列で記述されます。 +## 以下の値の組み合わせで指定します。 +## +## mandb-disable-man +## mandb 構築の際に man page を参照しません。 +## +## mandb-help +## mandb 構築の際に $CMD --help の結果を解析します。 +## mandb-help=%COMMAND +## mandb 構築の際に COMMAND の実行結果を利用します。 +## mandb-help=@HELPTEXT +## mandb 構築の際に HELPTEXT を解析します。 +## mandb-help-usage +## mandb 構築を mandb-help を通して行う時に [-abc] [-a ARG] の形の使用方法 +## からオプションを抽出します。 +## +## mandb-usage +## mandb 構築の際に $CMD --usage の結果を解析します。 +## +## plus-options +## plus-options=xyzw +## "'+' CHAR" の形式のオプションを受け取る事を示します。 +## 引数を指定した場合には更に対応している plus option の集合と解釈します。 +## 例えば xyzw を指定した時、+x, +y, +z, +w に対応している事を示します。 +## +## no-options +## オプションを解釈しない事を示します。 +## stop-options-on=REX_STOP +## 指定したパターンに一致する引数より後はオプションの解釈を行わないません。 +## stop-options-unless=REX_CONT +## 指定したパターンに一致しない引数より後はオプションの解釈を行わないません。 +## stop-options-at=IWORD +## 指定した位置以降の引数ではオプションの解釈を行わない事を示します。 +## stop-options-postarg +## 通常引数の後はオプションの解釈を行わない事を示します。 +## この設定は stop-options-unless により上書きされます。 +## disable-double-hyphen +## オプション '--' 以降もオプションの解釈を行います。 +## この設定は stop-options-on により上書きされます。 +## + +builtin eval -- "${_ble_util_gdict_declare//NAME/_ble_cmdspec_opts}" + +function ble/cmdspec/opts { + local spec=$1 command; shift + for command; do + ble/gdict#set _ble_cmdspec_opts "$command" "$spec" + done +} +## @fn ble/cmdspec/opts#load command [default_value] +## @var[out] cmdspec_opts +function ble/cmdspec/opts#load { + cmdspec_opts=$2 + local ret= + if ble/gdict#get _ble_cmdspec_opts "$1" || + { [[ $1 == */*[!/] ]] && ble/gdict#get _ble_cmdspec_opts "${1##*/}"; } + then + cmdspec_opts=$ret + fi +} diff --git a/lib/core-cmdspec.sh b/lib/core-cmdspec.sh index b5fe4c4a..deaa81e2 100644 --- a/lib/core-cmdspec.sh +++ b/lib/core-cmdspec.sh @@ -1,7 +1,38 @@ # -*- mode: sh; mode: sh-bash -*- function ble/cmdspec/initialize { return 0; } -ble-import core-complete + +function ble/complete/opts/initialize { + ble/cmdspec/opts mandb-help printf + ble/cmdspec/opts mandb-disable-man:mandb-help bind + ble/cmdspec/opts mandb-disable-man:mandb-help:mandb-help-usage complete + ble/cmdspec/opts mandb-disable-man:no-options : true false + + ble/cmdspec/opts mandb-help=%'help echo':stop-options-unless='^-[neE]+$' echo + + local conditional_operators=' + -eq (NUM1 -eq NUM2) Arithmetic comparison ==. + -ne (NUM1 -ne NUM2) Arithmetic comparison !=. + -lt (NUM1 -lt NUM2) Arithmetic comparison < . + -le (NUM1 -le NUM2) Arithmetic comparison <=. + -gt (NUM1 -gt NUM2) Arithmetic comparison > . + -ge (NUM1 -ge NUM2) Arithmetic comparison >=. + -nt (FILE1 -nt FILE2) True if file1 is newer than file2 (according to modification date). + -ot (FILE1 -ot FILE2) True if file1 is older than file2. + -ef (FILE1 -ef FILE2) True if file1 is a hard link to file2.' + ble/cmdspec/opts disable-double-hyphen:mandb-help=%'help test':mandb-help=@"$conditional_operators" '[[' + + local test_operators=$conditional_operators' + -a (EXPR1 -a EXPR2) True if both expr1 AND expr2 are true. + -a (EXPR1 -o EXPR2) True if either expr1 OR expr2 is true.' + ble/cmdspec/opts disable-double-hyphen:mandb-help=%'help test':mandb-help=@"$test_operators" 'test' '[' + + ble/cmdspec/opts mandb-disable-man:mandb-help:stop-options-postarg:plus-options=aAilnrtux declare typeset local + ble/cmdspec/opts mandb-disable-man:mandb-help:stop-options-postarg local export readonly + ble/cmdspec/opts mandb-disable-man:mandb-help:stop-options-postarg alias +} +ble/complete/opts/initialize + function ble/cmdinfo/cmd:declare/chroma.wattr { local ret @@ -10,8 +41,8 @@ function ble/cmdinfo/cmd:declare/chroma.wattr { ble/progcolor/highlight-filename.wattr "$ret" "$wend" else ble/progcolor/eval-word || return "$?" - local wval=$ret + if ble/string#match "$wval" '^([_a-zA-Z][_a-zA-Z0-9]*)(\[.+\])?$'; then # ToDo: properly support quoted case local varname=${BASH_REMATCH[1]} diff --git a/lib/core-complete.sh b/lib/core-complete.sh index aaea113e..2d0b5a9f 100644 --- a/lib/core-complete.sh +++ b/lib/core-complete.sh @@ -3802,9 +3802,9 @@ function ble/complete/mandb/.generate-cache-from-man { function ble/complete/mandb:help/generate-cache { local opts=$1 local -x cfg_usage= cfg_help=1 cfg_plus= cfg_plus_generate= - [[ :$opts: == *:help-usage:* ]] && cfg_usage=1 - [[ :$opts: == *:usage:* ]] && cfg_usage=1 cfg_help= - ble/string#match ":$opts:" ':plus-option(=[^:]+)?:' && + [[ :$opts: == *:mandb-help-usage:* ]] && cfg_usage=1 + [[ :$opts: == *:mandb-usage:* ]] && cfg_usage=1 cfg_help= + ble/string#match ":$opts:" ':plus-options(=[^:]+)?:' && cfg_plus=1 cfg_plus_generate=${BASH_REMATCH[1]:1} local rex_argsep='(\[?[:=]| ?|\[)' @@ -4068,53 +4068,6 @@ function ble/complete/mandb:_parse_help/generate-cache { fi >| "$subcache" } -builtin eval -- "${_ble_util_gdict_declare//NAME/_ble_complete_mandb_opts}" -function ble/complete/mandb/opt { - local spec=$1 command; shift - for command; do - ble/gdict#set _ble_complete_mandb_opts "$command" "$spec" - done -} -function ble/complete/mandb/initialize-mandb-opts { - ble/complete/mandb/opt help printf - ble/complete/mandb/opt no-man:help bind - ble/complete/mandb/opt no-man:help:help-usage complete - - local conditional_operators=' - -eq (NUM1 -eq NUM2) Arithmetic comparison ==. - -ne (NUM1 -ne NUM2) Arithmetic comparison !=. - -lt (NUM1 -lt NUM2) Arithmetic comparison < . - -le (NUM1 -le NUM2) Arithmetic comparison <=. - -gt (NUM1 -gt NUM2) Arithmetic comparison > . - -ge (NUM1 -ge NUM2) Arithmetic comparison >=. - -nt (FILE1 -nt FILE2) True if file1 is newer than file2 (according to modification date). - -ot (FILE1 -ot FILE2) True if file1 is older than file2. - -ef (FILE1 -ef FILE2) True if file1 is a hard link to file2.' - ble/complete/mandb/opt ignore-double-hyphen:help=%'help test':help=@"$conditional_operators" '[[' - - local test_operators=$conditional_operators' - -a (EXPR1 -a EXPR2) True if both expr1 AND expr2 are true. - -a (EXPR1 -o EXPR2) True if either expr1 OR expr2 is true.' - ble/complete/mandb/opt ignore-double-hyphen:help=%'help test':help=@"$test_operators" 'test' '[' - - ble/complete/mandb/opt no-man:help:stop-after-argument:plus-option=aAilnrtux declare typeset local - ble/complete/mandb/opt no-man:help:stop-after-argument local export readonly - ble/complete/mandb/opt no-man:help:stop-after-argument alias -} -ble/complete/mandb/initialize-mandb-opts - -## @fn ble/complete/mandb/get-opts command [default_value] -## @var[out] mandb_opts -function ble/complete/mandb/get-opts { - mandb_opts=$2 - local ret= - if ble/gdict#get _ble_complete_mandb_opts "$1" || - { [[ $1 == */*[!/] ]] && ble/gdict#get _ble_complete_mandb_opts "${1##*/}"; } - then - mandb_opts=:$ret: - fi -} - ## @fn ble/complete/mandb/generate-cache cmdname ## @var[out] ret ## キャッシュファイル名を返します。 @@ -4124,10 +4077,11 @@ function ble/complete/mandb/generate-cache { local mandb_cache_dir=$_ble_base_cache/complete.mandb/${lc_messages//'/'/%} local fcache=$mandb_cache_dir/$command - local mandb_opts; ble/complete/mandb/get-opts "$command" + local cmdspec_opts; ble/cmdspec/opts#load "$command" + [[ :$cmdspec_opts: == *:no-options:* ]] && return 1 # fcache_help - if ble/opts#extract-all-optargs help "$mandb_opts" --help; then + if ble/opts#extract-all-optargs "$cmdspec_opts" mandb-help --help; then local -a helpspecs; helpspecs=("${ret[@]}") local subcache=$mandb_cache_dir/help.d/$command if ! [[ -s $subcache && $subcache -nt $_ble_base/lib/core-complete.sh ]]; then @@ -4142,12 +4096,12 @@ function ble/complete/mandb/generate-cache { ble/string#split-words helpspec "${helpspec#+}" "$command" "${helpspec[@]}" 2>&1 fi - done | ble/complete/mandb:help/generate-cache "$mandb_opts" >| "$subcache" + done | ble/complete/mandb:help/generate-cache "$cmdspec_opts" >| "$subcache" fi fi # fcache_man - if [[ :$mandb_opts: != *:no-man:* ]] && ble/bin#has "$1"; then + if [[ :$cmdspec_opts: != *:mandb-disable-man:* ]] && ble/bin#has "$1"; then local subcache=$mandb_cache_dir/man.d/$command if ! [[ -s $subcache && $subcache -nt $_ble_base/lib/core-complete.sh ]]; then ble/util/mkd "${subcache%/*}" @@ -4233,34 +4187,32 @@ function ble/complete/mandb/load-cache { ble/util/mapfile ret < "$ret" } -## @fn ble/complete/source:option/.stops-option args... +## @fn ble/complete/source:option/.is-option-context args... ## args... に "--" などのオプション解釈を停止する様な引数が含まれて ## いないか判定します。 ## ## @param[in] args... -## @var[in] mandb_opts +## @var[in] cmdspec_opts ## -function ble/complete/source:option/.stops-option { - (($#)) || return 1 - - local reject= rexreq= - [[ :$mandb_opts: != *:ignore-double-hyphen:* ]] && reject=-- - if [[ :$mandb_opts: == *:stop-after-argument:* ]]; then - rexreq='^-.+' - if ble/string#match ":$mandb_opts:" ':plus-option(=[^:]*)?:'; then - rexreq='^[-+].+' - fi +function ble/complete/source:option/.is-option-context { + #(($#)) || return 0 + + local rexrej rexreq stopat + ble/progcolor/stop-option#init "$cmdspec_opts" + if [[ $stopat ]] && ((stopat<=$#)); then + return 1 + elif [[ ! $rexrej$rexreq ]]; then + return 0 fi - [[ $reject$rexreq ]] || return 1 local word ret for word; do ble/syntax:bash/simple-word/is-simple "$word" && ble/syntax:bash/simple-word/eval "$word" noglob && - [[ $reject && $ret == $reject || $rexreq && ! ( $ret =~ $rexreq ) ]] && - return 0 + ble/progcolor/stop-option#test "$ret" && + return 1 done - return 1 + return 0 } function ble/complete/source:option { @@ -4294,12 +4246,12 @@ function ble/complete/source:option { done local -a entries; entries=("${ret[@]}") - local ret mandb_opts= + local ret cmdspec_opts= ble/syntax:bash/simple-word/is-simple "$cmd" && ble/syntax:bash/simple-word/eval "$cmd" noglob && - ble/complete/mandb/get-opts "$ret" + ble/cmdspec/opts#load "$ret" # "--" や非オプション引数など、オプション無効化条件をチェック - ble/complete/source:option/.stops-option "${prev_args[@]}" && return 1 + ble/complete/source:option/.is-option-context "${prev_args[@]}" || return 1 local entry fs=$_ble_term_FS has_desc= for entry in "${entries[@]}"; do diff --git a/lib/core-syntax.sh b/lib/core-syntax.sh index 20a24757..7d8ba880 100644 --- a/lib/core-syntax.sh +++ b/lib/core-syntax.sh @@ -6550,42 +6550,106 @@ function ble/progcolor/eval-word { return 0 } +## @fn ble/progcolor/load-cmdspec-opts +## @var[out] cmdspec_opts +function ble/progcolor/load-cmdspec-opts { + if [[ $progcolor_cmdspec_opts ]]; then + cmdspec_opts=$progcolor_cmdspec_opts + else + ble/cmdspec/opts#load "${comp_words[0]}" + progcolor_cmdspec_opts=${cmdspec_opts:-:} + fi +} + +## @fn ble/progcolor/stop-option#init cmdspec_opts +## @var[out] rexrej rexreq stopat +function ble/progcolor/stop-option#init { + rexrej='^--$' rexreq= stopat= + local cmdspec_opts=$1 + if [[ $cmdspec_opts ]]; then + # copied from ble/complete/source:option/.stops-option + if [[ :$cmdspec_opts: == *:no-options:* ]]; then + stopat=0 + return 1 + elif ble/opts#extract-first-optarg "$cmdspec_opts" stop-options-at && [[ $ret ]]; then + ((stopat=ret)) + return 1 + fi + + local ret + if ble/opts#extract-first-optarg "$cmdspec_opts" stop-options-on && [[ $ret ]]; then + rexrej=$ret + elif [[ :$cmdspec_opts: == *:disable-double-hyphen:* ]]; then + rexrej= + fi + if ble/opts#extract-first-optarg "$cmdspec_opts" stop-options-unless && [[ $ret ]]; then + rexreq=$ret + elif [[ :$cmdspec_opts: == *:stop-options-postarg:* ]]; then + rexreq='^-.+' + ble/opts#has "$cmdspec_opts" plus-options && rexreq='^[-+].+' + fi + fi +} +## @fn ble/progcolor/stop-option#test value +## @var[in] rexrej rexreq +function ble/progcolor/stop-option#test { + [[ $rexrej && $1 =~ $rexrej || $rexreq && ! ( $1 =~ $rexreq ) ]] +} + +## @fn ble/progcolor/is-option-context +## 現在の単語位置がオプションが解釈される文脈かどうかを判定します。 +## +## @var progcolor_iword +## 現在の単語の位置。 +## +## @var progcolor_optctx[0] +## 空の時には未だオプションを初期化していない事を示します。 +## 現在のコマンドについて何処までオプション停止条件を検査済みかを記録します。 +## +## @var progcolor_optctx[1] +## 負の時は常にオプションが有効である事を示す。 +## 0の時は常にオプションが無効である事を示す。 +## 正の時はその位置以前の引数でオプションが有効である事を示す。 +## +## @var progcolor_optctx[2] +## @var progcolor_optctx[3] +## @var progcolor_optctx[4] +## それぞれ抽出済みの rexrej rexreq stopat の値を記録します。 +## ble/progcolor/stop-option#init によって初期化された値のキャッシュです。 +## function ble/progcolor/is-option-context { # 既にオプション停止位置が計算済みの場合 if [[ ${progcolor_optctx[1]} ]]; then # Note: 等号は停止を引き起こした引数 -- 自体の時 (オプションとして有効) - ((progcolor_iword<=progcolor_optctx[1])) + ((progcolor_optctx[1]<0?1:(progcolor_iword<=progcolor_optctx[1]))) return $? fi - local reject rexreq + local rexrej rexreq stopat if [[ ! ${progcolor_optctx[0]} ]]; then progcolor_optctx[0]=1 - - reject=-- rexreq= - if ble/is-function ble/complete/mandb/get-opts; then - # copied from ble/complete/source:option/.stops-option - local mandb_opts; ble/complete/mandb/get-opts "${comp_words[0]}" - [[ :$mandb_opts: != *:ignore-double-hyphen:* ]] && reject=-- - if [[ :$mandb_opts: == *:stop-after-argument:* ]]; then - rexreq='^-.+' - if ble/string#match ":$mandb_opts:" ':plus-option(=[^:]*)?:'; then - rexreq='^[-+].+' - fi - fi + local cmdspec_opts + ble/progcolor/load-cmdspec-opts + ble/progcolor/stop-option#init "$cmdspec_opts" + if [[ ! $rexrej$rexreq ]]; then + progcolor_optctx[1]=${stopat:--1} + ((progcolor_optctx[1]<0?1:(progcolor_iword<=progcolor_optctx[1]))) + return $? fi - progcolor_optctx[2]=$reject + progcolor_optctx[2]=$rexrej progcolor_optctx[3]=$rexreq + progcolor_optctx[4]=$stopat else - reject=${progcolor_optctx[2]} + rexrej=${progcolor_optctx[2]} rexreq=${progcolor_optctx[3]} + stopat=${progcolor_optctx[4]} fi - [[ $reject$rexreq ]] || return 0 + [[ $stopat ]] && ((progcolor_iword>stopat)) && return 1 local iword for ((iword=progcolor_optctx[0];iword