4
4
require 'tempfile'
5
5
6
6
class Reline ::LineEditor
7
- # TODO: undo
8
7
# TODO: Use "private alias_method" idiom after drop Ruby 2.5.
9
8
attr_reader :byte_pointer
10
9
attr_accessor :confirm_multiline_termination_proc
@@ -251,6 +250,8 @@ def reset_variables(prompt = '', encoding:)
251
250
@resized = false
252
251
@cache = { }
253
252
@rendered_screen = RenderedScreen . new ( base_y : 0 , lines : [ ] , cursor_y : 0 )
253
+ @past_lines = [ ]
254
+ @undoing = false
254
255
reset_line
255
256
end
256
257
@@ -948,7 +949,8 @@ def dialog_proc_scope_completion_journey_data
948
949
unless @waiting_proc
949
950
byte_pointer_diff = @byte_pointer - old_byte_pointer
950
951
@byte_pointer = old_byte_pointer
951
- send ( @vi_waiting_operator , byte_pointer_diff )
952
+ method_obj = method ( @vi_waiting_operator )
953
+ wrap_method_call ( @vi_waiting_operator , method_obj , byte_pointer_diff )
952
954
cleanup_waiting
953
955
end
954
956
else
@@ -1009,7 +1011,8 @@ def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
1009
1011
if @vi_waiting_operator
1010
1012
byte_pointer_diff = @byte_pointer - old_byte_pointer
1011
1013
@byte_pointer = old_byte_pointer
1012
- send ( @vi_waiting_operator , byte_pointer_diff )
1014
+ method_obj = method ( @vi_waiting_operator )
1015
+ wrap_method_call ( @vi_waiting_operator , method_obj , byte_pointer_diff )
1013
1016
cleanup_waiting
1014
1017
end
1015
1018
@kill_ring . process
@@ -1106,6 +1109,7 @@ def update(key)
1106
1109
end
1107
1110
1108
1111
def input_key ( key )
1112
+ save_old_buffer
1109
1113
@config . reset_oneshot_key_bindings
1110
1114
@dialogs . each do |dialog |
1111
1115
if key . char . instance_of? ( Symbol ) and key . char == dialog . name
@@ -1120,7 +1124,6 @@ def input_key(key)
1120
1124
finish
1121
1125
return
1122
1126
end
1123
- old_lines = @buffer_of_lines . dup
1124
1127
@first_char = false
1125
1128
@completion_occurs = false
1126
1129
@@ -1134,12 +1137,15 @@ def input_key(key)
1134
1137
@completion_journey_state = nil
1135
1138
end
1136
1139
1140
+ push_past_lines unless @undoing
1141
+ @undoing = false
1142
+
1137
1143
if @in_pasting
1138
1144
clear_dialogs
1139
1145
return
1140
1146
end
1141
1147
1142
- modified = old_lines != @buffer_of_lines
1148
+ modified = @old_buffer_of_lines != @buffer_of_lines
1143
1149
if !@completion_occurs && modified && !@config . disable_completion && @config . autocompletion
1144
1150
# Auto complete starts only when edited
1145
1151
process_insert ( force : true )
@@ -1148,6 +1154,26 @@ def input_key(key)
1148
1154
modified
1149
1155
end
1150
1156
1157
+ def save_old_buffer
1158
+ @old_buffer_of_lines = @buffer_of_lines . dup
1159
+ @old_byte_pointer = @byte_pointer . dup
1160
+ @old_line_index = @line_index . dup
1161
+ end
1162
+
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 ] )
1166
+ end
1167
+ trim_past_lines
1168
+ end
1169
+
1170
+ MAX_PAST_LINES = 100
1171
+ def trim_past_lines
1172
+ if @past_lines . size > MAX_PAST_LINES
1173
+ @past_lines . shift
1174
+ end
1175
+ end
1176
+
1151
1177
def scroll_into_view
1152
1178
_wrapped_cursor_x , wrapped_cursor_y = wrapped_cursor_position
1153
1179
if wrapped_cursor_y < screen_scroll_top
@@ -1224,6 +1250,18 @@ def set_current_line(line, byte_pointer = nil)
1224
1250
process_auto_indent
1225
1251
end
1226
1252
1253
+ def set_current_lines ( lines , byte_pointer = nil , line_index = 0 )
1254
+ cursor = current_byte_pointer_cursor
1255
+ @buffer_of_lines = lines
1256
+ @line_index = line_index
1257
+ if byte_pointer
1258
+ @byte_pointer = byte_pointer
1259
+ else
1260
+ calculate_nearest_cursor ( cursor )
1261
+ end
1262
+ process_auto_indent
1263
+ end
1264
+
1227
1265
def retrieve_completion_block ( set_completion_quote_character = false )
1228
1266
if Reline . completer_word_break_characters . empty?
1229
1267
word_break_regexp = nil
@@ -1306,13 +1344,15 @@ def confirm_multiline_termination
1306
1344
end
1307
1345
1308
1346
def insert_pasted_text ( text )
1347
+ save_old_buffer
1309
1348
pre = @buffer_of_lines [ @line_index ] . byteslice ( 0 , @byte_pointer )
1310
1349
post = @buffer_of_lines [ @line_index ] . byteslice ( @byte_pointer ..)
1311
1350
lines = ( pre + text . gsub ( /\r \n ?/ , "\n " ) + post ) . split ( "\n " , -1 )
1312
1351
lines << '' if lines . empty?
1313
1352
@buffer_of_lines [ @line_index , 1 ] = lines
1314
1353
@line_index += lines . size - 1
1315
1354
@byte_pointer = @buffer_of_lines [ @line_index ] . bytesize - post . bytesize
1355
+ push_past_lines
1316
1356
end
1317
1357
1318
1358
def insert_text ( text )
@@ -2487,4 +2527,15 @@ def finish
2487
2527
private def vi_editing_mode ( key )
2488
2528
@config . editing_mode = :vi_insert
2489
2529
end
2530
+
2531
+ private def undo ( _key )
2532
+ return if @past_lines . empty?
2533
+
2534
+ @undoing = true
2535
+
2536
+ target_lines , target_cursor_x , target_cursor_y = @past_lines . last
2537
+ set_current_lines ( target_lines , target_cursor_x , target_cursor_y )
2538
+
2539
+ @past_lines . pop
2540
+ end
2490
2541
end
0 commit comments