reline 0.2.8.pre.9 → 0.3.1

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.
@@ -5,6 +5,7 @@ require 'tempfile'
5
5
 
6
6
  class Reline::LineEditor
7
7
  # TODO: undo
8
+ # TODO: Use "private alias_method" idiom after drop Ruby 2.5.
8
9
  attr_reader :line
9
10
  attr_reader :byte_pointer
10
11
  attr_accessor :confirm_multiline_termination_proc
@@ -92,7 +93,7 @@ class Reline::LineEditor
92
93
  mode_string
93
94
  end
94
95
 
95
- private def check_multiline_prompt(buffer, prompt)
96
+ private def check_multiline_prompt(buffer)
96
97
  if @vi_arg
97
98
  prompt = "(arg: #{@vi_arg}) "
98
99
  @rerender_all = true
@@ -120,7 +121,7 @@ class Reline::LineEditor
120
121
  if use_cached_prompt_list
121
122
  prompt_list = @cached_prompt_list
122
123
  else
123
- prompt_list = @cached_prompt_list = @prompt_proc.(buffer)
124
+ prompt_list = @cached_prompt_list = @prompt_proc.(buffer).map { |pr| pr.gsub("\n", "\\n") }
124
125
  @prompt_cache_time = Time.now.to_f
125
126
  end
126
127
  prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
@@ -150,6 +151,73 @@ class Reline::LineEditor
150
151
  @screen_size = Reline::IOGate.get_screen_size
151
152
  @screen_height = @screen_size.first
152
153
  reset_variables(prompt, encoding: encoding)
154
+ Reline::IOGate.set_winch_handler do
155
+ @resized = true
156
+ end
157
+ if ENV.key?('RELINE_ALT_SCROLLBAR')
158
+ @full_block = '::'
159
+ @upper_half_block = "''"
160
+ @lower_half_block = '..'
161
+ @block_elem_width = 2
162
+ elsif Reline::IOGate.win?
163
+ @full_block = '█'
164
+ @upper_half_block = '▀'
165
+ @lower_half_block = '▄'
166
+ @block_elem_width = 1
167
+ elsif @encoding == Encoding::UTF_8
168
+ @full_block = '█'
169
+ @upper_half_block = '▀'
170
+ @lower_half_block = '▄'
171
+ @block_elem_width = Reline::Unicode.calculate_width('█')
172
+ else
173
+ @full_block = '::'
174
+ @upper_half_block = "''"
175
+ @lower_half_block = '..'
176
+ @block_elem_width = 2
177
+ end
178
+ end
179
+
180
+ def resize
181
+ return unless @resized
182
+ @resized = false
183
+ @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
184
+ old_screen_size = @screen_size
185
+ @screen_size = Reline::IOGate.get_screen_size
186
+ @screen_height = @screen_size.first
187
+ if old_screen_size.last < @screen_size.last # columns increase
188
+ @rerender_all = true
189
+ rerender
190
+ else
191
+ back = 0
192
+ new_buffer = whole_lines
193
+ prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer)
194
+ new_buffer.each_with_index do |line, index|
195
+ prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
196
+ width = prompt_width + calculate_width(line)
197
+ height = calculate_height_by_width(width)
198
+ back += height
199
+ end
200
+ @highest_in_all = back
201
+ @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
202
+ @first_line_started_from =
203
+ if @line_index.zero?
204
+ 0
205
+ else
206
+ calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
207
+ end
208
+ if @prompt_proc
209
+ prompt = prompt_list[@line_index]
210
+ prompt_width = calculate_width(prompt, true)
211
+ end
212
+ calculate_nearest_cursor
213
+ @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
214
+ Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
215
+ @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
216
+ @rerender_all = true
217
+ end
218
+ end
219
+
220
+ def set_signal_handlers
153
221
  @old_trap = Signal.trap('INT') {
154
222
  clear_dialog
155
223
  if @scroll_partial_screen
@@ -167,7 +235,7 @@ class Reline::LineEditor
167
235
  when 'EXIT'
168
236
  exit
169
237
  else
170
- @old_trap.call
238
+ @old_trap.call if @old_trap.respond_to?(:call)
171
239
  end
172
240
  }
173
241
  begin
@@ -177,44 +245,6 @@ class Reline::LineEditor
177
245
  }
178
246
  rescue ArgumentError
179
247
  end
180
- Reline::IOGate.set_winch_handler do
181
- @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
182
- old_screen_size = @screen_size
183
- @screen_size = Reline::IOGate.get_screen_size
184
- @screen_height = @screen_size.first
185
- if old_screen_size.last < @screen_size.last # columns increase
186
- @rerender_all = true
187
- rerender
188
- else
189
- back = 0
190
- new_buffer = whole_lines
191
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
192
- new_buffer.each_with_index do |line, index|
193
- prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
194
- width = prompt_width + calculate_width(line)
195
- height = calculate_height_by_width(width)
196
- back += height
197
- end
198
- @highest_in_all = back
199
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
200
- @first_line_started_from =
201
- if @line_index.zero?
202
- 0
203
- else
204
- calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
205
- end
206
- if @prompt_proc
207
- prompt = prompt_list[@line_index]
208
- prompt_width = calculate_width(prompt, true)
209
- end
210
- calculate_nearest_cursor
211
- @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
212
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
213
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
214
- @rerender_all = true
215
- end
216
- end
217
- @block_elem_width = Reline::Unicode.calculate_width('█')
218
248
  end
219
249
 
220
250
  def finalize
@@ -230,7 +260,7 @@ class Reline::LineEditor
230
260
  end
231
261
 
232
262
  def reset_variables(prompt = '', encoding:)
233
- @prompt = prompt
263
+ @prompt = prompt.gsub("\n", "\\n")
234
264
  @mark_pointer = nil
235
265
  @encoding = encoding
236
266
  @is_multiline = false
@@ -264,6 +294,7 @@ class Reline::LineEditor
264
294
  @auto_indent_proc = nil
265
295
  @dialogs = []
266
296
  @last_key = nil
297
+ @resized = false
267
298
  reset_line
268
299
  end
269
300
 
@@ -407,7 +438,7 @@ class Reline::LineEditor
407
438
  show_menu
408
439
  @menu_info = nil
409
440
  end
410
- prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
441
+ prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
411
442
  if @cleared
412
443
  clear_screen_buffer(prompt, prompt_list, prompt_width)
413
444
  @cleared = false
@@ -418,7 +449,7 @@ class Reline::LineEditor
418
449
  Reline::IOGate.move_cursor_up(@first_line_started_from + @started_from - @scroll_partial_screen)
419
450
  Reline::IOGate.move_cursor_column(0)
420
451
  @scroll_partial_screen = nil
421
- prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
452
+ prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
422
453
  if @previous_line_index
423
454
  new_lines = whole_lines(index: @previous_line_index, line: @line)
424
455
  else
@@ -464,7 +495,7 @@ class Reline::LineEditor
464
495
  end
465
496
  line = modify_lines(new_lines)[@line_index]
466
497
  clear_dialog
467
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
498
+ prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
468
499
  render_partial(prompt, prompt_width, line, @first_line_started_from)
469
500
  move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
470
501
  scroll_down(1)
@@ -473,7 +504,7 @@ class Reline::LineEditor
473
504
  else
474
505
  if not rendered and not @in_pasting
475
506
  line = modify_lines(whole_lines)[@line_index]
476
- prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
507
+ prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
477
508
  render_partial(prompt, prompt_width, line, @first_line_started_from)
478
509
  end
479
510
  render_dialog((prompt_width + @cursor) % @screen_size.last)
@@ -560,7 +591,7 @@ class Reline::LineEditor
560
591
 
561
592
  class Dialog
562
593
  attr_reader :name, :contents, :width
563
- attr_accessor :scroll_top, :scrollbar_pos, :column, :vertical_offset, :lines_backup, :trap_key
594
+ attr_accessor :scroll_top, :scrollbar_pos, :pointer, :column, :vertical_offset, :lines_backup, :trap_key
564
595
 
565
596
  def initialize(name, config, proc_scope)
566
597
  @name = name
@@ -568,6 +599,7 @@ class Reline::LineEditor
568
599
  @proc_scope = proc_scope
569
600
  @width = nil
570
601
  @scroll_top = 0
602
+ @trap_key = nil
571
603
  end
572
604
 
573
605
  def set_cursor_pos(col, row)
@@ -605,11 +637,15 @@ class Reline::LineEditor
605
637
  end
606
638
 
607
639
  def add_dialog_proc(name, p, context = nil)
608
- return if @dialogs.any? { |d| d.name == name }
609
- @dialogs << Dialog.new(name, @config, DialogProcScope.new(self, @config, p, context))
640
+ dialog = Dialog.new(name, @config, DialogProcScope.new(self, @config, p, context))
641
+ if index = @dialogs.find_index { |d| d.name == name }
642
+ @dialogs[index] = dialog
643
+ else
644
+ @dialogs << dialog
645
+ end
610
646
  end
611
647
 
612
- DIALOG_HEIGHT = 20
648
+ DIALOG_DEFAULT_HEIGHT = 20
613
649
  private def render_dialog(cursor_column)
614
650
  @dialogs.each do |dialog|
615
651
  render_each_dialog(dialog, cursor_column)
@@ -622,6 +658,7 @@ class Reline::LineEditor
622
658
 
623
659
  private def render_each_dialog(dialog, cursor_column)
624
660
  if @in_pasting
661
+ clear_each_dialog(dialog)
625
662
  dialog.contents = nil
626
663
  dialog.trap_key = nil
627
664
  return
@@ -643,27 +680,30 @@ class Reline::LineEditor
643
680
  end
644
681
  old_dialog = dialog.clone
645
682
  dialog.contents = dialog_render_info.contents
646
- pointer = dialog_render_info.pointer
683
+ pointer = dialog.pointer
647
684
  if dialog_render_info.width
648
685
  dialog.width = dialog_render_info.width
649
686
  else
650
687
  dialog.width = dialog.contents.map { |l| calculate_width(l, true) }.max
651
688
  end
652
- height = dialog_render_info.height || DIALOG_HEIGHT
689
+ height = dialog_render_info.height || DIALOG_DEFAULT_HEIGHT
653
690
  height = dialog.contents.size if dialog.contents.size < height
654
691
  if dialog.contents.size > height
655
- if dialog_render_info.pointer
656
- if dialog_render_info.pointer < 0
692
+ if dialog.pointer
693
+ if dialog.pointer < 0
657
694
  dialog.scroll_top = 0
658
- elsif (dialog_render_info.pointer - dialog.scroll_top) >= (height - 1)
659
- dialog.scroll_top = dialog_render_info.pointer - (height - 1)
660
- elsif (dialog_render_info.pointer - dialog.scroll_top) < 0
661
- dialog.scroll_top = dialog_render_info.pointer
695
+ elsif (dialog.pointer - dialog.scroll_top) >= (height - 1)
696
+ dialog.scroll_top = dialog.pointer - (height - 1)
697
+ elsif (dialog.pointer - dialog.scroll_top) < 0
698
+ dialog.scroll_top = dialog.pointer
662
699
  end
663
- pointer = dialog_render_info.pointer - dialog.scroll_top
700
+ pointer = dialog.pointer - dialog.scroll_top
664
701
  end
665
702
  dialog.contents = dialog.contents[dialog.scroll_top, height]
666
703
  end
704
+ if dialog.contents and dialog.scroll_top >= dialog.contents.size
705
+ dialog.scroll_top = dialog.contents.size - height
706
+ end
667
707
  if dialog_render_info.scrollbar and dialog_render_info.contents.size > height
668
708
  bar_max_height = height * 2
669
709
  moving_distance = (dialog_render_info.contents.size - height) * 2
@@ -674,25 +714,28 @@ class Reline::LineEditor
674
714
  dialog.scrollbar_pos = nil
675
715
  end
676
716
  upper_space = @first_line_started_from - @started_from
677
- lower_space = @highest_in_all - @first_line_started_from - @started_from - 1
678
717
  dialog.column = dialog_render_info.pos.x
679
- diff = (dialog.column + dialog.width) - (@screen_size.last - 1)
718
+ dialog.width += @block_elem_width if dialog.scrollbar_pos
719
+ diff = (dialog.column + dialog.width) - (@screen_size.last)
680
720
  if diff > 0
681
721
  dialog.column -= diff
682
722
  end
683
- if (lower_space + @rest_height - dialog_render_info.pos.y) >= height
723
+ if (@rest_height - dialog_render_info.pos.y) >= height
684
724
  dialog.vertical_offset = dialog_render_info.pos.y + 1
685
725
  elsif upper_space >= height
686
726
  dialog.vertical_offset = dialog_render_info.pos.y - height
687
727
  else
688
- if (lower_space + @rest_height - dialog_render_info.pos.y) < height
728
+ if (@rest_height - dialog_render_info.pos.y) < height
689
729
  scroll_down(height + dialog_render_info.pos.y)
690
730
  move_cursor_up(height + dialog_render_info.pos.y)
691
731
  end
692
732
  dialog.vertical_offset = dialog_render_info.pos.y + 1
693
733
  end
694
734
  Reline::IOGate.hide_cursor
695
- dialog.width += @block_elem_width if dialog.scrollbar_pos
735
+ if dialog.column < 0
736
+ dialog.column = 0
737
+ dialog.width = @screen_size.last
738
+ end
696
739
  reset_dialog(dialog, old_dialog)
697
740
  move_cursor_down(dialog.vertical_offset)
698
741
  Reline::IOGate.move_cursor_column(dialog.column)
@@ -711,19 +754,18 @@ class Reline::LineEditor
711
754
  @output.write "\e[#{bg_color}m#{str}"
712
755
  if dialog.scrollbar_pos and (dialog.scrollbar_pos != old_dialog.scrollbar_pos or dialog.column != old_dialog.column)
713
756
  @output.write "\e[37m"
714
- if dialog.scrollbar_pos <= (i * 2) and (i * 2 + @block_elem_width) < (dialog.scrollbar_pos + bar_height)
715
- @output.write '█'
757
+ if dialog.scrollbar_pos <= (i * 2) and (i * 2 + 1) < (dialog.scrollbar_pos + bar_height)
758
+ @output.write @full_block
716
759
  elsif dialog.scrollbar_pos <= (i * 2) and (i * 2) < (dialog.scrollbar_pos + bar_height)
717
- @output.write '▀'
760
+ @output.write @upper_half_block
718
761
  str += ''
719
- elsif dialog.scrollbar_pos <= (i * 2 + @block_elem_width) and (i * 2) < (dialog.scrollbar_pos + bar_height)
720
- @output.write '▄'
762
+ elsif dialog.scrollbar_pos <= (i * 2 + 1) and (i * 2) < (dialog.scrollbar_pos + bar_height)
763
+ @output.write @lower_half_block
721
764
  else
722
765
  @output.write ' ' * @block_elem_width
723
766
  end
724
- @output.write "\e[39m"
725
767
  end
726
- @output.write "\e[49m"
768
+ @output.write "\e[0m"
727
769
  Reline::IOGate.move_cursor_column(dialog.column)
728
770
  move_cursor_down(1) if i < (dialog.contents.size - 1)
729
771
  end
@@ -741,7 +783,7 @@ class Reline::LineEditor
741
783
 
742
784
  private def reset_dialog(dialog, old_dialog)
743
785
  return if dialog.lines_backup.nil? or old_dialog.contents.nil?
744
- prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
786
+ prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines])
745
787
  visual_lines = []
746
788
  visual_start = nil
747
789
  dialog.lines_backup[:lines].each_with_index { |l, i|
@@ -769,7 +811,7 @@ class Reline::LineEditor
769
811
  s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width)
770
812
  s = padding_space_with_escape_sequences(s, old_dialog.width)
771
813
  end
772
- @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
814
+ @output.write "\e[0m#{s}\e[0m"
773
815
  move_cursor_down(1) if i < (line_num - 1)
774
816
  end
775
817
  move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
@@ -787,7 +829,7 @@ class Reline::LineEditor
787
829
  s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width)
788
830
  s = padding_space_with_escape_sequences(s, old_dialog.width)
789
831
  end
790
- @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
832
+ @output.write "\e[0m#{s}\e[0m"
791
833
  move_cursor_down(1) if i < (line_num - 1)
792
834
  end
793
835
  move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
@@ -806,7 +848,7 @@ class Reline::LineEditor
806
848
  s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, width)
807
849
  s = padding_space_with_escape_sequences(s, dialog.width)
808
850
  end
809
- @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
851
+ @output.write "\e[0m#{s}\e[0m"
810
852
  move_cursor_down(1) if i < (line_num - 1)
811
853
  end
812
854
  move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
@@ -823,10 +865,11 @@ class Reline::LineEditor
823
865
  s = ' ' * width
824
866
  else
825
867
  s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column + dialog.width, width)
826
- s = padding_space_with_escape_sequences(s, dialog.width)
868
+ rerender_width = old_dialog.width - dialog.width
869
+ s = padding_space_with_escape_sequences(s, rerender_width)
827
870
  end
828
871
  Reline::IOGate.move_cursor_column(dialog.column + dialog.width)
829
- @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
872
+ @output.write "\e[0m#{s}\e[0m"
830
873
  move_cursor_down(1) if i < (line_num - 1)
831
874
  end
832
875
  move_cursor_up(old_dialog.vertical_offset + line_num - 1 + y_diff)
@@ -843,7 +886,7 @@ class Reline::LineEditor
843
886
  private def clear_each_dialog(dialog)
844
887
  dialog.trap_key = nil
845
888
  return unless dialog.contents
846
- prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
889
+ prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines])
847
890
  visual_lines = []
848
891
  visual_lines_under_dialog = []
849
892
  visual_start = nil
@@ -866,10 +909,10 @@ class Reline::LineEditor
866
909
  Reline::IOGate.move_cursor_column(dialog.column)
867
910
  str = Reline::Unicode.take_range(visual_lines_under_dialog[i], dialog.column, dialog.width)
868
911
  str = padding_space_with_escape_sequences(str, dialog.width)
869
- @output.write "\e[39m\e[49m#{str}\e[39m\e[49m"
912
+ @output.write "\e[0m#{str}\e[0m"
870
913
  else
871
914
  Reline::IOGate.move_cursor_column(dialog.column)
872
- @output.write "\e[39m\e[49m#{' ' * dialog.width}\e[39m\e[49m"
915
+ @output.write "\e[0m#{' ' * dialog.width}\e[0m"
873
916
  end
874
917
  move_cursor_down(1) if i < (dialog_vertical_size - 1)
875
918
  end
@@ -928,7 +971,7 @@ class Reline::LineEditor
928
971
  end
929
972
 
930
973
  def just_move_cursor
931
- prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines, prompt)
974
+ prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines)
932
975
  move_cursor_up(@started_from)
933
976
  new_first_line_started_from =
934
977
  if @line_index.zero?
@@ -965,7 +1008,7 @@ class Reline::LineEditor
965
1008
  else
966
1009
  new_lines = whole_lines
967
1010
  end
968
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
1011
+ prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
969
1012
  all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
970
1013
  diff = all_height - @highest_in_all
971
1014
  move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
@@ -1012,7 +1055,7 @@ class Reline::LineEditor
1012
1055
  Reline::IOGate.move_cursor_column(0)
1013
1056
  back = 0
1014
1057
  new_buffer = whole_lines
1015
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
1058
+ prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer)
1016
1059
  new_buffer.each_with_index do |line, index|
1017
1060
  prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
1018
1061
  width = prompt_width + calculate_width(line)
@@ -1402,7 +1445,10 @@ class Reline::LineEditor
1402
1445
  end
1403
1446
  @waiting_operator_proc = nil
1404
1447
  @waiting_operator_vi_arg = nil
1405
- @vi_arg = nil
1448
+ if @vi_arg
1449
+ @rerender_all = true
1450
+ @vi_arg = nil
1451
+ end
1406
1452
  else
1407
1453
  block.(false)
1408
1454
  end
@@ -1453,7 +1499,10 @@ class Reline::LineEditor
1453
1499
  wrap_method_call(method_symbol, method_obj, key) if method_obj
1454
1500
  end
1455
1501
  @kill_ring.process
1456
- @vi_arg = nil
1502
+ if @vi_arg
1503
+ @rerender_al = true
1504
+ @vi_arg = nil
1505
+ end
1457
1506
  elsif @vi_arg
1458
1507
  if key.chr =~ /[0-9]/
1459
1508
  ed_argument_digit(key)
@@ -1470,7 +1519,10 @@ class Reline::LineEditor
1470
1519
  ed_insert(key) unless @config.editing_mode_is?(:vi_command)
1471
1520
  end
1472
1521
  @kill_ring.process
1473
- @vi_arg = nil
1522
+ if @vi_arg
1523
+ @rerender_all = true
1524
+ @vi_arg = nil
1525
+ end
1474
1526
  end
1475
1527
  elsif @waiting_proc
1476
1528
  @waiting_proc.(key)
@@ -1909,6 +1961,8 @@ class Reline::LineEditor
1909
1961
  end
1910
1962
  end
1911
1963
 
1964
+ # Editline:: +ed-unassigned+ This editor command always results in an error.
1965
+ # GNU Readline:: There is no corresponding macro.
1912
1966
  private def ed_unassigned(key) end # do nothing
1913
1967
 
1914
1968
  private def process_insert(force: false)
@@ -1926,6 +1980,19 @@ class Reline::LineEditor
1926
1980
  @continuous_insertion_buffer.clear
1927
1981
  end
1928
1982
 
1983
+ # Editline:: +ed-insert+ (vi input: almost all; emacs: printable characters)
1984
+ # In insert mode, insert the input character left of the cursor
1985
+ # position. In replace mode, overwrite the character at the
1986
+ # cursor and move the cursor to the right by one character
1987
+ # position. Accept an argument to do this repeatedly. It is an
1988
+ # error if the input character is the NUL character (+Ctrl-@+).
1989
+ # Failure to enlarge the edit buffer also results in an error.
1990
+ # Editline:: +ed-digit+ (emacs: 0 to 9) If in argument input mode, append
1991
+ # the input digit to the argument being read. Otherwise, call
1992
+ # +ed-insert+. It is an error if the input character is not a
1993
+ # digit or if the existing argument is already greater than a
1994
+ # million.
1995
+ # GNU Readline:: +self-insert+ (a, b, A, 1, !, …) Insert yourself.
1929
1996
  private def ed_insert(key)
1930
1997
  str = nil
1931
1998
  width = nil
@@ -1962,8 +2029,16 @@ class Reline::LineEditor
1962
2029
  last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
1963
2030
  @byte_pointer += bytesize
1964
2031
  last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
1965
- if last_byte_size != 0 and (last_mbchar + str).grapheme_clusters.size == 1
1966
- width = 0
2032
+ combined_char = last_mbchar + str
2033
+ if last_byte_size != 0 and combined_char.grapheme_clusters.size == 1
2034
+ # combined char
2035
+ last_mbchar_width = Reline::Unicode.get_mbchar_width(last_mbchar)
2036
+ combined_char_width = Reline::Unicode.get_mbchar_width(combined_char)
2037
+ if combined_char_width > last_mbchar_width
2038
+ width = combined_char_width - last_mbchar_width
2039
+ else
2040
+ width = 0
2041
+ end
1967
2042
  end
1968
2043
  @cursor += width
1969
2044
  @cursor_max += width
@@ -1976,6 +2051,8 @@ class Reline::LineEditor
1976
2051
  arg.times do
1977
2052
  if key == "\C-j".ord or key == "\C-m".ord
1978
2053
  key_newline(key)
2054
+ elsif key == 0
2055
+ # Ignore NUL.
1979
2056
  else
1980
2057
  ed_insert(key)
1981
2058
  end
@@ -2410,6 +2487,7 @@ class Reline::LineEditor
2410
2487
  arg -= 1
2411
2488
  ed_prev_history(key, arg: arg) if arg > 0
2412
2489
  end
2490
+ alias_method :previous_history, :ed_prev_history
2413
2491
 
2414
2492
  private def ed_next_history(key, arg: 1)
2415
2493
  if @is_multiline and @line_index < (@buffer_of_lines.size - 1)
@@ -2457,6 +2535,7 @@ class Reline::LineEditor
2457
2535
  arg -= 1
2458
2536
  ed_next_history(key, arg: arg) if arg > 0
2459
2537
  end
2538
+ alias_method :next_history, :ed_next_history
2460
2539
 
2461
2540
  private def ed_newline(key)
2462
2541
  process_insert(force: true)
@@ -2491,7 +2570,7 @@ class Reline::LineEditor
2491
2570
  end
2492
2571
  end
2493
2572
 
2494
- private def em_delete_prev_char(key)
2573
+ private def em_delete_prev_char(key, arg: 1)
2495
2574
  if @is_multiline and @cursor == 0 and @line_index > 0
2496
2575
  @buffer_of_lines[@line_index] = @line
2497
2576
  @cursor = calculate_width(@buffer_of_lines[@line_index - 1])
@@ -2509,9 +2588,16 @@ class Reline::LineEditor
2509
2588
  @cursor -= width
2510
2589
  @cursor_max -= width
2511
2590
  end
2591
+ arg -= 1
2592
+ em_delete_prev_char(key, arg: arg) if arg > 0
2512
2593
  end
2513
2594
  alias_method :backward_delete_char, :em_delete_prev_char
2514
2595
 
2596
+ # Editline:: +ed-kill-line+ (vi command: +D+, +Ctrl-K+; emacs: +Ctrl-K+,
2597
+ # +Ctrl-U+) + Kill from the cursor to the end of the line.
2598
+ # GNU Readline:: +kill-line+ (+C-k+) Kill the text from point to the end of
2599
+ # the line. With a negative numeric argument, kill backward
2600
+ # from the cursor to the beginning of the current line.
2515
2601
  private def ed_kill_line(key)
2516
2602
  if @line.bytesize > @byte_pointer
2517
2603
  @line, deleted = byteslice!(@line, @byte_pointer, @line.bytesize - @byte_pointer)
@@ -2528,8 +2614,14 @@ class Reline::LineEditor
2528
2614
  @rest_height += 1
2529
2615
  end
2530
2616
  end
2617
+ alias_method :kill_line, :ed_kill_line
2531
2618
 
2532
- private def em_kill_line(key)
2619
+ # Editline:: +vi-kill-line-prev+ (vi: +Ctrl-U+) Delete the string from the
2620
+ # beginning of the edit buffer to the cursor and save it to the
2621
+ # cut buffer.
2622
+ # GNU Readline:: +unix-line-discard+ (+C-u+) Kill backward from the cursor
2623
+ # to the beginning of the current line.
2624
+ private def vi_kill_line_prev(key)
2533
2625
  if @byte_pointer > 0
2534
2626
  @line, deleted = byteslice!(@line, 0, @byte_pointer)
2535
2627
  @byte_pointer = 0
@@ -2538,7 +2630,22 @@ class Reline::LineEditor
2538
2630
  @cursor = 0
2539
2631
  end
2540
2632
  end
2541
- alias_method :kill_line, :em_kill_line
2633
+ alias_method :unix_line_discard, :vi_kill_line_prev
2634
+
2635
+ # Editline:: +em-kill-line+ (not bound) Delete the entire contents of the
2636
+ # edit buffer and save it to the cut buffer. +vi-kill-line-prev+
2637
+ # GNU Readline:: +kill-whole-line+ (not bound) Kill all characters on the
2638
+ # current line, no matter where point is.
2639
+ private def em_kill_line(key)
2640
+ if @line.size > 0
2641
+ @kill_ring.append(@line.dup, true)
2642
+ @line.clear
2643
+ @byte_pointer = 0
2644
+ @cursor_max = 0
2645
+ @cursor = 0
2646
+ end
2647
+ end
2648
+ alias_method :kill_whole_line, :em_kill_line
2542
2649
 
2543
2650
  private def em_delete(key)
2544
2651
  if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
@@ -3043,7 +3150,14 @@ class Reline::LineEditor
3043
3150
 
3044
3151
  private def ed_argument_digit(key)
3045
3152
  if @vi_arg.nil?
3046
- unless key.chr.to_i.zero?
3153
+ if key.chr.to_i.zero?
3154
+ if key.anybits?(0b10000000)
3155
+ unescaped_key = key ^ 0b10000000
3156
+ unless unescaped_key.chr.to_i.zero?
3157
+ @vi_arg = unescaped_key.chr.to_i
3158
+ end
3159
+ end
3160
+ else
3047
3161
  @vi_arg = key.chr.to_i
3048
3162
  end
3049
3163
  else