Skip to content

Commit 7c6e4bc

Browse files
verdy89matzbot
authored andcommitted
[ruby/reline] Implement the redo command
(ruby/reline#707) * Implement the redo command * Commented out a test that does not pass * Changed key assignment for redo from "\C-[" to "\C-g" * Changed redo key assignment from `\C-g` to `\M-\C-_` * Revert the first implemantation * Implemented redo by sharing `@past_lines` between undo and redo * Fixed the index of past_lines that is updated when the cursor is moved * Fixed deletion of the redo history in regular input * Renamed variables: past_lines -> input_lines * Rename @position to @input_lines_position * Deleted unused variables: `@old_byte_pointer` and `@old_line_index` ruby/reline@0b2d9fab5f
1 parent bc47ca5 commit 7c6e4bc

File tree

4 files changed

+152
-19
lines changed

4 files changed

+152
-19
lines changed

lib/reline/key_actor/emacs.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
319319
# 158 M-^^
320320
:ed_unassigned,
321321
# 159 M-^_
322-
:ed_unassigned,
322+
:redo,
323323
# 160 M-SPACE
324324
:em_set_mark,
325325
# 161 M-!

lib/reline/line_editor.rb

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,8 @@ def reset_variables(prompt = '', encoding:)
250250
@resized = false
251251
@cache = {}
252252
@rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
253-
@past_lines = []
253+
@input_lines = [[[""], 0, 0]]
254+
@input_lines_position = 0
254255
@undoing = false
255256
reset_line
256257
end
@@ -1137,7 +1138,7 @@ def input_key(key)
11371138
@completion_journey_state = nil
11381139
end
11391140

1140-
push_past_lines unless @undoing
1141+
push_input_lines unless @undoing
11411142
@undoing = false
11421143

11431144
if @in_pasting
@@ -1156,21 +1157,24 @@ def input_key(key)
11561157

11571158
def save_old_buffer
11581159
@old_buffer_of_lines = @buffer_of_lines.dup
1159-
@old_byte_pointer = @byte_pointer.dup
1160-
@old_line_index = @line_index.dup
11611160
end
11621161

1163-
def push_past_lines
1164-
if @old_buffer_of_lines != @buffer_of_lines
1165-
@past_lines.push([@old_buffer_of_lines, @old_byte_pointer, @old_line_index])
1162+
def push_input_lines
1163+
if @old_buffer_of_lines == @buffer_of_lines
1164+
@input_lines[@input_lines_position] = [@buffer_of_lines.dup, @byte_pointer, @line_index]
1165+
else
1166+
@input_lines = @input_lines[0..@input_lines_position]
1167+
@input_lines_position += 1
1168+
@input_lines.push([@buffer_of_lines.dup, @byte_pointer, @line_index])
11661169
end
1167-
trim_past_lines
1170+
trim_input_lines
11681171
end
11691172

1170-
MAX_PAST_LINES = 100
1171-
def trim_past_lines
1172-
if @past_lines.size > MAX_PAST_LINES
1173-
@past_lines.shift
1173+
MAX_INPUT_LINES = 100
1174+
def trim_input_lines
1175+
if @input_lines.size > MAX_INPUT_LINES
1176+
@input_lines.shift
1177+
@input_lines_position -= 1
11741178
end
11751179
end
11761180

@@ -1352,7 +1356,7 @@ def insert_pasted_text(text)
13521356
@buffer_of_lines[@line_index, 1] = lines
13531357
@line_index += lines.size - 1
13541358
@byte_pointer = @buffer_of_lines[@line_index].bytesize - post.bytesize
1355-
push_past_lines
1359+
push_input_lines
13561360
end
13571361

13581362
def insert_text(text)
@@ -2529,13 +2533,22 @@ def finish
25292533
end
25302534

25312535
private def undo(_key)
2532-
return if @past_lines.empty?
2536+
@undoing = true
2537+
2538+
return if @input_lines_position <= 0
2539+
2540+
@input_lines_position -= 1
2541+
target_lines, target_cursor_x, target_cursor_y = @input_lines[@input_lines_position]
2542+
set_current_lines(target_lines.dup, target_cursor_x, target_cursor_y)
2543+
end
25332544

2545+
private def redo(_key)
25342546
@undoing = true
25352547

2536-
target_lines, target_cursor_x, target_cursor_y = @past_lines.last
2537-
set_current_lines(target_lines, target_cursor_x, target_cursor_y)
2548+
return if @input_lines_position >= @input_lines.size - 1
25382549

2539-
@past_lines.pop
2550+
@input_lines_position += 1
2551+
target_lines, target_cursor_x, target_cursor_y = @input_lines[@input_lines_position]
2552+
set_current_lines(target_lines.dup, target_cursor_x, target_cursor_y)
25402553
end
25412554
end

test/reline/test_key_actor_emacs.rb

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1498,11 +1498,115 @@ def test_undo_with_multiline
14981498
end
14991499

15001500
def test_undo_with_many_times
1501-
str = "a" + "b" * 100
1501+
str = "a" + "b" * 99
15021502
input_keys(str, false)
15031503
100.times { input_keys("\C-_", false) }
15041504
assert_line_around_cursor('a', '')
15051505
input_keys("\C-_", false)
15061506
assert_line_around_cursor('a', '')
15071507
end
1508+
1509+
def test_redo
1510+
input_keys("aあb", false)
1511+
assert_line_around_cursor('aあb', '')
1512+
input_keys("\M-\C-_", false)
1513+
assert_line_around_cursor('aあb', '')
1514+
input_keys("\C-_", false)
1515+
assert_line_around_cursor('aあ', '')
1516+
input_keys("\C-_", false)
1517+
assert_line_around_cursor('a', '')
1518+
input_keys("\M-\C-_", false)
1519+
assert_line_around_cursor('aあ', '')
1520+
input_keys("\M-\C-_", false)
1521+
assert_line_around_cursor('aあb', '')
1522+
input_keys("\C-_", false)
1523+
assert_line_around_cursor('aあ', '')
1524+
input_keys("c", false)
1525+
assert_line_around_cursor('aあc', '')
1526+
input_keys("\M-\C-_", false)
1527+
assert_line_around_cursor('aあc', '')
1528+
end
1529+
1530+
def test_redo_with_cursor_position
1531+
input_keys("abc\C-b\C-h", false)
1532+
assert_line_around_cursor('a', 'c')
1533+
input_keys("\M-\C-_", false)
1534+
assert_line_around_cursor('a', 'c')
1535+
input_keys("\C-_", false)
1536+
assert_line_around_cursor('ab', 'c')
1537+
input_keys("\M-\C-_", false)
1538+
assert_line_around_cursor('a', 'c')
1539+
end
1540+
1541+
def test_redo_with_multiline
1542+
@line_editor.multiline_on
1543+
@line_editor.confirm_multiline_termination_proc = proc {}
1544+
input_keys("1\n2\n3", false)
1545+
assert_whole_lines(["1", "2", "3"])
1546+
assert_line_index(2)
1547+
assert_line_around_cursor('3', '')
1548+
1549+
input_keys("\C-_", false)
1550+
assert_whole_lines(["1", "2", ""])
1551+
assert_line_index(2)
1552+
assert_line_around_cursor('', '')
1553+
1554+
input_keys("\C-_", false)
1555+
assert_whole_lines(["1", "2"])
1556+
assert_line_index(1)
1557+
assert_line_around_cursor('2', '')
1558+
1559+
input_keys("\M-\C-_", false)
1560+
assert_whole_lines(["1", "2", ""])
1561+
assert_line_index(2)
1562+
assert_line_around_cursor('', '')
1563+
1564+
input_keys("\M-\C-_", false)
1565+
assert_whole_lines(["1", "2", "3"])
1566+
assert_line_index(2)
1567+
assert_line_around_cursor('3', '')
1568+
1569+
input_keys("\C-p\C-h\C-h", false)
1570+
assert_whole_lines(["1", "3"])
1571+
assert_line_index(0)
1572+
assert_line_around_cursor('1', '')
1573+
1574+
input_keys("\C-n", false)
1575+
assert_whole_lines(["1", "3"])
1576+
assert_line_index(1)
1577+
assert_line_around_cursor('3', '')
1578+
1579+
input_keys("\C-_", false)
1580+
assert_whole_lines(["1", "", "3"])
1581+
assert_line_index(1)
1582+
assert_line_around_cursor('', '')
1583+
1584+
input_keys("\C-_", false)
1585+
assert_whole_lines(["1", "2", "3"])
1586+
assert_line_index(1)
1587+
assert_line_around_cursor('2', '')
1588+
1589+
input_keys("\M-\C-_", false)
1590+
assert_whole_lines(["1", "", "3"])
1591+
assert_line_index(1)
1592+
assert_line_around_cursor('', '')
1593+
1594+
input_keys("\M-\C-_", false)
1595+
assert_whole_lines(["1", "3"])
1596+
assert_line_index(1)
1597+
assert_line_around_cursor('3', '')
1598+
end
1599+
1600+
def test_redo_with_many_times
1601+
str = "a" + "b" * 98 + "c"
1602+
input_keys(str, false)
1603+
100.times { input_keys("\C-_", false) }
1604+
assert_line_around_cursor('a', '')
1605+
input_keys("\C-_", false)
1606+
assert_line_around_cursor('a', '')
1607+
100.times { input_keys("\M-\C-_", false) }
1608+
assert_line_around_cursor(str, '')
1609+
input_keys("\M-\C-_", false)
1610+
assert_line_around_cursor(str, '')
1611+
end
15081612
end

test/reline/yamatanooroti/test_rendering.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,22 @@ def test_bracketed_paste_with_undo
569569
EOC
570570
end
571571

572+
def test_bracketed_paste_with_redo
573+
omit if Reline.core.io_gate.win?
574+
start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
575+
write("abc")
576+
write("\e[200~def hoge\r\t3\rend\e[201~")
577+
write("\C-_")
578+
write("\M-\C-_")
579+
close
580+
assert_screen(<<~EOC)
581+
Multiline REPL.
582+
prompt> abcdef hoge
583+
prompt> 3
584+
prompt> end
585+
EOC
586+
end
587+
572588
def test_backspace_until_returns_to_initial
573589
start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
574590
write("ABC")

0 commit comments

Comments
 (0)