diff --git a/docs/ChangeLog.md b/docs/ChangeLog.md index f2822d31..e0c28817 100644 --- a/docs/ChangeLog.md +++ b/docs/ChangeLog.md @@ -82,6 +82,7 @@ - menu (menu-style:desc): support multicolumns (motivated by Shahabaz-Bagwan) `#D1686` 231dc39 - term: let DECSCUSR pass through terminal multiplexers (motivated by cmplstofB) `#D1697` 0000000 - complete: requote for more compact representations on full completions `#D1700` 0000000 +- complete: improve support for `declare` and `[[ ... ]]` `#D1701` 0000000 ## Changes diff --git a/lib/core-complete.sh b/lib/core-complete.sh index 45d34058..4e731c37 100644 --- a/lib/core-complete.sh +++ b/lib/core-complete.sh @@ -3197,6 +3197,11 @@ function ble/complete/progcomp { #------------------------------------------------------------------------------ # mandb +# オプション名に現れる事を許す文字の集合 (- と + を除く) +# Exclude non-ASCII or symbols /[][()<>{}="'\''`]/ +# Note: awk の正規表現内部で使っても大丈夫な様に \ と / をエスケープしている。 +_ble_complete_option_chars='_!#$%&:;.,^~|\\?\/*a-zA-Z0-9' + # action:mandb # # DATA ... cmd FS menu_suffix FS insert_suffix FS desc @@ -3703,7 +3708,7 @@ function ble/complete/mandb/.generate-cache-from-man { gsub(/[\x00-\x1F]/, "", line); # Give up all the other control chars gsub(/^[[:space:]]*|[[:space:]]*$/, "", line); gsub(/[[:space:]]+/, " ", line); - if (line !~ /^-./) return; + if (line !~ /^[-+]./) return; n = split(line, specs, /,[[:space:]]+/); prev_optarg = ""; @@ -3713,7 +3718,7 @@ function ble/complete/mandb/.generate-cache-from-man { # Exclude non-options. # Exclude FS (\034) because it is used for separators in the cache format. - if (spec !~ /^-/ || spec ~ /\034/) { specs[i] = ""; continue; } + if (spec !~ /^[-+]/ || spec ~ /\034/) { specs[i] = ""; continue; } if (match(spec, /\[[:=]?|[:=[:space:]]/)) { option = substr(spec, 1, RSTART - 1); @@ -3729,7 +3734,7 @@ function ble/complete/mandb/.generate-cache-from-man { # Carry previous optarg if (prev_optarg ~ /[A-Z]|<.+>/) { optarg = prev_optarg; - if (option ~ /^-.$/) { + if (option ~ /^[-+].$/) { sub(/^\[=/, "[", optarg); sub(/^=/, "", optarg); sub(/^[^[:space:][]/, " &", optarg); @@ -3796,12 +3801,14 @@ function ble/complete/mandb/.generate-cache-from-man { ## @fn ble/complete/mandb:help/generate-cache [opts] function ble/complete/mandb:help/generate-cache { local opts=$1 - local -x cfg_usage= cfg_help=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(=[^:]+)?:' && + cfg_plus=1 cfg_plus_generate=${BASH_REMATCH[1]:1} local rex_argsep='(\[?[:=]| ?|\[)' - local rex_option='-(,|[^]:=[:space:],[]+)('$rex_argsep'[^-[:space:]][^[:space:]]*)?' + local rex_option='[-+](,|[^]:=[:space:],[]+)('$rex_argsep'(<[^<>]+>|\([^()]+\)|[^-[:space:]])[^[:space:]]*)?' ble/bin/awk -F "$_ble_term_FS" ' BEGIN { cfg_help = ENVIRON["cfg_help"]; @@ -3811,6 +3818,9 @@ function ble/complete/mandb:help/generate-cache { cfg_usage = ENVIRON["cfg_usage"]; g_usage_count = 0; + + cfg_plus_generate = ENVIRON["cfg_plus_generate"]; + cfg_plus = ENVIRON["cfg_plus"] cfg_plus_generate; } function split_option_optarg_suffix(optspec, _, key, suffix, optarg) { @@ -3828,14 +3838,15 @@ function ble/complete/mandb:help/generate-cache { suffix = " "; } - # Note: Exclude option names containing non-ASCII or symbols [][()<>{}="'\''`] - if (key ~ /[^-_!#$%&:;.,^~|\\?\/+*a-zA-Z0-9]/) return ""; + # Note: Exclude option names containing non-option characters + if (key ~ /[^-+'"$_ble_complete_option_chars"']/) return ""; return key FS optarg FS suffix; } function print_entry(entry, _, name) { name = entry; sub(/'"$_ble_term_FS"'.*$/, "", name); + if (name ~ /^\+/ && !cfg_plus) return; if (!g_hash[name]++) # uniq print entry; } @@ -3846,10 +3857,20 @@ function ble/complete/mandb:help/generate-cache { gsub(/[\x00-\x1F]/, ""); # Remove all the other C0 chars } + #-------------------------------------------------------------------------- + # Generate + options without descriptions + + function generate_plus(_, i, n) { + if (!cfg_plus_generate) return; + n = length(cfg_plus_generate); + for (i = 1; i <= n; i++) + print_entry("+" substr(cfg_plus_generate, i, 1) FS FS FS); + } + #-------------------------------------------------------------------------- # Extract usage [-DEI] [-f[helo] | --prefix=PATH] - function parse_usage(line, _, optspec, optspec1, option, optarg, n, i) { + function parse_usage(line, _, optspec, optspec1, option, optarg, n, i, o) { while (match(line, /\[[[:space:]]*([^][]|\[[^][]*\])+[[:space:]]*\]/)) { optspec = substr(line, RSTART + 1, RLENGTH - 2); line = substr(line, RSTART + RLENGTH); @@ -3861,7 +3882,7 @@ function ble/complete/mandb:help/generate-cache { gsub(/^[[:space:]]+|[[:space:]]+$/, "", optspec1); # optspec1: "--option optarg", "-f[optarg]", "-xzvf", etc. - if (match(optspec1, /^-[^]:=[:space:][]+/)) { + if (match(optspec1, /^[-+][^]:=[:space:][]+/)) { option = substr(optspec1, RSTART, RLENGTH); optarg = substr(optspec1, RSTART + RLENGTH); n = RLENGTH; @@ -3869,8 +3890,9 @@ function ble/complete/mandb:help/generate-cache { if ((keyinfo = split_option_optarg_suffix(optspec1)) != "") g_usage[g_usage_count++] = keyinfo; } else { + o = substr(option, 1, 1); for (i = 2; i <= n; i++) - if ((keyinfo = split_option_optarg_suffix("-" substr(option, i, 1) optarg)) != "") + if ((keyinfo = split_option_optarg_suffix(o substr(option, i, 1) optarg)) != "") g_usage[g_usage_count++] = keyinfo; } } @@ -3940,7 +3962,7 @@ function ble/complete/mandb:help/generate-cache { sub(/^[[:space:]]+/, "", optarg); if (optarg !~ /[A-Z]|<.+>/) optarg = ""; } else if (optarg != ""){ - if (keys[i] ~ /^-.$/) { + if (keys[i] ~ /^[-+].$/) { optarg2 = optarg; sub(/^\[=/, "[", optarg2); sub(/^=/, "", optarg2); @@ -3993,6 +4015,7 @@ function ble/complete/mandb:help/generate-cache { END { flush_data(); print_usage(); + generate_plus(); } ' | ble/bin/sort -t "$_ble_term_FS" -k 1 } @@ -4046,18 +4069,49 @@ function ble/complete/mandb:_parse_help/generate-cache { } builtin eval -- "${_ble_util_gdict_declare//NAME/_ble_complete_mandb_opts}" -ble/gdict#set _ble_complete_mandb_opts printf help -ble/gdict#set _ble_complete_mandb_opts bind help -ble/gdict#set _ble_complete_mandb_opts complete help:help-usage -ble/gdict#set _ble_complete_mandb_opts '[' 'help=%help test' -ble/gdict#set _ble_complete_mandb_opts 'test' 'help=%help test' +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 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 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/initialize-mandb-opts ## @fn ble/complete/mandb/get-opts command -## @var[out] +## @var[out] mandb_opts function ble/complete/mandb/get-opts { - local ret - ble/gdict#get _ble_complete_mandb_opts "$1" - mandb_opts=:$ret: + 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 @@ -4072,19 +4126,22 @@ function ble/complete/mandb/generate-cache { local mandb_opts; ble/complete/mandb/get-opts "$command" # fcache_help - if local rex=':help(=([^:]*))?:'; [[ :$mandb_opts: =~ $rex ]]; then + if ble/opts#extract-all-optargs help "$mandb_opts" --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 ble/util/mkd "${subcache%/*}" - local options=${BASH_REMATCH[2]:---help} - if [[ $options == %* ]]; then - builtin eval -- "${options:1}" - elif [[ $options == @* ]]; then - ble/util/print "${options:1}" - else - ble/string#split-words options "${options#+}" - "$command" "${options[@]}" 2>&1 - fi | ble/complete/mandb:help/generate-cache "$mandb_opts" >| "$subcache" + local helpspec + for helpspec in "${helpspecs[@]}"; do + if [[ $helpspec == %* ]]; then + builtin eval -- "${helpspec:1}" + elif [[ $helpspec == @* ]]; then + ble/util/print "${helpspec:1}" + else + ble/string#split-words helpspec "${helpspec#+}" + "$command" "${helpspec[@]}" 2>&1 + fi + done | ble/complete/mandb:help/generate-cache "$mandb_opts" >| "$subcache" fi fi @@ -4109,24 +4166,61 @@ function ble/complete/mandb/generate-cache { done if [[ $update ]]; then - if ((${#subcaches[@]}==1)); then - ble/bin/cp -f "${subcaches[0]}" "$fcache" - else - ble/bin/awk -F "$_ble_term_FS" ' - $4 == "" { - nodesc_entry[nodesc_count] = $0; + local fs=$_ble_term_FS + ble/bin/awk -F "$_ble_term_FS" ' + $4 == "" { + if ($1 ~ /^\+/) { + plus_name[plus_count] = $1; + plus_entry[plus_count] = $0; + plus_count++; + } else { nodesc_name[nodesc_count] = $1; + nodesc_entry[nodesc_count] = $0; nodesc_count++; - next; } - !hash[$1]++ { print; } - END { - for (i = 0; i < nodesc_count; i++) - if (!hash[nodesc_name[i]]++) - print nodesc_entry[i]; + next; + } + !hash[$1] { hash[$1] = $0; print; } + + END { + # minus options + for (i = 0; i < nodesc_count; i++) { + if (!hash[nodesc_name[i]]) { + hash[nodesc_name[i]] = nodesc_entry[i]; + print nodesc_entry[i]; + } } - ' "${subcaches[@]}" >| "$fcache" - fi + + # plus options + for (i = 0; i < plus_count; i++) { + name = plus_name[i]; + if (hash[name]) continue; + + split(plus_entry[i], record, FS); + optarg = record[2]; + suffix = record[3]; + desc = ""; + + mname = name; + sub(/^\+/, "-", mname); + if (hash[mname]) { + if (!optarg) { + split(hash[mname], record, FS); + optarg = record[2]; + suffix = record[3]; + } + + desc = hash[mname]; + sub(/^[^'$fs']*'$fs'[^'$fs']*'$fs'[^'$fs']*'$fs'/, "", desc); + if (desc) desc = "\033[1mReverse[\033[m " desc " \033[;1m]\033[m"; + } + + if (!desc) desc = "reverse of \033[4m" mname "\033[m"; + hash[name] = name FS optarg FS suffix FS desc; + print hash[name]; + } + } + ' "${subcaches[@]}" >| "$fcache" fi ret=$fcache @@ -4138,6 +4232,92 @@ function ble/complete/mandb/load-cache { ble/util/mapfile ret < "$ret" } +## @fn ble/complete/source:option/.stops-option args... +## args... に "--" などのオプション解釈を停止する様な引数が含まれて +## いないか判定します。 +## +## @param[in] args... +## @var[in] mandb_opts +## +function ble/complete/source:option/.stops-option { + (($#)) || return 1 + + local reject= rex_req= + [[ :$mandb_opts: != *:ignore-double-hyphen:* ]] && reject=-- + if [[ :$mandb_opts: == *:stop-after-argument:* ]]; then + rex_req='^-.+' + if ble/string#match ":$mandb_opts:" ':plus-option(=[^:]+)?:'; then + if [[ ${BASH_REMATCH[1]} ]]; then + [[ ${BASH_REMATCH[1]:1} ]] && + rex_req='^-.+|^\+['${BASH_REMATCH[1]:1}']' + else + rex_req='^[-+].+' + fi + fi + fi + [[ $reject$rex_req ]] || 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 || $rex_req && ! ( $ret =~ $rex_req ) ]] && + return 0 + done + return 1 +} + +function ble/complete/source:option { + # 空文字列もしくは /^[-+].*/ の時にだけ候補生成 (曖昧補完で最初の /^[-+]/ は補わない) + local rex='^-[-+'$_ble_complete_option_chars']*$|^\+[_'$_ble_complete_option_chars']*$' + [[ ! $COMPV || $COMPV =~ $rex ]] || return 0 + + local COMPS=$COMPS COMPV=$COMPV + ble/complete/source/reduce-compv-for-ambiguous-match + [[ :$comp_type: == *:[maA]:* ]] && local COMP2=$COMP1 + + local comp_words comp_line comp_point comp_cword + ble/syntax:bash/extract-command "$COMP2" || return 1 + + local -a prev_args=() + ((comp_cword>=1)) && prev_args=("${comp_words[@]:1:comp_cword-1}") + + local cmd=${comp_words[0]} + local alias_checked=' ' + while local ret; ! ble/complete/mandb/load-cache "$cmd"; do + alias_checked=$alias_checked$cmd' ' + ble/util/expand-alias "$cmd" + local words; ble/string#split-words ret "$ret"; words=("${ret[@]}") + + # 変数代入は読み飛ばし + local iword=0 rex='^[_a-zA-Z][_a-zA-Z0-9]*\+?=' + while [[ ${words[iword]} =~ $rex ]]; do ((iword++)); done + [[ ${words[iword]} && $alias_checked != *" ${words[iword]} "* ]] || return 1 + prev_args=("${words[@]:iword+1}" "${prev_args[@]}") + cmd=${words[iret]} + done + local -a entries; entries=("${ret[@]}") + + local ret mandb_opts= + ble/syntax:bash/simple-word/is-simple "$cmd" && + ble/syntax:bash/simple-word/eval "$cmd" noglob && + ble/complete/mandb/get-opts "$ret" + # "--" や非オプション引数など、オプション無効化条件をチェック + ble/complete/source:option/.stops-option "${prev_args[@]}" && return 1 + + local entry fs=$_ble_term_FS has_desc= + for entry in "${entries[@]}"; do + ((cand_iloop++%bleopt_complete_polling_cycle==0)) && + ble/complete/check-cancel && return 148 + local CAND=${entry%%$_ble_term_FS*} + [[ $CAND == "$COMPV"* ]] || continue + ble/complete/cand/yield mandb "$CAND" "$entry" + [[ $entry == *"$fs"*"$fs"*"$fs"?* ]] && has_desc=1 + done + + [[ $has_desc ]] && bleopt complete_menu_style=desc +} + #------------------------------------------------------------------------------ # source:argument @@ -4209,60 +4389,6 @@ function ble/complete/source:argument/.generate-user-defined-completion { fi } -function ble/complete/source:argument/.contains-literal-option { - local word ret - for word; do - ble/syntax:bash/simple-word/is-simple "$word" && - ble/syntax:bash/simple-word/eval "$word" noglob && - [[ $ret == -- ]] && - return 0 - done - return 1 -} - -function ble/complete/source:argument/.generate-from-mandb { - local COMPS=$COMPS COMPV=$COMPV - ble/complete/source/reduce-compv-for-ambiguous-match - [[ :$comp_type: == *:[maA]:* ]] && local COMP2=$COMP1 - - local comp_words comp_line comp_point comp_cword - ble/syntax:bash/extract-command "$COMP2" || return 1 - - # 現在の単語よりも前に -- がある場合にはオプションを候補として生成しない。 - ((comp_cword>=1)) && - ble/complete/source:argument/.contains-literal-option "${comp_words[@]:1:comp_cword-1}" && - return 1 - - local old_cand_count=$cand_count - - local cmd=${comp_words[0]} - local alias_checked=' ' - while local ret; ! ble/complete/mandb/load-cache "$cmd"; do - alias_checked=$alias_checked$cmd' ' - ble/util/expand-alias "$cmd" - local words; ble/string#split-words ret "$ret"; words=("${ret[@]}") - - # 変数代入は読み飛ばし - local iword=0 rex='^[_a-zA-Z][_a-zA-Z0-9]*\+?=' - while [[ ${words[iword]} =~ $rex ]]; do ((iword++)); done - [[ ${words[iword]} && $alias_checked != *" ${words[iword]} "* ]] || return 1 - ble/complete/source:argument/.contains-literal-option "${words[@]:iword+1}" && return 1 - cmd=${words[iret]} - done - - local entry fs=$_ble_term_FS has_desc= - for entry in "${ret[@]}"; do - ((cand_iloop++%bleopt_complete_polling_cycle==0)) && - ble/complete/check-cancel && return 148 - local CAND=${entry%%$_ble_term_FS*} - [[ $CAND == "$COMPV"* ]] || continue - ble/complete/cand/yield mandb "$CAND" "$entry" - [[ $entry == *"$fs"*"$fs"*"$fs"?* ]] && has_desc=1 - done - - [[ $has_desc ]] && bleopt complete_menu_style=desc -} - function ble/complete/source:argument { local comp_opts=: @@ -4284,10 +4410,8 @@ function ble/complete/source:argument { ((ext==148||cand_count>old_cand_count)) && return "$ext" # "-option" の時は complete options based on mandb - if local rex='^-[-_a-zA-Z0-9]*$'; [[ ! $COMPV || $COMPV =~ $rex ]]; then - ble/complete/source:argument/.generate-from-mandb; local ext=$? - ((ext==148||cand_count>old_cand_count&&${#COMPV})) && return "$ext" - fi + ble/complete/source:option; local ext=$? + ((ext==148||cand_count>old_cand_count&&${#COMPV})) && return "$ext" # 候補が見付からない場合 (または曖昧補完で COMPV に / が含まれる場合) if [[ $comp_opts == *:dirnames:* ]]; then diff --git a/lib/core-syntax.sh b/lib/core-syntax.sh index c2a957d6..1ec9fd6d 100644 --- a/lib/core-syntax.sh +++ b/lib/core-syntax.sh @@ -5260,12 +5260,12 @@ _ble_syntax_bash_complete_check_prefix[CTX_CARGQ1]='inside-argument argument' _ble_syntax_bash_complete_check_prefix[CTX_CPATI]='inside-argument argument' _ble_syntax_bash_complete_check_prefix[CTX_CPATQ]='inside-argument argument' _ble_syntax_bash_complete_check_prefix[CTX_COARGI]='inside-argument variable command' -_ble_syntax_bash_complete_check_prefix[CTX_VALI]='inside-argument file' -_ble_syntax_bash_complete_check_prefix[CTX_VALQ]='inside-argument file' -_ble_syntax_bash_complete_check_prefix[CTX_CONDI]='inside-argument file' -_ble_syntax_bash_complete_check_prefix[CTX_CONDQ]='inside-argument file' -_ble_syntax_bash_complete_check_prefix[CTX_ARGVI]='inside-argument variable:=' -_ble_syntax_bash_complete_check_prefix[CTX_ARGEI]='inside-argument variable:= command:D file' +_ble_syntax_bash_complete_check_prefix[CTX_VALI]='inside-argument sabbrev file' +_ble_syntax_bash_complete_check_prefix[CTX_VALQ]='inside-argument sabbrev file' +_ble_syntax_bash_complete_check_prefix[CTX_CONDI]='inside-argument sabbrev file option' +_ble_syntax_bash_complete_check_prefix[CTX_CONDQ]='inside-argument sabbrev file' +_ble_syntax_bash_complete_check_prefix[CTX_ARGVI]='inside-argument sabbrev variable:=' +_ble_syntax_bash_complete_check_prefix[CTX_ARGEI]='inside-argument command:D variable:= file' function ble/syntax/completion-context/.check-prefix/ctx:inside-argument { if ((wlen>=0)); then local source @@ -5363,13 +5363,15 @@ function ble/syntax/completion-context/.check-prefix/ctx:next-argument { # Note: variable:w でも variable:= でもなく variable にしているのは、 # coproc の後は変数名が来ても "変数代入 " か "coproc 配列名" # か分からないので、取り敢えず何も挿入しない様にする為。 - source=(variable command) + source=(command variable) elif ((ctx==CTX_ARGVX)); then - source=(variable:=) + source=(sabbrev variable:= option) elif ((ctx==CTX_ARGEX)); then - source=(variable:= command:D file) + source=(command:D variable:= file) + elif ((ctx==CTX_CONDX)); then + source=(sabbrev file option) else - source=(file) + source=(sabbrev file) fi local word=${text:istat:index-istat} @@ -5378,14 +5380,14 @@ function ble/syntax/completion-context/.check-prefix/ctx:next-argument { local src for src in "${source[@]}"; do ble/syntax/completion-context/.add "$src" "$istat" - if [[ $src != argument ]]; then - local rex="^([^'\"\$\\]|\\.)*=" - if [[ $word =~ $rex ]]; then - word=${word:${#BASH_REMATCH}} - ble/syntax/completion-context/.add "$src" $((index-${#word})) - fi - fi done + + # 引数の途中に unquoted '=' がある場合 + local rex="^([^'\"\$\\]|\\.)*=" + if [[ $word =~ $rex ]]; then + word=${word:${#BASH_REMATCH}} + ble/syntax/completion-context/.add rhs $((index-${#word})) + fi elif ble/syntax/completion-context/.check-prefix/.test-redirection "$word"; then true elif [[ $word =~ ^$_ble_syntax_bash_RexSpaces$ ]]; then @@ -5723,21 +5725,41 @@ function ble/syntax/completion-context/.check-here { elif ((ctx==CTX_FARGX1||ctx==CTX_SARGX1)); then ble/syntax/completion-context/.add variable:w "$index" ble/syntax/completion-context/.add sabbrev "$index" + elif ((ctx==CTX_ARGVX)); then + # declare @ + ble/syntax/completion-context/.add variable:= "$index" + ble/syntax/completion-context/.add option "$index" + ble/syntax/completion-context/.add sabbrev "$index" + elif ((ctx==CTX_ARGEX)); then + # eval @, eval echo @ + ble/syntax/completion-context/.add variable:= "$index" + ble/syntax/completion-context/.add command:D "$index" + ble/syntax/completion-context/.add file "$index" elif ((ctx==CTX_CARGX2)); then + # case a @ ble/syntax/completion-context/.add wordlist:-rs:'in' "$index" elif ((ctx==CTX_FARGX2)); then + # for a @ ble/syntax/completion-context/.add wordlist:-rs:'in:do' "$index" elif ((ctx==CTX_TARGX1)); then + # time @ local words='-p' ((_ble_bash>=50100)) && words='-p':'--' ble/syntax/completion-context/.add command "$index" ble/syntax/completion-context/.add wordlist:--:"$words" "$index" elif ((ctx==CTX_TARGX2)); then + # time -p @ ble/syntax/completion-context/.add command "$index" ble/syntax/completion-context/.add wordlist:--:'--' "$index" elif ((ctx==CTX_COARGX)); then + # coproc @ ble/syntax/completion-context/.add variable:w "$index" ble/syntax/completion-context/.add command "$index" + elif ((ctx==CTX_CONDX)); then + # [[ @ + ble/syntax/completion-context/.add sabbrev "$index" + ble/syntax/completion-context/.add option "$index" + ble/syntax/completion-context/.add file "$index" elif ((ctx==CTX_CPATI||ctx==CTX_RDRF||ctx==CTX_RDRS)); then ble/syntax/completion-context/.add file "$index" elif ((ctx==CTX_RDRD)); then diff --git a/note.txt b/note.txt index 8303b640..d2751baa 100644 --- a/note.txt +++ b/note.txt @@ -1621,6 +1621,18 @@ bash_tips * PROMPT_COMMAND / trap DEBUG で問題が起こる? (found by rashad-moves) https://github.com/rashad-moves/HomeConfigurationFiles/commit/efbac4153fd5021f1bc00d42c618fd9d6f4090b9 + * bash-completion: curl --http0, --http1, --proxy1 等存在しないオプションが生成されている + + * complete (source:rhs): 変数名依存の補完に対応しても良いのでは。 + + * complete: ARGEX (eval 文脈) の補完 + + 現在は "variable:=, command:D, file" で生成しているが、本来は一番先頭の引数 + を元にして argument (including progcomp, etc.) を呼び出すべきである。 + + * complete: [[ の中の文法も考慮した補完。これは [[ の中の文法にちゃんと対応し + た後で考える事の気がする。 + 2021-12-06 * mandb: コマンドの名称を抽出して保持して置けば binary の呼び出し時に使える。 @@ -1662,7 +1674,7 @@ bash_tips 取り敢えず --help 対応が終われば、変数名の補完はできないにしても、オプショ ンの表示ぐらいはできる様になる。printf の場合には最初の引数には '' を指定す - るのが良い気がする。 + るのが良い気がする → 取り敢えずオプションは表示される様になった。 * bash-completion: better support for test, [ https://github.com/akinomyoga/ble.sh/issues/158 @@ -1685,28 +1697,6 @@ bash_tips 点で候補を再生成するべきの気がする。或いは - から始まる候補も追加する? 追加 する場合には以前にあった物と重複する候補を削除しなければならない事に注意する。 - * complete: [[ 及び declare の user-customization を許可? - https://github.com/akinomyoga/ble.sh/issues/155 - https://github.com/akinomyoga/ble.sh/issues/157 - - 或いは自前で実装しても良い - - うーん。declare に関しては a=(....) 等が含まれる場合の取り扱いが謎だし、 - 自前で実装する方が良いのではないかという気もする。 - - 一方で [[ についてはユーザーが指定できる様にする? と思ったが、それでも今度 - は [[ (a == b && c== d)||x == y ]] 等の様な入れ子構造があった時に、ユーザー - に渡すコマンドラインを構築するのが大変である (実は現状の実装だとそんなに難 - しくないかもしれないが、此処は将来的に改修する予定なので現段階で下手にユー - ザーインターフェイスを提供すると、将来弄る時に面倒な事になる)。また'[[' '(' - 'a' '==' 'b' '&&' ... という具合に分割するのが自然だと思われるが、一方で勝 - 手に空白を挿入しても良いのかだとか色々よく分からない (まあこれは実装を詳し - く観察して調整すれば済む話ではある)。 - - うーん。結局、どちらも自前で実装した方が良い様に思われる。これは仕方がない - ので、取り敢えず自前の実装を提供する事にして customization については諦めて - 貰う事にする。 - * syntax: for $(echo hello) に対しては着色しない。もしくはエラーとする。 これは heredoc 単語と同様の問題である。因みに heredoc の単語に $() を含めた @@ -5628,6 +5618,69 @@ bash_tips 2021-12-11 + * complete: [[ 及び declare の補完 (requested by EmilySeville7cfg) [#D1701] + https://github.com/akinomyoga/ble.sh/issues/155 + https://github.com/akinomyoga/ble.sh/issues/157 + + | user-customization を許可? 或いは自前で実装しても良い + | + | うーん。declare に関しては a=(....) 等が含まれる場合の取り扱いが謎だし、 + | 自前で実装する方が良いのではないかという気もする。 + | + | 一方で [[ についてはユーザーが指定できる様にする? と思ったが、それでも今度 + | は [[ (a == b && c== d)||x == y ]] 等の様な入れ子構造があった時に、ユーザー + | に渡すコマンドラインを構築するのが大変である (実は現状の実装だとそんなに難 + | しくないかもしれないが、此処は将来的に改修する予定なので現段階で下手にユー + | ザーインターフェイスを提供すると、将来弄る時に面倒な事になる)。また'[[' '(' + | 'a' '==' 'b' '&&' ... という具合に分割するのが自然だと思われるが、一方で勝 + | 手に空白を挿入しても良いのかだとか色々よく分からない (まあこれは実装を詳し + | く観察して調整すれば済む話ではある)。 + | + | うーん。結局、どちらも自前で実装した方が良い様に思われる。これは仕方がない + | ので、取り敢えず自前の実装を提供する事にして customization については諦めて + | 貰う事にする。 + + 将来的に自前で実装する事にする。 + + 取り敢えず declare 系統は簡単な気がするので先に実装することにする。 + + * done: 現在 source:option に於いて ["--" 以降のオプションを解釈しない] や + [非オプションよりも後のオプションを解釈しない] などの各コマンドの性質はほぼ + ハードコードされている。これを外部から指定できる様にする。 + + * ok: ble/complete/mandb/get-opts ... alias 展開もする? → refactor して、 + alias 展開なども全て処理して mandb が見つかった時点で初めて get-opts を呼 + び出す様に変更した。 + + * done: 更に $_ble_complete_option_chars もちゃんと様々の箇所で使う様にする。 + →これは元々より緩いのを部分的に厳しくしているだけなのでそんなに気にしな + くて良い様に思われる。 + + x fixed: "declare [TAB]" で補完が開始されない。 + + * done: +o に関しては一番最後の集計時に説明を適用に生成する。 + + * done: mandb: 現在 + で始まるオプションは積極的に抽出していない。これにも対応したい。 + + * ble/complete/mandb:help/generate-cache (--help|--usage 抽出用) については対応した。 + + * generate-from-man は未だ → 対応した。微妙な修正しかしていないが多分これ + で大丈夫。 + + x "top -[TAB]" が正しく動作しない。 + + 取り敢えず declare については大体完了した。 + + [[ に関しては文法も考慮したより正しい補完をすぐに実装するのは難しいが、取り + 敢えずオプションだけは生成できる様にするのが良い。 + + * 文脈だけでも確認する → 部分的には対応していたみたいである。ファイル名を + 補完していた。其処に source:option を追加する。 + * それから空文字列からの補完の設定が欠けていたのでそれも追加する。 + * mandb_opts に複数の help=... を記述できる様にする。 + * オプション抽出で -a, --file=<...> や -b (this is test) 等の形式の物にも対 + 応した。 + * complete: 沢山 quote が発生する場合には '' で囲むべきなのではないか [#D1700] 然しこれだと、例えば 'a b c' と 'alpha' というファイルがあった時に前者は 'a diff --git a/src/util.sh b/src/util.sh index 69b421ee..22f41f9d 100644 --- a/src/util.sh +++ b/src/util.sh @@ -1393,6 +1393,38 @@ function ble/path#contains { builtin eval "[[ :\${$1}: == *:\"\$2\":* ]]" } +## @fn ble/opts#extract-first-optarg key opts +function ble/opts#extract-first-optarg { + ret=() + local rex=':'$1'(=[^:]*)?:' + [[ :$2: =~ $rex ]] || return 1 + [[ ${BASH_REMATCH[1]} ]] && ret=${BASH_REMATCH[1]:1} + return 0 +} +## @fn ble/opts#extract-last-optarg key opts +function ble/opts#extract-last-optarg { + ret=() + local rex='.*:'$1'(=[^:]*)?:' + [[ :$2: =~ $rex ]] || return 1 + [[ ${BASH_REMATCH[1]} ]] && ret=${BASH_REMATCH[1]:1} + return 0 +} +## @fn ble/opts#extract-all-optargs key opts [default_value] +function ble/opts#extract-all-optargs { + ret=() + local value=:$2: rex=':'$1'(=[^:]*)?(:.*)$' count=0 + while [[ $value =~ $rex ]]; do + ((count++)) + if [[ ${BASH_REMATCH[1]} ]]; then + ble/array#push ret "${BASH_REMATCH[1]:1}" + elif [[ ${3+set} ]]; then + ble/array#push ret "$3" + fi + value=${BASH_REMATCH[2]} + done + ((count)) +} + if ((_ble_bash>=40000)); then _ble_util_set_declare=(declare -A NAME) function ble/set#add { builtin eval -- "$1[x\$2]=1"; }