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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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