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

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: 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