From b91b8bc8a8d44148095568a1cd2bde897724e8dd Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Thu, 16 Mar 2023 22:38:44 +0900 Subject: [PATCH] menu-complete: support selection by index --- blerc.template | 18 ++-- docs/ChangeLog.md | 1 + keymap/vi.sh | 59 +++++++----- lib/core-complete-def.sh | 8 +- lib/core-complete.sh | 187 +++++++++++++++++++++++++++++++-------- note.txt | 77 +++++++++++++--- src/edit.sh | 11 ++- 7 files changed, 281 insertions(+), 80 deletions(-) diff --git a/blerc.template b/blerc.template index 7ef1d52f..8572d781 100644 --- a/blerc.template +++ b/blerc.template @@ -688,11 +688,19 @@ #bleopt complete_menu_maxlines=10 -## The following setting specifies the prefix for complete_menu_style=linewise. -## For example, the candidate number can be shown by setting the value "%d". -## ANSI escape sequences can be used. - -#bleopt menu_linewise_prefix= +## The following settings specify the prefix of the menu items. "menu_prefix" +## specifies the default prefix for any menu-style. +## "menu_{align,desc,linewise,dense}_prefix" specify the prefixes in the +## respective menu-styles. The value is specified by a printf format, where +## the first argument is the index of the candidate. ANSI escape sequences can +## also be used. For example, the candidate index can be shown by setting the +## value '%d '. The default value is empty. + +#bleopt menu_align= +#bleopt menu_align_prefix='\e[1m%d:\e[m ' +#bleopt menu_desc_prefix='\e[1m%d.\e[m ' +#bleopt menu_linewise_prefix='\e[1;36m%d:\e[m ' +#bleopt menu_dense_prefix='\e[1;32m>\e[m ' ## The following setting specifies the minimum column width for the multicolumn diff --git a/docs/ChangeLog.md b/docs/ChangeLog.md index 35df4329..47ba2393 100644 --- a/docs/ChangeLog.md +++ b/docs/ChangeLog.md @@ -140,6 +140,7 @@ - sabbrev: support inline and linewise sabbre with `ble-sabbrev -il` `#D2012` 56208534 - complete: add `bleopt complete_source_sabbrev_{opts,ignore}` (motivated by mozirilla213) `#D2013` f95eb0cc `#D2016` 45c76746 - util.bgproc: separate `ble/util/bgproc` from `histdb` (motivated by bkerin) `#D2017` 7803305f +- menu-complete: support selection by index (requested by bkerin) `#D2023` xxxxxxxx ## Changes diff --git a/keymap/vi.sh b/keymap/vi.sh index 223b4145..87c8eab8 100644 --- a/keymap/vi.sh +++ b/keymap/vi.sh @@ -7891,29 +7891,28 @@ function ble-decode/keymap:vi_imap/define { ble-bind -f 'SP' 'magic-space' ble-bind -f '/' 'magic-slash' - # ble-bind -f 'M-^' 'history-expand-line' - - # ble-bind -f 'C-c' 'discard-line' - ble-bind -f 'C-j' 'accept-line' - ble-bind -f 'C-RET' 'accept-line' - ble-bind -f 'C-m' 'accept-single-line-or-newline' - ble-bind -f 'RET' 'accept-single-line-or-newline' - # ble-bind -f 'C-o' 'accept-and-next' - # ble-bind -f 'M-#' 'insert-comment' - ble-bind -f 'C-x C-e' 'edit-and-execute-command' - # ble-bind -f 'M-C-e' 'shell-expand-line' - # ble-bind -f 'M-&' 'tilde-expand' - ble-bind -f 'C-g' 'bell' - ble-bind -f 'C-x C-g' 'bell' - # ble-bind -f 'C-M-g' 'bell' - - ble-bind -f 'C-l' 'clear-screen' - # ble-bind -f 'C-M-l' 'redraw-line' - - ble-bind -f 'f1' 'command-help' - ble-bind -f 'C-x C-v' 'display-shell-version' - ble-bind -c 'C-z' 'fg' - # ble-bind -c 'M-z' 'fg' + + # ble-bind -f 'C-c' 'discard-line' + ble-bind -f 'C-j' 'accept-line' + ble-bind -f 'C-RET' 'accept-line' + ble-bind -f 'C-m' 'accept-single-line-or-newline' + ble-bind -f 'RET' 'accept-single-line-or-newline' + # ble-bind -f 'C-o' 'accept-and-next' + ble-bind -f 'C-x C-e' 'edit-and-execute-command' + ble-bind -f 'C-g' 'bell' + ble-bind -f 'C-x C-g' 'bell' + + ble-bind -f 'C-l' 'clear-screen' + + ble-bind -f 'f1' 'command-help' + ble-bind -f 'C-x C-v' 'display-shell-version' + ble-bind -c 'C-z' 'fg' + + # args + local key + for key in {,C-}{0..9}; do + ble-bind -f "$key" 'append-arg' + done #---------------------------------------------------------------------------- # vi_imap modifications @@ -8043,6 +8042,20 @@ function ble-decode/keymap:vi_imap/define-meta-bindings { ble-bind -f 'M-g' 'complete context=glob' ble-bind -f 'M-C-i' 'complete context=dynamic-history' ble-bind -f 'M-TAB' 'complete context=dynamic-history' + + #---------------------------------------------------------------------------- + # from ble-decode/keymap:safe/bind-arg + + ble-bind -f 'M-0' 'append-arg' + ble-bind -f 'M-1' 'append-arg' + ble-bind -f 'M-2' 'append-arg' + ble-bind -f 'M-3' 'append-arg' + ble-bind -f 'M-4' 'append-arg' + ble-bind -f 'M-5' 'append-arg' + ble-bind -f 'M-6' 'append-arg' + ble-bind -f 'M-7' 'append-arg' + ble-bind -f 'M-8' 'append-arg' + ble-bind -f 'M-9' 'append-arg' } #------------------------------------------------------------------------------ diff --git a/lib/core-complete-def.sh b/lib/core-complete-def.sh index c41c0b72..a455d000 100644 --- a/lib/core-complete-def.sh +++ b/lib/core-complete-def.sh @@ -82,8 +82,6 @@ ble/util/autoload "$_ble_base/lib/core-complete.sh" \ ble/complete/menu-style:linewise/construct-page \ ble/complete/menu-style:desc{,-raw}/construct-page -bleopt/declare -v menu_linewise_prefix '' -bleopt/declare -v menu_desc_multicolumn_width 65 bleopt/declare -v complete_menu_complete 1 bleopt/declare -v complete_menu_complete_opts 'insert-selection' bleopt/declare -v complete_menu_filter 1 @@ -101,9 +99,15 @@ function ble/complete/.init-bind-readline-variables { } ble/complete/.init-bind-readline-variables +bleopt/declare -v menu_prefix '' +bleopt/declare -v menu_align_prefix '' bleopt/declare -n menu_align_min 4 bleopt/declare -n menu_align_max 20 bleopt/declare -o complete_menu_align menu_align_max +bleopt/declare -v menu_dense_prefix '' +bleopt/declare -v menu_linewise_prefix '' +bleopt/declare -v menu_desc_prefix '' +bleopt/declare -v menu_desc_multicolumn_width 65 ble/util/autoload "$_ble_base/lib/core-complete.sh" \ ble/complete/menu#start \ diff --git a/lib/core-complete.sh b/lib/core-complete.sh index 0925d15b..9cbafd90 100644 --- a/lib/core-complete.sh +++ b/lib/core-complete.sh @@ -141,6 +141,47 @@ function ble/complete/menu#render-item { ret=$sgr0$ret$_ble_term_sgr0 } +## @fn ble/complete/menu#get-prefix-width format column_width +## @param[in] format +## @param[in] column_width +## @var[out] prefix_width +## @var[out] prefix_format +function ble/complete/menu#get-prefix-width { + prefix_width=0 + prefix_format=${1:-$bleopt_menu_prefix} + if [[ $prefix_format ]]; then + local prefix1 column_width=$2 + ble/util/sprintf prefix1 "$prefix_format" "${#menu_items[@]}" + local x1 y1 x2 y2 + LINES=1 COLUMNS=$column_width x=0 y=0 ble/canvas/trace "$prefix1" truncate:measure-bbox + if ((x2<=column_width/2)); then + prefix_width=$x2 + ble/string#reserve-prototype "$prefix_width" + fi + fi +} + +## @fn ble/complete/menu#render-prefix index +## @param[in] index +## @param[in,opt] column_width +## @var[in] prefix_width +## @var[in] prefix_format +## @var[out] prefix_esc +function ble/complete/menu#render-prefix { + prefix_esc= + local index=$1 + if ((prefix_width)); then + local prefix1; ble/util/sprintf prefix1 "$prefix_format" "$((index+1))" + local x=0 y=0 + LINES=1 COLUMNS=$prefix_width ble/canvas/trace "$prefix1" truncate:relative:measure-bbox + prefix_esc=$ret$_ble_term_sgr0 + if ((x=lines&&(x=x0,y=y0,1))) && break else + ((x+=prefix_width)) ble/complete/menu#render-item "$item" || ((begin==index)) || # [Note: 少なくとも1個ははみ出ても表示する] { x=$x0 y=$y0; break; }; esc1=$ret fi fi - _ble_complete_menu_style_icons[index]=$x0,$y0,$x,$y,${#item},${#esc1}:$item$esc1 - esc=$esc$esc1 + _ble_complete_menu_style_icons[index]=$((x0+prefix_width)),$y0,$x,$y,${#item},${#esc1}:$item$esc1 + esc=$esc$prefix_esc$esc1 # 候補と候補の間の空白 if ((++indexcols&&(y+=x/cols,x%=cols))) ble/complete/menu#render-item "$item" || ((index==begin)) || { x=$x0 y=$y0; break; }; esc1=$ret @@ -292,15 +349,17 @@ function ble/complete/menu-style:dense/construct-page { if ((y>y0&&x>0||y>y0+1)); then ((++y0>=lines)) && break esc=$esc$'\n' - ((y=y0,x=x0=0)) + ((y=y0,x0=0,x=prefix_width)) ble/complete/menu#render-item "$item" || ((begin==index)) || { x=$x0 y=$y0; break; }; esc1=$ret fi fi - _ble_complete_menu_style_icons[index]=$x0,$y0,$x,$y,${#item},${#esc1}:$item$esc1 - esc=$esc$esc1 + local x1=$((x0+prefix_width)) y1=$y0 + ((x1>=cols)) && ((y1+=x1/cols,x1%=cols)) + _ble_complete_menu_style_icons[index]=$x1,$y1,$x,$y,${#item},${#esc1}:$item$esc1 + esc=$esc$prefix_esc$esc1 # 候補と候補の間の空白 if ((++index=prefix_width+15)) || prefix_width=0 + + local wcand_limit=$(((wcolumn-prefix_width+1)*2/3)) + ((wcand_limit<10&&(wcand_limit=wcolumn-prefix_width))) local -a DRAW_BUFF=() local index=$begin icolumn ymax=0 @@ -443,14 +491,14 @@ function ble/complete/menu-style:desc/construct-page { x=0 y=0 lines=1 cols=$wcand_limit ble/complete/menu#render-item "$pack"; esc1=$ret - ((w=y*wcolumn+x,w>max_width&&(max_width=w))) + ((w=y*wcand_limit+x,w>max_width&&(max_width=w))) ble/array#push measure "$w:${#pack}:$pack$esc1" done local cand_width=$max_width - local desc_x=$((cand_width+1)); ((desc_x>wcolumn&&(desc_x=wcolumn))) - local desc_prefix=; ((wcolumn-desc_x>30)) && desc_prefix=': ' + local desc_x=$((prefix_width+cand_width+1)); ((desc_x>wcolumn&&(desc_x=wcolumn))) + local desc_prefix=; ((wcolumn-prefix_width-desc_x>30)) && desc_prefix=': ' local xcolumn=$((icolumn*(wcolumn+${#colsep}))) @@ -463,6 +511,11 @@ function ble/complete/menu-style:desc/construct-page { s=${entry%%:*} entry=${entry#*:} pack=${entry::s} esc1=${entry:s} + local prefix_esc + ble/complete/menu#render-prefix "$index" + ble/canvas/put.draw "$prefix_esc" + ((x+=prefix_width)) + # 候補表示 ((x0=x,y0=y,x+=w)) _ble_complete_menu_style_icons[index]=$((xcolumn+x0)),$y0,$((xcolumn+x)),$y,${#pack},${#esc1},"0 0 $wcand_limit 1":$pack$esc1 @@ -7353,6 +7406,22 @@ function ble/widget/menu-complete { ble/widget/complete enter_menu:insert_unique:$opts } +function ble/widget/complete/.select-menu-with-arg { + [[ $bleopt_complete_menu_complete && $_ble_complete_menu_active ]] || return 1 + + local footprint; ble/complete/menu/get-footprint + [[ $footprint == "$_ble_complete_menu_footprint" ]] || return 1 + + local arg_opts= opts=$1 + [[ :$opts: == *:enter-menu:* ]] && arg_opts=always + + # 現在のキーが実際に引数の一部として解釈され得る時のみ menu に入る + ble/widget/menu/append-arg/.is-argument "$arg_opts" || return 1 + ble/complete/menu-complete/enter + ble/widget/menu/append-arg "$arg_opts" + return 0 +} + #------------------------------------------------------------------------------ # menu-filter @@ -7708,6 +7777,52 @@ function ble-decode/keymap:menu_complete/define { ble-bind -f next 'menu/forward-page' ble-bind -f home 'menu/beginning-of-page' ble-bind -f end 'menu/end-of-page' + + local key + for key in {,M-,C-}{0..9}; do + ble-bind -f "$key" 'menu/append-arg' + done +} + +_ble_complete_menu_arg= +## @fn ble/widget/menu/append-arg [opts] +## @param[in,opt] opts +function ble/widget/menu/append-arg { + [[ ${LASTWIDGET%%' '*} == */append-arg ]] || _ble_complete_menu_arg= + + # 引数入力が開始されていなくて (修飾なしの) 数字キーの時はそのまま通常の数字 + # 入力として扱う。 + local i=${#KEYS[@]}; ((i&&i--)) + local flag=$((KEYS[i]&_ble_decode_MaskFlag)) + if ! [[ :$1: == *:always:* || flag -ne 0 || $_ble_complete_menu_arg ]]; then + ble/widget/menu_complete/exit-default + return "$?" + fi + + local code=$((KEYS[i]&_ble_decode_MaskChar)) + ((48<=code&&code<=57)) || return 1 + local ret; ble/util/c2s "$code"; local ch=$ret + ((_ble_complete_menu_arg=10#0$_ble_complete_menu_arg$ch)) + + # 番号が範囲内になければ頭から数字を削っていく + local count=${#_ble_complete_menu_items[@]} + while ((_ble_complete_menu_arg>count)); do + ((_ble_complete_menu_arg=10#0${_ble_complete_menu_arg:1})) + done + ((_ble_complete_menu_arg)) || return 0 + + # 移動 + ble/complete/menu#select "$((_ble_complete_menu_arg-1))" +} + +## @fn ble/widget/menu/append-arg/.is-argument [opts] +## @param[in,opt] opts +function ble/widget/menu/append-arg/.is-argument { + local i=${#KEYS[@]}; ((i&&i--)) + local flag=$((KEYS[i]&_ble_decode_MaskFlag)) + local code=$((KEYS[i]&_ble_decode_MaskChar)) + [[ :$1: == *:always:* ]] || ((flag)) || return 1 + ((48<=code&&code<=57)) } #------------------------------------------------------------------------------ diff --git a/note.txt b/note.txt index 519db2c5..1f8f802f 100644 --- a/note.txt +++ b/note.txt @@ -2040,19 +2040,6 @@ bash_tips 回復するという事。端末によっては何かそういう様な拡張をしていた様な気がする のでそれを有効にする可能性はある。 -2023-03-06 - - * menu-complete: 番号で選択する - https://github.com/akinomyoga/ble.sh/discussions/284 - - これは実は実装はそんなに大変ではない気がする。 - - LASTWIDGET のチェックは必要? 否、他のものは全て抜けるから関係ない? と思っ - たが TAB や cursor key 等の移動コマンドの後も番号はリセットしたい。なのでや - はり LASTWIDGET はチェックする。番号が溜まって一致する物がなくなったら一つ - ずつ古い文字を削除して一致するものが出るまで削る。一つも一致しなかったら削 - る前にリセットして新しい文字自体をなかった事にする? - 2023-03-02 * menu-complete: C-TAB を連続で押すと仮挿入をそのままに新しい補完が始まる @@ -6727,6 +6714,70 @@ bash_tips Done (実装ログ) ------------------------------------------------------------------------------- +2023-03-16 + + * 2023-03-06 menu-complete: 番号で選択する (requested by bkerin) [#D2023] + https://github.com/akinomyoga/ble.sh/discussions/284 + + これは実は実装はそんなに大変ではない気がする。 + + LASTWIDGET のチェックは必要? 否、他のものは全て抜けるから関係ない? と思っ + たが TAB や cursor key 等の移動コマンドの後も番号はリセットしたい。なのでや + はり LASTWIDGET はチェックする。番号が溜まって一致する物がなくなったら一つ + ずつ古い文字を削除して一致するものが出るまで削る。一つも一致しなかったら削 + る前にリセットして新しい文字自体をなかった事にする? + + 試しに実装してみたが操作性に疑問が残る。先ず menu-complete の中に入らないと + 番号で選択できない。一方で、menu を表示しているだけの場合にも選択しようとす + るのが自然の気がする。 + + a menu-complete の外からでも M-0 等で選択できる様にするとしても + menu-complete に入らずに select しても仕方がない。 + + もう一つの問題点は menu が表示されていたらどの文脈であっても M-0 で menu + に入ってしまうのかという事。auto-menu を実装していたりすると実質的に M-0 + は常に menu に入ってしまうという事になり操作できない。或いは、TAB を二回 + 押した時に complete に入るのと同じ様に、LASTWIDGET が complete だった時に + M-0 で menu-complete に入るというのは手の気がする。 + + 更にもう一つの問題点は menu の中に番号を表示していない時でも番号による選 + 択を有効にするのかという事 → これは M-0 等のキーを使っている時点で、実際 + に番号が表示されているかどうかに関係なく menu-complete に入るという事で良 + い気がする。 + + b 或いは、menu-complete の外で M-0 等で引数を入力し始めたらその場で + menu-complete に入るという可能性も考えられる。 + + * どの menu-style についても番号を表示する機能が欲しい。 + + 設定名を変更する? + + bleopt menu_item_prefix + bleopt menu_item_prefix_linewise + bleopt menu_item_prefix_align + bleopt menu_item_prefix_desc + + と思ったが現在の設定名は他の設定と共に menu_STYLE_NAME の形で一貫している。 + これを壊すのも変な気がするので、既存のルールは保持する。全体の設定を一括 + で変更するオプションを用意したい気がするが、その名前は何にするか。単に + menu_prefix で良いだろうか。 + + bleopt menu_prefix + bleopt menu_linewise_prefix + bleopt menu_align_prefix + bleopt menu_desc_prefix + + 取り敢えず align と desc について候補番号に対応した。dense は対応していな + い。が、完全性を期する為にはやはり dense も対応した方が良い気がする。→対 + 応した。 + + * done: wiki, blerc: menu_prefix, menu_{aling,dense,linewise,desc}_prefix + + * 返信 + + for key in {0..9}; do ble-bind -m vi_imap -f "$key" 'append-arg enter-menu'; done + for key in {0..9}; do ble-bind -m menu_complete -f "$key" 'menu/append-arg always'; done + 2023-03-14 * 2021-10-26 ble/builtin/read: WINCH を受信できていない気がする [#D2022] diff --git a/src/edit.sh b/src/edit.sh index cb80521a..97401b14 100644 --- a/src/edit.sh +++ b/src/edit.sh @@ -2428,7 +2428,16 @@ function ble/keymap:generic/clear-arg { fi } +## @fn ble/widget/append-arg [opts] +## @fn ble/widget/append-arg-or widget [opts] +## @param[in] widget +## @param[in,opt] opts +## enter-menu +## 修飾なしの数字であっても常に引数として取り扱います。 function ble/widget/append-arg-or { + # ble/widget/complete 直後 (menu 表示時) の引数で menu に入る + ble/function#try ble/widget/complete/.select-menu-with-arg "${@:2}" && return 0 + local n=${#KEYS[@]}; ((n&&n--)) local code=$((KEYS[n]&_ble_decode_MaskChar)) ((code==0)) && return 1 @@ -2451,7 +2460,7 @@ function ble/widget/append-arg-or { fi } function ble/widget/append-arg { - ble/widget/append-arg-or self-insert + ble/widget/append-arg-or self-insert "$@" } function ble/widget/universal-arg { ble/decode/widget/skip-lastwidget