reline 0.2.6 → 0.2.8.pre.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc0f6fb7c331a37907554b292249a75b218646aa14298586790cc09d6c174894
4
- data.tar.gz: 79fd7b502a7ab601f6642964bb0987dae5d0a62bc8cfda016d8806c8b76bb723
3
+ metadata.gz: 92eb26edb27bc79f95c9f54a4396b86e764acb928a4d704db8b8d1016331aa67
4
+ data.tar.gz: 04b7af1f6107bc8cd302494a057e6cfd812c60ec3768b3cd475105d8e119d20c
5
5
  SHA512:
6
- metadata.gz: 2b4300db4d7bef4ab3ddc8f45db07e20e37b371d89e7a21f343dd3c7096209d0c19028def435d584c7bc1f0a9f18d0d6c783b47d28967eb3d34e4d0eaa613490
7
- data.tar.gz: '081a683b1980b9c8d27822e62dcdc776024cc408e3f93b0c999f8da2c6b2fef92c4eded142f7bee8f95fcd7a18fc8431a87f2cd19d1c1064fa677f7de606dcf0'
6
+ metadata.gz: 40503d95634408feb981bcb3448679ed6c847123465b5b965399c75c4bbc9ac0ddbb5422386d209613e45b34a85f2ecb275fac7587272da88de7a16b2feb40b0
7
+ data.tar.gz: '030383e1c77101f296344c794443439af32c6fd7f2f86f0230666287df901e5aea9f25d78242ab9ea16253ab17faa97c02fc234137e166a71edbac2de2f44f16'
data/lib/reline/ansi.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'io/console'
2
+ require 'io/wait'
2
3
  require 'timeout'
3
4
  require_relative 'terminfo'
4
5
 
@@ -36,6 +37,7 @@ class Reline::ANSI
36
37
  # default bindings
37
38
  [27, 32] => :em_set_mark, # M-<space>
38
39
  [24, 24] => :em_exchange_mark, # C-x C-x
40
+ [27, 91, 90] => :completion_journey_up, # S-Tab
39
41
  }.each_pair do |key, func|
40
42
  config.add_default_key_binding_by_keymap(:emacs, key, func)
41
43
  end
@@ -183,12 +185,7 @@ class Reline::ANSI
183
185
  unless @@buf.empty?
184
186
  return false
185
187
  end
186
- rs, = IO.select([@@input], [], [], 0.00001)
187
- if rs and rs[0]
188
- false
189
- else
190
- true
191
- end
188
+ !@@input.wait_readable(0)
192
189
  end
193
190
 
194
191
  def self.ungetc(c)
@@ -197,8 +194,7 @@ class Reline::ANSI
197
194
 
198
195
  def self.retrieve_keybuffer
199
196
  begin
200
- result = select([@@input], [], [], 0.001)
201
- return if result.nil?
197
+ return unless @@input.wait_readable(0.001)
202
198
  str = @@input.read_nonblock(1024)
203
199
  str.bytes.each do |c|
204
200
  @@buf.push(c)
@@ -279,6 +275,22 @@ class Reline::ANSI
279
275
  end
280
276
  end
281
277
 
278
+ def self.hide_cursor
279
+ if Reline::Terminfo.enabled?
280
+ @@output.write Reline::Terminfo.tigetstr('civis')
281
+ else
282
+ # ignored
283
+ end
284
+ end
285
+
286
+ def self.show_cursor
287
+ if Reline::Terminfo.enabled?
288
+ @@output.write Reline::Terminfo.tigetstr('cnorm')
289
+ else
290
+ # ignored
291
+ end
292
+ end
293
+
282
294
  def self.erase_after_cursor
283
295
  @@output.write "\e[K"
284
296
  end
@@ -300,8 +312,6 @@ class Reline::ANSI
300
312
 
301
313
  def self.prep
302
314
  retrieve_keybuffer
303
- int_handle = Signal.trap('INT', 'IGNORE')
304
- Signal.trap('INT', int_handle)
305
315
  nil
306
316
  end
307
317
 
data/lib/reline/config.rb CHANGED
@@ -65,6 +65,7 @@ class Reline::Config
65
65
  @history_size = -1 # unlimited
66
66
  @keyseq_timeout = 500
67
67
  @test_mode = false
68
+ @autocompletion = false
68
69
  end
69
70
 
70
71
  def reset
@@ -74,6 +75,7 @@ class Reline::Config
74
75
  @additional_key_bindings.keys.each do |key|
75
76
  @additional_key_bindings[key].clear
76
77
  end
78
+ reset_default_key_bindings
77
79
  end
78
80
 
79
81
  def editing_mode
@@ -88,6 +90,14 @@ class Reline::Config
88
90
  (val.respond_to?(:any?) ? val : [val]).any?(@editing_mode_label)
89
91
  end
90
92
 
93
+ def autocompletion=(val)
94
+ @autocompletion = val
95
+ end
96
+
97
+ def autocompletion
98
+ @autocompletion
99
+ end
100
+
91
101
  def keymap
92
102
  @key_actors[@keymap_label]
93
103
  end
@@ -157,8 +167,15 @@ class Reline::Config
157
167
  end
158
168
 
159
169
  def read_lines(lines, file = nil)
160
- if lines.first.encoding != Reline.encoding_system_needs
161
- lines = lines.map { |l| l.encode(Reline.encoding_system_needs) }
170
+ if not lines.empty? and lines.first.encoding != Reline.encoding_system_needs
171
+ begin
172
+ lines = lines.map do |l|
173
+ l.encode(Reline.encoding_system_needs)
174
+ rescue Encoding::UndefinedConversionError
175
+ mes = "The inputrc encoded in #{lines.first.encoding.name} can't be converted to the locale #{Reline.encoding_system_needs.name}."
176
+ raise Reline::ConfigEncodingConversionError.new(mes)
177
+ end
178
+ end
162
179
  end
163
180
  conditions = [@skip_section, @if_stack]
164
181
  @skip_section = nil
@@ -150,7 +150,8 @@ class Reline::LineEditor
150
150
  @screen_size = Reline::IOGate.get_screen_size
151
151
  @screen_height = @screen_size.first
152
152
  reset_variables(prompt, encoding: encoding)
153
- @old_trap = Signal.trap('SIGINT') {
153
+ @old_trap = Signal.trap(:INT) {
154
+ clear_dialog
154
155
  if @scroll_partial_screen
155
156
  move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
156
157
  else
@@ -158,8 +159,16 @@ class Reline::LineEditor
158
159
  end
159
160
  Reline::IOGate.move_cursor_column(0)
160
161
  scroll_down(1)
161
- @old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
162
- raise Interrupt
162
+ case @old_trap
163
+ when 'DEFAULT', 'SYSTEM_DEFAULT'
164
+ raise Interrupt
165
+ when 'IGNORE'
166
+ # Do nothing
167
+ when 'EXIT'
168
+ exit
169
+ else
170
+ @old_trap.call
171
+ end
163
172
  }
164
173
  Reline::IOGate.set_winch_handler do
165
174
  @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
@@ -241,6 +250,7 @@ class Reline::LineEditor
241
250
  @drop_terminate_spaces = false
242
251
  @in_pasting = false
243
252
  @auto_indent_proc = nil
253
+ @dialogs = []
244
254
  reset_line
245
255
  end
246
256
 
@@ -406,6 +416,7 @@ class Reline::LineEditor
406
416
  Reline::IOGate.erase_after_cursor
407
417
  end
408
418
  @output.flush
419
+ clear_dialog
409
420
  return
410
421
  end
411
422
  new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
@@ -416,6 +427,7 @@ class Reline::LineEditor
416
427
  else
417
428
  if @just_cursor_moving and not @rerender_all
418
429
  rendered = just_move_cursor
430
+ render_dialog((prompt_width + @cursor) % @screen_size.last)
419
431
  @just_cursor_moving = false
420
432
  return
421
433
  elsif @previous_line_index or new_highest_in_this != @highest_in_this
@@ -438,18 +450,20 @@ class Reline::LineEditor
438
450
  new_lines = whole_lines
439
451
  end
440
452
  line = modify_lines(new_lines)[@line_index]
453
+ clear_dialog
441
454
  prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
442
455
  render_partial(prompt, prompt_width, line, @first_line_started_from)
443
456
  move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
444
457
  scroll_down(1)
445
458
  Reline::IOGate.move_cursor_column(0)
446
459
  Reline::IOGate.erase_after_cursor
447
- elsif not rendered
448
- unless @in_pasting
460
+ else
461
+ if not rendered and not @in_pasting
449
462
  line = modify_lines(whole_lines)[@line_index]
450
463
  prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
451
464
  render_partial(prompt, prompt_width, line, @first_line_started_from)
452
465
  end
466
+ render_dialog((prompt_width + @cursor) % @screen_size.last)
453
467
  end
454
468
  @buffer_of_lines[@line_index] = @line
455
469
  @rest_height = 0 if @scroll_partial_screen
@@ -464,6 +478,294 @@ class Reline::LineEditor
464
478
  end
465
479
  end
466
480
 
481
+ class DialogProcScope
482
+ def initialize(line_editor, config, proc_to_exec, context)
483
+ @line_editor = line_editor
484
+ @config = config
485
+ @proc_to_exec = proc_to_exec
486
+ @context = context
487
+ @cursor_pos = Reline::CursorPos.new
488
+ end
489
+
490
+ def context
491
+ @context
492
+ end
493
+
494
+ def retrieve_completion_block(set_completion_quote_character = false)
495
+ @line_editor.retrieve_completion_block(set_completion_quote_character)
496
+ end
497
+
498
+ def call_completion_proc_with_checking_args(pre, target, post)
499
+ @line_editor.call_completion_proc_with_checking_args(pre, target, post)
500
+ end
501
+
502
+ def set_cursor_pos(col, row)
503
+ @cursor_pos.x = col
504
+ @cursor_pos.y = row
505
+ end
506
+
507
+ def cursor_pos
508
+ @cursor_pos
509
+ end
510
+
511
+ def just_cursor_moving
512
+ @line_editor.instance_variable_get(:@just_cursor_moving)
513
+ end
514
+
515
+ def screen_width
516
+ @line_editor.instance_variable_get(:@screen_size).last
517
+ end
518
+
519
+ def completion_journey_data
520
+ @line_editor.instance_variable_get(:@completion_journey_data)
521
+ end
522
+
523
+ def config
524
+ @config
525
+ end
526
+
527
+ def call
528
+ instance_exec(&@proc_to_exec)
529
+ end
530
+ end
531
+
532
+ class Dialog
533
+ attr_reader :name
534
+ attr_accessor :column, :vertical_offset, :contents, :lines_backup
535
+
536
+ def initialize(name, proc_scope)
537
+ @name = name
538
+ @proc_scope = proc_scope
539
+ end
540
+
541
+ def set_cursor_pos(col, row)
542
+ @proc_scope.set_cursor_pos(col, row)
543
+ end
544
+
545
+ def call
546
+ @proc_scope.call
547
+ end
548
+ end
549
+
550
+ def add_dialog_proc(name, p, context = nil)
551
+ return if @dialogs.any? { |d| d.name == name }
552
+ @dialogs << Dialog.new(name, DialogProcScope.new(self, @config, p, context))
553
+ end
554
+
555
+ DIALOG_HEIGHT = 20
556
+ DIALOG_WIDTH = 40
557
+ private def render_dialog(cursor_column)
558
+ @dialogs.each do |dialog|
559
+ render_each_dialog(dialog, cursor_column)
560
+ end
561
+ end
562
+
563
+ private def render_each_dialog(dialog, cursor_column)
564
+ if @in_pasting
565
+ dialog.contents = nil
566
+ return
567
+ end
568
+ dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from)
569
+ pos, result, pointer, bg = dialog.call
570
+ old_dialog_contents = dialog.contents
571
+ old_dialog_column = dialog.column
572
+ old_dialog_vertical_offset = dialog.vertical_offset
573
+ if result and not result.empty?
574
+ dialog.contents = result
575
+ dialog.contents = dialog.contents[0...DIALOG_HEIGHT] if dialog.contents.size > DIALOG_HEIGHT
576
+ else
577
+ dialog.lines_backup = {
578
+ lines: modify_lines(whole_lines),
579
+ line_index: @line_index,
580
+ first_line_started_from: @first_line_started_from,
581
+ started_from: @started_from,
582
+ byte_pointer: @byte_pointer
583
+ }
584
+ clear_each_dialog(dialog)
585
+ dialog.contents = nil
586
+ return
587
+ end
588
+ upper_space = @first_line_started_from - @started_from
589
+ lower_space = @highest_in_all - @first_line_started_from - @started_from - 1
590
+ dialog.column = pos.x
591
+ diff = (dialog.column + DIALOG_WIDTH) - (@screen_size.last - 1)
592
+ if diff > 0
593
+ dialog.column -= diff
594
+ end
595
+ if (lower_space + @rest_height) >= DIALOG_HEIGHT
596
+ dialog.vertical_offset = pos.y + 1
597
+ elsif upper_space >= DIALOG_HEIGHT
598
+ dialog.vertical_offset = pos.y + -(DIALOG_HEIGHT + 1)
599
+ else
600
+ if (lower_space + @rest_height) < DIALOG_HEIGHT
601
+ scroll_down(DIALOG_HEIGHT)
602
+ move_cursor_up(DIALOG_HEIGHT)
603
+ end
604
+ dialog.vertical_offset = pos.y + 1
605
+ end
606
+ Reline::IOGate.hide_cursor
607
+ reset_dialog(dialog, old_dialog_contents, old_dialog_column, old_dialog_vertical_offset)
608
+ move_cursor_down(dialog.vertical_offset)
609
+ Reline::IOGate.move_cursor_column(dialog.column)
610
+ dialog.contents.each_with_index do |item, i|
611
+ if i == pointer
612
+ bg_color = '45'
613
+ else
614
+ if bg
615
+ bg_color = bg
616
+ else
617
+ bg_color = '46'
618
+ end
619
+ end
620
+ @output.write "\e[#{bg_color}m%-#{DIALOG_WIDTH}s\e[49m" % item.slice(0, DIALOG_WIDTH)
621
+ Reline::IOGate.move_cursor_column(dialog.column)
622
+ move_cursor_down(1) if i < (dialog.contents.size - 1)
623
+ end
624
+ Reline::IOGate.move_cursor_column(cursor_column)
625
+ move_cursor_up(dialog.vertical_offset + dialog.contents.size - 1)
626
+ Reline::IOGate.show_cursor
627
+ dialog.lines_backup = {
628
+ lines: modify_lines(whole_lines),
629
+ line_index: @line_index,
630
+ first_line_started_from: @first_line_started_from,
631
+ started_from: @started_from,
632
+ byte_pointer: @byte_pointer
633
+ }
634
+ end
635
+
636
+ private def reset_dialog(dialog, old_dialog_contents, old_dialog_column, old_dialog_vertical_offset)
637
+ return if dialog.lines_backup.nil? or old_dialog_contents.nil?
638
+ prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
639
+ visual_lines = []
640
+ visual_start = nil
641
+ dialog.lines_backup[:lines].each_with_index { |l, i|
642
+ pr = prompt_list ? prompt_list[i] : prompt
643
+ vl, _ = split_by_width(pr + l, @screen_size.last)
644
+ vl.compact!
645
+ if i == dialog.lines_backup[:line_index]
646
+ visual_start = visual_lines.size + dialog.lines_backup[:started_from]
647
+ end
648
+ visual_lines.concat(vl)
649
+ }
650
+ old_y = dialog.lines_backup[:first_line_started_from] + dialog.lines_backup[:started_from]
651
+ y = @first_line_started_from + @started_from
652
+ y_diff = y - old_y
653
+ if (old_y + old_dialog_vertical_offset) < (y + dialog.vertical_offset)
654
+ # rerender top
655
+ move_cursor_down(old_dialog_vertical_offset - y_diff)
656
+ start = visual_start + old_dialog_vertical_offset
657
+ line_num = dialog.vertical_offset - old_dialog_vertical_offset
658
+ line_num.times do |i|
659
+ Reline::IOGate.move_cursor_column(old_dialog_column)
660
+ if visual_lines[start + i].nil?
661
+ s = ' ' * DIALOG_WIDTH
662
+ else
663
+ s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, DIALOG_WIDTH)
664
+ end
665
+ @output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % s
666
+ move_cursor_down(1) if i < (line_num - 1)
667
+ end
668
+ move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
669
+ end
670
+ if (old_y + old_dialog_vertical_offset + old_dialog_contents.size) > (y + dialog.vertical_offset + dialog.contents.size)
671
+ # rerender bottom
672
+ move_cursor_down(dialog.vertical_offset + dialog.contents.size - y_diff)
673
+ start = visual_start + dialog.vertical_offset + dialog.contents.size
674
+ line_num = (old_dialog_vertical_offset + old_dialog_contents.size) - (dialog.vertical_offset + dialog.contents.size)
675
+ line_num.times do |i|
676
+ Reline::IOGate.move_cursor_column(old_dialog_column)
677
+ if visual_lines[start + i].nil?
678
+ s = ' ' * DIALOG_WIDTH
679
+ else
680
+ s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, DIALOG_WIDTH)
681
+ end
682
+ @output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % s
683
+ move_cursor_down(1) if i < (line_num - 1)
684
+ end
685
+ move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
686
+ end
687
+ if old_dialog_column < dialog.column
688
+ # rerender left
689
+ move_cursor_down(old_dialog_vertical_offset - y_diff)
690
+ width = dialog.column - old_dialog_column
691
+ start = visual_start + old_dialog_vertical_offset
692
+ line_num = old_dialog_contents.size
693
+ line_num.times do |i|
694
+ Reline::IOGate.move_cursor_column(old_dialog_column)
695
+ if visual_lines[start + i].nil?
696
+ s = ' ' * width
697
+ else
698
+ s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, width)
699
+ end
700
+ @output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
701
+ move_cursor_down(1) if i < (line_num - 1)
702
+ end
703
+ move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
704
+ end
705
+ if (old_dialog_column + DIALOG_WIDTH) > (dialog.column + DIALOG_WIDTH)
706
+ # rerender right
707
+ move_cursor_down(old_dialog_vertical_offset + y_diff)
708
+ width = (old_dialog_column + DIALOG_WIDTH) - (dialog.column + DIALOG_WIDTH)
709
+ start = visual_start + old_dialog_vertical_offset
710
+ line_num = old_dialog_contents.size
711
+ line_num.times do |i|
712
+ Reline::IOGate.move_cursor_column(old_dialog_column + DIALOG_WIDTH)
713
+ if visual_lines[start + i].nil?
714
+ s = ' ' * width
715
+ else
716
+ s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column + DIALOG_WIDTH, width)
717
+ end
718
+ Reline::IOGate.move_cursor_column(dialog.column + DIALOG_WIDTH)
719
+ @output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
720
+ move_cursor_down(1) if i < (line_num - 1)
721
+ end
722
+ move_cursor_up(old_dialog_vertical_offset + line_num - 1 + y_diff)
723
+ end
724
+ Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
725
+ end
726
+
727
+ private def clear_dialog
728
+ @dialogs.each do |dialog|
729
+ clear_each_dialog(dialog)
730
+ end
731
+ end
732
+
733
+ private def clear_each_dialog(dialog)
734
+ return unless dialog.contents
735
+ prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
736
+ visual_lines = []
737
+ visual_lines_under_dialog = []
738
+ visual_start = nil
739
+ dialog.lines_backup[:lines].each_with_index { |l, i|
740
+ pr = prompt_list ? prompt_list[i] : prompt
741
+ vl, _ = split_by_width(pr + l, @screen_size.last)
742
+ vl.compact!
743
+ if i == dialog.lines_backup[:line_index]
744
+ visual_start = visual_lines.size + dialog.lines_backup[:started_from] + dialog.vertical_offset
745
+ end
746
+ visual_lines.concat(vl)
747
+ }
748
+ visual_lines_under_dialog = visual_lines[visual_start, dialog.contents.size]
749
+ visual_lines_under_dialog = [] if visual_lines_under_dialog.nil?
750
+ Reline::IOGate.hide_cursor
751
+ move_cursor_down(dialog.vertical_offset)
752
+ dialog_vertical_size = dialog.contents.size
753
+ dialog_vertical_size.times do |i|
754
+ if i < visual_lines_under_dialog.size
755
+ Reline::IOGate.move_cursor_column(0)
756
+ @output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % visual_lines_under_dialog[i]
757
+ else
758
+ Reline::IOGate.move_cursor_column(dialog.column)
759
+ @output.write "\e[39m\e[49m#{' ' * DIALOG_WIDTH}\e[39m\e[49m"
760
+ end
761
+ Reline::IOGate.erase_after_cursor
762
+ move_cursor_down(1) if i < (dialog_vertical_size - 1)
763
+ end
764
+ move_cursor_up(dialog_vertical_size - 1 + dialog.vertical_offset)
765
+ Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
766
+ Reline::IOGate.show_cursor
767
+ end
768
+
467
769
  private def calculate_scroll_partial_screen(highest_in_all, cursor_y)
468
770
  if @screen_height < highest_in_all
469
771
  old_scroll_partial_screen = @scroll_partial_screen
@@ -924,6 +1226,16 @@ class Reline::LineEditor
924
1226
  @completion_journey_data = CompletionJourneyData.new(
925
1227
  preposing, postposing,
926
1228
  [target] + list.select{ |item| item.start_with?(target) }, 0)
1229
+ if @completion_journey_data.list.size == 1
1230
+ @completion_journey_data.pointer = 0
1231
+ else
1232
+ case direction
1233
+ when :up
1234
+ @completion_journey_data.pointer = @completion_journey_data.list.size - 1
1235
+ when :down
1236
+ @completion_journey_data.pointer = 1
1237
+ end
1238
+ end
927
1239
  @completion_state = CompletionState::JOURNEY
928
1240
  else
929
1241
  case direction
@@ -938,13 +1250,13 @@ class Reline::LineEditor
938
1250
  @completion_journey_data.pointer = 0
939
1251
  end
940
1252
  end
941
- completed = @completion_journey_data.list[@completion_journey_data.pointer]
942
- @line = @completion_journey_data.preposing + completed + @completion_journey_data.postposing
943
- line_to_pointer = @completion_journey_data.preposing + completed
944
- @cursor_max = calculate_width(@line)
945
- @cursor = calculate_width(line_to_pointer)
946
- @byte_pointer = line_to_pointer.bytesize
947
1253
  end
1254
+ completed = @completion_journey_data.list[@completion_journey_data.pointer]
1255
+ @line = @completion_journey_data.preposing + completed + @completion_journey_data.postposing
1256
+ line_to_pointer = @completion_journey_data.preposing + completed
1257
+ @cursor_max = calculate_width(@line)
1258
+ @cursor = calculate_width(line_to_pointer)
1259
+ @byte_pointer = line_to_pointer.bytesize
948
1260
  end
949
1261
 
950
1262
  private def run_for_operators(key, method_symbol, &block)
@@ -1119,7 +1431,20 @@ class Reline::LineEditor
1119
1431
  if result.is_a?(Array)
1120
1432
  completion_occurs = true
1121
1433
  process_insert
1122
- complete(result)
1434
+ if @config.autocompletion
1435
+ move_completed_list(result, :down)
1436
+ else
1437
+ complete(result)
1438
+ end
1439
+ end
1440
+ end
1441
+ elsif @config.editing_mode_is?(:emacs, :vi_insert) and key.char == :completion_journey_up
1442
+ if not @config.disable_completion and @config.autocompletion
1443
+ result = call_completion_proc
1444
+ if result.is_a?(Array)
1445
+ completion_occurs = true
1446
+ process_insert
1447
+ move_completed_list(result, :up)
1123
1448
  end
1124
1449
  end
1125
1450
  elsif not @config.disable_completion and @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
@@ -1138,6 +1463,7 @@ class Reline::LineEditor
1138
1463
  end
1139
1464
  unless completion_occurs
1140
1465
  @completion_state = CompletionState::NORMAL
1466
+ @completion_journey_data = nil
1141
1467
  end
1142
1468
  if not @in_pasting and @just_cursor_moving.nil?
1143
1469
  if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
@@ -1157,7 +1483,13 @@ class Reline::LineEditor
1157
1483
 
1158
1484
  def call_completion_proc
1159
1485
  result = retrieve_completion_block(true)
1160
- preposing, target, postposing = result
1486
+ pre, target, post = result
1487
+ result = call_completion_proc_with_checking_args(pre, target, post)
1488
+ Reline.core.instance_variable_set(:@completion_quote_character, nil)
1489
+ result
1490
+ end
1491
+
1492
+ def call_completion_proc_with_checking_args(pre, target, post)
1161
1493
  if @completion_proc and target
1162
1494
  argnum = @completion_proc.parameters.inject(0) { |result, item|
1163
1495
  case item.first
@@ -1171,12 +1503,11 @@ class Reline::LineEditor
1171
1503
  when 1
1172
1504
  result = @completion_proc.(target)
1173
1505
  when 2
1174
- result = @completion_proc.(target, preposing)
1506
+ result = @completion_proc.(target, pre)
1175
1507
  when 3..Float::INFINITY
1176
- result = @completion_proc.(target, preposing, postposing)
1508
+ result = @completion_proc.(target, pre, post)
1177
1509
  end
1178
1510
  end
1179
- Reline.core.instance_variable_set(:@completion_quote_character, nil)
1180
1511
  result
1181
1512
  end
1182
1513
 
@@ -6,12 +6,39 @@ module Reline::Terminfo
6
6
 
7
7
  class TerminfoError < StandardError; end
8
8
 
9
- @curses_dl = nil
9
+ def self.curses_dl_files
10
+ case RUBY_PLATFORM
11
+ when /mingw/, /mswin/
12
+ # aren't supported
13
+ []
14
+ when /cygwin/
15
+ %w[cygncursesw-10.dll cygncurses-10.dll]
16
+ when /darwin/
17
+ %w[libncursesw.dylib libcursesw.dylib libncurses.dylib libcurses.dylib]
18
+ else
19
+ %w[libncursesw.so libcursesw.so libncurses.so libcurses.so]
20
+ end
21
+ end
22
+
23
+ @curses_dl = false
10
24
  def self.curses_dl
11
- return @curses_dl if @curses_dl
12
- if Fiddle.const_defined?(:VERSION) and Gem::Version.create(Fiddle::VERSION) >= Gem::Version.create('1.0.1')
25
+ return @curses_dl unless @curses_dl == false
26
+ if RUBY_VERSION >= '3.0.0'
27
+ # Gem module isn't defined in test-all of the Ruby repository, and
28
+ # Fiddle in Ruby 3.0.0 or later supports Fiddle::TYPE_VARIADIC.
29
+ fiddle_supports_variadic = true
30
+ elsif Fiddle.const_defined?(:VERSION) and Gem::Version.create(Fiddle::VERSION) >= Gem::Version.create('1.0.1')
13
31
  # Fiddle::TYPE_VARIADIC is supported from Fiddle 1.0.1.
14
- %w[libncursesw.so libcursesw.so libncurses.so libcurses.so].each do |curses_name|
32
+ fiddle_supports_variadic = true
33
+ else
34
+ fiddle_supports_variadic = false
35
+ end
36
+ if fiddle_supports_variadic and not Fiddle.const_defined?(:TYPE_VARIADIC)
37
+ # If the libffi version is not 3.0.5 or higher, there isn't TYPE_VARIADIC.
38
+ fiddle_supports_variadic = false
39
+ end
40
+ if fiddle_supports_variadic
41
+ curses_dl_files.each do |curses_name|
15
42
  result = Fiddle::Handle.new(curses_name)
16
43
  rescue Fiddle::DLError
17
44
  next
@@ -20,6 +47,7 @@ module Reline::Terminfo
20
47
  break
21
48
  end
22
49
  end
50
+ @curses_dl = nil if @curses_dl == false
23
51
  @curses_dl
24
52
  end
25
53
  end
@@ -30,8 +58,15 @@ module Reline::Terminfo
30
58
  @setupterm = Fiddle::Function.new(curses_dl['setupterm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
31
59
  #extern 'char *tigetstr(char *capname)'
32
60
  @tigetstr = Fiddle::Function.new(curses_dl['tigetstr'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP)
33
- #extern 'char *tiparm(const char *str, ...)'
34
- @tiparm = Fiddle::Function.new(curses_dl['tiparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
61
+ begin
62
+ #extern 'char *tiparm(const char *str, ...)'
63
+ @tiparm = Fiddle::Function.new(curses_dl['tiparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
64
+ rescue Fiddle::DLError
65
+ # OpenBSD lacks tiparm
66
+ #extern 'char *tparm(const char *str, ...)'
67
+ @tiparm = Fiddle::Function.new(curses_dl['tparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
68
+ end
69
+ # TODO: add int tigetflag(char *capname) and int tigetnum(char *capname)
35
70
 
36
71
  def self.setupterm(term, fildes)
37
72
  errret_int = String.new("\x00" * 8, encoding: 'ASCII-8BIT')
@@ -56,12 +91,19 @@ module Reline::Terminfo
56
91
  end
57
92
  end
58
93
 
59
- def self.tigetstr(capname)
60
- result = @tigetstr.(capname).to_s
61
- def result.tiparm(*args) # for method chain
94
+ class StringWithTiparm < String
95
+ def tiparm(*args) # for method chain
62
96
  Reline::Terminfo.tiparm(self, *args)
63
97
  end
64
- result
98
+ end
99
+
100
+ def self.tigetstr(capname)
101
+ capability = @tigetstr.(capname)
102
+ case capability.to_i
103
+ when 0, -1
104
+ raise TerminfoError, "can't find capability: #{capname}"
105
+ end
106
+ StringWithTiparm.new(capability.to_s)
65
107
  end
66
108
 
67
109
  def self.tiparm(str, *args)
@@ -185,6 +185,36 @@ class Reline::Unicode
185
185
  [lines, height]
186
186
  end
187
187
 
188
+ # Take a chunk of a String with escape sequences.
189
+ def self.take_range(str, col, length, encoding = str.encoding)
190
+ chunk = String.new(encoding: encoding)
191
+ width = 0
192
+ rest = str.encode(Encoding::UTF_8)
193
+ in_zero_width = false
194
+ rest.scan(WIDTH_SCANNER) do |gc|
195
+ case
196
+ when gc[NON_PRINTING_START_INDEX]
197
+ in_zero_width = true
198
+ when gc[NON_PRINTING_END_INDEX]
199
+ in_zero_width = false
200
+ when gc[CSI_REGEXP_INDEX]
201
+ chunk << gc[CSI_REGEXP_INDEX]
202
+ when gc[OSC_REGEXP_INDEX]
203
+ chunk << gc[OSC_REGEXP_INDEX]
204
+ when gc[GRAPHEME_CLUSTER_INDEX]
205
+ gc = gc[GRAPHEME_CLUSTER_INDEX]
206
+ if in_zero_width
207
+ chunk << gc
208
+ else
209
+ width = get_mbchar_width(gc)
210
+ break if (width + length) <= col
211
+ chunk << gc if col <= width
212
+ end
213
+ end
214
+ end
215
+ chunk
216
+ end
217
+
188
218
  def self.get_next_mbchar_size(line, byte_pointer)
189
219
  grapheme = line.byteslice(byte_pointer..-1).grapheme_clusters.first
190
220
  grapheme ? grapheme.bytesize : 0
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.2.6'
2
+ VERSION = '0.2.8.pre.3'
3
3
  end
@@ -42,6 +42,14 @@ class Reline::Windows
42
42
  }.each_pair do |key, func|
43
43
  config.add_default_key_binding_by_keymap(:emacs, key, func)
44
44
  end
45
+
46
+ # Emulate ANSI key sequence.
47
+ {
48
+ [27, 91, 90] => :completion_journey_up, # S-Tab
49
+ }.each_pair do |key, func|
50
+ config.add_default_key_binding_by_keymap(:emacs, key, func)
51
+ config.add_default_key_binding_by_keymap(:vi_insert, key, func)
52
+ end
45
53
  end
46
54
 
47
55
  if defined? JRUBY_VERSION
@@ -91,6 +99,7 @@ class Reline::Windows
91
99
  VK_LMENU = 0xA4
92
100
  VK_CONTROL = 0x11
93
101
  VK_SHIFT = 0x10
102
+ VK_DIVIDE = 0x6F
94
103
 
95
104
  KEY_EVENT = 0x01
96
105
  WINDOW_BUFFER_SIZE_EVENT = 0x04
@@ -105,6 +114,7 @@ class Reline::Windows
105
114
  SCROLLLOCK_ON = 0x0040
106
115
  SHIFT_PRESSED = 0x0010
107
116
 
117
+ VK_TAB = 0x09
108
118
  VK_END = 0x23
109
119
  VK_HOME = 0x24
110
120
  VK_LEFT = 0x25
@@ -132,9 +142,11 @@ class Reline::Windows
132
142
  @@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
133
143
  @@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
134
144
  @@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
145
+ @@SetConsoleCursorInfo = Win32API.new('kernel32', 'SetConsoleCursorInfo', ['L', 'P'], 'L')
135
146
 
136
147
  @@GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L')
137
148
  @@SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L')
149
+ @@WaitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', ['L', 'L'], 'L')
138
150
  ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
139
151
 
140
152
  private_class_method def self.getconsolemode
@@ -180,51 +192,47 @@ class Reline::Windows
180
192
  name =~ /(msys-|cygwin-).*-pty/ ? true : false
181
193
  end
182
194
 
195
+ KEY_MAP = [
196
+ # It's treated as Meta+Enter on Windows.
197
+ [ { control_keys: :CTRL, virtual_key_code: 0x0D }, "\e\r".bytes ],
198
+ [ { control_keys: :SHIFT, virtual_key_code: 0x0D }, "\e\r".bytes ],
199
+
200
+ # It's treated as Meta+Space on Windows.
201
+ [ { control_keys: :CTRL, char_code: 0x20 }, "\e ".bytes ],
202
+
203
+ # Emulate getwch() key sequences.
204
+ [ { control_keys: [], virtual_key_code: VK_UP }, [0, 72] ],
205
+ [ { control_keys: [], virtual_key_code: VK_DOWN }, [0, 80] ],
206
+ [ { control_keys: [], virtual_key_code: VK_RIGHT }, [0, 77] ],
207
+ [ { control_keys: [], virtual_key_code: VK_LEFT }, [0, 75] ],
208
+ [ { control_keys: [], virtual_key_code: VK_DELETE }, [0, 83] ],
209
+ [ { control_keys: [], virtual_key_code: VK_HOME }, [0, 71] ],
210
+ [ { control_keys: [], virtual_key_code: VK_END }, [0, 79] ],
211
+
212
+ # Emulate ANSI key sequence.
213
+ [ { control_keys: :SHIFT, virtual_key_code: VK_TAB }, [27, 91, 90] ],
214
+ ]
215
+
183
216
  def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
184
- char = char_code.chr(Encoding::UTF_8)
185
- if char_code == 0x0D and control_key_state.anybits?(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED | SHIFT_PRESSED)
186
- # It's treated as Meta+Enter on Windows.
187
- @@output_buf.push("\e".ord)
188
- @@output_buf.push(char_code)
189
- elsif char_code == 0x20 and control_key_state.anybits?(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
190
- # It's treated as Meta+Space on Windows.
191
- @@output_buf.push("\e".ord)
192
- @@output_buf.push(char_code)
193
- elsif control_key_state.anybits?(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
194
- @@output_buf.push("\e".ord)
195
- @@output_buf.concat(char.bytes)
196
- elsif control_key_state.anybits?(ENHANCED_KEY)
197
- case virtual_key_code # Emulate getwch() key sequences.
198
- when VK_END
199
- @@output_buf.push(0, 79)
200
- when VK_HOME
201
- @@output_buf.push(0, 71)
202
- when VK_LEFT
203
- @@output_buf.push(0, 75)
204
- when VK_UP
205
- @@output_buf.push(0, 72)
206
- when VK_RIGHT
207
- @@output_buf.push(0, 77)
208
- when VK_DOWN
209
- @@output_buf.push(0, 80)
210
- when VK_DELETE
211
- @@output_buf.push(0, 83)
212
- end
213
- elsif char_code == 0 and control_key_state != 0
214
- # unknown
215
- else
216
- case virtual_key_code
217
- when VK_RETURN
218
- @@output_buf.push("\n".ord)
219
- else
220
- @@output_buf.concat(char.bytes)
221
- end
217
+
218
+ key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state)
219
+
220
+ match = KEY_MAP.find { |args,| key.matches?(**args) }
221
+ unless match.nil?
222
+ @@output_buf.concat(match.last)
223
+ return
222
224
  end
225
+
226
+ # no char, only control keys
227
+ return if key.char_code == 0 and key.control_keys.any?
228
+
229
+ @@output_buf.concat(key.char.bytes)
223
230
  end
224
231
 
225
232
  def self.check_input_event
226
233
  num_of_events = 0.chr * 8
227
234
  while @@output_buf.empty? #or true
235
+ next if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
228
236
  next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack('L').first == 0
229
237
  input_record = 0.chr * 18
230
238
  read_event = 0.chr * 4
@@ -348,6 +356,20 @@ class Reline::Windows
348
356
  raise NotImplementedError
349
357
  end
350
358
 
359
+ def self.hide_cursor
360
+ size = 100
361
+ visible = 0 # 0 means false
362
+ cursor_info = [size, visible].pack('Li')
363
+ @@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
364
+ end
365
+
366
+ def self.show_cursor
367
+ size = 100
368
+ visible = 1 # 1 means true
369
+ cursor_info = [size, visible].pack('Li')
370
+ @@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
371
+ end
372
+
351
373
  def self.set_winch_handler(&handler)
352
374
  @@winch_handler = handler
353
375
  end
@@ -360,4 +382,43 @@ class Reline::Windows
360
382
  def self.deprep(otio)
361
383
  # do nothing
362
384
  end
385
+
386
+ class KeyEventRecord
387
+
388
+ attr_reader :virtual_key_code, :char_code, :control_key_state, :control_keys
389
+
390
+ def initialize(virtual_key_code, char_code, control_key_state)
391
+ @virtual_key_code = virtual_key_code
392
+ @char_code = char_code
393
+ @control_key_state = control_key_state
394
+ @enhanced = control_key_state & ENHANCED_KEY != 0
395
+
396
+ (@control_keys = []).tap do |control_keys|
397
+ # symbols must be sorted to make comparison is easier later on
398
+ control_keys << :ALT if control_key_state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) != 0
399
+ control_keys << :CTRL if control_key_state & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0
400
+ control_keys << :SHIFT if control_key_state & SHIFT_PRESSED != 0
401
+ end.freeze
402
+ end
403
+
404
+ def char
405
+ @char_code.chr(Encoding::UTF_8)
406
+ end
407
+
408
+ def enhanced?
409
+ @enhanced
410
+ end
411
+
412
+ # Verifies if the arguments match with this key event.
413
+ # Nil arguments are ignored, but at least one must be passed as non-nil.
414
+ # To verify that no control keys were pressed, pass an empty array: `control_keys: []`.
415
+ def matches?(control_keys: nil, virtual_key_code: nil, char_code: nil)
416
+ raise ArgumentError, 'No argument was passed to match key event' if control_keys.nil? && virtual_key_code.nil? && char_code.nil?
417
+
418
+ (control_keys.nil? || [*control_keys].sort == @control_keys) &&
419
+ (virtual_key_code.nil? || @virtual_key_code == virtual_key_code) &&
420
+ (char_code.nil? || char_code == @char_code)
421
+ end
422
+
423
+ end
363
424
  end
data/lib/reline.rb CHANGED
@@ -7,12 +7,15 @@ require 'reline/key_actor'
7
7
  require 'reline/key_stroke'
8
8
  require 'reline/line_editor'
9
9
  require 'reline/history'
10
+ require 'reline/terminfo'
10
11
  require 'rbconfig'
11
12
 
12
13
  module Reline
13
14
  FILENAME_COMPLETION_PROC = nil
14
15
  USERNAME_COMPLETION_PROC = nil
15
16
 
17
+ class ConfigEncodingConversionError < StandardError; end
18
+
16
19
  Key = Struct.new('Key', :char, :combined_char, :with_meta)
17
20
  CursorPos = Struct.new(:x, :y)
18
21
 
@@ -41,6 +44,7 @@ module Reline
41
44
 
42
45
  def initialize
43
46
  self.output = STDOUT
47
+ @dialog_proc_list = []
44
48
  yield self
45
49
  @completion_quote_character = nil
46
50
  @bracketed_paste_finished = false
@@ -103,6 +107,14 @@ module Reline
103
107
  @completion_proc = p
104
108
  end
105
109
 
110
+ def autocompletion
111
+ @config.autocompletion
112
+ end
113
+
114
+ def autocompletion=(val)
115
+ @config.autocompletion = val
116
+ end
117
+
106
118
  def output_modifier_proc=(p)
107
119
  raise ArgumentError unless p.respond_to?(:call) or p.nil?
108
120
  @output_modifier_proc = p
@@ -127,6 +139,12 @@ module Reline
127
139
  @dig_perfect_match_proc = p
128
140
  end
129
141
 
142
+ def add_dialog_proc(name_sym, p, context = nil)
143
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
144
+ raise ArgumentError unless name_sym.instance_of?(Symbol)
145
+ @dialog_proc_list << [name_sym, p, context]
146
+ end
147
+
130
148
  def input=(val)
131
149
  raise TypeError unless val.respond_to?(:getc) or val.nil?
132
150
  if val.respond_to?(:getc)
@@ -168,6 +186,45 @@ module Reline
168
186
  Reline::IOGate.get_screen_size
169
187
  end
170
188
 
189
+ Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
190
+ # autocomplete
191
+ return nil unless config.autocompletion
192
+ if just_cursor_moving and completion_journey_data.nil?
193
+ # Auto complete starts only when edited
194
+ return nil
195
+ end
196
+ pre, target, post= retrieve_completion_block(true)
197
+ if target.nil? or target.empty?# or target.size <= 3
198
+ return nil
199
+ end
200
+ if completion_journey_data and completion_journey_data.list
201
+ result = completion_journey_data.list.dup
202
+ result.shift
203
+ pointer = completion_journey_data.pointer - 1
204
+ else
205
+ result = call_completion_proc_with_checking_args(pre, target, post)
206
+ pointer = nil
207
+ end
208
+ if result and result.size == 1 and result[0] == target
209
+ result = nil
210
+ end
211
+ target_width = Reline::Unicode.calculate_width(target)
212
+ x = cursor_pos.x - target_width
213
+ if x < 0
214
+ x = screen_width + x
215
+ y = -1
216
+ else
217
+ y = 0
218
+ end
219
+ cursor_pos_to_render = Reline::CursorPos.new(x, y)
220
+ if context and context.is_a?(Array)
221
+ context.clear
222
+ context.push(cursor_pos_to_render, result, pointer)
223
+ end
224
+ [cursor_pos_to_render, result, pointer, nil]
225
+ }
226
+ Reline::DEFAULT_DIALOG_CONTEXT = Array.new
227
+
171
228
  def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
172
229
  unless confirm_multiline_termination
173
230
  raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
@@ -227,6 +284,10 @@ module Reline
227
284
  line_editor.auto_indent_proc = auto_indent_proc
228
285
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
229
286
  line_editor.pre_input_hook = pre_input_hook
287
+ @dialog_proc_list.each do |d|
288
+ name_sym, dialog_proc, context = d
289
+ line_editor.add_dialog_proc(name_sym, dialog_proc, context)
290
+ end
230
291
 
231
292
  unless config.test_mode
232
293
  config.read
@@ -421,6 +482,8 @@ module Reline
421
482
  def_single_delegators :core, :ambiguous_width
422
483
  def_single_delegators :core, :last_incremental_search
423
484
  def_single_delegators :core, :last_incremental_search=
485
+ def_single_delegators :core, :add_dialog_proc
486
+ def_single_delegators :core, :autocompletion, :autocompletion=
424
487
 
425
488
  def_single_delegators :core, :readmultiline
426
489
  def_instance_delegators self, :readmultiline
@@ -442,6 +505,7 @@ module Reline
442
505
  core.completer_quote_characters = '"\''
443
506
  core.filename_quote_characters = ""
444
507
  core.special_prefixes = ""
508
+ core.add_dialog_proc(:autocomplete, Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE, Reline::DEFAULT_DIALOG_CONTEXT)
445
509
  }
446
510
  end
447
511
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.8.pre.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-21 00:00:00.000000000 Z
11
+ date: 2021-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -69,11 +69,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
69
69
  version: '2.5'
70
70
  required_rubygems_version: !ruby/object:Gem::Requirement
71
71
  requirements:
72
- - - ">="
72
+ - - ">"
73
73
  - !ruby/object:Gem::Version
74
- version: '0'
74
+ version: 1.3.1
75
75
  requirements: []
76
- rubygems_version: 3.2.17
76
+ rubygems_version: 3.2.22
77
77
  signing_key:
78
78
  specification_version: 4
79
79
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.