Skip to content

Commit 777dffa

Browse files
authored
Refactor waiting_proc and waiting_operator_proc (#649)
* Fix waiting_proc precedence * Fix waiting_operator bugs * Add waiting_proc and vi_waiting_operator test * Fix vi waiting operator arg number vi_arg and vi_waiting_operator_arg should be multiplied * Implement `yy` copies whole line in vi_command mode * Simplify incremental search cancel test * Add complex vi test with waiting_proc and vi_waiting_operator, split test input
1 parent 2ccdb37 commit 777dffa

File tree

3 files changed

+149
-73
lines changed

3 files changed

+149
-73
lines changed

lib/reline/line_editor.rb

Lines changed: 85 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ def reset_variables(prompt = '', encoding:)
229229
@vi_clipboard = ''
230230
@vi_arg = nil
231231
@waiting_proc = nil
232-
@waiting_operator_proc = nil
233-
@waiting_operator_vi_arg = nil
232+
@vi_waiting_operator = nil
233+
@vi_waiting_operator_arg = nil
234234
@completion_journey_state = nil
235235
@completion_state = CompletionState::NORMAL
236236
@completion_occurs = false
@@ -936,37 +936,23 @@ def dialog_proc_scope_completion_journey_data
936936
end
937937

938938
private def run_for_operators(key, method_symbol, &block)
939-
if @waiting_operator_proc
939+
if @vi_waiting_operator
940940
if VI_MOTIONS.include?(method_symbol)
941941
old_byte_pointer = @byte_pointer
942-
@vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg&.> 1
942+
@vi_arg = (@vi_arg || 1) * @vi_waiting_operator_arg
943943
block.(true)
944944
unless @waiting_proc
945945
byte_pointer_diff = @byte_pointer - old_byte_pointer
946946
@byte_pointer = old_byte_pointer
947-
@waiting_operator_proc.(byte_pointer_diff)
948-
else
949-
old_waiting_proc = @waiting_proc
950-
old_waiting_operator_proc = @waiting_operator_proc
951-
current_waiting_operator_proc = @waiting_operator_proc
952-
@waiting_proc = proc { |k|
953-
old_byte_pointer = @byte_pointer
954-
old_waiting_proc.(k)
955-
byte_pointer_diff = @byte_pointer - old_byte_pointer
956-
@byte_pointer = old_byte_pointer
957-
current_waiting_operator_proc.(byte_pointer_diff)
958-
@waiting_operator_proc = old_waiting_operator_proc
959-
}
947+
send(@vi_waiting_operator, byte_pointer_diff)
948+
cleanup_waiting
960949
end
961950
else
962951
# Ignores operator when not motion is given.
963952
block.(false)
953+
cleanup_waiting
964954
end
965-
@waiting_operator_proc = nil
966-
@waiting_operator_vi_arg = nil
967-
if @vi_arg
968-
@vi_arg = nil
969-
end
955+
@vi_arg = nil
970956
else
971957
block.(false)
972958
end
@@ -983,7 +969,7 @@ def dialog_proc_scope_completion_journey_data
983969
end
984970

985971
def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
986-
if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
972+
if @config.editing_mode_is?(:emacs, :vi_insert) and @vi_waiting_operator.nil?
987973
not_insertion = method_symbol != :ed_insert
988974
process_insert(force: not_insertion)
989975
end
@@ -1002,11 +988,32 @@ def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
1002988
end
1003989
end
1004990

991+
private def cleanup_waiting
992+
@waiting_proc = nil
993+
@vi_waiting_operator = nil
994+
@vi_waiting_operator_arg = nil
995+
@searching_prompt = nil
996+
@drop_terminate_spaces = false
997+
end
998+
1005999
private def process_key(key, method_symbol)
1000+
if key.is_a?(Symbol)
1001+
cleanup_waiting
1002+
elsif @waiting_proc
1003+
old_byte_pointer = @byte_pointer
1004+
@waiting_proc.call(key)
1005+
if @vi_waiting_operator
1006+
byte_pointer_diff = @byte_pointer - old_byte_pointer
1007+
@byte_pointer = old_byte_pointer
1008+
send(@vi_waiting_operator, byte_pointer_diff)
1009+
cleanup_waiting
1010+
end
1011+
@kill_ring.process
1012+
return
1013+
end
1014+
10061015
if method_symbol and respond_to?(method_symbol, true)
10071016
method_obj = method(method_symbol)
1008-
else
1009-
method_obj = nil
10101017
end
10111018
if method_symbol and key.is_a?(Symbol)
10121019
if @vi_arg and argumentable?(method_obj)
@@ -1028,8 +1035,6 @@ def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
10281035
run_for_operators(key, method_symbol) do |with_operator|
10291036
wrap_method_call(method_symbol, method_obj, key, with_operator)
10301037
end
1031-
elsif @waiting_proc
1032-
@waiting_proc.(key)
10331038
elsif method_obj
10341039
wrap_method_call(method_symbol, method_obj, key)
10351040
else
@@ -1040,9 +1045,6 @@ def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
10401045
@vi_arg = nil
10411046
end
10421047
end
1043-
elsif @waiting_proc
1044-
@waiting_proc.(key)
1045-
@kill_ring.process
10461048
elsif method_obj
10471049
if method_symbol == :ed_argument_digit
10481050
wrap_method_call(method_symbol, method_obj, key)
@@ -2325,46 +2327,63 @@ def finish
23252327
@byte_pointer = 0
23262328
end
23272329

2328-
private def vi_change_meta(key, arg: 1)
2329-
@drop_terminate_spaces = true
2330-
@waiting_operator_proc = proc { |byte_pointer_diff|
2331-
if byte_pointer_diff > 0
2332-
line, cut = byteslice!(current_line, @byte_pointer, byte_pointer_diff)
2333-
elsif byte_pointer_diff < 0
2334-
line, cut = byteslice!(current_line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2335-
end
2336-
set_current_line(line)
2337-
copy_for_vi(cut)
2338-
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
2339-
@config.editing_mode = :vi_insert
2340-
@drop_terminate_spaces = false
2341-
}
2342-
@waiting_operator_vi_arg = arg
2330+
private def vi_change_meta(key, arg: nil)
2331+
if @vi_waiting_operator
2332+
set_current_line('', 0) if @vi_waiting_operator == :vi_change_meta_confirm && arg.nil?
2333+
@vi_waiting_operator = nil
2334+
@vi_waiting_operator_arg = nil
2335+
else
2336+
@drop_terminate_spaces = true
2337+
@vi_waiting_operator = :vi_change_meta_confirm
2338+
@vi_waiting_operator_arg = arg || 1
2339+
end
23432340
end
23442341

2345-
private def vi_delete_meta(key, arg: 1)
2346-
@waiting_operator_proc = proc { |byte_pointer_diff|
2347-
if byte_pointer_diff > 0
2348-
line, cut = byteslice!(current_line, @byte_pointer, byte_pointer_diff)
2349-
elsif byte_pointer_diff < 0
2350-
line, cut = byteslice!(current_line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2351-
end
2352-
copy_for_vi(cut)
2353-
set_current_line(line || '', @byte_pointer + (byte_pointer_diff < 0 ? byte_pointer_diff : 0))
2354-
}
2355-
@waiting_operator_vi_arg = arg
2342+
private def vi_change_meta_confirm(byte_pointer_diff)
2343+
vi_delete_meta_confirm(byte_pointer_diff)
2344+
@config.editing_mode = :vi_insert
2345+
@drop_terminate_spaces = false
23562346
end
23572347

2358-
private def vi_yank(key, arg: 1)
2359-
@waiting_operator_proc = proc { |byte_pointer_diff|
2360-
if byte_pointer_diff > 0
2361-
cut = current_line.byteslice(@byte_pointer, byte_pointer_diff)
2362-
elsif byte_pointer_diff < 0
2363-
cut = current_line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2364-
end
2365-
copy_for_vi(cut)
2366-
}
2367-
@waiting_operator_vi_arg = arg
2348+
private def vi_delete_meta(key, arg: nil)
2349+
if @vi_waiting_operator
2350+
set_current_line('', 0) if @vi_waiting_operator == :vi_delete_meta_confirm && arg.nil?
2351+
@vi_waiting_operator = nil
2352+
@vi_waiting_operator_arg = nil
2353+
else
2354+
@vi_waiting_operator = :vi_delete_meta_confirm
2355+
@vi_waiting_operator_arg = arg || 1
2356+
end
2357+
end
2358+
2359+
private def vi_delete_meta_confirm(byte_pointer_diff)
2360+
if byte_pointer_diff > 0
2361+
line, cut = byteslice!(current_line, @byte_pointer, byte_pointer_diff)
2362+
elsif byte_pointer_diff < 0
2363+
line, cut = byteslice!(current_line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2364+
end
2365+
copy_for_vi(cut)
2366+
set_current_line(line || '', @byte_pointer + (byte_pointer_diff < 0 ? byte_pointer_diff : 0))
2367+
end
2368+
2369+
private def vi_yank(key, arg: nil)
2370+
if @vi_waiting_operator
2371+
copy_for_vi(current_line) if @vi_waiting_operator == :vi_yank_confirm && arg.nil?
2372+
@vi_waiting_operator = nil
2373+
@vi_waiting_operator_arg = nil
2374+
else
2375+
@vi_waiting_operator = :vi_yank_confirm
2376+
@vi_waiting_operator_arg = arg || 1
2377+
end
2378+
end
2379+
2380+
private def vi_yank_confirm(byte_pointer_diff)
2381+
if byte_pointer_diff > 0
2382+
cut = current_line.byteslice(@byte_pointer, byte_pointer_diff)
2383+
elsif byte_pointer_diff < 0
2384+
cut = current_line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2385+
end
2386+
copy_for_vi(cut)
23682387
end
23692388

23702389
private def vi_list_or_eof(key)

test/reline/test_key_actor_emacs.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,14 @@ def test_ed_search_next_history_with_empty
13091309
assert_line_around_cursor('', '')
13101310
end
13111311

1312+
def test_incremental_search_history_cancel_by_symbol_key
1313+
# ed_prev_char should move cursor left and cancel incremental search
1314+
input_keys("abc\C-r")
1315+
input_key_by_symbol(:ed_prev_char)
1316+
input_keys('d')
1317+
assert_line_around_cursor('abd', 'c')
1318+
end
1319+
13121320
# Unicode emoji test
13131321
def test_ed_insert_for_include_zwj_emoji
13141322
omit "This test is for UTF-8 but the locale is #{Reline.core.encoding}" if Reline.core.encoding != Encoding::UTF_8

test/reline/test_key_actor_vi.rb

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -739,10 +739,16 @@ def test_vi_delete_meta_with_vi_next_char
739739
end
740740

741741
def test_vi_delete_meta_with_arg
742-
input_keys("aaa bbb ccc\C-[02w")
743-
assert_line_around_cursor('aaa bbb ', 'ccc')
742+
input_keys("aaa bbb ccc ddd\C-[03w")
743+
assert_line_around_cursor('aaa bbb ccc ', 'ddd')
744744
input_keys('2dl')
745-
assert_line_around_cursor('aaa bbb ', 'c')
745+
assert_line_around_cursor('aaa bbb ccc ', 'd')
746+
input_keys('d2h')
747+
assert_line_around_cursor('aaa bbb cc', 'd')
748+
input_keys('2d3h')
749+
assert_line_around_cursor('aaa ', 'd')
750+
input_keys('dd')
751+
assert_line_around_cursor('', '')
746752
end
747753

748754
def test_vi_change_meta
@@ -765,6 +771,45 @@ def test_vi_change_meta_with_vi_next_word
765771
assert_line_around_cursor('foo hog', 'e baz')
766772
end
767773

774+
def test_vi_waiting_operator_with_waiting_proc
775+
input_keys("foo foo foo foo foo\C-[0")
776+
input_keys('2d3fo')
777+
assert_line_around_cursor('', ' foo foo')
778+
input_keys('fo')
779+
assert_line_around_cursor(' f', 'oo foo')
780+
end
781+
782+
def test_vi_waiting_operator_cancel
783+
input_keys("aaa bbb ccc\C-[02w")
784+
assert_line_around_cursor('aaa bbb ', 'ccc')
785+
# dc dy should cancel delete_meta
786+
input_keys('dch')
787+
input_keys('dyh')
788+
# cd cy should cancel change_meta
789+
input_keys('cdh')
790+
input_keys('cyh')
791+
# yd yc should cancel yank_meta
792+
# P should not paste yanked text because yank_meta is canceled
793+
input_keys('ydhP')
794+
input_keys('ychP')
795+
assert_line_around_cursor('aa', 'a bbb ccc')
796+
end
797+
798+
def test_cancel_waiting_with_symbol_key
799+
input_keys("aaa bbb lll\C-[0")
800+
assert_line_around_cursor('', 'aaa bbb lll')
801+
# ed_next_char should move cursor right and cancel vi_next_char
802+
input_keys('f')
803+
input_key_by_symbol(:ed_next_char)
804+
input_keys('l')
805+
assert_line_around_cursor('aa', 'a bbb lll')
806+
# ed_next_char should move cursor right and cancel delete_meta
807+
input_keys('d')
808+
input_key_by_symbol(:ed_next_char)
809+
input_keys('l')
810+
assert_line_around_cursor('aaa ', 'bbb lll')
811+
end
812+
768813
def test_unimplemented_vi_command_should_be_no_op
769814
input_keys("abc\C-[h")
770815
assert_line_around_cursor('a', 'bc')
@@ -773,12 +818,16 @@ def test_unimplemented_vi_command_should_be_no_op
773818
end
774819

775820
def test_vi_yank
776-
input_keys("foo bar\C-[0")
777-
assert_line_around_cursor('', 'foo bar')
821+
input_keys("foo bar\C-[2h")
822+
assert_line_around_cursor('foo ', 'bar')
778823
input_keys('y3l')
779-
assert_line_around_cursor('', 'foo bar')
824+
assert_line_around_cursor('foo ', 'bar')
780825
input_keys('P')
781-
assert_line_around_cursor('fo', 'ofoo bar')
826+
assert_line_around_cursor('foo ba', 'rbar')
827+
input_keys('3h3yhP')
828+
assert_line_around_cursor('foofo', 'o barbar')
829+
input_keys('yyP')
830+
assert_line_around_cursor('foofofoofoo barba', 'ro barbar')
782831
end
783832

784833
def test_vi_end_word_with_operator

0 commit comments

Comments
 (0)