Skip to content

Commit

Permalink
keymap/vi: improve text-object in omap for brackets
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed Dec 12, 2023
1 parent 8b3f6f8 commit d1a1d53
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 79 deletions.
1 change: 1 addition & 0 deletions docs/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
- make: specify bash to search the awk path using `type -p` (reported by rashil2000) `#D2089` 26826354
- keymap/vi: fix the behavior of text-object for quotes in xmap (reported by Darukutsu) `#D2094` 5f9a44ec
- edit(redo): fix broken common prefix/suffix determination (reported by Darukutsu) `#D2098` xxxxxxxx
- keymap/vi: improve text-object in omap for brackets (reported by Darukutsu) `#D2100` xxxxxxxx

## Compatibility

Expand Down
135 changes: 62 additions & 73 deletions lib/keymap.vi.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4849,35 +4849,68 @@ function ble/keymap:vi/text-object:block/.outer-range {
fi
}

## @fn ble/keymap:vi/text-object:block/.search-block min max L R [opts]
## @var[out] beg end
function ble/keymap:vi/text-object:block/.search-block {
local ret p1=$1 p2=$2 L=$3 R=$4 opts=$5
[[ ${_ble_edit_str:p1:1} == "$L" ]] && ((p1++))
if ble/keymap:vi/text-object:block/.prev-matching-lparen "$p1" "$arg"; then
# We first attempt to search for "a surrounding pair (...)" at the
# specified level of $arg.
beg=$ret
ble/keymap:vi/text-object:block/.next-matching-rparen "$p1" "$arg" || return 1
end=$ret

if [[ :$opts: == *:reject-empty-here:* ]]; then
((beg+1<end)) || return 1
fi

if [[ :$opts: == *:check-expand:* ]]; then
# If the new range is essentially identical (or a prefix) to the current
# selection, we try to capture the range upper by one level.
local outer_beg outer_end
ble/keymap:vi/text-object:block/.outer-range "$p1" "$p2"
if ((outer_beg==beg&&outer_end>=end)); then
[[ $type == i* ]] && ((p1--))
ble/keymap:vi/text-object:block/.expand-one-level "$outer_beg" || return 1
fi
fi
elif ((ret<=0&&arg==1)); then
# When we fail to find "the surrounding pair (...)" at the top level, we
# next try "the next pair (...)". This is attempted only when the
# specified level is arg=1.
p1=$1
[[ ${_ble_edit_str:p1:1} == "$R" ]] && ((p1++))
ble/keymap:vi/text-object:block/.next-matching-lparen "$p1" || return 1
beg=$ret
ble/keymap:vi/text-object:block/.next-matching-rparen "$((beg+1))" || return 1
end=$ret
# Note: In Vim, ((beg+1<end)) check does not seem to be performed here.
# See also the next Note in the code comment below.

if [[ :$opts: == *:check-expand:* ]]; then
if [[ $type == i* ]]; then
local outer_end=$p2
case ${_ble_edit_str:outer_end+1} in
($'\n'"$rparen"*) ((outer_end+=2)) ;;
("$rparen"*) ((outer_end++)) ;;
esac
((outer_end<end)) || return 1
fi
fi
else
return 1
fi
return 0
}

## @fn ble/keymap:vi/text-object:block/.xmap
## @var[in] lparen rparen
## @var[in] arg
function ble/keymap:vi/text-object:block/.xmap {
if ((_ble_edit_ind==_ble_edit_mark)); then
local ret p1=$_ble_edit_ind
[[ ${_ble_edit_str:p1:1} == "$lparen" ]] && ((p1++))
if ble/keymap:vi/text-object:block/.prev-matching-lparen "$p1" "$arg"; then
# We first attempt to search for "a surrounding pair (...)" at the
# specified level of $arg.
local beg=$ret
ble/keymap:vi/text-object:block/.next-matching-rparen "$p1" "$arg" || return 1
local end=$ret
((beg+1<end)) || return 1
elif ((ret<=0&&arg==1)); then
# When we fail to find "the surrounding pair (...)" at the top level, we
# next try "the next pair (...)". This is attempted only when the
# specified level is arg=1.
p1=$_ble_edit_ind
[[ ${_ble_edit_str:p1:1} == "$rparen" ]] && ((p1++))
ble/keymap:vi/text-object:block/.next-matching-lparen "$p1" || return 1
local beg=$ret
ble/keymap:vi/text-object:block/.next-matching-rparen "$((beg+1))" || return 1
local end=$ret
# Note: In Vim, ((beg+1<end)) check does not seem to be performed here.
# See also the next Note in the code comment below.
else
return 1
fi
local beg end p=$_ble_edit_ind
ble/keymap:vi/text-object:block/.search-block "$p" "$p" "$lparen" "$rparen" reject-empty-here || return 1

# i, a に応じて適切に範囲を決定する
if [[ $type == i* ]]; then
Expand All @@ -4895,39 +4928,7 @@ function ble/keymap:vi/text-object:block/.xmap {
else
local min=$_ble_edit_mark max=$_ble_edit_ind
((min<max)) || local min=$max max=$min
local p1=$min p2=$max
[[ ${_ble_edit_str:p1:1} == "$rparen" ]] && ((p1++)) # rparen と lparen が逆
if ble/keymap:vi/text-object:block/.prev-matching-lparen "$p1" "$arg"; then
local beg=$ret
ble/keymap:vi/text-object:block/.next-matching-rparen "$p1" "$arg" || return 1
local end=$ret
((beg+1<end)) || return 1

# If the new range is essentially identical (or a prefix) to the current
# selection, we try to capture the range upper by one level.
local outer_beg outer_end
ble/keymap:vi/text-object:block/.outer-range "$p1" "$p2"
if ((outer_beg==beg&&outer_end>=end)); then
[[ $type == i* ]] && ((p1--))
ble/keymap:vi/text-object:block/.expand-one-level "$outer_beg" || return 1
fi
elif ((ret<=0&&arg==1)); then
local p1=$min p2=$max
[[ ${_ble_edit_str:p1:1} == "$lparen" ]] && ((p1++))
ble/keymap:vi/text-object:block/.next-matching-lparen "$p1" || return 1
local beg=$ret
ble/keymap:vi/text-object:block/.next-matching-rparen "$((beg+1))" || return 1
local end=$ret

if [[ $type == i* ]]; then
local outer_end=$p2
case ${_ble_edit_str:outer_end+1} in
($'\n'"$rparen"*) ((outer_end+=2)) ;;
("$rparen"*) ((outer_end++)) ;;
esac
((outer_end<end)) || return 1
fi
fi
ble/keymap:vi/text-object:block/.search-block "$min" "$max" "$rparen" "$lparen" reject-empty-here:check-expand || return 1

if [[ $type == i* ]]; then
((beg++,end--))
Expand All @@ -4952,21 +4953,12 @@ function ble/keymap:vi/text-object/block.impl {
fi
fi

local axis=$_ble_edit_ind
[[ ${_ble_edit_str:axis:1} == "$lparen" ]] && ((axis++))

local ret
if ! ble/keymap:vi/text-object:block/.prev-matching-lparen "$axis" "$arg"; then
local beg end p=$_ble_edit_ind
if ! ble/keymap:vi/text-object:block/.search-block "$p" "$p" "$lparen" "$rparen"; then
ble/widget/vi-command/bell
return 1
fi
local beg=$ret

if ! ble/keymap:vi/text-object:block/.next-matching-rparen "$axis" "$arg"; then
ble/widget/vi-command/bell
return 1
fi
local end=$((ret+1))
((end++))

local linewise=
if [[ $type == *i* ]]; then
Expand All @@ -4976,10 +4968,7 @@ function ble/keymap:vi/text-object/block.impl {
((beg<end)) && ble-edit/content/bolp "$beg" && ble-edit/content/eolp "$end" && linewise=1
fi

if [[ $_ble_decode_keymap == vi_[xs]map ]]; then
_ble_edit_mark=$beg
ble/widget/vi-command/exclusive-goto.impl "$end"
elif [[ $linewise ]]; then
if [[ $linewise ]]; then
ble/widget/vi-command/linewise-range.impl "$beg" "$end" "$flag" "$reg" goto_bol
else
ble/widget/vi-command/exclusive-range.impl "$beg" "$end" "$flag" "$reg"
Expand Down
65 changes: 59 additions & 6 deletions lib/keymap.vi_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ function ble/widget/vi-command:check-vi-mode/surround {

ble/keymap:vi_test/show-summary
}
function ble/widget/vi-command:check-vi-mode/xmap_txtobj_quote {
function ble/widget/vi-command:check-vi-mode/txtobj_quote_xmap {
ble/keymap:vi_test/start-section 'xmap text object i" a"'

# A. xmap txtobj i"/a"、開始点と終了点が同じとき
Expand Down Expand Up @@ -258,8 +258,60 @@ function ble/widget/vi-command:check-vi-mode/xmap_txtobj_quote {
ble/keymap:vi_test/show-summary
}

function ble/widget/vi-command:check-vi-mode/xmap_txtobj_block {
ble/keymap:vi_test/start-section 'xmap text object i( a('
function ble/widget/vi-command:check-vi-mode/txtobj_block_omap {
ble/keymap:vi_test/start-section 'txtobj block omap (ib ab)'

ble/keymap:vi_test/check A1a '@:echo @foo ( bar ) baz (hello) world (vim) xxxx' 'd i b' '@:echo foo (@) baz (hello) world (vim) xxxx'
ble/keymap:vi_test/check A1b '@:echo foo@ ( bar ) baz (hello) world (vim) xxxx' 'd i b' '@:echo foo (@) baz (hello) world (vim) xxxx'
ble/keymap:vi_test/check A1c '@:echo foo @( bar ) baz (hello) world (vim) xxxx' 'd i b' '@:echo foo (@) baz (hello) world (vim) xxxx'
ble/keymap:vi_test/check A1d '@:echo foo (@ bar ) baz (hello) world (vim) xxxx' 'd i b' '@:echo foo (@) baz (hello) world (vim) xxxx'
ble/keymap:vi_test/check A1e '@:echo foo ( @bar ) baz (hello) world (vim) xxxx' 'd i b' '@:echo foo (@) baz (hello) world (vim) xxxx'
ble/keymap:vi_test/check A1f '@:echo foo ( bar@ ) baz (hello) world (vim) xxxx' 'd i b' '@:echo foo (@) baz (hello) world (vim) xxxx'
ble/keymap:vi_test/check A1g '@:echo foo ( bar @) baz (hello) world (vim) xxxx' 'd i b' '@:echo foo (@) baz (hello) world (vim) xxxx'
ble/keymap:vi_test/check A1h '@:echo foo ( bar )@ baz (hello) world (vim) xxxx' 'd i b' '@:echo foo ( bar ) baz (@) world (vim) xxxx'
ble/keymap:vi_test/check A1i '@:echo foo ( bar ) @baz (hello) world (vim) xxxx' 'd i b' '@:echo foo ( bar ) baz (@) world (vim) xxxx'
ble/keymap:vi_test/check A1i '@:echo foo ( bar ) baz (hello) world (vim@) xxxx' 'd i b' '@:echo foo ( bar ) baz (hello) world (@) xxxx'
ble/keymap:vi_test/check A1i '@:echo foo ( bar ) baz (hello) world (vim)@ xxxx' 'd i b' '@:echo foo ( bar ) baz (hello) world (vim)@ xxxx'
ble/keymap:vi_test/check A1i '@:echo foo ( bar ) baz (hello) world (vim) @xxxx' 'd i b' '@:echo foo ( bar ) baz (hello) world (vim) @xxxx'

ble/keymap:vi_test/check B1a '@:echo ( @foo ( bar ) baz (hello) world (vim) ) xxxx' 'd i b' '@:echo (@) xxxx'
ble/keymap:vi_test/check B1b '@:echo ( foo @( bar ) baz (hello) world (vim) ) xxxx' 'd i b' '@:echo ( foo (@) baz (hello) world (vim) ) xxxx'
ble/keymap:vi_test/check B1c '@:echo ( foo ( @bar ) baz (hello) world (vim) ) xxxx' 'd i b' '@:echo ( foo (@) baz (hello) world (vim) ) xxxx'
ble/keymap:vi_test/check B1d '@:echo ( foo ( bar @) baz (hello) world (vim) ) xxxx' 'd i b' '@:echo ( foo (@) baz (hello) world (vim) ) xxxx'
ble/keymap:vi_test/check B1e '@:echo ( foo ( bar )@ baz (hello) world (vim) ) xxxx' 'd i b' '@:echo (@) xxxx'
ble/keymap:vi_test/check B1f '@:echo ( foo ( bar ) baz@ (hello) world (vim) ) xxxx' 'd i b' '@:echo (@) xxxx'
ble/keymap:vi_test/check B1g '@:echo ( foo ( bar ) baz @(hello) world (vim) ) xxxx' 'd i b' '@:echo ( foo ( bar ) baz (@) world (vim) ) xxxx'
ble/keymap:vi_test/check B2a '@:echo ( @foo ( bar ) baz (hello) world (vim) ) xxxx' 'd 2 i b' '@:echo ( @foo ( bar ) baz (hello) world (vim) ) xxxx'
ble/keymap:vi_test/check B2b '@:echo ( foo @( bar ) baz (hello) world (vim) ) xxxx' 'd 2 i b' '@:echo (@) xxxx'
ble/keymap:vi_test/check B2c '@:echo ( foo ( @bar ) baz (hello) world (vim) ) xxxx' 'd 2 i b' '@:echo (@) xxxx'
ble/keymap:vi_test/check B2d '@:echo ( foo ( bar @) baz (hello) world (vim) ) xxxx' 'd 2 i b' '@:echo (@) xxxx'
ble/keymap:vi_test/check B2e '@:echo ( foo ( bar )@ baz (hello) world (vim) ) xxxx' 'd 2 i b' '@:echo ( foo ( bar )@ baz (hello) world (vim) ) xxxx'
ble/keymap:vi_test/check B2f '@:echo ( foo ( bar ) baz@ (hello) world (vim) ) xxxx' 'd 2 i b' '@:echo ( foo ( bar ) baz@ (hello) world (vim) ) xxxx'
ble/keymap:vi_test/check B2g '@:echo ( foo ( bar ) baz @(hello) world (vim) ) xxxx' 'd 2 i b' '@:echo (@) xxxx'

ble/keymap:vi_test/check C1a '@:echo ( @foo ( bar ) baz (hello) world (vim) xxxx' 'd i b' '@:echo ( @foo ( bar ) baz (hello) world (vim) xxxx'
ble/keymap:vi_test/check C1b '@:echo ( foo (@ bar ) baz (hello) world (vim) xxxx' 'd i b' '@:echo ( foo (@) baz (hello) world (vim) xxxx'
ble/keymap:vi_test/check C2a '@:echo @foo ( bar ) baz (hello) world (vim) ) xxxx' 'd i b' '@:echo foo (@) baz (hello) world (vim) ) xxxx'
ble/keymap:vi_test/check C2b '@:echo foo (@ bar ) baz (hello) world (vim) ) xxxx' 'd i b' '@:echo foo (@) baz (hello) world (vim) ) xxxx'

ble/keymap:vi_test/check D1a '@:echo @vim) test ( quick ) world ( foo )' 'd i b' '@:echo @vim) test ( quick ) world ( foo )'
ble/keymap:vi_test/check D1a '@:echo vi@m) test ( quick ) world ( foo )' 'd i b' '@:echo vi@m) test ( quick ) world ( foo )'
ble/keymap:vi_test/check D1a '@:echo vim@) test ( quick ) world ( foo )' 'd i b' '@:echo vim) test (@) world ( foo )'
ble/keymap:vi_test/check D1a '@:echo vim) @test ( quick ) world ( foo )' 'd i b' '@:echo vim) test (@) world ( foo )'
ble/keymap:vi_test/check D1a '@:echo vim) test @( quick ) world ( foo )' 'd i b' '@:echo vim) test (@) world ( foo )'

ble/keymap:vi_test/check E1a '@:echo @foo () bar' 'd i b' '@:echo foo (@) bar'
ble/keymap:vi_test/check E1b '@:echo foo @() bar' 'd i b' '@:echo foo (@) bar'
ble/keymap:vi_test/check E1c '@:echo foo (@) bar' 'd i b' '@:echo foo (@) bar'
ble/keymap:vi_test/check E1d '@:echo @foo () bar' 'd a b' '@:echo foo @ bar'
ble/keymap:vi_test/check E1e '@:echo foo @() bar' 'd a b' '@:echo foo @ bar'
ble/keymap:vi_test/check E1f '@:echo foo (@) bar' 'd a b' '@:echo foo @ bar'

ble/keymap:vi_test/show-summary
}

function ble/widget/vi-command:check-vi-mode/txtobj_block_xmap {
ble/keymap:vi_test/start-section 'txtobj block xmap (ib ab)'

# xmap txtobj i"/a"、開始点と終了点が同じとき

Expand Down Expand Up @@ -609,10 +661,11 @@ function ble/widget/vi-command:check-vi-mode {
ble/widget/vi-command:check-vi-mode/increment
ble/widget/vi-command:check-vi-mode/macro
ble/widget/vi-command:check-vi-mode/surround
ble/widget/vi-command:check-vi-mode/xmap_txtobj_quote
ble/widget/vi-command:check-vi-mode/xmap_txtobj_block
ble/widget/vi-command:check-vi-mode/op.2018-02-22
ble/widget/vi-command:check-vi-mode/txtobj_quote_xmap
ble/widget/vi-command:check-vi-mode/txtobj_block_omap
ble/widget/vi-command:check-vi-mode/txtobj_block_xmap
ble/widget/vi-command:check-vi-mode/txtobj_word
ble/widget/vi-command:check-vi-mode/op.2018-02-22

#----------------------------------------------------------------------------

Expand Down
23 changes: 23 additions & 0 deletions note.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7092,6 +7092,29 @@ bash_tips

2023-12-13

* keymap/vi: text objects for brackets が omap でも変 (reported by Darukutsu) [#D2100]

恐らく真面目に実装していなかったという事? 最初に block.impl を導入したのは
441ed955 だがこの commit の段階では not yet implemented 等と書かれている。
とは言いつつ現在の処理が大体書かれている。

% ? no: という事を考えると暫定実装がそのまま残っていたという事だろうか?
% ca0fc1ac まで行くと not yet implemented という文字列は消えてなくなって
% いるが、実装に本質的な変更がある様には見えない。not yet implemented が
% 置き換わっているのは bd392394c だが、これは単に bell をまとめただけの様
% に見える。余り本質的な変更ではない。そもそも not yet implemented と書い
% ていたのは本体の処理ではなくて bell の部分だったので、妥当である。
%
% → not yet implemented と書かれていたのは bell の処理であって本体の処理
% についてではない。

ちゃんとした記録は残っていないが成立の経緯を考えると恐らく適当に最初に実装
したのがそのまま残っていただけ。ちゃんと振る舞いを観察して実装し直すという
事をするべき。先ずはテストから書く必要がある。

取り敢えず実装した。xmap の実装も含めて繰り返しが多いので整理する。未だ動い
ている。取り敢えずこれで良い事にする。

* docs: CONTRIBUTING の BNF に間違いがある [#D2099]

と思って修正したが他にも英語の間違いがある。これを機に全体的に Grammarly を
Expand Down

0 comments on commit d1a1d53

Please sign in to comment.