reline 0.2.8.pre.7 → 0.2.8.pre.11

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
@@ -150,7 +151,7 @@ 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)
153
- @old_trap = Signal.trap(:INT) {
154
+ @old_trap = Signal.trap('INT') {
154
155
  clear_dialog
155
156
  if @scroll_partial_screen
156
157
  move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
@@ -170,47 +171,85 @@ class Reline::LineEditor
170
171
  @old_trap.call
171
172
  end
172
173
  }
174
+ begin
175
+ @old_tstp_trap = Signal.trap('TSTP') {
176
+ Reline::IOGate.ungetc("\C-z".ord)
177
+ @old_tstp_trap.call if @old_tstp_trap.respond_to?(:call)
178
+ }
179
+ rescue ArgumentError
180
+ end
173
181
  Reline::IOGate.set_winch_handler do
174
- @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
175
- old_screen_size = @screen_size
176
- @screen_size = Reline::IOGate.get_screen_size
177
- @screen_height = @screen_size.first
178
- if old_screen_size.last < @screen_size.last # columns increase
179
- @rerender_all = true
180
- rerender
181
- else
182
- back = 0
183
- new_buffer = whole_lines
184
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
185
- new_buffer.each_with_index do |line, index|
186
- prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
187
- width = prompt_width + calculate_width(line)
188
- height = calculate_height_by_width(width)
189
- back += height
190
- end
191
- @highest_in_all = back
192
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
193
- @first_line_started_from =
194
- if @line_index.zero?
195
- 0
196
- else
197
- calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
198
- end
199
- if @prompt_proc
200
- prompt = prompt_list[@line_index]
201
- prompt_width = calculate_width(prompt, true)
182
+ @resized = true
183
+ end
184
+ if ENV.key?('RELINE_ALT_SCROLLBAR')
185
+ @full_block = '::'
186
+ @upper_half_block = "''"
187
+ @lower_half_block = '..'
188
+ @block_elem_width = 2
189
+ elsif Reline::IOGate.win?
190
+ @full_block = '█'
191
+ @upper_half_block = '▀'
192
+ @lower_half_block = '▄'
193
+ @block_elem_width = 1
194
+ elsif @encoding == Encoding::UTF_8
195
+ @full_block = '█'
196
+ @upper_half_block = '▀'
197
+ @lower_half_block = '▄'
198
+ @block_elem_width = Reline::Unicode.calculate_width('█')
199
+ else
200
+ @full_block = '::'
201
+ @upper_half_block = "''"
202
+ @lower_half_block = '..'
203
+ @block_elem_width = 2
204
+ end
205
+ end
206
+
207
+ def resize
208
+ return unless @resized
209
+ @resized = false
210
+ @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
211
+ old_screen_size = @screen_size
212
+ @screen_size = Reline::IOGate.get_screen_size
213
+ @screen_height = @screen_size.first
214
+ if old_screen_size.last < @screen_size.last # columns increase
215
+ @rerender_all = true
216
+ rerender
217
+ else
218
+ back = 0
219
+ new_buffer = whole_lines
220
+ prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
221
+ new_buffer.each_with_index do |line, index|
222
+ prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
223
+ width = prompt_width + calculate_width(line)
224
+ height = calculate_height_by_width(width)
225
+ back += height
226
+ end
227
+ @highest_in_all = back
228
+ @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
229
+ @first_line_started_from =
230
+ if @line_index.zero?
231
+ 0
232
+ else
233
+ calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
202
234
  end
203
- calculate_nearest_cursor
204
- @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
205
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
206
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
207
- @rerender_all = true
235
+ if @prompt_proc
236
+ prompt = prompt_list[@line_index]
237
+ prompt_width = calculate_width(prompt, true)
208
238
  end
239
+ calculate_nearest_cursor
240
+ @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
241
+ Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
242
+ @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
243
+ @rerender_all = true
209
244
  end
210
245
  end
211
246
 
212
247
  def finalize
213
- Signal.trap('SIGINT', @old_trap)
248
+ Signal.trap('INT', @old_trap)
249
+ begin
250
+ Signal.trap('TSTP', @old_tstp_trap)
251
+ rescue ArgumentError
252
+ end
214
253
  end
215
254
 
216
255
  def eof?
@@ -252,6 +291,7 @@ class Reline::LineEditor
252
291
  @auto_indent_proc = nil
253
292
  @dialogs = []
254
293
  @last_key = nil
294
+ @resized = false
255
295
  reset_line
256
296
  end
257
297
 
@@ -548,13 +588,15 @@ class Reline::LineEditor
548
588
 
549
589
  class Dialog
550
590
  attr_reader :name, :contents, :width
551
- attr_accessor :scroll_top, :column, :vertical_offset, :lines_backup, :trap_key
591
+ attr_accessor :scroll_top, :scrollbar_pos, :pointer, :column, :vertical_offset, :lines_backup, :trap_key
552
592
 
553
- def initialize(name, proc_scope)
593
+ def initialize(name, config, proc_scope)
554
594
  @name = name
595
+ @config = config
555
596
  @proc_scope = proc_scope
556
597
  @width = nil
557
598
  @scroll_top = 0
599
+ @trap_key = nil
558
600
  end
559
601
 
560
602
  def set_cursor_pos(col, row)
@@ -575,16 +617,28 @@ class Reline::LineEditor
575
617
  def call(key)
576
618
  @proc_scope.set_dialog(self)
577
619
  @proc_scope.set_key(key)
578
- @proc_scope.call
620
+ dialog_render_info = @proc_scope.call
621
+ if @trap_key
622
+ if @trap_key.any?{ |i| i.is_a?(Array) } # multiple trap
623
+ @trap_key.each do |t|
624
+ @config.add_oneshot_key_binding(t, @name)
625
+ end
626
+ elsif @trap_key.is_a?(Array)
627
+ @config.add_oneshot_key_binding(@trap_key, @name)
628
+ elsif @trap_key.is_a?(Integer) or @trap_key.is_a?(Reline::Key)
629
+ @config.add_oneshot_key_binding([@trap_key], @name)
630
+ end
631
+ end
632
+ dialog_render_info
579
633
  end
580
634
  end
581
635
 
582
636
  def add_dialog_proc(name, p, context = nil)
583
637
  return if @dialogs.any? { |d| d.name == name }
584
- @dialogs << Dialog.new(name, DialogProcScope.new(self, @config, p, context))
638
+ @dialogs << Dialog.new(name, @config, DialogProcScope.new(self, @config, p, context))
585
639
  end
586
640
 
587
- DIALOG_HEIGHT = 20
641
+ DIALOG_DEFAULT_HEIGHT = 20
588
642
  private def render_dialog(cursor_column)
589
643
  @dialogs.each do |dialog|
590
644
  render_each_dialog(dialog, cursor_column)
@@ -618,31 +672,44 @@ class Reline::LineEditor
618
672
  end
619
673
  old_dialog = dialog.clone
620
674
  dialog.contents = dialog_render_info.contents
621
- pointer = dialog_render_info.pointer
675
+ pointer = dialog.pointer
622
676
  if dialog_render_info.width
623
677
  dialog.width = dialog_render_info.width
624
678
  else
625
679
  dialog.width = dialog.contents.map { |l| calculate_width(l, true) }.max
626
680
  end
627
- height = dialog_render_info.height || DIALOG_HEIGHT
681
+ height = dialog_render_info.height || DIALOG_DEFAULT_HEIGHT
628
682
  height = dialog.contents.size if dialog.contents.size < height
629
683
  if dialog.contents.size > height
630
- if dialog_render_info.pointer
631
- if dialog_render_info.pointer < 0
684
+ if dialog.pointer
685
+ if dialog.pointer < 0
632
686
  dialog.scroll_top = 0
633
- elsif (dialog_render_info.pointer - dialog.scroll_top) >= (height - 1)
634
- dialog.scroll_top = dialog_render_info.pointer - (height - 1)
635
- elsif (dialog_render_info.pointer - dialog.scroll_top) < 0
636
- dialog.scroll_top = dialog_render_info.pointer
687
+ elsif (dialog.pointer - dialog.scroll_top) >= (height - 1)
688
+ dialog.scroll_top = dialog.pointer - (height - 1)
689
+ elsif (dialog.pointer - dialog.scroll_top) < 0
690
+ dialog.scroll_top = dialog.pointer
637
691
  end
638
- pointer = dialog_render_info.pointer - dialog.scroll_top
692
+ pointer = dialog.pointer - dialog.scroll_top
639
693
  end
640
694
  dialog.contents = dialog.contents[dialog.scroll_top, height]
641
695
  end
696
+ if dialog.contents and dialog.scroll_top >= dialog.contents.size
697
+ dialog.scroll_top = dialog.contents.size - height
698
+ end
699
+ if dialog_render_info.scrollbar and dialog_render_info.contents.size > height
700
+ bar_max_height = height * 2
701
+ moving_distance = (dialog_render_info.contents.size - height) * 2
702
+ position_ratio = dialog.scroll_top.zero? ? 0.0 : ((dialog.scroll_top * 2).to_f / moving_distance)
703
+ bar_height = (bar_max_height * ((dialog.contents.size * 2).to_f / (dialog_render_info.contents.size * 2))).floor.to_i
704
+ dialog.scrollbar_pos = ((bar_max_height - bar_height) * position_ratio).floor.to_i
705
+ else
706
+ dialog.scrollbar_pos = nil
707
+ end
642
708
  upper_space = @first_line_started_from - @started_from
643
709
  lower_space = @highest_in_all - @first_line_started_from - @started_from - 1
644
710
  dialog.column = dialog_render_info.pos.x
645
- diff = (dialog.column + dialog.width) - (@screen_size.last - 1)
711
+ dialog.width += @block_elem_width if dialog.scrollbar_pos
712
+ diff = (dialog.column + dialog.width) - (@screen_size.last)
646
713
  if diff > 0
647
714
  dialog.column -= diff
648
715
  end
@@ -658,6 +725,10 @@ class Reline::LineEditor
658
725
  dialog.vertical_offset = dialog_render_info.pos.y + 1
659
726
  end
660
727
  Reline::IOGate.hide_cursor
728
+ if dialog.column < 0
729
+ dialog.column = 0
730
+ dialog.width = @screen_size.last
731
+ end
661
732
  reset_dialog(dialog, old_dialog)
662
733
  move_cursor_down(dialog.vertical_offset)
663
734
  Reline::IOGate.move_cursor_column(dialog.column)
@@ -671,8 +742,23 @@ class Reline::LineEditor
671
742
  bg_color = '46'
672
743
  end
673
744
  end
674
- str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, dialog.width), dialog.width)
675
- @output.write "\e[#{bg_color}m#{str}\e[49m"
745
+ str_width = dialog.width - (dialog.scrollbar_pos.nil? ? 0 : @block_elem_width)
746
+ str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width)
747
+ @output.write "\e[#{bg_color}m#{str}"
748
+ if dialog.scrollbar_pos and (dialog.scrollbar_pos != old_dialog.scrollbar_pos or dialog.column != old_dialog.column)
749
+ @output.write "\e[37m"
750
+ if dialog.scrollbar_pos <= (i * 2) and (i * 2 + 1) < (dialog.scrollbar_pos + bar_height)
751
+ @output.write @full_block
752
+ elsif dialog.scrollbar_pos <= (i * 2) and (i * 2) < (dialog.scrollbar_pos + bar_height)
753
+ @output.write @upper_half_block
754
+ str += ''
755
+ elsif dialog.scrollbar_pos <= (i * 2 + 1) and (i * 2) < (dialog.scrollbar_pos + bar_height)
756
+ @output.write @lower_half_block
757
+ else
758
+ @output.write ' ' * @block_elem_width
759
+ end
760
+ end
761
+ @output.write "\e[0m"
676
762
  Reline::IOGate.move_cursor_column(dialog.column)
677
763
  move_cursor_down(1) if i < (dialog.contents.size - 1)
678
764
  end
@@ -713,12 +799,12 @@ class Reline::LineEditor
713
799
  line_num.times do |i|
714
800
  Reline::IOGate.move_cursor_column(old_dialog.column)
715
801
  if visual_lines[start + i].nil?
716
- s = ' ' * dialog.width
802
+ s = ' ' * old_dialog.width
717
803
  else
718
- s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, dialog.width)
719
- s = padding_space_with_escape_sequences(s, dialog.width)
804
+ s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width)
805
+ s = padding_space_with_escape_sequences(s, old_dialog.width)
720
806
  end
721
- @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
807
+ @output.write "\e[0m#{s}\e[0m"
722
808
  move_cursor_down(1) if i < (line_num - 1)
723
809
  end
724
810
  move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
@@ -731,12 +817,12 @@ class Reline::LineEditor
731
817
  line_num.times do |i|
732
818
  Reline::IOGate.move_cursor_column(old_dialog.column)
733
819
  if visual_lines[start + i].nil?
734
- s = ' ' * dialog.width
820
+ s = ' ' * old_dialog.width
735
821
  else
736
- s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, dialog.width)
737
- s = padding_space_with_escape_sequences(s, dialog.width)
822
+ s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width)
823
+ s = padding_space_with_escape_sequences(s, old_dialog.width)
738
824
  end
739
- @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
825
+ @output.write "\e[0m#{s}\e[0m"
740
826
  move_cursor_down(1) if i < (line_num - 1)
741
827
  end
742
828
  move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
@@ -755,7 +841,7 @@ class Reline::LineEditor
755
841
  s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, width)
756
842
  s = padding_space_with_escape_sequences(s, dialog.width)
757
843
  end
758
- @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
844
+ @output.write "\e[0m#{s}\e[0m"
759
845
  move_cursor_down(1) if i < (line_num - 1)
760
846
  end
761
847
  move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
@@ -775,7 +861,7 @@ class Reline::LineEditor
775
861
  s = padding_space_with_escape_sequences(s, dialog.width)
776
862
  end
777
863
  Reline::IOGate.move_cursor_column(dialog.column + dialog.width)
778
- @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
864
+ @output.write "\e[0m#{s}\e[0m"
779
865
  move_cursor_down(1) if i < (line_num - 1)
780
866
  end
781
867
  move_cursor_up(old_dialog.vertical_offset + line_num - 1 + y_diff)
@@ -815,10 +901,10 @@ class Reline::LineEditor
815
901
  Reline::IOGate.move_cursor_column(dialog.column)
816
902
  str = Reline::Unicode.take_range(visual_lines_under_dialog[i], dialog.column, dialog.width)
817
903
  str = padding_space_with_escape_sequences(str, dialog.width)
818
- @output.write "\e[39m\e[49m#{str}\e[39m\e[49m"
904
+ @output.write "\e[0m#{str}\e[0m"
819
905
  else
820
906
  Reline::IOGate.move_cursor_column(dialog.column)
821
- @output.write "\e[39m\e[49m#{' ' * dialog.width}\e[39m\e[49m"
907
+ @output.write "\e[0m#{' ' * dialog.width}\e[0m"
822
908
  end
823
909
  move_cursor_down(1) if i < (dialog_vertical_size - 1)
824
910
  end
@@ -1168,7 +1254,7 @@ class Reline::LineEditor
1168
1254
  height = render_partial(prompt, prompt_width, line, back, with_control: false)
1169
1255
  end
1170
1256
  if index < (@buffer_of_lines.size - 1)
1171
- move_cursor_down(height)
1257
+ move_cursor_down(1)
1172
1258
  back += height
1173
1259
  end
1174
1260
  end
@@ -1351,7 +1437,10 @@ class Reline::LineEditor
1351
1437
  end
1352
1438
  @waiting_operator_proc = nil
1353
1439
  @waiting_operator_vi_arg = nil
1354
- @vi_arg = nil
1440
+ if @vi_arg
1441
+ @rerender_all = true
1442
+ @vi_arg = nil
1443
+ end
1355
1444
  else
1356
1445
  block.(false)
1357
1446
  end
@@ -1402,7 +1491,10 @@ class Reline::LineEditor
1402
1491
  wrap_method_call(method_symbol, method_obj, key) if method_obj
1403
1492
  end
1404
1493
  @kill_ring.process
1405
- @vi_arg = nil
1494
+ if @vi_arg
1495
+ @rerender_al = true
1496
+ @vi_arg = nil
1497
+ end
1406
1498
  elsif @vi_arg
1407
1499
  if key.chr =~ /[0-9]/
1408
1500
  ed_argument_digit(key)
@@ -1419,7 +1511,10 @@ class Reline::LineEditor
1419
1511
  ed_insert(key) unless @config.editing_mode_is?(:vi_command)
1420
1512
  end
1421
1513
  @kill_ring.process
1422
- @vi_arg = nil
1514
+ if @vi_arg
1515
+ @rerender_all = true
1516
+ @vi_arg = nil
1517
+ end
1423
1518
  end
1424
1519
  elsif @waiting_proc
1425
1520
  @waiting_proc.(key)
@@ -1478,9 +1573,9 @@ class Reline::LineEditor
1478
1573
 
1479
1574
  def input_key(key)
1480
1575
  @last_key = key
1576
+ @config.reset_oneshot_key_bindings
1481
1577
  @dialogs.each do |dialog|
1482
- # The dialog will intercept the key if trap_key is set.
1483
- if dialog.trap_key and dialog.trap_key.match?(key)
1578
+ if key.char.instance_of?(Symbol) and key.char == dialog.name
1484
1579
  return
1485
1580
  end
1486
1581
  end
@@ -1858,6 +1953,8 @@ class Reline::LineEditor
1858
1953
  end
1859
1954
  end
1860
1955
 
1956
+ # Editline:: +ed-unassigned+ This editor command always results in an error.
1957
+ # GNU Readline:: There is no corresponding macro.
1861
1958
  private def ed_unassigned(key) end # do nothing
1862
1959
 
1863
1960
  private def process_insert(force: false)
@@ -1875,6 +1972,19 @@ class Reline::LineEditor
1875
1972
  @continuous_insertion_buffer.clear
1876
1973
  end
1877
1974
 
1975
+ # Editline:: +ed-insert+ (vi input: almost all; emacs: printable characters)
1976
+ # In insert mode, insert the input character left of the cursor
1977
+ # position. In replace mode, overwrite the character at the
1978
+ # cursor and move the cursor to the right by one character
1979
+ # position. Accept an argument to do this repeatedly. It is an
1980
+ # error if the input character is the NUL character (+Ctrl-@+).
1981
+ # Failure to enlarge the edit buffer also results in an error.
1982
+ # Editline:: +ed-digit+ (emacs: 0 to 9) If in argument input mode, append
1983
+ # the input digit to the argument being read. Otherwise, call
1984
+ # +ed-insert+. It is an error if the input character is not a
1985
+ # digit or if the existing argument is already greater than a
1986
+ # million.
1987
+ # GNU Readline:: +self-insert+ (a, b, A, 1, !, …) Insert yourself.
1878
1988
  private def ed_insert(key)
1879
1989
  str = nil
1880
1990
  width = nil
@@ -1925,6 +2035,8 @@ class Reline::LineEditor
1925
2035
  arg.times do
1926
2036
  if key == "\C-j".ord or key == "\C-m".ord
1927
2037
  key_newline(key)
2038
+ elsif key == 0
2039
+ # Ignore NUL.
1928
2040
  else
1929
2041
  ed_insert(key)
1930
2042
  end
@@ -2359,6 +2471,7 @@ class Reline::LineEditor
2359
2471
  arg -= 1
2360
2472
  ed_prev_history(key, arg: arg) if arg > 0
2361
2473
  end
2474
+ alias_method :previous_history, :ed_prev_history
2362
2475
 
2363
2476
  private def ed_next_history(key, arg: 1)
2364
2477
  if @is_multiline and @line_index < (@buffer_of_lines.size - 1)
@@ -2406,6 +2519,7 @@ class Reline::LineEditor
2406
2519
  arg -= 1
2407
2520
  ed_next_history(key, arg: arg) if arg > 0
2408
2521
  end
2522
+ alias_method :next_history, :ed_next_history
2409
2523
 
2410
2524
  private def ed_newline(key)
2411
2525
  process_insert(force: true)
@@ -2440,7 +2554,7 @@ class Reline::LineEditor
2440
2554
  end
2441
2555
  end
2442
2556
 
2443
- private def em_delete_prev_char(key)
2557
+ private def em_delete_prev_char(key, arg: 1)
2444
2558
  if @is_multiline and @cursor == 0 and @line_index > 0
2445
2559
  @buffer_of_lines[@line_index] = @line
2446
2560
  @cursor = calculate_width(@buffer_of_lines[@line_index - 1])
@@ -2458,9 +2572,16 @@ class Reline::LineEditor
2458
2572
  @cursor -= width
2459
2573
  @cursor_max -= width
2460
2574
  end
2575
+ arg -= 1
2576
+ em_delete_prev_char(key, arg: arg) if arg > 0
2461
2577
  end
2462
2578
  alias_method :backward_delete_char, :em_delete_prev_char
2463
2579
 
2580
+ # Editline:: +ed-kill-line+ (vi command: +D+, +Ctrl-K+; emacs: +Ctrl-K+,
2581
+ # +Ctrl-U+) + Kill from the cursor to the end of the line.
2582
+ # GNU Readline:: +kill-line+ (+C-k+) Kill the text from point to the end of
2583
+ # the line. With a negative numeric argument, kill backward
2584
+ # from the cursor to the beginning of the current line.
2464
2585
  private def ed_kill_line(key)
2465
2586
  if @line.bytesize > @byte_pointer
2466
2587
  @line, deleted = byteslice!(@line, @byte_pointer, @line.bytesize - @byte_pointer)
@@ -2477,8 +2598,14 @@ class Reline::LineEditor
2477
2598
  @rest_height += 1
2478
2599
  end
2479
2600
  end
2601
+ alias_method :kill_line, :ed_kill_line
2480
2602
 
2481
- private def em_kill_line(key)
2603
+ # Editline:: +vi-kill-line-prev+ (vi: +Ctrl-U+) Delete the string from the
2604
+ # beginning of the edit buffer to the cursor and save it to the
2605
+ # cut buffer.
2606
+ # GNU Readline:: +unix-line-discard+ (+C-u+) Kill backward from the cursor
2607
+ # to the beginning of the current line.
2608
+ private def vi_kill_line_prev(key)
2482
2609
  if @byte_pointer > 0
2483
2610
  @line, deleted = byteslice!(@line, 0, @byte_pointer)
2484
2611
  @byte_pointer = 0
@@ -2487,7 +2614,22 @@ class Reline::LineEditor
2487
2614
  @cursor = 0
2488
2615
  end
2489
2616
  end
2490
- alias_method :kill_line, :em_kill_line
2617
+ alias_method :unix_line_discard, :vi_kill_line_prev
2618
+
2619
+ # Editline:: +em-kill-line+ (not bound) Delete the entire contents of the
2620
+ # edit buffer and save it to the cut buffer. +vi-kill-line-prev+
2621
+ # GNU Readline:: +kill-whole-line+ (not bound) Kill all characters on the
2622
+ # current line, no matter where point is.
2623
+ private def em_kill_line(key)
2624
+ if @line.size > 0
2625
+ @kill_ring.append(@line.dup, true)
2626
+ @line.clear
2627
+ @byte_pointer = 0
2628
+ @cursor_max = 0
2629
+ @cursor = 0
2630
+ end
2631
+ end
2632
+ alias_method :kill_whole_line, :em_kill_line
2491
2633
 
2492
2634
  private def em_delete(key)
2493
2635
  if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
@@ -2992,7 +3134,14 @@ class Reline::LineEditor
2992
3134
 
2993
3135
  private def ed_argument_digit(key)
2994
3136
  if @vi_arg.nil?
2995
- unless key.chr.to_i.zero?
3137
+ if key.chr.to_i.zero?
3138
+ if key.anybits?(0b10000000)
3139
+ unescaped_key = key ^ 0b10000000
3140
+ unless unescaped_key.chr.to_i.zero?
3141
+ @vi_arg = unescaped_key.chr.to_i
3142
+ end
3143
+ end
3144
+ else
2996
3145
  @vi_arg = key.chr.to_i
2997
3146
  end
2998
3147
  else
@@ -71,7 +71,7 @@ module Reline::Terminfo
71
71
  def self.setupterm(term, fildes)
72
72
  errret_int = String.new("\x00" * 8, encoding: 'ASCII-8BIT')
73
73
  ret = @setupterm.(term, fildes, errret_int)
74
- errret = errret_int.unpack('i')[0]
74
+ errret = errret_int.unpack1('i')
75
75
  case ret
76
76
  when 0 # OK
77
77
  0
@@ -101,9 +101,9 @@ class Reline::Unicode
101
101
 
102
102
  def self.get_mbchar_width(mbchar)
103
103
  ord = mbchar.ord
104
- if (0x00 <= ord and ord <= 0x1F)
104
+ if (0x00 <= ord and ord <= 0x1F) # in EscapedPairs
105
105
  return 2
106
- elsif (0x20 <= ord and ord <= 0x7E)
106
+ elsif (0x20 <= ord and ord <= 0x7E) # printable ASCII chars
107
107
  return 1
108
108
  end
109
109
  m = mbchar.encode(Encoding::UTF_8).match(MBCharWidthRE)
@@ -185,7 +185,7 @@ class Reline::Unicode
185
185
  [lines, height]
186
186
  end
187
187
 
188
- # Take a chunk of a String with escape sequences.
188
+ # Take a chunk of a String cut by width with escape sequences.
189
189
  def self.take_range(str, start_col, max_width, encoding = str.encoding)
190
190
  chunk = String.new(encoding: encoding)
191
191
  total_width = 0
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.2.8.pre.7'
2
+ VERSION = '0.2.8.pre.11'
3
3
  end
@@ -184,7 +184,7 @@ class Reline::Windows
184
184
  # DWORD FileNameLength;
185
185
  # WCHAR FileName[1];
186
186
  # } FILE_NAME_INFO
187
- len = p_buffer[0, 4].unpack("L")[0]
187
+ len = p_buffer[0, 4].unpack1("L")
188
188
  name = p_buffer[4, len].encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace)
189
189
 
190
190
  # Check if this could be a MSYS2 pty pipe ('\msys-XXXX-ptyN-XX')
@@ -234,22 +234,23 @@ class Reline::Windows
234
234
  def self.check_input_event
235
235
  num_of_events = 0.chr * 8
236
236
  while @@output_buf.empty? #or true
237
+ Reline.core.line_editor.resize
237
238
  next if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
238
- next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack('L').first == 0
239
+ next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack1('L') == 0
239
240
  input_record = 0.chr * 18
240
241
  read_event = 0.chr * 4
241
242
  if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
242
- event = input_record[0, 2].unpack('s*').first
243
+ event = input_record[0, 2].unpack1('s*')
243
244
  case event
244
245
  when WINDOW_BUFFER_SIZE_EVENT
245
246
  @@winch_handler.()
246
247
  when KEY_EVENT
247
- key_down = input_record[4, 4].unpack('l*').first
248
- repeat_count = input_record[8, 2].unpack('s*').first
249
- virtual_key_code = input_record[10, 2].unpack('s*').first
250
- virtual_scan_code = input_record[12, 2].unpack('s*').first
251
- char_code = input_record[14, 2].unpack('S*').first
252
- control_key_state = input_record[16, 2].unpack('S*').first
248
+ key_down = input_record[4, 4].unpack1('l*')
249
+ repeat_count = input_record[8, 2].unpack1('s*')
250
+ virtual_key_code = input_record[10, 2].unpack1('s*')
251
+ virtual_scan_code = input_record[12, 2].unpack1('s*')
252
+ char_code = input_record[14, 2].unpack1('S*')
253
+ control_key_state = input_record[16, 2].unpack1('S*')
253
254
  is_key_down = key_down.zero? ? false : true
254
255
  if is_key_down
255
256
  process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
@@ -291,8 +292,8 @@ class Reline::Windows
291
292
  def self.cursor_pos
292
293
  csbi = 0.chr * 22
293
294
  @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
294
- x = csbi[4, 2].unpack('s*').first
295
- y = csbi[6, 2].unpack('s*').first
295
+ x = csbi[4, 2].unpack1('s*')
296
+ y = csbi[6, 2].unpack1('s*')
296
297
  Reline::CursorPos.new(x, y)
297
298
  end
298
299
 
@@ -322,12 +323,13 @@ class Reline::Windows
322
323
  end
323
324
 
324
325
  def self.erase_after_cursor
325
- csbi = 0.chr * 24
326
+ csbi = 0.chr * 22
326
327
  @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
327
- cursor = csbi[4, 4].unpack('L').first
328
+ attributes = csbi[8, 2].unpack1('S')
329
+ cursor = csbi[4, 4].unpack1('L')
328
330
  written = 0.chr * 4
329
331
  @@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, get_screen_size.last - cursor_pos.x, cursor, written)
330
- @@FillConsoleOutputAttribute.call(@@hConsoleHandle, 0, get_screen_size.last - cursor_pos.x, cursor, written)
332
+ @@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, get_screen_size.last - cursor_pos.x, cursor, written)
331
333
  end
332
334
 
333
335
  def self.scroll_down(val)
@@ -343,8 +345,8 @@ class Reline::Windows
343
345
  def self.clear_screen
344
346
  csbi = 0.chr * 22
345
347
  return if @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi) == 0
346
- buffer_width = csbi[0, 2].unpack('S').first
347
- attributes = csbi[8, 2].unpack('S').first
348
+ buffer_width = csbi[0, 2].unpack1('S')
349
+ attributes = csbi[8, 2].unpack1('S')
348
350
  _window_left, window_top, _window_right, window_bottom = *csbi[10,8].unpack('S*')
349
351
  fill_length = buffer_width * (window_bottom - window_top + 1)
350
352
  screen_topleft = window_top * 65536