reline 0.5.2 → 0.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/reline/ansi.rb +24 -33
- data/lib/reline/config.rb +35 -56
- data/lib/reline/key_actor/base.rb +0 -4
- data/lib/reline/key_actor/emacs.rb +3 -3
- data/lib/reline/key_actor/vi_insert.rb +3 -3
- data/lib/reline/line_editor.rb +322 -409
- data/lib/reline/unicode.rb +66 -12
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +21 -12
- metadata +3 -3
data/lib/reline/line_editor.rb
CHANGED
@@ -4,7 +4,6 @@ require 'reline/unicode'
|
|
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
|
@@ -75,7 +74,7 @@ class Reline::LineEditor
|
|
75
74
|
def initialize(config, encoding)
|
76
75
|
@config = config
|
77
76
|
@completion_append_character = ''
|
78
|
-
@screen_size =
|
77
|
+
@screen_size = [0, 0] # Should be initialized with actual winsize in LineEditor#reset
|
79
78
|
reset_variables(encoding: encoding)
|
80
79
|
end
|
81
80
|
|
@@ -112,7 +111,11 @@ class Reline::LineEditor
|
|
112
111
|
else
|
113
112
|
prompt = @prompt
|
114
113
|
end
|
115
|
-
if
|
114
|
+
if !@is_multiline
|
115
|
+
mode_string = check_mode_string
|
116
|
+
prompt = mode_string + prompt if mode_string
|
117
|
+
[prompt] + [''] * (buffer.size - 1)
|
118
|
+
elsif @prompt_proc
|
116
119
|
prompt_list = @prompt_proc.(buffer).map { |pr| pr.gsub("\n", "\\n") }
|
117
120
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
118
121
|
prompt_list = [prompt] if prompt_list.empty?
|
@@ -231,7 +234,6 @@ class Reline::LineEditor
|
|
231
234
|
@vi_waiting_operator_arg = nil
|
232
235
|
@completion_journey_state = nil
|
233
236
|
@completion_state = CompletionState::NORMAL
|
234
|
-
@completion_occurs = false
|
235
237
|
@perfect_matched = nil
|
236
238
|
@menu_info = nil
|
237
239
|
@searching_prompt = nil
|
@@ -248,6 +250,9 @@ class Reline::LineEditor
|
|
248
250
|
@resized = false
|
249
251
|
@cache = {}
|
250
252
|
@rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
|
253
|
+
@input_lines = [[[""], 0, 0]]
|
254
|
+
@input_lines_position = 0
|
255
|
+
@undoing = false
|
251
256
|
reset_line
|
252
257
|
end
|
253
258
|
|
@@ -280,7 +285,7 @@ class Reline::LineEditor
|
|
280
285
|
indent1 = @auto_indent_proc.(@buffer_of_lines.take(@line_index - 1).push(''), @line_index - 1, 0, true)
|
281
286
|
indent2 = @auto_indent_proc.(@buffer_of_lines.take(@line_index), @line_index - 1, @buffer_of_lines[@line_index - 1].bytesize, false)
|
282
287
|
indent = indent2 || indent1
|
283
|
-
@buffer_of_lines[@line_index - 1] = ' ' * indent + @buffer_of_lines[@line_index - 1].gsub(/\A
|
288
|
+
@buffer_of_lines[@line_index - 1] = ' ' * indent + @buffer_of_lines[@line_index - 1].gsub(/\A\s*/, '')
|
284
289
|
)
|
285
290
|
process_auto_indent @line_index, add_newline: true
|
286
291
|
else
|
@@ -291,8 +296,8 @@ class Reline::LineEditor
|
|
291
296
|
end
|
292
297
|
end
|
293
298
|
|
294
|
-
private def split_by_width(str, max_width)
|
295
|
-
Reline::Unicode.split_by_width(str, max_width, @encoding)
|
299
|
+
private def split_by_width(str, max_width, offset: 0)
|
300
|
+
Reline::Unicode.split_by_width(str, max_width, @encoding, offset: offset)
|
296
301
|
end
|
297
302
|
|
298
303
|
def current_byte_pointer_cursor
|
@@ -366,7 +371,7 @@ class Reline::LineEditor
|
|
366
371
|
@scroll_partial_screen
|
367
372
|
end
|
368
373
|
|
369
|
-
def
|
374
|
+
def wrapped_prompt_and_input_lines
|
370
375
|
with_cache(__method__, @buffer_of_lines.size, modified_lines, prompt_list, screen_width) do |n, lines, prompts, width, prev_cache_key, cached_value|
|
371
376
|
prev_n, prev_lines, prev_prompts, prev_width = prev_cache_key
|
372
377
|
cached_wraps = {}
|
@@ -377,9 +382,14 @@ class Reline::LineEditor
|
|
377
382
|
end
|
378
383
|
|
379
384
|
n.times.map do |i|
|
380
|
-
prompt = prompts[i]
|
381
|
-
line = lines[i]
|
382
|
-
cached_wraps[[prompt, line]]
|
385
|
+
prompt = prompts[i] || ''
|
386
|
+
line = lines[i] || ''
|
387
|
+
if (cached = cached_wraps[[prompt, line]])
|
388
|
+
next cached
|
389
|
+
end
|
390
|
+
*wrapped_prompts, code_line_prompt = split_by_width(prompt, width).first.compact
|
391
|
+
wrapped_lines = split_by_width(line, width, offset: calculate_width(code_line_prompt, true)).first.compact
|
392
|
+
wrapped_prompts.map { |p| [p, ''] } + [[code_line_prompt, wrapped_lines.first]] + wrapped_lines.drop(1).map { |c| ['', c] }
|
383
393
|
end
|
384
394
|
end
|
385
395
|
end
|
@@ -405,8 +415,13 @@ class Reline::LineEditor
|
|
405
415
|
@output.write "#{Reline::IOGate::RESET_COLOR}#{' ' * width}"
|
406
416
|
else
|
407
417
|
x, w, content = new_items[level]
|
408
|
-
|
409
|
-
|
418
|
+
cover_begin = base_x != 0 && new_levels[base_x - 1] == level
|
419
|
+
cover_end = new_levels[base_x + width] == level
|
420
|
+
pos = 0
|
421
|
+
unless x == base_x && w == width
|
422
|
+
content, pos = Reline::Unicode.take_mbchar_range(content, base_x - x, width, cover_begin: cover_begin, cover_end: cover_end, padding: true)
|
423
|
+
end
|
424
|
+
Reline::IOGate.move_cursor_column x + pos
|
410
425
|
@output.write "#{Reline::IOGate::RESET_COLOR}#{content}#{Reline::IOGate::RESET_COLOR}"
|
411
426
|
end
|
412
427
|
base_x += width
|
@@ -422,7 +437,7 @@ class Reline::LineEditor
|
|
422
437
|
prompt_width = calculate_width(prompt_list[@line_index], true)
|
423
438
|
line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
|
424
439
|
wrapped_line_before_cursor = split_by_width(' ' * prompt_width + line_before_cursor, screen_width).first.compact
|
425
|
-
wrapped_cursor_y =
|
440
|
+
wrapped_cursor_y = wrapped_prompt_and_input_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
|
426
441
|
wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
|
427
442
|
[wrapped_cursor_x, wrapped_cursor_y]
|
428
443
|
end
|
@@ -486,8 +501,9 @@ class Reline::LineEditor
|
|
486
501
|
wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
|
487
502
|
|
488
503
|
rendered_lines = @rendered_screen.lines
|
489
|
-
new_lines =
|
490
|
-
|
504
|
+
new_lines = wrapped_prompt_and_input_lines.flatten(1)[screen_scroll_top, screen_height].map do |prompt, line|
|
505
|
+
prompt_width = Reline::Unicode.calculate_width(prompt, true)
|
506
|
+
[[0, prompt_width, prompt], [prompt_width, Reline::Unicode.calculate_width(line, true), line]]
|
491
507
|
end
|
492
508
|
if @menu_info
|
493
509
|
@menu_info.lines(screen_width).each do |item|
|
@@ -503,7 +519,8 @@ class Reline::LineEditor
|
|
503
519
|
y_range.each do |row|
|
504
520
|
next if row < 0 || row >= screen_height
|
505
521
|
dialog_rows = new_lines[row] ||= []
|
506
|
-
|
522
|
+
# index 0 is for prompt, index 1 is for line, index 2.. is for dialog
|
523
|
+
dialog_rows[index + 2] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]]
|
507
524
|
end
|
508
525
|
end
|
509
526
|
|
@@ -688,13 +705,6 @@ class Reline::LineEditor
|
|
688
705
|
|
689
706
|
DIALOG_DEFAULT_HEIGHT = 20
|
690
707
|
|
691
|
-
private def padding_space_with_escape_sequences(str, width)
|
692
|
-
padding_width = width - calculate_width(str, true)
|
693
|
-
# padding_width should be only positive value. But macOS and Alacritty returns negative value.
|
694
|
-
padding_width = 0 if padding_width < 0
|
695
|
-
str + (' ' * padding_width)
|
696
|
-
end
|
697
|
-
|
698
708
|
private def dialog_range(dialog, dialog_y)
|
699
709
|
x_range = dialog.column...dialog.column + dialog.width
|
700
710
|
y_range = dialog_y + dialog.vertical_offset...dialog_y + dialog.vertical_offset + dialog.contents.size
|
@@ -767,7 +777,7 @@ class Reline::LineEditor
|
|
767
777
|
dialog.contents = contents.map.with_index do |item, i|
|
768
778
|
line_sgr = i == pointer ? enhanced_sgr : default_sgr
|
769
779
|
str_width = dialog.width - (scrollbar_pos.nil? ? 0 : @block_elem_width)
|
770
|
-
str =
|
780
|
+
str, = Reline::Unicode.take_mbchar_range(item, 0, str_width, padding: true)
|
771
781
|
colored_content = "#{line_sgr}#{str}"
|
772
782
|
if scrollbar_pos
|
773
783
|
if scrollbar_pos <= (i * 2) and (i * 2 + 1) < (scrollbar_pos + bar_height)
|
@@ -847,7 +857,7 @@ class Reline::LineEditor
|
|
847
857
|
[target, preposing, completed, postposing]
|
848
858
|
end
|
849
859
|
|
850
|
-
private def
|
860
|
+
private def perform_completion(list, just_show_list)
|
851
861
|
case @completion_state
|
852
862
|
when CompletionState::NORMAL
|
853
863
|
@completion_state = CompletionState::COMPLETION
|
@@ -876,10 +886,12 @@ class Reline::LineEditor
|
|
876
886
|
@completion_state = CompletionState::PERFECT_MATCH
|
877
887
|
else
|
878
888
|
@completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
|
889
|
+
perform_completion(list, true) if @config.show_all_if_ambiguous
|
879
890
|
end
|
880
891
|
@perfect_matched = completed
|
881
892
|
else
|
882
893
|
@completion_state = CompletionState::MENU
|
894
|
+
perform_completion(list, true) if @config.show_all_if_ambiguous
|
883
895
|
end
|
884
896
|
if not just_show_list and target < completed
|
885
897
|
@buffer_of_lines[@line_index] = (preposing + completed + completion_append_character.to_s + postposing).split("\n")[@line_index] || String.new(encoding: @encoding)
|
@@ -938,7 +950,8 @@ class Reline::LineEditor
|
|
938
950
|
unless @waiting_proc
|
939
951
|
byte_pointer_diff = @byte_pointer - old_byte_pointer
|
940
952
|
@byte_pointer = old_byte_pointer
|
941
|
-
|
953
|
+
method_obj = method(@vi_waiting_operator)
|
954
|
+
wrap_method_call(@vi_waiting_operator, method_obj, byte_pointer_diff)
|
942
955
|
cleanup_waiting
|
943
956
|
end
|
944
957
|
else
|
@@ -999,7 +1012,8 @@ class Reline::LineEditor
|
|
999
1012
|
if @vi_waiting_operator
|
1000
1013
|
byte_pointer_diff = @byte_pointer - old_byte_pointer
|
1001
1014
|
@byte_pointer = old_byte_pointer
|
1002
|
-
|
1015
|
+
method_obj = method(@vi_waiting_operator)
|
1016
|
+
wrap_method_call(@vi_waiting_operator, method_obj, byte_pointer_diff)
|
1003
1017
|
cleanup_waiting
|
1004
1018
|
end
|
1005
1019
|
@kill_ring.process
|
@@ -1054,10 +1068,6 @@ class Reline::LineEditor
|
|
1054
1068
|
end
|
1055
1069
|
|
1056
1070
|
private def normal_char(key)
|
1057
|
-
if key.combined_char.is_a?(Symbol)
|
1058
|
-
process_key(key.combined_char, key.combined_char)
|
1059
|
-
return
|
1060
|
-
end
|
1061
1071
|
@multibyte_buffer << key.combined_char
|
1062
1072
|
if @multibyte_buffer.size > 1
|
1063
1073
|
if @multibyte_buffer.dup.force_encoding(@encoding).valid_encoding?
|
@@ -1100,6 +1110,7 @@ class Reline::LineEditor
|
|
1100
1110
|
end
|
1101
1111
|
|
1102
1112
|
def input_key(key)
|
1113
|
+
save_old_buffer
|
1103
1114
|
@config.reset_oneshot_key_bindings
|
1104
1115
|
@dialogs.each do |dialog|
|
1105
1116
|
if key.char.instance_of?(Symbol) and key.char == dialog.name
|
@@ -1107,38 +1118,17 @@ class Reline::LineEditor
|
|
1107
1118
|
end
|
1108
1119
|
end
|
1109
1120
|
if key.char.nil?
|
1121
|
+
process_insert(force: true)
|
1110
1122
|
if @first_char
|
1111
1123
|
@eof = true
|
1112
1124
|
end
|
1113
1125
|
finish
|
1114
1126
|
return
|
1115
1127
|
end
|
1116
|
-
old_lines = @buffer_of_lines.dup
|
1117
1128
|
@first_char = false
|
1118
1129
|
@completion_occurs = false
|
1119
|
-
|
1120
|
-
|
1121
|
-
process_insert(force: true)
|
1122
|
-
if @config.autocompletion
|
1123
|
-
@completion_state = CompletionState::NORMAL
|
1124
|
-
@completion_occurs = move_completed_list(:down)
|
1125
|
-
else
|
1126
|
-
@completion_journey_state = nil
|
1127
|
-
result = call_completion_proc
|
1128
|
-
if result.is_a?(Array)
|
1129
|
-
@completion_occurs = true
|
1130
|
-
complete(result, false)
|
1131
|
-
end
|
1132
|
-
end
|
1133
|
-
end
|
1134
|
-
elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
|
1135
|
-
# In vi mode, move completed list even if autocompletion is off
|
1136
|
-
if not @config.disable_completion
|
1137
|
-
process_insert(force: true)
|
1138
|
-
@completion_state = CompletionState::NORMAL
|
1139
|
-
@completion_occurs = move_completed_list("\C-p".ord == key.char ? :up : :down)
|
1140
|
-
end
|
1141
|
-
elsif Symbol === key.char and respond_to?(key.char, true)
|
1130
|
+
|
1131
|
+
if key.char.is_a?(Symbol)
|
1142
1132
|
process_key(key.char, key.char)
|
1143
1133
|
else
|
1144
1134
|
normal_char(key)
|
@@ -1148,12 +1138,15 @@ class Reline::LineEditor
|
|
1148
1138
|
@completion_journey_state = nil
|
1149
1139
|
end
|
1150
1140
|
|
1141
|
+
push_input_lines unless @undoing
|
1142
|
+
@undoing = false
|
1143
|
+
|
1151
1144
|
if @in_pasting
|
1152
1145
|
clear_dialogs
|
1153
1146
|
return
|
1154
1147
|
end
|
1155
1148
|
|
1156
|
-
modified =
|
1149
|
+
modified = @old_buffer_of_lines != @buffer_of_lines
|
1157
1150
|
if !@completion_occurs && modified && !@config.disable_completion && @config.autocompletion
|
1158
1151
|
# Auto complete starts only when edited
|
1159
1152
|
process_insert(force: true)
|
@@ -1162,6 +1155,29 @@ class Reline::LineEditor
|
|
1162
1155
|
modified
|
1163
1156
|
end
|
1164
1157
|
|
1158
|
+
def save_old_buffer
|
1159
|
+
@old_buffer_of_lines = @buffer_of_lines.dup
|
1160
|
+
end
|
1161
|
+
|
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])
|
1169
|
+
end
|
1170
|
+
trim_input_lines
|
1171
|
+
end
|
1172
|
+
|
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
|
1178
|
+
end
|
1179
|
+
end
|
1180
|
+
|
1165
1181
|
def scroll_into_view
|
1166
1182
|
_wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
|
1167
1183
|
if wrapped_cursor_y < screen_scroll_top
|
@@ -1220,7 +1236,7 @@ class Reline::LineEditor
|
|
1220
1236
|
end
|
1221
1237
|
|
1222
1238
|
def line()
|
1223
|
-
|
1239
|
+
@buffer_of_lines.join("\n") unless eof?
|
1224
1240
|
end
|
1225
1241
|
|
1226
1242
|
def current_line
|
@@ -1238,6 +1254,18 @@ class Reline::LineEditor
|
|
1238
1254
|
process_auto_indent
|
1239
1255
|
end
|
1240
1256
|
|
1257
|
+
def set_current_lines(lines, byte_pointer = nil, line_index = 0)
|
1258
|
+
cursor = current_byte_pointer_cursor
|
1259
|
+
@buffer_of_lines = lines
|
1260
|
+
@line_index = line_index
|
1261
|
+
if byte_pointer
|
1262
|
+
@byte_pointer = byte_pointer
|
1263
|
+
else
|
1264
|
+
calculate_nearest_cursor(cursor)
|
1265
|
+
end
|
1266
|
+
process_auto_indent
|
1267
|
+
end
|
1268
|
+
|
1241
1269
|
def retrieve_completion_block(set_completion_quote_character = false)
|
1242
1270
|
if Reline.completer_word_break_characters.empty?
|
1243
1271
|
word_break_regexp = nil
|
@@ -1304,14 +1332,12 @@ class Reline::LineEditor
|
|
1304
1332
|
end
|
1305
1333
|
target = before
|
1306
1334
|
end
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
|
1314
|
-
end
|
1335
|
+
lines = whole_lines
|
1336
|
+
if @line_index > 0
|
1337
|
+
preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
|
1338
|
+
end
|
1339
|
+
if (lines.size - 1) > @line_index
|
1340
|
+
postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
|
1315
1341
|
end
|
1316
1342
|
[preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
|
1317
1343
|
end
|
@@ -1321,6 +1347,18 @@ class Reline::LineEditor
|
|
1321
1347
|
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
|
1322
1348
|
end
|
1323
1349
|
|
1350
|
+
def insert_pasted_text(text)
|
1351
|
+
save_old_buffer
|
1352
|
+
pre = @buffer_of_lines[@line_index].byteslice(0, @byte_pointer)
|
1353
|
+
post = @buffer_of_lines[@line_index].byteslice(@byte_pointer..)
|
1354
|
+
lines = (pre + text.gsub(/\r\n?/, "\n") + post).split("\n", -1)
|
1355
|
+
lines << '' if lines.empty?
|
1356
|
+
@buffer_of_lines[@line_index, 1] = lines
|
1357
|
+
@line_index += lines.size - 1
|
1358
|
+
@byte_pointer = @buffer_of_lines[@line_index].bytesize - post.bytesize
|
1359
|
+
push_input_lines
|
1360
|
+
end
|
1361
|
+
|
1324
1362
|
def insert_text(text)
|
1325
1363
|
if @buffer_of_lines[@line_index].bytesize == @byte_pointer
|
1326
1364
|
@buffer_of_lines[@line_index] += text
|
@@ -1333,20 +1371,16 @@ class Reline::LineEditor
|
|
1333
1371
|
|
1334
1372
|
def delete_text(start = nil, length = nil)
|
1335
1373
|
if start.nil? and length.nil?
|
1336
|
-
if @
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
@byte_pointer = 0
|
1347
|
-
end
|
1348
|
-
else
|
1349
|
-
set_current_line('', 0)
|
1374
|
+
if @buffer_of_lines.size == 1
|
1375
|
+
@buffer_of_lines[@line_index] = ''
|
1376
|
+
@byte_pointer = 0
|
1377
|
+
elsif @line_index == (@buffer_of_lines.size - 1) and @line_index > 0
|
1378
|
+
@buffer_of_lines.pop
|
1379
|
+
@line_index -= 1
|
1380
|
+
@byte_pointer = 0
|
1381
|
+
elsif @line_index < (@buffer_of_lines.size - 1)
|
1382
|
+
@buffer_of_lines.delete_at(@line_index)
|
1383
|
+
@byte_pointer = 0
|
1350
1384
|
end
|
1351
1385
|
elsif not start.nil? and not length.nil?
|
1352
1386
|
if current_line
|
@@ -1423,13 +1457,42 @@ class Reline::LineEditor
|
|
1423
1457
|
end
|
1424
1458
|
end
|
1425
1459
|
|
1426
|
-
private def
|
1427
|
-
if
|
1460
|
+
private def complete(_key)
|
1461
|
+
return if @config.disable_completion
|
1462
|
+
|
1463
|
+
process_insert(force: true)
|
1464
|
+
if @config.autocompletion
|
1428
1465
|
@completion_state = CompletionState::NORMAL
|
1429
|
-
@completion_occurs = move_completed_list(:
|
1466
|
+
@completion_occurs = move_completed_list(:down)
|
1467
|
+
else
|
1468
|
+
@completion_journey_state = nil
|
1469
|
+
result = call_completion_proc
|
1470
|
+
if result.is_a?(Array)
|
1471
|
+
@completion_occurs = true
|
1472
|
+
perform_completion(result, false)
|
1473
|
+
end
|
1430
1474
|
end
|
1431
1475
|
end
|
1432
|
-
|
1476
|
+
|
1477
|
+
private def completion_journey_move(direction)
|
1478
|
+
return if @config.disable_completion
|
1479
|
+
|
1480
|
+
process_insert(force: true)
|
1481
|
+
@completion_state = CompletionState::NORMAL
|
1482
|
+
@completion_occurs = move_completed_list(direction)
|
1483
|
+
end
|
1484
|
+
|
1485
|
+
private def menu_complete(_key)
|
1486
|
+
completion_journey_move(:down)
|
1487
|
+
end
|
1488
|
+
|
1489
|
+
private def menu_complete_backward(_key)
|
1490
|
+
completion_journey_move(:up)
|
1491
|
+
end
|
1492
|
+
|
1493
|
+
private def completion_journey_up(_key)
|
1494
|
+
completion_journey_move(:up) if @config.autocompletion
|
1495
|
+
end
|
1433
1496
|
|
1434
1497
|
# Editline:: +ed-unassigned+ This editor command always results in an error.
|
1435
1498
|
# GNU Readline:: There is no corresponding macro.
|
@@ -1502,7 +1565,7 @@ class Reline::LineEditor
|
|
1502
1565
|
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
1503
1566
|
if (@byte_pointer < current_line.bytesize)
|
1504
1567
|
@byte_pointer += byte_size
|
1505
|
-
elsif @
|
1568
|
+
elsif @config.editing_mode_is?(:emacs) and @byte_pointer == current_line.bytesize and @line_index < @buffer_of_lines.size - 1
|
1506
1569
|
@byte_pointer = 0
|
1507
1570
|
@line_index += 1
|
1508
1571
|
end
|
@@ -1515,7 +1578,7 @@ class Reline::LineEditor
|
|
1515
1578
|
if @byte_pointer > 0
|
1516
1579
|
byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
|
1517
1580
|
@byte_pointer -= byte_size
|
1518
|
-
elsif @
|
1581
|
+
elsif @config.editing_mode_is?(:emacs) and @byte_pointer == 0 and @line_index > 0
|
1519
1582
|
@line_index -= 1
|
1520
1583
|
@byte_pointer = current_line.bytesize
|
1521
1584
|
end
|
@@ -1535,139 +1598,99 @@ class Reline::LineEditor
|
|
1535
1598
|
alias_method :vi_zero, :ed_move_to_beg
|
1536
1599
|
|
1537
1600
|
private def ed_move_to_end(key)
|
1538
|
-
@byte_pointer =
|
1539
|
-
while @byte_pointer < current_line.bytesize
|
1540
|
-
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
1541
|
-
@byte_pointer += byte_size
|
1542
|
-
end
|
1601
|
+
@byte_pointer = current_line.bytesize
|
1543
1602
|
end
|
1544
1603
|
alias_method :end_of_line, :ed_move_to_end
|
1545
1604
|
|
1546
|
-
private def generate_searcher
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
case
|
1553
|
-
when "\C-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1605
|
+
private def generate_searcher(search_key)
|
1606
|
+
search_word = String.new(encoding: @encoding)
|
1607
|
+
multibyte_buf = String.new(encoding: 'ASCII-8BIT')
|
1608
|
+
hit_pointer = nil
|
1609
|
+
lambda do |key|
|
1610
|
+
search_again = false
|
1611
|
+
case key
|
1612
|
+
when "\C-h".ord, "\C-?".ord
|
1613
|
+
grapheme_clusters = search_word.grapheme_clusters
|
1614
|
+
if grapheme_clusters.size > 0
|
1615
|
+
grapheme_clusters.pop
|
1616
|
+
search_word = grapheme_clusters.join
|
1617
|
+
end
|
1618
|
+
when "\C-r".ord, "\C-s".ord
|
1619
|
+
search_again = true if search_key == key
|
1620
|
+
search_key = key
|
1621
|
+
else
|
1622
|
+
multibyte_buf << key
|
1623
|
+
if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
|
1624
|
+
search_word << multibyte_buf.dup.force_encoding(@encoding)
|
1625
|
+
multibyte_buf.clear
|
1626
|
+
end
|
1557
1627
|
end
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
grapheme_clusters = search_word.grapheme_clusters
|
1567
|
-
if grapheme_clusters.size > 0
|
1568
|
-
grapheme_clusters.pop
|
1569
|
-
search_word = grapheme_clusters.join
|
1570
|
-
end
|
1571
|
-
when "\C-r".ord, "\C-s".ord
|
1572
|
-
search_again = true if prev_search_key == key
|
1573
|
-
prev_search_key = key
|
1574
|
-
else
|
1575
|
-
multibyte_buf << key
|
1576
|
-
if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
|
1577
|
-
search_word << multibyte_buf.dup.force_encoding(@encoding)
|
1578
|
-
multibyte_buf.clear
|
1628
|
+
hit = nil
|
1629
|
+
if not search_word.empty? and @line_backup_in_history&.include?(search_word)
|
1630
|
+
hit_pointer = Reline::HISTORY.size
|
1631
|
+
hit = @line_backup_in_history
|
1632
|
+
else
|
1633
|
+
if search_again
|
1634
|
+
if search_word.empty? and Reline.last_incremental_search
|
1635
|
+
search_word = Reline.last_incremental_search
|
1579
1636
|
end
|
1580
|
-
|
1581
|
-
|
1582
|
-
if not search_word.empty? and @line_backup_in_history&.include?(search_word)
|
1583
|
-
@history_pointer = nil
|
1584
|
-
hit = @line_backup_in_history
|
1585
|
-
else
|
1586
|
-
if search_again
|
1587
|
-
if search_word.empty? and Reline.last_incremental_search
|
1588
|
-
search_word = Reline.last_incremental_search
|
1589
|
-
end
|
1590
|
-
if @history_pointer
|
1591
|
-
case prev_search_key
|
1592
|
-
when "\C-r".ord
|
1593
|
-
history_pointer_base = 0
|
1594
|
-
history = Reline::HISTORY[0..(@history_pointer - 1)]
|
1595
|
-
when "\C-s".ord
|
1596
|
-
history_pointer_base = @history_pointer + 1
|
1597
|
-
history = Reline::HISTORY[(@history_pointer + 1)..-1]
|
1598
|
-
end
|
1599
|
-
else
|
1600
|
-
history_pointer_base = 0
|
1601
|
-
history = Reline::HISTORY
|
1602
|
-
end
|
1603
|
-
elsif @history_pointer
|
1604
|
-
case prev_search_key
|
1637
|
+
if @history_pointer
|
1638
|
+
case search_key
|
1605
1639
|
when "\C-r".ord
|
1606
1640
|
history_pointer_base = 0
|
1607
|
-
history = Reline::HISTORY[0
|
1641
|
+
history = Reline::HISTORY[0..(@history_pointer - 1)]
|
1608
1642
|
when "\C-s".ord
|
1609
|
-
history_pointer_base = @history_pointer
|
1610
|
-
history = Reline::HISTORY[@history_pointer..-1]
|
1643
|
+
history_pointer_base = @history_pointer + 1
|
1644
|
+
history = Reline::HISTORY[(@history_pointer + 1)..-1]
|
1611
1645
|
end
|
1612
1646
|
else
|
1613
1647
|
history_pointer_base = 0
|
1614
1648
|
history = Reline::HISTORY
|
1615
1649
|
end
|
1616
|
-
|
1650
|
+
elsif @history_pointer
|
1651
|
+
case search_key
|
1617
1652
|
when "\C-r".ord
|
1618
|
-
|
1619
|
-
|
1620
|
-
}
|
1653
|
+
history_pointer_base = 0
|
1654
|
+
history = Reline::HISTORY[0..@history_pointer]
|
1621
1655
|
when "\C-s".ord
|
1622
|
-
|
1623
|
-
|
1624
|
-
}
|
1625
|
-
end
|
1626
|
-
if hit_index
|
1627
|
-
@history_pointer = history_pointer_base + hit_index
|
1628
|
-
hit = Reline::HISTORY[@history_pointer]
|
1656
|
+
history_pointer_base = @history_pointer
|
1657
|
+
history = Reline::HISTORY[@history_pointer..-1]
|
1629
1658
|
end
|
1659
|
+
else
|
1660
|
+
history_pointer_base = 0
|
1661
|
+
history = Reline::HISTORY
|
1630
1662
|
end
|
1631
|
-
case
|
1663
|
+
case search_key
|
1632
1664
|
when "\C-r".ord
|
1633
|
-
|
1665
|
+
hit_index = history.rindex { |item|
|
1666
|
+
item.include?(search_word)
|
1667
|
+
}
|
1634
1668
|
when "\C-s".ord
|
1635
|
-
|
1669
|
+
hit_index = history.index { |item|
|
1670
|
+
item.include?(search_word)
|
1671
|
+
}
|
1636
1672
|
end
|
1637
|
-
if
|
1638
|
-
|
1639
|
-
|
1640
|
-
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1641
|
-
@line_index = @buffer_of_lines.size - 1
|
1642
|
-
@byte_pointer = current_line.bytesize
|
1643
|
-
@searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
|
1644
|
-
else
|
1645
|
-
@buffer_of_lines = [hit]
|
1646
|
-
@byte_pointer = hit.bytesize
|
1647
|
-
@searching_prompt = "(%s)`%s': %s" % [prompt_name, search_word, hit]
|
1648
|
-
end
|
1649
|
-
last_hit = hit
|
1650
|
-
else
|
1651
|
-
if @is_multiline
|
1652
|
-
@searching_prompt = "(failed %s)`%s'" % [prompt_name, search_word]
|
1653
|
-
else
|
1654
|
-
@searching_prompt = "(failed %s)`%s': %s" % [prompt_name, search_word, last_hit]
|
1655
|
-
end
|
1673
|
+
if hit_index
|
1674
|
+
hit_pointer = history_pointer_base + hit_index
|
1675
|
+
hit = Reline::HISTORY[hit_pointer]
|
1656
1676
|
end
|
1657
1677
|
end
|
1678
|
+
case search_key
|
1679
|
+
when "\C-r".ord
|
1680
|
+
prompt_name = 'reverse-i-search'
|
1681
|
+
when "\C-s".ord
|
1682
|
+
prompt_name = 'i-search'
|
1683
|
+
end
|
1684
|
+
prompt_name = "failed #{prompt_name}" unless hit
|
1685
|
+
[search_word, prompt_name, hit_pointer]
|
1658
1686
|
end
|
1659
1687
|
end
|
1660
1688
|
|
1661
1689
|
private def incremental_search_history(key)
|
1662
1690
|
unless @history_pointer
|
1663
|
-
|
1664
|
-
@line_backup_in_history = whole_buffer
|
1665
|
-
else
|
1666
|
-
@line_backup_in_history = current_line
|
1667
|
-
end
|
1691
|
+
@line_backup_in_history = whole_buffer
|
1668
1692
|
end
|
1669
|
-
searcher = generate_searcher
|
1670
|
-
searcher.resume(key)
|
1693
|
+
searcher = generate_searcher(key)
|
1671
1694
|
@searching_prompt = "(reverse-i-search)`': "
|
1672
1695
|
termination_keys = ["\C-j".ord]
|
1673
1696
|
termination_keys.concat(@config.isearch_terminators&.chars&.map(&:ord)) if @config.isearch_terminators
|
@@ -1679,53 +1702,41 @@ class Reline::LineEditor
|
|
1679
1702
|
else
|
1680
1703
|
buffer = @line_backup_in_history
|
1681
1704
|
end
|
1682
|
-
|
1683
|
-
|
1684
|
-
|
1685
|
-
@line_index = @buffer_of_lines.size - 1
|
1686
|
-
else
|
1687
|
-
@buffer_of_lines = [buffer]
|
1688
|
-
end
|
1705
|
+
@buffer_of_lines = buffer.split("\n")
|
1706
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1707
|
+
@line_index = @buffer_of_lines.size - 1
|
1689
1708
|
@searching_prompt = nil
|
1690
1709
|
@waiting_proc = nil
|
1691
1710
|
@byte_pointer = 0
|
1692
|
-
searcher.resume(-1)
|
1693
1711
|
when "\C-g".ord
|
1694
|
-
|
1695
|
-
|
1696
|
-
|
1697
|
-
|
1698
|
-
else
|
1699
|
-
@buffer_of_lines = [@line_backup_in_history]
|
1700
|
-
end
|
1701
|
-
@history_pointer = nil
|
1712
|
+
@buffer_of_lines = @line_backup_in_history.split("\n")
|
1713
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1714
|
+
@line_index = @buffer_of_lines.size - 1
|
1715
|
+
move_history(nil, line: :end, cursor: :end, save_buffer: false)
|
1702
1716
|
@searching_prompt = nil
|
1703
1717
|
@waiting_proc = nil
|
1704
|
-
@line_backup_in_history = nil
|
1705
1718
|
@byte_pointer = 0
|
1706
1719
|
else
|
1707
1720
|
chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
|
1708
1721
|
if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
|
1709
|
-
searcher.
|
1722
|
+
search_word, prompt_name, hit_pointer = searcher.call(k)
|
1723
|
+
Reline.last_incremental_search = search_word
|
1724
|
+
@searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
|
1725
|
+
@searching_prompt += ': ' unless @is_multiline
|
1726
|
+
move_history(hit_pointer, line: :end, cursor: :end, save_buffer: false) if hit_pointer
|
1710
1727
|
else
|
1711
1728
|
if @history_pointer
|
1712
1729
|
line = Reline::HISTORY[@history_pointer]
|
1713
1730
|
else
|
1714
1731
|
line = @line_backup_in_history
|
1715
1732
|
end
|
1716
|
-
|
1717
|
-
|
1718
|
-
|
1719
|
-
|
1720
|
-
@line_index = @buffer_of_lines.size - 1
|
1721
|
-
else
|
1722
|
-
@line_backup_in_history = current_line
|
1723
|
-
@buffer_of_lines = [line]
|
1724
|
-
end
|
1733
|
+
@line_backup_in_history = whole_buffer
|
1734
|
+
@buffer_of_lines = line.split("\n")
|
1735
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1736
|
+
@line_index = @buffer_of_lines.size - 1
|
1725
1737
|
@searching_prompt = nil
|
1726
1738
|
@waiting_proc = nil
|
1727
1739
|
@byte_pointer = 0
|
1728
|
-
searcher.resume(-1)
|
1729
1740
|
end
|
1730
1741
|
end
|
1731
1742
|
}
|
@@ -1741,191 +1752,95 @@ class Reline::LineEditor
|
|
1741
1752
|
end
|
1742
1753
|
alias_method :forward_search_history, :vi_search_next
|
1743
1754
|
|
1744
|
-
private def
|
1745
|
-
|
1746
|
-
|
1747
|
-
|
1748
|
-
|
1749
|
-
|
1750
|
-
return if not current_line.empty? and substr.empty?
|
1751
|
-
history = Reline::HISTORY
|
1752
|
-
elsif @history_pointer.zero?
|
1753
|
-
history = nil
|
1754
|
-
h_pointer = nil
|
1755
|
-
else
|
1756
|
-
history = Reline::HISTORY.slice(0, @history_pointer)
|
1757
|
-
end
|
1758
|
-
return if history.nil?
|
1759
|
-
if @is_multiline
|
1760
|
-
h_pointer = history.rindex { |h|
|
1761
|
-
h.split("\n").each_with_index { |l, i|
|
1762
|
-
if l.start_with?(substr)
|
1763
|
-
line_no = i
|
1764
|
-
break
|
1765
|
-
end
|
1766
|
-
}
|
1767
|
-
not line_no.nil?
|
1768
|
-
}
|
1769
|
-
else
|
1770
|
-
h_pointer = history.rindex { |l|
|
1771
|
-
l.start_with?(substr)
|
1772
|
-
}
|
1773
|
-
end
|
1774
|
-
return if h_pointer.nil?
|
1775
|
-
@history_pointer = h_pointer
|
1776
|
-
cursor = current_byte_pointer_cursor
|
1777
|
-
if @is_multiline
|
1778
|
-
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1779
|
-
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1780
|
-
@line_index = line_no
|
1781
|
-
calculate_nearest_cursor(cursor)
|
1782
|
-
else
|
1783
|
-
@buffer_of_lines = [Reline::HISTORY[@history_pointer]]
|
1784
|
-
calculate_nearest_cursor(cursor)
|
1755
|
+
private def search_history(prefix, pointer_range)
|
1756
|
+
pointer_range.each do |pointer|
|
1757
|
+
lines = Reline::HISTORY[pointer].split("\n")
|
1758
|
+
lines.each_with_index do |line, index|
|
1759
|
+
return [pointer, index] if line.start_with?(prefix)
|
1760
|
+
end
|
1785
1761
|
end
|
1762
|
+
nil
|
1763
|
+
end
|
1764
|
+
|
1765
|
+
private def ed_search_prev_history(key, arg: 1)
|
1766
|
+
substr = current_line.byteslice(0, @byte_pointer)
|
1767
|
+
return if @history_pointer == 0
|
1768
|
+
return if @history_pointer.nil? && substr.empty? && !current_line.empty?
|
1769
|
+
|
1770
|
+
history_range = 0...(@history_pointer || Reline::HISTORY.size)
|
1771
|
+
h_pointer, line_index = search_history(substr, history_range.reverse_each)
|
1772
|
+
return unless h_pointer
|
1773
|
+
move_history(h_pointer, line: line_index || :start, cursor: @byte_pointer)
|
1786
1774
|
arg -= 1
|
1787
1775
|
ed_search_prev_history(key, arg: arg) if arg > 0
|
1788
1776
|
end
|
1789
1777
|
alias_method :history_search_backward, :ed_search_prev_history
|
1790
1778
|
|
1791
1779
|
private def ed_search_next_history(key, arg: 1)
|
1792
|
-
substr = current_line.
|
1793
|
-
if @history_pointer.nil?
|
1794
|
-
|
1795
|
-
|
1796
|
-
|
1797
|
-
end
|
1798
|
-
history = Reline::HISTORY.slice((@history_pointer + 1)..-1)
|
1799
|
-
h_pointer = nil
|
1800
|
-
line_no = nil
|
1801
|
-
if @is_multiline
|
1802
|
-
h_pointer = history.index { |h|
|
1803
|
-
h.split("\n").each_with_index { |l, i|
|
1804
|
-
if l.start_with?(substr)
|
1805
|
-
line_no = i
|
1806
|
-
break
|
1807
|
-
end
|
1808
|
-
}
|
1809
|
-
not line_no.nil?
|
1810
|
-
}
|
1811
|
-
else
|
1812
|
-
h_pointer = history.index { |l|
|
1813
|
-
l.start_with?(substr)
|
1814
|
-
}
|
1815
|
-
end
|
1816
|
-
h_pointer += @history_pointer + 1 if h_pointer and @history_pointer
|
1780
|
+
substr = current_line.byteslice(0, @byte_pointer)
|
1781
|
+
return if @history_pointer.nil?
|
1782
|
+
|
1783
|
+
history_range = @history_pointer + 1...Reline::HISTORY.size
|
1784
|
+
h_pointer, line_index = search_history(substr, history_range)
|
1817
1785
|
return if h_pointer.nil? and not substr.empty?
|
1818
|
-
|
1819
|
-
|
1820
|
-
if @history_pointer.nil? and substr.empty?
|
1821
|
-
@buffer_of_lines = []
|
1822
|
-
@line_index = 0
|
1823
|
-
@byte_pointer = 0
|
1824
|
-
else
|
1825
|
-
cursor = current_byte_pointer_cursor
|
1826
|
-
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1827
|
-
@line_index = line_no
|
1828
|
-
calculate_nearest_cursor(cursor)
|
1829
|
-
end
|
1830
|
-
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1831
|
-
else
|
1832
|
-
if @history_pointer.nil? and substr.empty?
|
1833
|
-
set_current_line('', 0)
|
1834
|
-
else
|
1835
|
-
set_current_line(Reline::HISTORY[@history_pointer])
|
1836
|
-
end
|
1837
|
-
end
|
1786
|
+
|
1787
|
+
move_history(h_pointer, line: line_index || :start, cursor: @byte_pointer)
|
1838
1788
|
arg -= 1
|
1839
1789
|
ed_search_next_history(key, arg: arg) if arg > 0
|
1840
1790
|
end
|
1841
1791
|
alias_method :history_search_forward, :ed_search_next_history
|
1842
1792
|
|
1793
|
+
private def move_history(history_pointer, line:, cursor:, save_buffer: true)
|
1794
|
+
history_pointer ||= Reline::HISTORY.size
|
1795
|
+
return if history_pointer < 0 || history_pointer > Reline::HISTORY.size
|
1796
|
+
old_history_pointer = @history_pointer || Reline::HISTORY.size
|
1797
|
+
if old_history_pointer == Reline::HISTORY.size
|
1798
|
+
@line_backup_in_history = save_buffer ? whole_buffer : ''
|
1799
|
+
else
|
1800
|
+
Reline::HISTORY[old_history_pointer] = whole_buffer if save_buffer
|
1801
|
+
end
|
1802
|
+
if history_pointer == Reline::HISTORY.size
|
1803
|
+
buf = @line_backup_in_history
|
1804
|
+
@history_pointer = @line_backup_in_history = nil
|
1805
|
+
else
|
1806
|
+
buf = Reline::HISTORY[history_pointer]
|
1807
|
+
@history_pointer = history_pointer
|
1808
|
+
end
|
1809
|
+
@buffer_of_lines = buf.split("\n")
|
1810
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1811
|
+
@line_index = line == :start ? 0 : line == :end ? @buffer_of_lines.size - 1 : line
|
1812
|
+
@byte_pointer = cursor == :start ? 0 : cursor == :end ? current_line.bytesize : cursor
|
1813
|
+
end
|
1814
|
+
|
1843
1815
|
private def ed_prev_history(key, arg: 1)
|
1844
|
-
if @
|
1816
|
+
if @line_index > 0
|
1845
1817
|
cursor = current_byte_pointer_cursor
|
1846
1818
|
@line_index -= 1
|
1847
1819
|
calculate_nearest_cursor(cursor)
|
1848
1820
|
return
|
1849
1821
|
end
|
1850
|
-
|
1851
|
-
|
1852
|
-
|
1853
|
-
|
1854
|
-
|
1855
|
-
cursor = current_byte_pointer_cursor
|
1856
|
-
if @is_multiline
|
1857
|
-
@line_backup_in_history = whole_buffer
|
1858
|
-
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1859
|
-
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1860
|
-
@line_index = @buffer_of_lines.size - 1
|
1861
|
-
calculate_nearest_cursor(cursor)
|
1862
|
-
else
|
1863
|
-
@line_backup_in_history = whole_buffer
|
1864
|
-
@buffer_of_lines = [Reline::HISTORY[@history_pointer]]
|
1865
|
-
calculate_nearest_cursor(cursor)
|
1866
|
-
end
|
1867
|
-
elsif @history_pointer.zero?
|
1868
|
-
return
|
1869
|
-
else
|
1870
|
-
if @is_multiline
|
1871
|
-
Reline::HISTORY[@history_pointer] = whole_buffer
|
1872
|
-
@history_pointer -= 1
|
1873
|
-
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1874
|
-
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1875
|
-
@line_index = @buffer_of_lines.size - 1
|
1876
|
-
else
|
1877
|
-
Reline::HISTORY[@history_pointer] = whole_buffer
|
1878
|
-
@history_pointer -= 1
|
1879
|
-
@buffer_of_lines = [Reline::HISTORY[@history_pointer]]
|
1880
|
-
end
|
1881
|
-
end
|
1882
|
-
if @config.editing_mode_is?(:emacs, :vi_insert)
|
1883
|
-
@byte_pointer = current_line.bytesize
|
1884
|
-
elsif @config.editing_mode_is?(:vi_command)
|
1885
|
-
@byte_pointer = 0
|
1886
|
-
end
|
1822
|
+
move_history(
|
1823
|
+
(@history_pointer || Reline::HISTORY.size) - 1,
|
1824
|
+
line: :end,
|
1825
|
+
cursor: @config.editing_mode_is?(:vi_command) ? :start : :end,
|
1826
|
+
)
|
1887
1827
|
arg -= 1
|
1888
1828
|
ed_prev_history(key, arg: arg) if arg > 0
|
1889
1829
|
end
|
1890
1830
|
alias_method :previous_history, :ed_prev_history
|
1891
1831
|
|
1892
1832
|
private def ed_next_history(key, arg: 1)
|
1893
|
-
if @
|
1833
|
+
if @line_index < (@buffer_of_lines.size - 1)
|
1894
1834
|
cursor = current_byte_pointer_cursor
|
1895
1835
|
@line_index += 1
|
1896
1836
|
calculate_nearest_cursor(cursor)
|
1897
1837
|
return
|
1898
1838
|
end
|
1899
|
-
|
1900
|
-
|
1901
|
-
|
1902
|
-
|
1903
|
-
|
1904
|
-
@buffer_of_lines = @line_backup_in_history.split("\n")
|
1905
|
-
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1906
|
-
@line_index = 0
|
1907
|
-
else
|
1908
|
-
@history_pointer = nil
|
1909
|
-
@buffer_of_lines = [@line_backup_in_history]
|
1910
|
-
end
|
1911
|
-
else
|
1912
|
-
if @is_multiline
|
1913
|
-
Reline::HISTORY[@history_pointer] = whole_buffer
|
1914
|
-
@history_pointer += 1
|
1915
|
-
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1916
|
-
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1917
|
-
@line_index = 0
|
1918
|
-
else
|
1919
|
-
Reline::HISTORY[@history_pointer] = whole_buffer
|
1920
|
-
@history_pointer += 1
|
1921
|
-
@buffer_of_lines = [Reline::HISTORY[@history_pointer]]
|
1922
|
-
end
|
1923
|
-
end
|
1924
|
-
if @config.editing_mode_is?(:emacs, :vi_insert)
|
1925
|
-
@byte_pointer = current_line.bytesize
|
1926
|
-
elsif @config.editing_mode_is?(:vi_command)
|
1927
|
-
@byte_pointer = 0
|
1928
|
-
end
|
1839
|
+
move_history(
|
1840
|
+
(@history_pointer || Reline::HISTORY.size) + 1,
|
1841
|
+
line: :start,
|
1842
|
+
cursor: @config.editing_mode_is?(:vi_command) ? :start : :end,
|
1843
|
+
)
|
1929
1844
|
arg -= 1
|
1930
1845
|
ed_next_history(key, arg: arg) if arg > 0
|
1931
1846
|
end
|
@@ -1956,17 +1871,13 @@ class Reline::LineEditor
|
|
1956
1871
|
end
|
1957
1872
|
end
|
1958
1873
|
else
|
1959
|
-
if @history_pointer
|
1960
|
-
Reline::HISTORY[@history_pointer] = whole_buffer
|
1961
|
-
@history_pointer = nil
|
1962
|
-
end
|
1963
1874
|
finish
|
1964
1875
|
end
|
1965
1876
|
end
|
1966
1877
|
|
1967
1878
|
private def em_delete_prev_char(key, arg: 1)
|
1968
1879
|
arg.times do
|
1969
|
-
if @
|
1880
|
+
if @byte_pointer == 0 and @line_index > 0
|
1970
1881
|
@byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
|
1971
1882
|
@buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
|
1972
1883
|
@line_index -= 1
|
@@ -1990,7 +1901,7 @@ class Reline::LineEditor
|
|
1990
1901
|
line, deleted = byteslice!(current_line, @byte_pointer, current_line.bytesize - @byte_pointer)
|
1991
1902
|
set_current_line(line, line.bytesize)
|
1992
1903
|
@kill_ring.append(deleted)
|
1993
|
-
elsif @
|
1904
|
+
elsif @byte_pointer == current_line.bytesize and @buffer_of_lines.size > @line_index + 1
|
1994
1905
|
set_current_line(current_line + @buffer_of_lines.delete_at(@line_index + 1), current_line.bytesize)
|
1995
1906
|
end
|
1996
1907
|
end
|
@@ -2030,7 +1941,7 @@ class Reline::LineEditor
|
|
2030
1941
|
alias_method :kill_whole_line, :em_kill_line
|
2031
1942
|
|
2032
1943
|
private def em_delete(key)
|
2033
|
-
if current_line.empty? and
|
1944
|
+
if current_line.empty? and @buffer_of_lines.size == 1 and key == "\C-d".ord
|
2034
1945
|
@eof = true
|
2035
1946
|
finish
|
2036
1947
|
elsif @byte_pointer < current_line.bytesize
|
@@ -2038,7 +1949,7 @@ class Reline::LineEditor
|
|
2038
1949
|
mbchar = splitted_last.grapheme_clusters.first
|
2039
1950
|
line, = byteslice!(current_line, @byte_pointer, mbchar.bytesize)
|
2040
1951
|
set_current_line(line)
|
2041
|
-
elsif @
|
1952
|
+
elsif @byte_pointer == current_line.bytesize and @buffer_of_lines.size > @line_index + 1
|
2042
1953
|
set_current_line(current_line + @buffer_of_lines.delete_at(@line_index + 1), current_line.bytesize)
|
2043
1954
|
end
|
2044
1955
|
end
|
@@ -2050,7 +1961,7 @@ class Reline::LineEditor
|
|
2050
1961
|
elsif !@config.autocompletion # show completed list
|
2051
1962
|
result = call_completion_proc
|
2052
1963
|
if result.is_a?(Array)
|
2053
|
-
|
1964
|
+
perform_completion(result, true)
|
2054
1965
|
end
|
2055
1966
|
end
|
2056
1967
|
end
|
@@ -2281,7 +2192,7 @@ class Reline::LineEditor
|
|
2281
2192
|
end
|
2282
2193
|
|
2283
2194
|
private def vi_delete_prev_char(key)
|
2284
|
-
if @
|
2195
|
+
if @byte_pointer == 0 and @line_index > 0
|
2285
2196
|
@byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
|
2286
2197
|
@buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
|
2287
2198
|
@line_index -= 1
|
@@ -2378,7 +2289,7 @@ class Reline::LineEditor
|
|
2378
2289
|
end
|
2379
2290
|
|
2380
2291
|
private def vi_list_or_eof(key)
|
2381
|
-
if
|
2292
|
+
if current_line.empty? and @buffer_of_lines.size == 1
|
2382
2293
|
set_current_line('', 0)
|
2383
2294
|
@eof = true
|
2384
2295
|
finish
|
@@ -2409,36 +2320,18 @@ class Reline::LineEditor
|
|
2409
2320
|
if Reline::HISTORY.empty?
|
2410
2321
|
return
|
2411
2322
|
end
|
2412
|
-
|
2413
|
-
@history_pointer = 0
|
2414
|
-
@line_backup_in_history = current_line
|
2415
|
-
set_current_line(Reline::HISTORY[@history_pointer], 0)
|
2416
|
-
elsif @history_pointer.zero?
|
2417
|
-
return
|
2418
|
-
else
|
2419
|
-
Reline::HISTORY[@history_pointer] = current_line
|
2420
|
-
@history_pointer = 0
|
2421
|
-
set_current_line(Reline::HISTORY[@history_pointer], 0)
|
2422
|
-
end
|
2323
|
+
move_history(0, line: :start, cursor: :start)
|
2423
2324
|
end
|
2424
2325
|
|
2425
2326
|
private def vi_histedit(key)
|
2426
2327
|
path = Tempfile.open { |fp|
|
2427
|
-
|
2428
|
-
fp.write whole_lines.join("\n")
|
2429
|
-
else
|
2430
|
-
fp.write current_line
|
2431
|
-
end
|
2328
|
+
fp.write whole_lines.join("\n")
|
2432
2329
|
fp.path
|
2433
2330
|
}
|
2434
2331
|
system("#{ENV['EDITOR']} #{path}")
|
2435
|
-
|
2436
|
-
|
2437
|
-
|
2438
|
-
@line_index = 0
|
2439
|
-
else
|
2440
|
-
@buffer_of_lines = File.read(path).split("\n")
|
2441
|
-
end
|
2332
|
+
@buffer_of_lines = File.read(path).split("\n")
|
2333
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2334
|
+
@line_index = 0
|
2442
2335
|
finish
|
2443
2336
|
end
|
2444
2337
|
|
@@ -2610,7 +2503,7 @@ class Reline::LineEditor
|
|
2610
2503
|
end
|
2611
2504
|
|
2612
2505
|
private def vi_join_lines(key, arg: 1)
|
2613
|
-
if @
|
2506
|
+
if @buffer_of_lines.size > @line_index + 1
|
2614
2507
|
next_line = @buffer_of_lines.delete_at(@line_index + 1).lstrip
|
2615
2508
|
set_current_line(current_line + ' ' + next_line, current_line.bytesize)
|
2616
2509
|
end
|
@@ -2638,4 +2531,24 @@ class Reline::LineEditor
|
|
2638
2531
|
private def vi_editing_mode(key)
|
2639
2532
|
@config.editing_mode = :vi_insert
|
2640
2533
|
end
|
2534
|
+
|
2535
|
+
private def undo(_key)
|
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
|
2544
|
+
|
2545
|
+
private def redo(_key)
|
2546
|
+
@undoing = true
|
2547
|
+
|
2548
|
+
return if @input_lines_position >= @input_lines.size - 1
|
2549
|
+
|
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)
|
2553
|
+
end
|
2641
2554
|
end
|