reline 0.2.8.pre.5 → 0.2.8.pre.9

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fca2b6f76f8c59a18589d6d730f7f159e25b95e639ae7d6866f772e9bb2655ab
4
- data.tar.gz: 61b69c3d01f74b957c6a2d646e58021cad4b49031d4fdf6437f2303d417d5f36
3
+ metadata.gz: b41b69340a1422f198eda02baddc77ce7ff82ed3ba1f61a8101d179e7700da23
4
+ data.tar.gz: a396bceef149a9436aba896305172bb68925e2302cb7a2b1074d3c2a0ebb300a
5
5
  SHA512:
6
- metadata.gz: 561ffdbf39c095852f0a642178d5328329cea072fdc91c32bd11edfdcecb4846414006961b6fe7adbf63c825022a8634d5a563fdeb574d127088b536967e1f9c
7
- data.tar.gz: 07e62e55ca7a45d3b747724823b26af09d109f9620ba82914cfaf7e6100251bb76bd0de7b01b9829b51e41248a32b028bbbb3617c7d4eedfc2c1d05ab701a236
6
+ metadata.gz: 2f83780bb2f061a88fd2d50c613efff97e56e346f484230f502ef34793bfffaa9bd374647ebdb297c80796323ab1646bd578ee59d807b665efdf24d95810267f
7
+ data.tar.gz: b5312082646ee37cd04bc2b1aa71115632c92727f56428bfb908a549bfae841d179e426041a94e3595443e218466fead012cd91dce4ebc984de5f16dc0d25d1a
data/lib/reline/config.rb CHANGED
@@ -50,6 +50,7 @@ class Reline::Config
50
50
  @additional_key_bindings[:emacs] = {}
51
51
  @additional_key_bindings[:vi_insert] = {}
52
52
  @additional_key_bindings[:vi_command] = {}
53
+ @oneshot_key_bindings = {}
53
54
  @skip_section = nil
54
55
  @if_stack = nil
55
56
  @editing_mode_label = :emacs
@@ -75,6 +76,7 @@ class Reline::Config
75
76
  @additional_key_bindings.keys.each do |key|
76
77
  @additional_key_bindings[key].clear
77
78
  end
79
+ @oneshot_key_bindings.clear
78
80
  reset_default_key_bindings
79
81
  end
80
82
 
@@ -128,8 +130,12 @@ class Reline::Config
128
130
  return home_rc_path
129
131
  end
130
132
 
133
+ private def default_inputrc_path
134
+ @default_inputrc_path ||= inputrc_path
135
+ end
136
+
131
137
  def read(file = nil)
132
- file ||= inputrc_path
138
+ file ||= default_inputrc_path
133
139
  begin
134
140
  if file.respond_to?(:readlines)
135
141
  lines = file.readlines
@@ -148,8 +154,19 @@ class Reline::Config
148
154
  end
149
155
 
150
156
  def key_bindings
151
- # override @key_actors[@editing_mode_label].default_key_bindings with @additional_key_bindings[@editing_mode_label]
152
- @key_actors[@editing_mode_label].default_key_bindings.merge(@additional_key_bindings[@editing_mode_label])
157
+ # The key bindings for each editing mode will be overwritten by the user-defined ones.
158
+ kb = @key_actors[@editing_mode_label].default_key_bindings.dup
159
+ kb.merge!(@additional_key_bindings[@editing_mode_label])
160
+ kb.merge!(@oneshot_key_bindings)
161
+ kb
162
+ end
163
+
164
+ def add_oneshot_key_binding(keystroke, target)
165
+ @oneshot_key_bindings[keystroke] = target
166
+ end
167
+
168
+ def reset_oneshot_key_bindings
169
+ @oneshot_key_bindings.clear
153
170
  end
154
171
 
155
172
  def add_default_key_binding_by_keymap(keymap, keystroke, target)
@@ -24,6 +24,7 @@ class Reline::GeneralIO
24
24
  end
25
25
 
26
26
  @@buf = []
27
+ @@input = STDIN
27
28
 
28
29
  def self.input=(val)
29
30
  @@input = val
@@ -1,8 +1,59 @@
1
1
  class Reline::KeyStroke
2
2
  using Module.new {
3
+ refine Integer do
4
+ def ==(other)
5
+ if other.is_a?(Reline::Key)
6
+ if other.combined_char == "\e".ord
7
+ false
8
+ else
9
+ other.combined_char == self
10
+ end
11
+ else
12
+ super
13
+ end
14
+ end
15
+ end
16
+
3
17
  refine Array do
4
18
  def start_with?(other)
5
- other.size <= size && other == self.take(other.size)
19
+ compressed_me = compress_meta_key
20
+ compressed_other = other.compress_meta_key
21
+ i = 0
22
+ loop do
23
+ my_c = compressed_me[i]
24
+ other_c = compressed_other[i]
25
+ other_is_last = (i + 1) == compressed_other.size
26
+ me_is_last = (i + 1) == compressed_me.size
27
+ if my_c != other_c
28
+ if other_c == "\e".ord and other_is_last and my_c.is_a?(Reline::Key) and my_c.with_meta
29
+ return true
30
+ else
31
+ return false
32
+ end
33
+ elsif other_is_last
34
+ return true
35
+ elsif me_is_last
36
+ return false
37
+ end
38
+ i += 1
39
+ end
40
+ end
41
+
42
+ def ==(other)
43
+ compressed_me = compress_meta_key
44
+ compressed_other = other.compress_meta_key
45
+ compressed_me.size == compressed_other.size and [compressed_me, compressed_other].transpose.all?{ |i| i[0] == i[1] }
46
+ end
47
+
48
+ def compress_meta_key
49
+ inject([]) { |result, key|
50
+ if result.size > 0 and result.last == "\e".ord
51
+ result[result.size - 1] = Reline::Key.new(key, key | 0b10000000, true)
52
+ else
53
+ result << key
54
+ end
55
+ result
56
+ }
6
57
  end
7
58
 
8
59
  def bytes
@@ -19,8 +70,8 @@ class Reline::KeyStroke
19
70
  key_mapping.keys.select { |lhs|
20
71
  lhs.start_with? input
21
72
  }.tap { |it|
22
- return :matched if it.size == 1 && (it.max_by(&:size)&.size&.== input.size)
23
- return :matching if it.size == 1 && (it.max_by(&:size)&.size&.!= input.size)
73
+ return :matched if it.size == 1 && (it[0] == input)
74
+ return :matching if it.size == 1 && (it[0] != input)
24
75
  return :matched if it.max_by(&:size)&.size&.< input.size
25
76
  return :matching if it.size > 1
26
77
  }
@@ -32,7 +83,8 @@ class Reline::KeyStroke
32
83
  end
33
84
 
34
85
  def expand(input)
35
- lhs = key_mapping.keys.select { |item| input.start_with? item }.sort_by(&:size).reverse.first
86
+ input = input.compress_meta_key
87
+ lhs = key_mapping.keys.select { |item| input.start_with? item }.sort_by(&:size).last
36
88
  return input unless lhs
37
89
  rhs = key_mapping[lhs]
38
90
 
@@ -150,7 +150,7 @@ 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(:INT) {
153
+ @old_trap = Signal.trap('INT') {
154
154
  clear_dialog
155
155
  if @scroll_partial_screen
156
156
  move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
@@ -170,6 +170,13 @@ class Reline::LineEditor
170
170
  @old_trap.call
171
171
  end
172
172
  }
173
+ begin
174
+ @old_tstp_trap = Signal.trap('TSTP') {
175
+ Reline::IOGate.ungetc("\C-z".ord)
176
+ @old_tstp_trap.call if @old_tstp_trap.respond_to?(:call)
177
+ }
178
+ rescue ArgumentError
179
+ end
173
180
  Reline::IOGate.set_winch_handler do
174
181
  @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
175
182
  old_screen_size = @screen_size
@@ -207,10 +214,15 @@ class Reline::LineEditor
207
214
  @rerender_all = true
208
215
  end
209
216
  end
217
+ @block_elem_width = Reline::Unicode.calculate_width('█')
210
218
  end
211
219
 
212
220
  def finalize
213
- Signal.trap('SIGINT', @old_trap)
221
+ Signal.trap('INT', @old_trap)
222
+ begin
223
+ Signal.trap('TSTP', @old_tstp_trap)
224
+ rescue ArgumentError
225
+ end
214
226
  end
215
227
 
216
228
  def eof?
@@ -251,6 +263,7 @@ class Reline::LineEditor
251
263
  @in_pasting = false
252
264
  @auto_indent_proc = nil
253
265
  @dialogs = []
266
+ @last_key = nil
254
267
  reset_line
255
268
  end
256
269
 
@@ -512,6 +525,14 @@ class Reline::LineEditor
512
525
  @cursor_pos.y = row
513
526
  end
514
527
 
528
+ def set_key(key)
529
+ @key = key
530
+ end
531
+
532
+ def key
533
+ @key
534
+ end
535
+
515
536
  def cursor_pos
516
537
  @cursor_pos
517
538
  end
@@ -539,10 +560,11 @@ class Reline::LineEditor
539
560
 
540
561
  class Dialog
541
562
  attr_reader :name, :contents, :width
542
- attr_accessor :scroll_top, :column, :vertical_offset, :lines_backup
563
+ attr_accessor :scroll_top, :scrollbar_pos, :column, :vertical_offset, :lines_backup, :trap_key
543
564
 
544
- def initialize(name, proc_scope)
565
+ def initialize(name, config, proc_scope)
545
566
  @name = name
567
+ @config = config
546
568
  @proc_scope = proc_scope
547
569
  @width = nil
548
570
  @scroll_top = 0
@@ -563,52 +585,50 @@ class Reline::LineEditor
563
585
  end
564
586
  end
565
587
 
566
- def call
588
+ def call(key)
567
589
  @proc_scope.set_dialog(self)
568
- @proc_scope.call
590
+ @proc_scope.set_key(key)
591
+ dialog_render_info = @proc_scope.call
592
+ if @trap_key
593
+ if @trap_key.any?{ |i| i.is_a?(Array) } # multiple trap
594
+ @trap_key.each do |t|
595
+ @config.add_oneshot_key_binding(t, @name)
596
+ end
597
+ elsif @trap_key.is_a?(Array)
598
+ @config.add_oneshot_key_binding(@trap_key, @name)
599
+ elsif @trap_key.is_a?(Integer) or @trap_key.is_a?(Reline::Key)
600
+ @config.add_oneshot_key_binding([@trap_key], @name)
601
+ end
602
+ end
603
+ dialog_render_info
569
604
  end
570
605
  end
571
606
 
572
607
  def add_dialog_proc(name, p, context = nil)
573
608
  return if @dialogs.any? { |d| d.name == name }
574
- @dialogs << Dialog.new(name, DialogProcScope.new(self, @config, p, context))
609
+ @dialogs << Dialog.new(name, @config, DialogProcScope.new(self, @config, p, context))
575
610
  end
576
611
 
577
612
  DIALOG_HEIGHT = 20
578
- DIALOG_WIDTH = 40
579
613
  private def render_dialog(cursor_column)
580
614
  @dialogs.each do |dialog|
581
615
  render_each_dialog(dialog, cursor_column)
582
616
  end
583
617
  end
584
618
 
619
+ private def padding_space_with_escape_sequences(str, width)
620
+ str + (' ' * (width - calculate_width(str, true)))
621
+ end
622
+
585
623
  private def render_each_dialog(dialog, cursor_column)
586
624
  if @in_pasting
587
625
  dialog.contents = nil
626
+ dialog.trap_key = nil
588
627
  return
589
628
  end
590
629
  dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from)
591
- dialog_render_info = dialog.call
592
- dialog.width = dialog_render_info.width if dialog_render_info and dialog_render_info.width
593
- old_dialog = dialog.clone
594
- if dialog_render_info and dialog_render_info.contents and not dialog_render_info.contents.empty?
595
- height = dialog_render_info.height || DIALOG_HEIGHT
596
- pointer = dialog_render_info.pointer
597
- dialog.contents = dialog_render_info.contents
598
- if dialog.contents.size > height
599
- if dialog_render_info.pointer
600
- if dialog_render_info.pointer < 0
601
- dialog.scroll_top = 0
602
- elsif (dialog_render_info.pointer - dialog.scroll_top) >= (height - 1)
603
- dialog.scroll_top = dialog_render_info.pointer - (height - 1)
604
- elsif (dialog_render_info.pointer - dialog.scroll_top) < 0
605
- dialog.scroll_top = dialog_render_info.pointer
606
- end
607
- pointer = dialog_render_info.pointer - dialog.scroll_top
608
- end
609
- dialog.contents = dialog.contents[dialog.scroll_top, height]
610
- end
611
- else
630
+ dialog_render_info = dialog.call(@last_key)
631
+ if dialog_render_info.nil? or dialog_render_info.contents.nil? or dialog_render_info.contents.empty?
612
632
  dialog.lines_backup = {
613
633
  lines: modify_lines(whole_lines),
614
634
  line_index: @line_index,
@@ -618,8 +638,41 @@ class Reline::LineEditor
618
638
  }
619
639
  clear_each_dialog(dialog)
620
640
  dialog.contents = nil
641
+ dialog.trap_key = nil
621
642
  return
622
643
  end
644
+ old_dialog = dialog.clone
645
+ dialog.contents = dialog_render_info.contents
646
+ pointer = dialog_render_info.pointer
647
+ if dialog_render_info.width
648
+ dialog.width = dialog_render_info.width
649
+ else
650
+ dialog.width = dialog.contents.map { |l| calculate_width(l, true) }.max
651
+ end
652
+ height = dialog_render_info.height || DIALOG_HEIGHT
653
+ height = dialog.contents.size if dialog.contents.size < height
654
+ if dialog.contents.size > height
655
+ if dialog_render_info.pointer
656
+ if dialog_render_info.pointer < 0
657
+ dialog.scroll_top = 0
658
+ elsif (dialog_render_info.pointer - dialog.scroll_top) >= (height - 1)
659
+ dialog.scroll_top = dialog_render_info.pointer - (height - 1)
660
+ elsif (dialog_render_info.pointer - dialog.scroll_top) < 0
661
+ dialog.scroll_top = dialog_render_info.pointer
662
+ end
663
+ pointer = dialog_render_info.pointer - dialog.scroll_top
664
+ end
665
+ dialog.contents = dialog.contents[dialog.scroll_top, height]
666
+ end
667
+ if dialog_render_info.scrollbar and dialog_render_info.contents.size > height
668
+ bar_max_height = height * 2
669
+ moving_distance = (dialog_render_info.contents.size - height) * 2
670
+ position_ratio = dialog.scroll_top.zero? ? 0.0 : ((dialog.scroll_top * 2).to_f / moving_distance)
671
+ bar_height = (bar_max_height * ((dialog.contents.size * 2).to_f / (dialog_render_info.contents.size * 2))).floor.to_i
672
+ dialog.scrollbar_pos = ((bar_max_height - bar_height) * position_ratio).floor.to_i
673
+ else
674
+ dialog.scrollbar_pos = nil
675
+ end
623
676
  upper_space = @first_line_started_from - @started_from
624
677
  lower_space = @highest_in_all - @first_line_started_from - @started_from - 1
625
678
  dialog.column = dialog_render_info.pos.x
@@ -630,7 +683,7 @@ class Reline::LineEditor
630
683
  if (lower_space + @rest_height - dialog_render_info.pos.y) >= height
631
684
  dialog.vertical_offset = dialog_render_info.pos.y + 1
632
685
  elsif upper_space >= height
633
- dialog.vertical_offset = dialog_render_info.pos.y + -(height + 1)
686
+ dialog.vertical_offset = dialog_render_info.pos.y - height
634
687
  else
635
688
  if (lower_space + @rest_height - dialog_render_info.pos.y) < height
636
689
  scroll_down(height + dialog_render_info.pos.y)
@@ -639,6 +692,7 @@ class Reline::LineEditor
639
692
  dialog.vertical_offset = dialog_render_info.pos.y + 1
640
693
  end
641
694
  Reline::IOGate.hide_cursor
695
+ dialog.width += @block_elem_width if dialog.scrollbar_pos
642
696
  reset_dialog(dialog, old_dialog)
643
697
  move_cursor_down(dialog.vertical_offset)
644
698
  Reline::IOGate.move_cursor_column(dialog.column)
@@ -652,7 +706,24 @@ class Reline::LineEditor
652
706
  bg_color = '46'
653
707
  end
654
708
  end
655
- @output.write "\e[#{bg_color}m%-#{dialog.width}s\e[49m" % Reline::Unicode.take_range(item, 0, dialog.width)
709
+ str_width = dialog.width - (dialog.scrollbar_pos.nil? ? 0 : @block_elem_width)
710
+ str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width)
711
+ @output.write "\e[#{bg_color}m#{str}"
712
+ if dialog.scrollbar_pos and (dialog.scrollbar_pos != old_dialog.scrollbar_pos or dialog.column != old_dialog.column)
713
+ @output.write "\e[37m"
714
+ if dialog.scrollbar_pos <= (i * 2) and (i * 2 + @block_elem_width) < (dialog.scrollbar_pos + bar_height)
715
+ @output.write '█'
716
+ elsif dialog.scrollbar_pos <= (i * 2) and (i * 2) < (dialog.scrollbar_pos + bar_height)
717
+ @output.write '▀'
718
+ str += ''
719
+ elsif dialog.scrollbar_pos <= (i * 2 + @block_elem_width) and (i * 2) < (dialog.scrollbar_pos + bar_height)
720
+ @output.write '▄'
721
+ else
722
+ @output.write ' ' * @block_elem_width
723
+ end
724
+ @output.write "\e[39m"
725
+ end
726
+ @output.write "\e[49m"
656
727
  Reline::IOGate.move_cursor_column(dialog.column)
657
728
  move_cursor_down(1) if i < (dialog.contents.size - 1)
658
729
  end
@@ -693,11 +764,12 @@ class Reline::LineEditor
693
764
  line_num.times do |i|
694
765
  Reline::IOGate.move_cursor_column(old_dialog.column)
695
766
  if visual_lines[start + i].nil?
696
- s = ' ' * dialog.width
767
+ s = ' ' * old_dialog.width
697
768
  else
698
- s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, dialog.width)
769
+ s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width)
770
+ s = padding_space_with_escape_sequences(s, old_dialog.width)
699
771
  end
700
- @output.write "\e[39m\e[49m%-#{dialog.width}s\e[39m\e[49m" % s
772
+ @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
701
773
  move_cursor_down(1) if i < (line_num - 1)
702
774
  end
703
775
  move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
@@ -710,11 +782,12 @@ class Reline::LineEditor
710
782
  line_num.times do |i|
711
783
  Reline::IOGate.move_cursor_column(old_dialog.column)
712
784
  if visual_lines[start + i].nil?
713
- s = ' ' * dialog.width
785
+ s = ' ' * old_dialog.width
714
786
  else
715
- s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, dialog.width)
787
+ s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width)
788
+ s = padding_space_with_escape_sequences(s, old_dialog.width)
716
789
  end
717
- @output.write "\e[39m\e[49m%-#{dialog.width}s\e[39m\e[49m" % s
790
+ @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
718
791
  move_cursor_down(1) if i < (line_num - 1)
719
792
  end
720
793
  move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
@@ -731,8 +804,9 @@ class Reline::LineEditor
731
804
  s = ' ' * width
732
805
  else
733
806
  s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, width)
807
+ s = padding_space_with_escape_sequences(s, dialog.width)
734
808
  end
735
- @output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
809
+ @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
736
810
  move_cursor_down(1) if i < (line_num - 1)
737
811
  end
738
812
  move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
@@ -749,9 +823,10 @@ class Reline::LineEditor
749
823
  s = ' ' * width
750
824
  else
751
825
  s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column + dialog.width, width)
826
+ s = padding_space_with_escape_sequences(s, dialog.width)
752
827
  end
753
828
  Reline::IOGate.move_cursor_column(dialog.column + dialog.width)
754
- @output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
829
+ @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
755
830
  move_cursor_down(1) if i < (line_num - 1)
756
831
  end
757
832
  move_cursor_up(old_dialog.vertical_offset + line_num - 1 + y_diff)
@@ -766,6 +841,7 @@ class Reline::LineEditor
766
841
  end
767
842
 
768
843
  private def clear_each_dialog(dialog)
844
+ dialog.trap_key = nil
769
845
  return unless dialog.contents
770
846
  prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
771
847
  visual_lines = []
@@ -787,13 +863,14 @@ class Reline::LineEditor
787
863
  dialog_vertical_size = dialog.contents.size
788
864
  dialog_vertical_size.times do |i|
789
865
  if i < visual_lines_under_dialog.size
790
- Reline::IOGate.move_cursor_column(0)
791
- @output.write "\e[39m\e[49m%-#{dialog.width}s\e[39m\e[49m" % visual_lines_under_dialog[i]
866
+ Reline::IOGate.move_cursor_column(dialog.column)
867
+ str = Reline::Unicode.take_range(visual_lines_under_dialog[i], dialog.column, dialog.width)
868
+ str = padding_space_with_escape_sequences(str, dialog.width)
869
+ @output.write "\e[39m\e[49m#{str}\e[39m\e[49m"
792
870
  else
793
871
  Reline::IOGate.move_cursor_column(dialog.column)
794
872
  @output.write "\e[39m\e[49m#{' ' * dialog.width}\e[39m\e[49m"
795
873
  end
796
- Reline::IOGate.erase_after_cursor
797
874
  move_cursor_down(1) if i < (dialog_vertical_size - 1)
798
875
  end
799
876
  move_cursor_up(dialog_vertical_size - 1 + dialog.vertical_offset)
@@ -1142,7 +1219,7 @@ class Reline::LineEditor
1142
1219
  height = render_partial(prompt, prompt_width, line, back, with_control: false)
1143
1220
  end
1144
1221
  if index < (@buffer_of_lines.size - 1)
1145
- move_cursor_down(height)
1222
+ move_cursor_down(1)
1146
1223
  back += height
1147
1224
  end
1148
1225
  end
@@ -1287,8 +1364,10 @@ class Reline::LineEditor
1287
1364
  end
1288
1365
  end
1289
1366
  completed = @completion_journey_data.list[@completion_journey_data.pointer]
1290
- @line = @completion_journey_data.preposing + completed + @completion_journey_data.postposing
1291
- line_to_pointer = @completion_journey_data.preposing + completed
1367
+ new_line = (@completion_journey_data.preposing + completed + @completion_journey_data.postposing).split("\n")[@line_index]
1368
+ @line = new_line.nil? ? String.new(encoding: @encoding) : new_line
1369
+ line_to_pointer = (@completion_journey_data.preposing + completed).split("\n").last
1370
+ line_to_pointer = String.new(encoding: @encoding) if line_to_pointer.nil?
1292
1371
  @cursor_max = calculate_width(@line)
1293
1372
  @cursor = calculate_width(line_to_pointer)
1294
1373
  @byte_pointer = line_to_pointer.bytesize
@@ -1449,6 +1528,13 @@ class Reline::LineEditor
1449
1528
  end
1450
1529
 
1451
1530
  def input_key(key)
1531
+ @last_key = key
1532
+ @config.reset_oneshot_key_bindings
1533
+ @dialogs.each do |dialog|
1534
+ if key.char.instance_of?(Symbol) and key.char == dialog.name
1535
+ return
1536
+ end
1537
+ end
1452
1538
  @just_cursor_moving = nil
1453
1539
  if key.char.nil?
1454
1540
  if @first_char
@@ -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.5'
2
+ VERSION = '0.2.8.pre.9'
3
3
  end
@@ -226,6 +226,8 @@ class Reline::Windows
226
226
  # no char, only control keys
227
227
  return if key.char_code == 0 and key.control_keys.any?
228
228
 
229
+ @@output_buf.push("\e".ord) if key.control_keys.include?(:ALT)
230
+
229
231
  @@output_buf.concat(key.char.bytes)
230
232
  end
231
233
 
data/lib/reline.rb CHANGED
@@ -16,9 +16,28 @@ module Reline
16
16
 
17
17
  class ConfigEncodingConversionError < StandardError; end
18
18
 
19
- Key = Struct.new('Key', :char, :combined_char, :with_meta)
19
+ Key = Struct.new('Key', :char, :combined_char, :with_meta) do
20
+ def match?(key)
21
+ if key.instance_of?(Reline::Key)
22
+ (key.char.nil? or char.nil? or char == key.char) and
23
+ (key.combined_char.nil? or combined_char.nil? or combined_char == key.combined_char) and
24
+ (key.with_meta.nil? or with_meta.nil? or with_meta == key.with_meta)
25
+ elsif key.is_a?(Integer) or key.is_a?(Symbol)
26
+ if not combined_char.nil? and combined_char == key
27
+ true
28
+ elsif combined_char.nil? and not char.nil? and char == key
29
+ true
30
+ else
31
+ false
32
+ end
33
+ else
34
+ false
35
+ end
36
+ end
37
+ alias_method :==, :match?
38
+ end
20
39
  CursorPos = Struct.new(:x, :y)
21
- DialogRenderInfo = Struct.new(:pos, :contents, :pointer, :bg_color, :width, :height, keyword_init: true)
40
+ DialogRenderInfo = Struct.new(:pos, :contents, :pointer, :bg_color, :width, :height, :scrollbar, keyword_init: true)
22
41
 
23
42
  class Core
24
43
  ATTR_READER_NAMES = %i(
@@ -194,8 +213,8 @@ module Reline
194
213
  # Auto complete starts only when edited
195
214
  return nil
196
215
  end
197
- pre, target, post= retrieve_completion_block(true)
198
- if target.nil? or target.empty?# or target.size <= 3
216
+ pre, target, post = retrieve_completion_block(true)
217
+ if target.nil? or target.empty? or (completion_journey_data&.pointer == -1 and target.size <= 3)
199
218
  return nil
200
219
  end
201
220
  if completion_journey_data and completion_journey_data.list
@@ -206,7 +225,7 @@ module Reline
206
225
  result = call_completion_proc_with_checking_args(pre, target, post)
207
226
  pointer = nil
208
227
  end
209
- if result and result.size == 1 and result[0] == target
228
+ if result and result.size == 1 and result[0] == target and pointer != 0
210
229
  result = nil
211
230
  end
212
231
  target_width = Reline::Unicode.calculate_width(target)
@@ -222,7 +241,7 @@ module Reline
222
241
  context.clear
223
242
  context.push(cursor_pos_to_render, result, pointer, dialog)
224
243
  end
225
- DialogRenderInfo.new(pos: cursor_pos_to_render, contents: result, pointer: pointer, height: 15)
244
+ DialogRenderInfo.new(pos: cursor_pos_to_render, contents: result, pointer: pointer, scrollbar: true, height: 15)
226
245
  }
227
246
  Reline::DEFAULT_DIALOG_CONTEXT = Array.new
228
247
 
@@ -362,25 +381,9 @@ module Reline
362
381
  break
363
382
  when :matching
364
383
  if buffer.size == 1
365
- begin
366
- succ_c = nil
367
- Timeout.timeout(keyseq_timeout / 1000.0) {
368
- succ_c = Reline::IOGate.getc
369
- }
370
- rescue Timeout::Error # cancel matching only when first byte
371
- block.([Reline::Key.new(c, c, false)])
372
- break
373
- else
374
- if key_stroke.match_status(buffer.dup.push(succ_c)) == :unmatched
375
- if c == "\e".ord
376
- block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
377
- else
378
- block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
379
- end
380
- break
381
- else
382
- Reline::IOGate.ungetc(succ_c)
383
- end
384
+ case read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
385
+ when :break then break
386
+ when :next then next
384
387
  end
385
388
  end
386
389
  when :unmatched
@@ -397,6 +400,38 @@ module Reline
397
400
  end
398
401
  end
399
402
 
403
+ private def read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
404
+ begin
405
+ succ_c = nil
406
+ Timeout.timeout(keyseq_timeout / 1000.0) {
407
+ succ_c = Reline::IOGate.getc
408
+ }
409
+ rescue Timeout::Error # cancel matching only when first byte
410
+ block.([Reline::Key.new(c, c, false)])
411
+ return :break
412
+ else
413
+ case key_stroke.match_status(buffer.dup.push(succ_c))
414
+ when :unmatched
415
+ if c == "\e".ord
416
+ block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
417
+ else
418
+ block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
419
+ end
420
+ return :break
421
+ when :matching
422
+ Reline::IOGate.ungetc(succ_c)
423
+ return :next
424
+ when :matched
425
+ buffer << succ_c
426
+ expanded = key_stroke.expand(buffer).map{ |expanded_c|
427
+ Reline::Key.new(expanded_c, expanded_c, false)
428
+ }
429
+ block.(expanded)
430
+ return :break
431
+ end
432
+ end
433
+ end
434
+
400
435
  private def read_escaped_key(keyseq_timeout, c, block)
401
436
  begin
402
437
  escaped_c = nil
@@ -448,7 +483,7 @@ module Reline
448
483
  #--------------------------------------------------------
449
484
 
450
485
  (Core::ATTR_READER_NAMES).each { |name|
451
- def_single_delegators :core, "#{name}", "#{name}="
486
+ def_single_delegators :core, :"#{name}", :"#{name}="
452
487
  }
453
488
  def_single_delegators :core, :input=, :output=
454
489
  def_single_delegators :core, :vi_editing_mode, :emacs_editing_mode
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.8.pre.5
4
+ version: 0.2.8.pre.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-31 00:00:00.000000000 Z
11
+ date: 2021-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console