reline 0.5.1 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -33,8 +33,6 @@ class Reline::LineEditor
33
33
  vi_next_big_word
34
34
  vi_prev_big_word
35
35
  vi_end_big_word
36
- vi_repeat_next_char
37
- vi_repeat_prev_char
38
36
  }
39
37
 
40
38
  module CompletionState
@@ -114,7 +112,11 @@ class Reline::LineEditor
114
112
  else
115
113
  prompt = @prompt
116
114
  end
117
- if @prompt_proc
115
+ if !@is_multiline
116
+ mode_string = check_mode_string
117
+ prompt = mode_string + prompt if mode_string
118
+ [prompt] + [''] * (buffer.size - 1)
119
+ elsif @prompt_proc
118
120
  prompt_list = @prompt_proc.(buffer).map { |pr| pr.gsub("\n", "\\n") }
119
121
  prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
120
122
  prompt_list = [prompt] if prompt_list.empty?
@@ -229,10 +231,11 @@ class Reline::LineEditor
229
231
  @vi_clipboard = ''
230
232
  @vi_arg = nil
231
233
  @waiting_proc = nil
232
- @waiting_operator_proc = nil
233
- @waiting_operator_vi_arg = nil
234
+ @vi_waiting_operator = nil
235
+ @vi_waiting_operator_arg = nil
234
236
  @completion_journey_state = nil
235
237
  @completion_state = CompletionState::NORMAL
238
+ @completion_occurs = false
236
239
  @perfect_matched = nil
237
240
  @menu_info = nil
238
241
  @searching_prompt = nil
@@ -292,8 +295,8 @@ class Reline::LineEditor
292
295
  end
293
296
  end
294
297
 
295
- private def split_by_width(str, max_width)
296
- Reline::Unicode.split_by_width(str, max_width, @encoding)
298
+ private def split_by_width(str, max_width, offset: 0)
299
+ Reline::Unicode.split_by_width(str, max_width, @encoding, offset: offset)
297
300
  end
298
301
 
299
302
  def current_byte_pointer_cursor
@@ -367,7 +370,7 @@ class Reline::LineEditor
367
370
  @scroll_partial_screen
368
371
  end
369
372
 
370
- def wrapped_lines
373
+ def wrapped_prompt_and_input_lines
371
374
  with_cache(__method__, @buffer_of_lines.size, modified_lines, prompt_list, screen_width) do |n, lines, prompts, width, prev_cache_key, cached_value|
372
375
  prev_n, prev_lines, prev_prompts, prev_width = prev_cache_key
373
376
  cached_wraps = {}
@@ -378,9 +381,14 @@ class Reline::LineEditor
378
381
  end
379
382
 
380
383
  n.times.map do |i|
381
- prompt = prompts[i]
382
- line = lines[i]
383
- cached_wraps[[prompt, line]] || split_by_width("#{prompt}#{line}", width).first.compact
384
+ prompt = prompts[i] || ''
385
+ line = lines[i] || ''
386
+ if (cached = cached_wraps[[prompt, line]])
387
+ next cached
388
+ end
389
+ *wrapped_prompts, code_line_prompt = split_by_width(prompt, width).first.compact
390
+ wrapped_lines = split_by_width(line, width, offset: calculate_width(code_line_prompt)).first.compact
391
+ wrapped_prompts.map { |p| [p, ''] } + [[code_line_prompt, wrapped_lines.first]] + wrapped_lines.drop(1).map { |c| ['', c] }
384
392
  end
385
393
  end
386
394
  end
@@ -406,8 +414,13 @@ class Reline::LineEditor
406
414
  @output.write "#{Reline::IOGate::RESET_COLOR}#{' ' * width}"
407
415
  else
408
416
  x, w, content = new_items[level]
409
- content = Reline::Unicode.take_range(content, base_x - x, width) unless x == base_x && w == width
410
- Reline::IOGate.move_cursor_column base_x
417
+ cover_begin = base_x != 0 && new_levels[base_x - 1] == level
418
+ cover_end = new_levels[base_x + width] == level
419
+ pos = 0
420
+ unless x == base_x && w == width
421
+ content, pos = Reline::Unicode.take_mbchar_range(content, base_x - x, width, cover_begin: cover_begin, cover_end: cover_end, padding: true)
422
+ end
423
+ Reline::IOGate.move_cursor_column x + pos
411
424
  @output.write "#{Reline::IOGate::RESET_COLOR}#{content}#{Reline::IOGate::RESET_COLOR}"
412
425
  end
413
426
  base_x += width
@@ -423,7 +436,7 @@ class Reline::LineEditor
423
436
  prompt_width = calculate_width(prompt_list[@line_index], true)
424
437
  line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
425
438
  wrapped_line_before_cursor = split_by_width(' ' * prompt_width + line_before_cursor, screen_width).first.compact
426
- wrapped_cursor_y = wrapped_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
439
+ wrapped_cursor_y = wrapped_prompt_and_input_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
427
440
  wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
428
441
  [wrapped_cursor_x, wrapped_cursor_y]
429
442
  end
@@ -487,8 +500,9 @@ class Reline::LineEditor
487
500
  wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
488
501
 
489
502
  rendered_lines = @rendered_screen.lines
490
- new_lines = wrapped_lines.flatten[screen_scroll_top, screen_height].map do |l|
491
- [[0, Reline::Unicode.calculate_width(l, true), l]]
503
+ new_lines = wrapped_prompt_and_input_lines.flatten(1)[screen_scroll_top, screen_height].map do |prompt, line|
504
+ prompt_width = Reline::Unicode.calculate_width(prompt, true)
505
+ [[0, prompt_width, prompt], [prompt_width, Reline::Unicode.calculate_width(line, true), line]]
492
506
  end
493
507
  if @menu_info
494
508
  @menu_info.lines(screen_width).each do |item|
@@ -504,7 +518,8 @@ class Reline::LineEditor
504
518
  y_range.each do |row|
505
519
  next if row < 0 || row >= screen_height
506
520
  dialog_rows = new_lines[row] ||= []
507
- dialog_rows[index + 1] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]]
521
+ # index 0 is for prompt, index 1 is for line, index 2.. is for dialog
522
+ dialog_rows[index + 2] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]]
508
523
  end
509
524
  end
510
525
 
@@ -541,10 +556,6 @@ class Reline::LineEditor
541
556
  new_lines.size - y
542
557
  end
543
558
 
544
- def current_row
545
- wrapped_lines.flatten[wrapped_cursor_y]
546
- end
547
-
548
559
  def upper_space_height(wrapped_cursor_y)
549
560
  wrapped_cursor_y - screen_scroll_top
550
561
  end
@@ -693,13 +704,6 @@ class Reline::LineEditor
693
704
 
694
705
  DIALOG_DEFAULT_HEIGHT = 20
695
706
 
696
- private def padding_space_with_escape_sequences(str, width)
697
- padding_width = width - calculate_width(str, true)
698
- # padding_width should be only positive value. But macOS and Alacritty returns negative value.
699
- padding_width = 0 if padding_width < 0
700
- str + (' ' * padding_width)
701
- end
702
-
703
707
  private def dialog_range(dialog, dialog_y)
704
708
  x_range = dialog.column...dialog.column + dialog.width
705
709
  y_range = dialog_y + dialog.vertical_offset...dialog_y + dialog.vertical_offset + dialog.contents.size
@@ -772,7 +776,7 @@ class Reline::LineEditor
772
776
  dialog.contents = contents.map.with_index do |item, i|
773
777
  line_sgr = i == pointer ? enhanced_sgr : default_sgr
774
778
  str_width = dialog.width - (scrollbar_pos.nil? ? 0 : @block_elem_width)
775
- str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width)
779
+ str, = Reline::Unicode.take_mbchar_range(item, 0, str_width, padding: true)
776
780
  colored_content = "#{line_sgr}#{str}"
777
781
  if scrollbar_pos
778
782
  if scrollbar_pos <= (i * 2) and (i * 2 + 1) < (scrollbar_pos + bar_height)
@@ -881,10 +885,12 @@ class Reline::LineEditor
881
885
  @completion_state = CompletionState::PERFECT_MATCH
882
886
  else
883
887
  @completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
888
+ complete(list, true) if @config.show_all_if_ambiguous
884
889
  end
885
890
  @perfect_matched = completed
886
891
  else
887
892
  @completion_state = CompletionState::MENU
893
+ complete(list, true) if @config.show_all_if_ambiguous
888
894
  end
889
895
  if not just_show_list and target < completed
890
896
  @buffer_of_lines[@line_index] = (preposing + completed + completion_append_character.to_s + postposing).split("\n")[@line_index] || String.new(encoding: @encoding)
@@ -935,37 +941,23 @@ class Reline::LineEditor
935
941
  end
936
942
 
937
943
  private def run_for_operators(key, method_symbol, &block)
938
- if @waiting_operator_proc
944
+ if @vi_waiting_operator
939
945
  if VI_MOTIONS.include?(method_symbol)
940
946
  old_byte_pointer = @byte_pointer
941
- @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg&.> 1
947
+ @vi_arg = (@vi_arg || 1) * @vi_waiting_operator_arg
942
948
  block.(true)
943
949
  unless @waiting_proc
944
950
  byte_pointer_diff = @byte_pointer - old_byte_pointer
945
951
  @byte_pointer = old_byte_pointer
946
- @waiting_operator_proc.(byte_pointer_diff)
947
- else
948
- old_waiting_proc = @waiting_proc
949
- old_waiting_operator_proc = @waiting_operator_proc
950
- current_waiting_operator_proc = @waiting_operator_proc
951
- @waiting_proc = proc { |k|
952
- old_byte_pointer = @byte_pointer
953
- old_waiting_proc.(k)
954
- byte_pointer_diff = @byte_pointer - old_byte_pointer
955
- @byte_pointer = old_byte_pointer
956
- current_waiting_operator_proc.(byte_pointer_diff)
957
- @waiting_operator_proc = old_waiting_operator_proc
958
- }
952
+ send(@vi_waiting_operator, byte_pointer_diff)
953
+ cleanup_waiting
959
954
  end
960
955
  else
961
956
  # Ignores operator when not motion is given.
962
957
  block.(false)
958
+ cleanup_waiting
963
959
  end
964
- @waiting_operator_proc = nil
965
- @waiting_operator_vi_arg = nil
966
- if @vi_arg
967
- @vi_arg = nil
968
- end
960
+ @vi_arg = nil
969
961
  else
970
962
  block.(false)
971
963
  end
@@ -982,7 +974,7 @@ class Reline::LineEditor
982
974
  end
983
975
 
984
976
  def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
985
- if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
977
+ if @config.editing_mode_is?(:emacs, :vi_insert) and @vi_waiting_operator.nil?
986
978
  not_insertion = method_symbol != :ed_insert
987
979
  process_insert(force: not_insertion)
988
980
  end
@@ -1001,11 +993,32 @@ class Reline::LineEditor
1001
993
  end
1002
994
  end
1003
995
 
996
+ private def cleanup_waiting
997
+ @waiting_proc = nil
998
+ @vi_waiting_operator = nil
999
+ @vi_waiting_operator_arg = nil
1000
+ @searching_prompt = nil
1001
+ @drop_terminate_spaces = false
1002
+ end
1003
+
1004
1004
  private def process_key(key, method_symbol)
1005
+ if key.is_a?(Symbol)
1006
+ cleanup_waiting
1007
+ elsif @waiting_proc
1008
+ old_byte_pointer = @byte_pointer
1009
+ @waiting_proc.call(key)
1010
+ if @vi_waiting_operator
1011
+ byte_pointer_diff = @byte_pointer - old_byte_pointer
1012
+ @byte_pointer = old_byte_pointer
1013
+ send(@vi_waiting_operator, byte_pointer_diff)
1014
+ cleanup_waiting
1015
+ end
1016
+ @kill_ring.process
1017
+ return
1018
+ end
1019
+
1005
1020
  if method_symbol and respond_to?(method_symbol, true)
1006
1021
  method_obj = method(method_symbol)
1007
- else
1008
- method_obj = nil
1009
1022
  end
1010
1023
  if method_symbol and key.is_a?(Symbol)
1011
1024
  if @vi_arg and argumentable?(method_obj)
@@ -1027,8 +1040,6 @@ class Reline::LineEditor
1027
1040
  run_for_operators(key, method_symbol) do |with_operator|
1028
1041
  wrap_method_call(method_symbol, method_obj, key, with_operator)
1029
1042
  end
1030
- elsif @waiting_proc
1031
- @waiting_proc.(key)
1032
1043
  elsif method_obj
1033
1044
  wrap_method_call(method_symbol, method_obj, key)
1034
1045
  else
@@ -1039,9 +1050,6 @@ class Reline::LineEditor
1039
1050
  @vi_arg = nil
1040
1051
  end
1041
1052
  end
1042
- elsif @waiting_proc
1043
- @waiting_proc.(key)
1044
- @kill_ring.process
1045
1053
  elsif method_obj
1046
1054
  if method_symbol == :ed_argument_digit
1047
1055
  wrap_method_call(method_symbol, method_obj, key)
@@ -1110,6 +1118,7 @@ class Reline::LineEditor
1110
1118
  end
1111
1119
  end
1112
1120
  if key.char.nil?
1121
+ process_insert(force: true)
1113
1122
  if @first_char
1114
1123
  @eof = true
1115
1124
  end
@@ -1118,42 +1127,35 @@ class Reline::LineEditor
1118
1127
  end
1119
1128
  old_lines = @buffer_of_lines.dup
1120
1129
  @first_char = false
1121
- completion_occurs = false
1130
+ @completion_occurs = false
1122
1131
  if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
1123
1132
  if !@config.disable_completion
1124
1133
  process_insert(force: true)
1125
1134
  if @config.autocompletion
1126
1135
  @completion_state = CompletionState::NORMAL
1127
- completion_occurs = move_completed_list(:down)
1136
+ @completion_occurs = move_completed_list(:down)
1128
1137
  else
1129
1138
  @completion_journey_state = nil
1130
1139
  result = call_completion_proc
1131
1140
  if result.is_a?(Array)
1132
- completion_occurs = true
1141
+ @completion_occurs = true
1133
1142
  complete(result, false)
1134
1143
  end
1135
1144
  end
1136
1145
  end
1137
- elsif @config.editing_mode_is?(:emacs, :vi_insert) and key.char == :completion_journey_up
1138
- if not @config.disable_completion and @config.autocompletion
1139
- process_insert(force: true)
1140
- @completion_state = CompletionState::NORMAL
1141
- completion_occurs = move_completed_list(:up)
1142
- end
1143
1146
  elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
1144
1147
  # In vi mode, move completed list even if autocompletion is off
1145
1148
  if not @config.disable_completion
1146
1149
  process_insert(force: true)
1147
1150
  @completion_state = CompletionState::NORMAL
1148
- completion_occurs = move_completed_list("\C-p".ord == key.char ? :up : :down)
1151
+ @completion_occurs = move_completed_list("\C-p".ord == key.char ? :up : :down)
1149
1152
  end
1150
1153
  elsif Symbol === key.char and respond_to?(key.char, true)
1151
1154
  process_key(key.char, key.char)
1152
1155
  else
1153
1156
  normal_char(key)
1154
1157
  end
1155
-
1156
- unless completion_occurs
1158
+ unless @completion_occurs
1157
1159
  @completion_state = CompletionState::NORMAL
1158
1160
  @completion_journey_state = nil
1159
1161
  end
@@ -1164,7 +1166,7 @@ class Reline::LineEditor
1164
1166
  end
1165
1167
 
1166
1168
  modified = old_lines != @buffer_of_lines
1167
- if !completion_occurs && modified && !@config.disable_completion && @config.autocompletion
1169
+ if !@completion_occurs && modified && !@config.disable_completion && @config.autocompletion
1168
1170
  # Auto complete starts only when edited
1169
1171
  process_insert(force: true)
1170
1172
  @completion_journey_state = retrieve_completion_journey_state
@@ -1230,7 +1232,7 @@ class Reline::LineEditor
1230
1232
  end
1231
1233
 
1232
1234
  def line()
1233
- current_line unless eof?
1235
+ @buffer_of_lines.join("\n") unless eof?
1234
1236
  end
1235
1237
 
1236
1238
  def current_line
@@ -1314,14 +1316,12 @@ class Reline::LineEditor
1314
1316
  end
1315
1317
  target = before
1316
1318
  end
1317
- if @is_multiline
1318
- lines = whole_lines
1319
- if @line_index > 0
1320
- preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
1321
- end
1322
- if (lines.size - 1) > @line_index
1323
- postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
1324
- end
1319
+ lines = whole_lines
1320
+ if @line_index > 0
1321
+ preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
1322
+ end
1323
+ if (lines.size - 1) > @line_index
1324
+ postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
1325
1325
  end
1326
1326
  [preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
1327
1327
  end
@@ -1343,20 +1343,16 @@ class Reline::LineEditor
1343
1343
 
1344
1344
  def delete_text(start = nil, length = nil)
1345
1345
  if start.nil? and length.nil?
1346
- if @is_multiline
1347
- if @buffer_of_lines.size == 1
1348
- @buffer_of_lines[@line_index] = ''
1349
- @byte_pointer = 0
1350
- elsif @line_index == (@buffer_of_lines.size - 1) and @line_index > 0
1351
- @buffer_of_lines.pop
1352
- @line_index -= 1
1353
- @byte_pointer = 0
1354
- elsif @line_index < (@buffer_of_lines.size - 1)
1355
- @buffer_of_lines.delete_at(@line_index)
1356
- @byte_pointer = 0
1357
- end
1358
- else
1359
- set_current_line('', 0)
1346
+ if @buffer_of_lines.size == 1
1347
+ @buffer_of_lines[@line_index] = ''
1348
+ @byte_pointer = 0
1349
+ elsif @line_index == (@buffer_of_lines.size - 1) and @line_index > 0
1350
+ @buffer_of_lines.pop
1351
+ @line_index -= 1
1352
+ @byte_pointer = 0
1353
+ elsif @line_index < (@buffer_of_lines.size - 1)
1354
+ @buffer_of_lines.delete_at(@line_index)
1355
+ @byte_pointer = 0
1360
1356
  end
1361
1357
  elsif not start.nil? and not length.nil?
1362
1358
  if current_line
@@ -1433,6 +1429,14 @@ class Reline::LineEditor
1433
1429
  end
1434
1430
  end
1435
1431
 
1432
+ private def completion_journey_up(key)
1433
+ if not @config.disable_completion and @config.autocompletion
1434
+ @completion_state = CompletionState::NORMAL
1435
+ @completion_occurs = move_completed_list(:up)
1436
+ end
1437
+ end
1438
+ alias_method :menu_complete_backward, :completion_journey_up
1439
+
1436
1440
  # Editline:: +ed-unassigned+ This editor command always results in an error.
1437
1441
  # GNU Readline:: There is no corresponding macro.
1438
1442
  private def ed_unassigned(key) end # do nothing
@@ -1504,7 +1508,7 @@ class Reline::LineEditor
1504
1508
  byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
1505
1509
  if (@byte_pointer < current_line.bytesize)
1506
1510
  @byte_pointer += byte_size
1507
- elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer == current_line.bytesize and @line_index < @buffer_of_lines.size - 1
1511
+ elsif @config.editing_mode_is?(:emacs) and @byte_pointer == current_line.bytesize and @line_index < @buffer_of_lines.size - 1
1508
1512
  @byte_pointer = 0
1509
1513
  @line_index += 1
1510
1514
  end
@@ -1517,7 +1521,7 @@ class Reline::LineEditor
1517
1521
  if @byte_pointer > 0
1518
1522
  byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
1519
1523
  @byte_pointer -= byte_size
1520
- elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer == 0 and @line_index > 0
1524
+ elsif @config.editing_mode_is?(:emacs) and @byte_pointer == 0 and @line_index > 0
1521
1525
  @line_index -= 1
1522
1526
  @byte_pointer = current_line.bytesize
1523
1527
  end
@@ -1534,141 +1538,102 @@ class Reline::LineEditor
1534
1538
  @byte_pointer = 0
1535
1539
  end
1536
1540
  alias_method :beginning_of_line, :ed_move_to_beg
1541
+ alias_method :vi_zero, :ed_move_to_beg
1537
1542
 
1538
1543
  private def ed_move_to_end(key)
1539
- @byte_pointer = 0
1540
- while @byte_pointer < current_line.bytesize
1541
- byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
1542
- @byte_pointer += byte_size
1543
- end
1544
+ @byte_pointer = current_line.bytesize
1544
1545
  end
1545
1546
  alias_method :end_of_line, :ed_move_to_end
1546
1547
 
1547
- private def generate_searcher
1548
- Fiber.new do |first_key|
1549
- prev_search_key = first_key
1550
- search_word = String.new(encoding: @encoding)
1551
- multibyte_buf = String.new(encoding: 'ASCII-8BIT')
1552
- last_hit = nil
1553
- case first_key
1554
- when "\C-r".ord
1555
- prompt_name = 'reverse-i-search'
1556
- when "\C-s".ord
1557
- prompt_name = 'i-search'
1548
+ private def generate_searcher(search_key)
1549
+ search_word = String.new(encoding: @encoding)
1550
+ multibyte_buf = String.new(encoding: 'ASCII-8BIT')
1551
+ hit_pointer = nil
1552
+ lambda do |key|
1553
+ search_again = false
1554
+ case key
1555
+ when "\C-h".ord, "\C-?".ord
1556
+ grapheme_clusters = search_word.grapheme_clusters
1557
+ if grapheme_clusters.size > 0
1558
+ grapheme_clusters.pop
1559
+ search_word = grapheme_clusters.join
1560
+ end
1561
+ when "\C-r".ord, "\C-s".ord
1562
+ search_again = true if search_key == key
1563
+ search_key = key
1564
+ else
1565
+ multibyte_buf << key
1566
+ if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
1567
+ search_word << multibyte_buf.dup.force_encoding(@encoding)
1568
+ multibyte_buf.clear
1569
+ end
1558
1570
  end
1559
- loop do
1560
- key = Fiber.yield(search_word)
1561
- search_again = false
1562
- case key
1563
- when -1 # determined
1564
- Reline.last_incremental_search = search_word
1565
- break
1566
- when "\C-h".ord, "\C-?".ord
1567
- grapheme_clusters = search_word.grapheme_clusters
1568
- if grapheme_clusters.size > 0
1569
- grapheme_clusters.pop
1570
- search_word = grapheme_clusters.join
1571
- end
1572
- when "\C-r".ord, "\C-s".ord
1573
- search_again = true if prev_search_key == key
1574
- prev_search_key = key
1575
- else
1576
- multibyte_buf << key
1577
- if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
1578
- search_word << multibyte_buf.dup.force_encoding(@encoding)
1579
- multibyte_buf.clear
1571
+ hit = nil
1572
+ if not search_word.empty? and @line_backup_in_history&.include?(search_word)
1573
+ hit_pointer = Reline::HISTORY.size
1574
+ hit = @line_backup_in_history
1575
+ else
1576
+ if search_again
1577
+ if search_word.empty? and Reline.last_incremental_search
1578
+ search_word = Reline.last_incremental_search
1580
1579
  end
1581
- end
1582
- hit = nil
1583
- if not search_word.empty? and @line_backup_in_history&.include?(search_word)
1584
- @history_pointer = nil
1585
- hit = @line_backup_in_history
1586
- else
1587
- if search_again
1588
- if search_word.empty? and Reline.last_incremental_search
1589
- search_word = Reline.last_incremental_search
1590
- end
1591
- if @history_pointer
1592
- case prev_search_key
1593
- when "\C-r".ord
1594
- history_pointer_base = 0
1595
- history = Reline::HISTORY[0..(@history_pointer - 1)]
1596
- when "\C-s".ord
1597
- history_pointer_base = @history_pointer + 1
1598
- history = Reline::HISTORY[(@history_pointer + 1)..-1]
1599
- end
1600
- else
1601
- history_pointer_base = 0
1602
- history = Reline::HISTORY
1603
- end
1604
- elsif @history_pointer
1605
- case prev_search_key
1580
+ if @history_pointer
1581
+ case search_key
1606
1582
  when "\C-r".ord
1607
1583
  history_pointer_base = 0
1608
- history = Reline::HISTORY[0..@history_pointer]
1584
+ history = Reline::HISTORY[0..(@history_pointer - 1)]
1609
1585
  when "\C-s".ord
1610
- history_pointer_base = @history_pointer
1611
- history = Reline::HISTORY[@history_pointer..-1]
1586
+ history_pointer_base = @history_pointer + 1
1587
+ history = Reline::HISTORY[(@history_pointer + 1)..-1]
1612
1588
  end
1613
1589
  else
1614
1590
  history_pointer_base = 0
1615
1591
  history = Reline::HISTORY
1616
1592
  end
1617
- case prev_search_key
1593
+ elsif @history_pointer
1594
+ case search_key
1618
1595
  when "\C-r".ord
1619
- hit_index = history.rindex { |item|
1620
- item.include?(search_word)
1621
- }
1596
+ history_pointer_base = 0
1597
+ history = Reline::HISTORY[0..@history_pointer]
1622
1598
  when "\C-s".ord
1623
- hit_index = history.index { |item|
1624
- item.include?(search_word)
1625
- }
1626
- end
1627
- if hit_index
1628
- @history_pointer = history_pointer_base + hit_index
1629
- hit = Reline::HISTORY[@history_pointer]
1599
+ history_pointer_base = @history_pointer
1600
+ history = Reline::HISTORY[@history_pointer..-1]
1630
1601
  end
1602
+ else
1603
+ history_pointer_base = 0
1604
+ history = Reline::HISTORY
1631
1605
  end
1632
- case prev_search_key
1606
+ case search_key
1633
1607
  when "\C-r".ord
1634
- prompt_name = 'reverse-i-search'
1608
+ hit_index = history.rindex { |item|
1609
+ item.include?(search_word)
1610
+ }
1635
1611
  when "\C-s".ord
1636
- prompt_name = 'i-search'
1612
+ hit_index = history.index { |item|
1613
+ item.include?(search_word)
1614
+ }
1637
1615
  end
1638
- if hit
1639
- if @is_multiline
1640
- @buffer_of_lines = hit.split("\n")
1641
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1642
- @line_index = @buffer_of_lines.size - 1
1643
- @byte_pointer = current_line.bytesize
1644
- @searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
1645
- else
1646
- @buffer_of_lines = [hit]
1647
- @byte_pointer = hit.bytesize
1648
- @searching_prompt = "(%s)`%s': %s" % [prompt_name, search_word, hit]
1649
- end
1650
- last_hit = hit
1651
- else
1652
- if @is_multiline
1653
- @searching_prompt = "(failed %s)`%s'" % [prompt_name, search_word]
1654
- else
1655
- @searching_prompt = "(failed %s)`%s': %s" % [prompt_name, search_word, last_hit]
1656
- end
1616
+ if hit_index
1617
+ hit_pointer = history_pointer_base + hit_index
1618
+ hit = Reline::HISTORY[hit_pointer]
1657
1619
  end
1658
1620
  end
1621
+ case search_key
1622
+ when "\C-r".ord
1623
+ prompt_name = 'reverse-i-search'
1624
+ when "\C-s".ord
1625
+ prompt_name = 'i-search'
1626
+ end
1627
+ prompt_name = "failed #{prompt_name}" unless hit
1628
+ [search_word, prompt_name, hit_pointer]
1659
1629
  end
1660
1630
  end
1661
1631
 
1662
1632
  private def incremental_search_history(key)
1663
1633
  unless @history_pointer
1664
- if @is_multiline
1665
- @line_backup_in_history = whole_buffer
1666
- else
1667
- @line_backup_in_history = current_line
1668
- end
1634
+ @line_backup_in_history = whole_buffer
1669
1635
  end
1670
- searcher = generate_searcher
1671
- searcher.resume(key)
1636
+ searcher = generate_searcher(key)
1672
1637
  @searching_prompt = "(reverse-i-search)`': "
1673
1638
  termination_keys = ["\C-j".ord]
1674
1639
  termination_keys.concat(@config.isearch_terminators&.chars&.map(&:ord)) if @config.isearch_terminators
@@ -1680,53 +1645,41 @@ class Reline::LineEditor
1680
1645
  else
1681
1646
  buffer = @line_backup_in_history
1682
1647
  end
1683
- if @is_multiline
1684
- @buffer_of_lines = buffer.split("\n")
1685
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1686
- @line_index = @buffer_of_lines.size - 1
1687
- else
1688
- @buffer_of_lines = [buffer]
1689
- end
1648
+ @buffer_of_lines = buffer.split("\n")
1649
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1650
+ @line_index = @buffer_of_lines.size - 1
1690
1651
  @searching_prompt = nil
1691
1652
  @waiting_proc = nil
1692
1653
  @byte_pointer = 0
1693
- searcher.resume(-1)
1694
1654
  when "\C-g".ord
1695
- if @is_multiline
1696
- @buffer_of_lines = @line_backup_in_history.split("\n")
1697
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1698
- @line_index = @buffer_of_lines.size - 1
1699
- else
1700
- @buffer_of_lines = [@line_backup_in_history]
1701
- end
1702
- @history_pointer = nil
1655
+ @buffer_of_lines = @line_backup_in_history.split("\n")
1656
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1657
+ @line_index = @buffer_of_lines.size - 1
1658
+ move_history(nil, line: :end, cursor: :end, save_buffer: false)
1703
1659
  @searching_prompt = nil
1704
1660
  @waiting_proc = nil
1705
- @line_backup_in_history = nil
1706
1661
  @byte_pointer = 0
1707
1662
  else
1708
1663
  chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
1709
1664
  if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
1710
- searcher.resume(k)
1665
+ search_word, prompt_name, hit_pointer = searcher.call(k)
1666
+ Reline.last_incremental_search = search_word
1667
+ @searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
1668
+ @searching_prompt += ': ' unless @is_multiline
1669
+ move_history(hit_pointer, line: :end, cursor: :end, save_buffer: false) if hit_pointer
1711
1670
  else
1712
1671
  if @history_pointer
1713
1672
  line = Reline::HISTORY[@history_pointer]
1714
1673
  else
1715
1674
  line = @line_backup_in_history
1716
1675
  end
1717
- if @is_multiline
1718
- @line_backup_in_history = whole_buffer
1719
- @buffer_of_lines = line.split("\n")
1720
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1721
- @line_index = @buffer_of_lines.size - 1
1722
- else
1723
- @line_backup_in_history = current_line
1724
- @buffer_of_lines = [line]
1725
- end
1676
+ @line_backup_in_history = whole_buffer
1677
+ @buffer_of_lines = line.split("\n")
1678
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1679
+ @line_index = @buffer_of_lines.size - 1
1726
1680
  @searching_prompt = nil
1727
1681
  @waiting_proc = nil
1728
1682
  @byte_pointer = 0
1729
- searcher.resume(-1)
1730
1683
  end
1731
1684
  end
1732
1685
  }
@@ -1742,191 +1695,95 @@ class Reline::LineEditor
1742
1695
  end
1743
1696
  alias_method :forward_search_history, :vi_search_next
1744
1697
 
1745
- private def ed_search_prev_history(key, arg: 1)
1746
- history = nil
1747
- h_pointer = nil
1748
- line_no = nil
1749
- substr = current_line.slice(0, @byte_pointer)
1750
- if @history_pointer.nil?
1751
- return if not current_line.empty? and substr.empty?
1752
- history = Reline::HISTORY
1753
- elsif @history_pointer.zero?
1754
- history = nil
1755
- h_pointer = nil
1756
- else
1757
- history = Reline::HISTORY.slice(0, @history_pointer)
1758
- end
1759
- return if history.nil?
1760
- if @is_multiline
1761
- h_pointer = history.rindex { |h|
1762
- h.split("\n").each_with_index { |l, i|
1763
- if l.start_with?(substr)
1764
- line_no = i
1765
- break
1766
- end
1767
- }
1768
- not line_no.nil?
1769
- }
1770
- else
1771
- h_pointer = history.rindex { |l|
1772
- l.start_with?(substr)
1773
- }
1774
- end
1775
- return if h_pointer.nil?
1776
- @history_pointer = h_pointer
1777
- cursor = current_byte_pointer_cursor
1778
- if @is_multiline
1779
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
1780
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1781
- @line_index = line_no
1782
- calculate_nearest_cursor(cursor)
1783
- else
1784
- @buffer_of_lines = [Reline::HISTORY[@history_pointer]]
1785
- calculate_nearest_cursor(cursor)
1698
+ private def search_history(prefix, pointer_range)
1699
+ pointer_range.each do |pointer|
1700
+ lines = Reline::HISTORY[pointer].split("\n")
1701
+ lines.each_with_index do |line, index|
1702
+ return [pointer, index] if line.start_with?(prefix)
1703
+ end
1786
1704
  end
1705
+ nil
1706
+ end
1707
+
1708
+ private def ed_search_prev_history(key, arg: 1)
1709
+ substr = current_line.byteslice(0, @byte_pointer)
1710
+ return if @history_pointer == 0
1711
+ return if @history_pointer.nil? && substr.empty? && !current_line.empty?
1712
+
1713
+ history_range = 0...(@history_pointer || Reline::HISTORY.size)
1714
+ h_pointer, line_index = search_history(substr, history_range.reverse_each)
1715
+ return unless h_pointer
1716
+ move_history(h_pointer, line: line_index || :start, cursor: @byte_pointer)
1787
1717
  arg -= 1
1788
1718
  ed_search_prev_history(key, arg: arg) if arg > 0
1789
1719
  end
1790
1720
  alias_method :history_search_backward, :ed_search_prev_history
1791
1721
 
1792
1722
  private def ed_search_next_history(key, arg: 1)
1793
- substr = current_line.slice(0, @byte_pointer)
1794
- if @history_pointer.nil?
1795
- return
1796
- elsif @history_pointer == (Reline::HISTORY.size - 1) and not substr.empty?
1797
- return
1798
- end
1799
- history = Reline::HISTORY.slice((@history_pointer + 1)..-1)
1800
- h_pointer = nil
1801
- line_no = nil
1802
- if @is_multiline
1803
- h_pointer = history.index { |h|
1804
- h.split("\n").each_with_index { |l, i|
1805
- if l.start_with?(substr)
1806
- line_no = i
1807
- break
1808
- end
1809
- }
1810
- not line_no.nil?
1811
- }
1812
- else
1813
- h_pointer = history.index { |l|
1814
- l.start_with?(substr)
1815
- }
1816
- end
1817
- h_pointer += @history_pointer + 1 if h_pointer and @history_pointer
1723
+ substr = current_line.byteslice(0, @byte_pointer)
1724
+ return if @history_pointer.nil?
1725
+
1726
+ history_range = @history_pointer + 1...Reline::HISTORY.size
1727
+ h_pointer, line_index = search_history(substr, history_range)
1818
1728
  return if h_pointer.nil? and not substr.empty?
1819
- @history_pointer = h_pointer
1820
- if @is_multiline
1821
- if @history_pointer.nil? and substr.empty?
1822
- @buffer_of_lines = []
1823
- @line_index = 0
1824
- @byte_pointer = 0
1825
- else
1826
- cursor = current_byte_pointer_cursor
1827
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
1828
- @line_index = line_no
1829
- calculate_nearest_cursor(cursor)
1830
- end
1831
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1832
- else
1833
- if @history_pointer.nil? and substr.empty?
1834
- set_current_line('', 0)
1835
- else
1836
- set_current_line(Reline::HISTORY[@history_pointer])
1837
- end
1838
- end
1729
+
1730
+ move_history(h_pointer, line: line_index || :start, cursor: @byte_pointer)
1839
1731
  arg -= 1
1840
1732
  ed_search_next_history(key, arg: arg) if arg > 0
1841
1733
  end
1842
1734
  alias_method :history_search_forward, :ed_search_next_history
1843
1735
 
1736
+ private def move_history(history_pointer, line:, cursor:, save_buffer: true)
1737
+ history_pointer ||= Reline::HISTORY.size
1738
+ return if history_pointer < 0 || history_pointer > Reline::HISTORY.size
1739
+ old_history_pointer = @history_pointer || Reline::HISTORY.size
1740
+ if old_history_pointer == Reline::HISTORY.size
1741
+ @line_backup_in_history = save_buffer ? whole_buffer : ''
1742
+ else
1743
+ Reline::HISTORY[old_history_pointer] = whole_buffer if save_buffer
1744
+ end
1745
+ if history_pointer == Reline::HISTORY.size
1746
+ buf = @line_backup_in_history
1747
+ @history_pointer = @line_backup_in_history = nil
1748
+ else
1749
+ buf = Reline::HISTORY[history_pointer]
1750
+ @history_pointer = history_pointer
1751
+ end
1752
+ @buffer_of_lines = buf.split("\n")
1753
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1754
+ @line_index = line == :start ? 0 : line == :end ? @buffer_of_lines.size - 1 : line
1755
+ @byte_pointer = cursor == :start ? 0 : cursor == :end ? current_line.bytesize : cursor
1756
+ end
1757
+
1844
1758
  private def ed_prev_history(key, arg: 1)
1845
- if @is_multiline and @line_index > 0
1759
+ if @line_index > 0
1846
1760
  cursor = current_byte_pointer_cursor
1847
1761
  @line_index -= 1
1848
1762
  calculate_nearest_cursor(cursor)
1849
1763
  return
1850
1764
  end
1851
- if Reline::HISTORY.empty?
1852
- return
1853
- end
1854
- if @history_pointer.nil?
1855
- @history_pointer = Reline::HISTORY.size - 1
1856
- cursor = current_byte_pointer_cursor
1857
- if @is_multiline
1858
- @line_backup_in_history = whole_buffer
1859
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
1860
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1861
- @line_index = @buffer_of_lines.size - 1
1862
- calculate_nearest_cursor(cursor)
1863
- else
1864
- @line_backup_in_history = whole_buffer
1865
- @buffer_of_lines = [Reline::HISTORY[@history_pointer]]
1866
- calculate_nearest_cursor(cursor)
1867
- end
1868
- elsif @history_pointer.zero?
1869
- return
1870
- else
1871
- if @is_multiline
1872
- Reline::HISTORY[@history_pointer] = whole_buffer
1873
- @history_pointer -= 1
1874
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
1875
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1876
- @line_index = @buffer_of_lines.size - 1
1877
- else
1878
- Reline::HISTORY[@history_pointer] = whole_buffer
1879
- @history_pointer -= 1
1880
- @buffer_of_lines = [Reline::HISTORY[@history_pointer]]
1881
- end
1882
- end
1883
- if @config.editing_mode_is?(:emacs, :vi_insert)
1884
- @byte_pointer = current_line.bytesize
1885
- elsif @config.editing_mode_is?(:vi_command)
1886
- @byte_pointer = 0
1887
- end
1765
+ move_history(
1766
+ (@history_pointer || Reline::HISTORY.size) - 1,
1767
+ line: :end,
1768
+ cursor: @config.editing_mode_is?(:vi_command) ? :start : :end,
1769
+ )
1888
1770
  arg -= 1
1889
1771
  ed_prev_history(key, arg: arg) if arg > 0
1890
1772
  end
1891
1773
  alias_method :previous_history, :ed_prev_history
1892
1774
 
1893
1775
  private def ed_next_history(key, arg: 1)
1894
- if @is_multiline and @line_index < (@buffer_of_lines.size - 1)
1776
+ if @line_index < (@buffer_of_lines.size - 1)
1895
1777
  cursor = current_byte_pointer_cursor
1896
1778
  @line_index += 1
1897
1779
  calculate_nearest_cursor(cursor)
1898
1780
  return
1899
1781
  end
1900
- if @history_pointer.nil?
1901
- return
1902
- elsif @history_pointer == (Reline::HISTORY.size - 1)
1903
- if @is_multiline
1904
- @history_pointer = nil
1905
- @buffer_of_lines = @line_backup_in_history.split("\n")
1906
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1907
- @line_index = 0
1908
- else
1909
- @history_pointer = nil
1910
- @buffer_of_lines = [@line_backup_in_history]
1911
- end
1912
- else
1913
- if @is_multiline
1914
- Reline::HISTORY[@history_pointer] = whole_buffer
1915
- @history_pointer += 1
1916
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
1917
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1918
- @line_index = 0
1919
- else
1920
- Reline::HISTORY[@history_pointer] = whole_buffer
1921
- @history_pointer += 1
1922
- @buffer_of_lines = [Reline::HISTORY[@history_pointer]]
1923
- end
1924
- end
1925
- if @config.editing_mode_is?(:emacs, :vi_insert)
1926
- @byte_pointer = current_line.bytesize
1927
- elsif @config.editing_mode_is?(:vi_command)
1928
- @byte_pointer = 0
1929
- end
1782
+ move_history(
1783
+ (@history_pointer || Reline::HISTORY.size) + 1,
1784
+ line: :start,
1785
+ cursor: @config.editing_mode_is?(:vi_command) ? :start : :end,
1786
+ )
1930
1787
  arg -= 1
1931
1788
  ed_next_history(key, arg: arg) if arg > 0
1932
1789
  end
@@ -1957,17 +1814,13 @@ class Reline::LineEditor
1957
1814
  end
1958
1815
  end
1959
1816
  else
1960
- if @history_pointer
1961
- Reline::HISTORY[@history_pointer] = whole_buffer
1962
- @history_pointer = nil
1963
- end
1964
1817
  finish
1965
1818
  end
1966
1819
  end
1967
1820
 
1968
1821
  private def em_delete_prev_char(key, arg: 1)
1969
1822
  arg.times do
1970
- if @is_multiline and @byte_pointer == 0 and @line_index > 0
1823
+ if @byte_pointer == 0 and @line_index > 0
1971
1824
  @byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
1972
1825
  @buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
1973
1826
  @line_index -= 1
@@ -1991,7 +1844,7 @@ class Reline::LineEditor
1991
1844
  line, deleted = byteslice!(current_line, @byte_pointer, current_line.bytesize - @byte_pointer)
1992
1845
  set_current_line(line, line.bytesize)
1993
1846
  @kill_ring.append(deleted)
1994
- elsif @is_multiline and @byte_pointer == current_line.bytesize and @buffer_of_lines.size > @line_index + 1
1847
+ elsif @byte_pointer == current_line.bytesize and @buffer_of_lines.size > @line_index + 1
1995
1848
  set_current_line(current_line + @buffer_of_lines.delete_at(@line_index + 1), current_line.bytesize)
1996
1849
  end
1997
1850
  end
@@ -2031,7 +1884,7 @@ class Reline::LineEditor
2031
1884
  alias_method :kill_whole_line, :em_kill_line
2032
1885
 
2033
1886
  private def em_delete(key)
2034
- if current_line.empty? and (not @is_multiline or @buffer_of_lines.size == 1) and key == "\C-d".ord
1887
+ if current_line.empty? and @buffer_of_lines.size == 1 and key == "\C-d".ord
2035
1888
  @eof = true
2036
1889
  finish
2037
1890
  elsif @byte_pointer < current_line.bytesize
@@ -2039,7 +1892,7 @@ class Reline::LineEditor
2039
1892
  mbchar = splitted_last.grapheme_clusters.first
2040
1893
  line, = byteslice!(current_line, @byte_pointer, mbchar.bytesize)
2041
1894
  set_current_line(line)
2042
- elsif @is_multiline and @byte_pointer == current_line.bytesize and @buffer_of_lines.size > @line_index + 1
1895
+ elsif @byte_pointer == current_line.bytesize and @buffer_of_lines.size > @line_index + 1
2043
1896
  set_current_line(current_line + @buffer_of_lines.delete_at(@line_index + 1), current_line.bytesize)
2044
1897
  end
2045
1898
  end
@@ -2282,7 +2135,7 @@ class Reline::LineEditor
2282
2135
  end
2283
2136
 
2284
2137
  private def vi_delete_prev_char(key)
2285
- if @is_multiline and @byte_pointer == 0 and @line_index > 0
2138
+ if @byte_pointer == 0 and @line_index > 0
2286
2139
  @byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
2287
2140
  @buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
2288
2141
  @line_index -= 1
@@ -2319,54 +2172,67 @@ class Reline::LineEditor
2319
2172
  copy_for_vi(deleted)
2320
2173
  end
2321
2174
 
2322
- private def vi_zero(key)
2323
- @byte_pointer = 0
2175
+ private def vi_change_meta(key, arg: nil)
2176
+ if @vi_waiting_operator
2177
+ set_current_line('', 0) if @vi_waiting_operator == :vi_change_meta_confirm && arg.nil?
2178
+ @vi_waiting_operator = nil
2179
+ @vi_waiting_operator_arg = nil
2180
+ else
2181
+ @drop_terminate_spaces = true
2182
+ @vi_waiting_operator = :vi_change_meta_confirm
2183
+ @vi_waiting_operator_arg = arg || 1
2184
+ end
2324
2185
  end
2325
2186
 
2326
- private def vi_change_meta(key, arg: 1)
2327
- @drop_terminate_spaces = true
2328
- @waiting_operator_proc = proc { |byte_pointer_diff|
2329
- if byte_pointer_diff > 0
2330
- line, cut = byteslice!(current_line, @byte_pointer, byte_pointer_diff)
2331
- elsif byte_pointer_diff < 0
2332
- line, cut = byteslice!(current_line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2333
- end
2334
- set_current_line(line)
2335
- copy_for_vi(cut)
2336
- @byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
2337
- @config.editing_mode = :vi_insert
2338
- @drop_terminate_spaces = false
2339
- }
2340
- @waiting_operator_vi_arg = arg
2187
+ private def vi_change_meta_confirm(byte_pointer_diff)
2188
+ vi_delete_meta_confirm(byte_pointer_diff)
2189
+ @config.editing_mode = :vi_insert
2190
+ @drop_terminate_spaces = false
2341
2191
  end
2342
2192
 
2343
- private def vi_delete_meta(key, arg: 1)
2344
- @waiting_operator_proc = proc { |byte_pointer_diff|
2345
- if byte_pointer_diff > 0
2346
- line, cut = byteslice!(current_line, @byte_pointer, byte_pointer_diff)
2347
- elsif byte_pointer_diff < 0
2348
- line, cut = byteslice!(current_line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2349
- end
2350
- copy_for_vi(cut)
2351
- set_current_line(line || '', @byte_pointer + (byte_pointer_diff < 0 ? byte_pointer_diff : 0))
2352
- }
2353
- @waiting_operator_vi_arg = arg
2193
+ private def vi_delete_meta(key, arg: nil)
2194
+ if @vi_waiting_operator
2195
+ set_current_line('', 0) if @vi_waiting_operator == :vi_delete_meta_confirm && arg.nil?
2196
+ @vi_waiting_operator = nil
2197
+ @vi_waiting_operator_arg = nil
2198
+ else
2199
+ @vi_waiting_operator = :vi_delete_meta_confirm
2200
+ @vi_waiting_operator_arg = arg || 1
2201
+ end
2354
2202
  end
2355
2203
 
2356
- private def vi_yank(key, arg: 1)
2357
- @waiting_operator_proc = proc { |byte_pointer_diff|
2358
- if byte_pointer_diff > 0
2359
- cut = current_line.byteslice(@byte_pointer, byte_pointer_diff)
2360
- elsif byte_pointer_diff < 0
2361
- cut = current_line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2362
- end
2363
- copy_for_vi(cut)
2364
- }
2365
- @waiting_operator_vi_arg = arg
2204
+ private def vi_delete_meta_confirm(byte_pointer_diff)
2205
+ if byte_pointer_diff > 0
2206
+ line, cut = byteslice!(current_line, @byte_pointer, byte_pointer_diff)
2207
+ elsif byte_pointer_diff < 0
2208
+ line, cut = byteslice!(current_line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2209
+ end
2210
+ copy_for_vi(cut)
2211
+ set_current_line(line || '', @byte_pointer + (byte_pointer_diff < 0 ? byte_pointer_diff : 0))
2212
+ end
2213
+
2214
+ private def vi_yank(key, arg: nil)
2215
+ if @vi_waiting_operator
2216
+ copy_for_vi(current_line) if @vi_waiting_operator == :vi_yank_confirm && arg.nil?
2217
+ @vi_waiting_operator = nil
2218
+ @vi_waiting_operator_arg = nil
2219
+ else
2220
+ @vi_waiting_operator = :vi_yank_confirm
2221
+ @vi_waiting_operator_arg = arg || 1
2222
+ end
2223
+ end
2224
+
2225
+ private def vi_yank_confirm(byte_pointer_diff)
2226
+ if byte_pointer_diff > 0
2227
+ cut = current_line.byteslice(@byte_pointer, byte_pointer_diff)
2228
+ elsif byte_pointer_diff < 0
2229
+ cut = current_line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2230
+ end
2231
+ copy_for_vi(cut)
2366
2232
  end
2367
2233
 
2368
2234
  private def vi_list_or_eof(key)
2369
- if (not @is_multiline and current_line.empty?) or (@is_multiline and current_line.empty? and @buffer_of_lines.size == 1)
2235
+ if current_line.empty? and @buffer_of_lines.size == 1
2370
2236
  set_current_line('', 0)
2371
2237
  @eof = true
2372
2238
  finish
@@ -2397,36 +2263,18 @@ class Reline::LineEditor
2397
2263
  if Reline::HISTORY.empty?
2398
2264
  return
2399
2265
  end
2400
- if @history_pointer.nil?
2401
- @history_pointer = 0
2402
- @line_backup_in_history = current_line
2403
- set_current_line(Reline::HISTORY[@history_pointer], 0)
2404
- elsif @history_pointer.zero?
2405
- return
2406
- else
2407
- Reline::HISTORY[@history_pointer] = current_line
2408
- @history_pointer = 0
2409
- set_current_line(Reline::HISTORY[@history_pointer], 0)
2410
- end
2266
+ move_history(0, line: :start, cursor: :start)
2411
2267
  end
2412
2268
 
2413
2269
  private def vi_histedit(key)
2414
2270
  path = Tempfile.open { |fp|
2415
- if @is_multiline
2416
- fp.write whole_lines.join("\n")
2417
- else
2418
- fp.write current_line
2419
- end
2271
+ fp.write whole_lines.join("\n")
2420
2272
  fp.path
2421
2273
  }
2422
2274
  system("#{ENV['EDITOR']} #{path}")
2423
- if @is_multiline
2424
- @buffer_of_lines = File.read(path).split("\n")
2425
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
2426
- @line_index = 0
2427
- else
2428
- @buffer_of_lines = File.read(path).split("\n")
2429
- end
2275
+ @buffer_of_lines = File.read(path).split("\n")
2276
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
2277
+ @line_index = 0
2430
2278
  finish
2431
2279
  end
2432
2280
 
@@ -2467,18 +2315,11 @@ class Reline::LineEditor
2467
2315
  end
2468
2316
 
2469
2317
  private def vi_to_column(key, arg: 0)
2470
- current_row_width = calculate_width(current_row)
2471
- @byte_pointer, = current_line.grapheme_clusters.inject([0, 0]) { |total, gc|
2472
- # total has [byte_size, cursor]
2318
+ # Implementing behavior of vi, not Readline's vi-mode.
2319
+ @byte_pointer, = current_line.grapheme_clusters.inject([0, 0]) { |(total_byte_size, total_width), gc|
2473
2320
  mbchar_width = Reline::Unicode.get_mbchar_width(gc)
2474
- if (total.last + mbchar_width) >= arg
2475
- break total
2476
- elsif (total.last + mbchar_width) >= current_row_width
2477
- break total
2478
- else
2479
- total = [total.first + gc.bytesize, total.last + mbchar_width]
2480
- total
2481
- end
2321
+ break [total_byte_size, total_width] if (total_width + mbchar_width) >= arg
2322
+ [total_byte_size + gc.bytesize, total_width + mbchar_width]
2482
2323
  }
2483
2324
  end
2484
2325
 
@@ -2605,7 +2446,7 @@ class Reline::LineEditor
2605
2446
  end
2606
2447
 
2607
2448
  private def vi_join_lines(key, arg: 1)
2608
- if @is_multiline and @buffer_of_lines.size > @line_index + 1
2449
+ if @buffer_of_lines.size > @line_index + 1
2609
2450
  next_line = @buffer_of_lines.delete_at(@line_index + 1).lstrip
2610
2451
  set_current_line(current_line + ' ' + next_line, current_line.bytesize)
2611
2452
  end
@@ -2626,6 +2467,11 @@ class Reline::LineEditor
2626
2467
  end
2627
2468
  alias_method :exchange_point_and_mark, :em_exchange_mark
2628
2469
 
2629
- private def em_meta_next(key)
2470
+ private def emacs_editing_mode(key)
2471
+ @config.editing_mode = :emacs
2472
+ end
2473
+
2474
+ private def vi_editing_mode(key)
2475
+ @config.editing_mode = :vi_insert
2630
2476
  end
2631
2477
  end