reline 0.2.8.pre.4 → 0.2.8.pre.8

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: 5e7c5785424de4deda942cdd54cab39bc7d96fd79b1451095626996b875a32d9
4
- data.tar.gz: 3afcf3fc84d2b3d4dd48481bca613a8fc08de049983619772e9fcd502caffa83
3
+ metadata.gz: 6f03d46c602265d3a197f00021df365a9686e4fd0b5d0ef2a037a9ad25143b91
4
+ data.tar.gz: e97bfb610d3c9498e651b25de97e87380e624b56ea970ab7048b25dca7be9488
5
5
  SHA512:
6
- metadata.gz: c1be959cdf8fc499ec71184bc3768964dbc1eb078085bbd8303a6d9b4b0ade3ccd25a8d90486c620316b38c4dfe7b7fb224d99912ff855bd8bc3fc64946f1d5d
7
- data.tar.gz: cfe31c94298208f8cda2c7fddeba8940e71589c60b06167fcb81e8c0d278055ffd94261713d1aa3a2d6031d6572c3dc9194412d15e57f83d0fb81e87361496c0
6
+ metadata.gz: be1a180b58930985ea7ba5d63be81b62c8ffb0b960daf33c5ad984652fa6021151519cc1fa41055c240d7675d628c6749ab69ca67c2c2bfa306a9deab8200c56
7
+ data.tar.gz: 68be299c9815a350c8afec2c5b27aa02905c8fcc04c6fbba331aded2aded1d0dc160d2e8b01be58a91f308b1ad1dd880209e237de770002422910c6014ddc51a
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
@@ -149,7 +155,15 @@ class Reline::Config
149
155
 
150
156
  def key_bindings
151
157
  # 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])
158
+ @key_actors[@editing_mode_label].default_key_bindings.merge(@additional_key_bindings[@editing_mode_label]).merge(@oneshot_key_bindings)
159
+ end
160
+
161
+ def add_oneshot_key_binding(keystroke, target)
162
+ @oneshot_key_bindings[keystroke] = target
163
+ end
164
+
165
+ def reset_oneshot_key_bindings
166
+ @oneshot_key_bindings.clear
153
167
  end
154
168
 
155
169
  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
 
@@ -251,6 +251,7 @@ class Reline::LineEditor
251
251
  @in_pasting = false
252
252
  @auto_indent_proc = nil
253
253
  @dialogs = []
254
+ @last_key = nil
254
255
  reset_line
255
256
  end
256
257
 
@@ -512,6 +513,14 @@ class Reline::LineEditor
512
513
  @cursor_pos.y = row
513
514
  end
514
515
 
516
+ def set_key(key)
517
+ @key = key
518
+ end
519
+
520
+ def key
521
+ @key
522
+ end
523
+
515
524
  def cursor_pos
516
525
  @cursor_pos
517
526
  end
@@ -538,12 +547,14 @@ class Reline::LineEditor
538
547
  end
539
548
 
540
549
  class Dialog
541
- attr_reader :name
542
- attr_accessor :scroll_top, :column, :vertical_offset, :contents, :lines_backup
550
+ attr_reader :name, :contents, :width
551
+ attr_accessor :scroll_top, :column, :vertical_offset, :lines_backup, :trap_key
543
552
 
544
- def initialize(name, proc_scope)
553
+ def initialize(name, config, proc_scope)
545
554
  @name = name
555
+ @config = config
546
556
  @proc_scope = proc_scope
557
+ @width = nil
547
558
  @scroll_top = 0
548
559
  end
549
560
 
@@ -551,54 +562,61 @@ class Reline::LineEditor
551
562
  @proc_scope.set_cursor_pos(col, row)
552
563
  end
553
564
 
554
- def call
565
+ def width=(v)
566
+ @width = v
567
+ end
568
+
569
+ def contents=(contents)
570
+ @contents = contents
571
+ if contents and @width.nil?
572
+ @width = contents.map{ |line| Reline::Unicode.calculate_width(line, true) }.max
573
+ end
574
+ end
575
+
576
+ def call(key)
555
577
  @proc_scope.set_dialog(self)
556
- @proc_scope.call
578
+ @proc_scope.set_key(key)
579
+ dialog_render_info = @proc_scope.call
580
+ if @trap_key
581
+ if @trap_key.any?{ |i| i.is_a?(Array) } # multiple trap
582
+ @trap_key.each do |t|
583
+ @config.add_oneshot_key_binding(t, @name)
584
+ end
585
+ elsif @trap_key.is_a?(Array)
586
+ @config.add_oneshot_key_binding(@trap_key, @name)
587
+ elsif @trap_key.is_a?(Integer) or @trap_key.is_a?(Reline::Key)
588
+ @config.add_oneshot_key_binding([@trap_key], @name)
589
+ end
590
+ end
591
+ dialog_render_info
557
592
  end
558
593
  end
559
594
 
560
595
  def add_dialog_proc(name, p, context = nil)
561
596
  return if @dialogs.any? { |d| d.name == name }
562
- @dialogs << Dialog.new(name, DialogProcScope.new(self, @config, p, context))
597
+ @dialogs << Dialog.new(name, @config, DialogProcScope.new(self, @config, p, context))
563
598
  end
564
599
 
565
600
  DIALOG_HEIGHT = 20
566
- DIALOG_WIDTH = 40
567
601
  private def render_dialog(cursor_column)
568
602
  @dialogs.each do |dialog|
569
603
  render_each_dialog(dialog, cursor_column)
570
604
  end
571
605
  end
572
606
 
607
+ private def padding_space_with_escape_sequences(str, width)
608
+ str + (' ' * (width - calculate_width(str, true)))
609
+ end
610
+
573
611
  private def render_each_dialog(dialog, cursor_column)
574
612
  if @in_pasting
575
613
  dialog.contents = nil
614
+ dialog.trap_key = nil
576
615
  return
577
616
  end
578
617
  dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from)
579
- dialog_render_info = dialog.call
580
- old_dialog_contents = dialog.contents
581
- old_dialog_column = dialog.column
582
- old_dialog_vertical_offset = dialog.vertical_offset
583
- start = 0
584
- if dialog_render_info and dialog_render_info.contents and not dialog_render_info.contents.empty?
585
- height = dialog_render_info.height || DIALOG_HEIGHT
586
- pointer = dialog_render_info.pointer
587
- dialog.contents = dialog_render_info.contents
588
- if dialog.contents.size > height
589
- if dialog_render_info.pointer
590
- if dialog_render_info.pointer < 0
591
- dialog.scroll_top = 0
592
- elsif (dialog_render_info.pointer - dialog.scroll_top) >= (height - 1)
593
- dialog.scroll_top = dialog_render_info.pointer - (height - 1)
594
- elsif (dialog_render_info.pointer - dialog.scroll_top) < 0
595
- dialog.scroll_top = dialog_render_info.pointer
596
- end
597
- pointer = dialog_render_info.pointer - dialog.scroll_top
598
- end
599
- dialog.contents = dialog.contents[dialog.scroll_top, height]
600
- end
601
- else
618
+ dialog_render_info = dialog.call(@last_key)
619
+ if dialog_render_info.nil? or dialog_render_info.contents.nil? or dialog_render_info.contents.empty?
602
620
  dialog.lines_backup = {
603
621
  lines: modify_lines(whole_lines),
604
622
  line_index: @line_index,
@@ -608,19 +626,43 @@ class Reline::LineEditor
608
626
  }
609
627
  clear_each_dialog(dialog)
610
628
  dialog.contents = nil
629
+ dialog.trap_key = nil
611
630
  return
612
631
  end
632
+ old_dialog = dialog.clone
633
+ dialog.contents = dialog_render_info.contents
634
+ pointer = dialog_render_info.pointer
635
+ if dialog_render_info.width
636
+ dialog.width = dialog_render_info.width
637
+ else
638
+ dialog.width = dialog.contents.map { |l| calculate_width(l, true) }.max
639
+ end
640
+ height = dialog_render_info.height || DIALOG_HEIGHT
641
+ height = dialog.contents.size if dialog.contents.size < height
642
+ if dialog.contents.size > height
643
+ if dialog_render_info.pointer
644
+ if dialog_render_info.pointer < 0
645
+ dialog.scroll_top = 0
646
+ elsif (dialog_render_info.pointer - dialog.scroll_top) >= (height - 1)
647
+ dialog.scroll_top = dialog_render_info.pointer - (height - 1)
648
+ elsif (dialog_render_info.pointer - dialog.scroll_top) < 0
649
+ dialog.scroll_top = dialog_render_info.pointer
650
+ end
651
+ pointer = dialog_render_info.pointer - dialog.scroll_top
652
+ end
653
+ dialog.contents = dialog.contents[dialog.scroll_top, height]
654
+ end
613
655
  upper_space = @first_line_started_from - @started_from
614
656
  lower_space = @highest_in_all - @first_line_started_from - @started_from - 1
615
657
  dialog.column = dialog_render_info.pos.x
616
- diff = (dialog.column + DIALOG_WIDTH) - (@screen_size.last - 1)
658
+ diff = (dialog.column + dialog.width) - (@screen_size.last - 1)
617
659
  if diff > 0
618
660
  dialog.column -= diff
619
661
  end
620
662
  if (lower_space + @rest_height - dialog_render_info.pos.y) >= height
621
663
  dialog.vertical_offset = dialog_render_info.pos.y + 1
622
664
  elsif upper_space >= height
623
- dialog.vertical_offset = dialog_render_info.pos.y + -(height + 1)
665
+ dialog.vertical_offset = dialog_render_info.pos.y - height
624
666
  else
625
667
  if (lower_space + @rest_height - dialog_render_info.pos.y) < height
626
668
  scroll_down(height + dialog_render_info.pos.y)
@@ -629,9 +671,16 @@ class Reline::LineEditor
629
671
  dialog.vertical_offset = dialog_render_info.pos.y + 1
630
672
  end
631
673
  Reline::IOGate.hide_cursor
632
- reset_dialog(dialog, old_dialog_contents, old_dialog_column, old_dialog_vertical_offset)
674
+ reset_dialog(dialog, old_dialog)
633
675
  move_cursor_down(dialog.vertical_offset)
634
676
  Reline::IOGate.move_cursor_column(dialog.column)
677
+ if dialog_render_info.scrollbar and dialog_render_info.contents.size > height
678
+ bar_max_height = height * 2
679
+ moving_distance = (dialog_render_info.contents.size - height) * 2
680
+ position_ratio = dialog.scroll_top.zero? ? 0.0 : ((dialog.scroll_top * 2).to_f / moving_distance)
681
+ bar_height = (bar_max_height * ((dialog.contents.size * 2).to_f / (dialog_render_info.contents.size * 2))).floor.to_i
682
+ position = ((bar_max_height - bar_height) * position_ratio).floor.to_i
683
+ end
635
684
  dialog.contents.each_with_index do |item, i|
636
685
  if i == pointer
637
686
  bg_color = '45'
@@ -642,10 +691,27 @@ class Reline::LineEditor
642
691
  bg_color = '46'
643
692
  end
644
693
  end
645
- @output.write "\e[#{bg_color}m%-#{DIALOG_WIDTH}s\e[49m" % item.slice(0, DIALOG_WIDTH)
694
+ str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, dialog.width), dialog.width)
695
+ @output.write "\e[#{bg_color}m#{str}"
696
+ if dialog_render_info.scrollbar and dialog_render_info.contents.size > height
697
+ @output.write "\e[37m"
698
+ if position <= (i * 2) and (i * 2 + 1) < (position + bar_height)
699
+ @output.write '█'
700
+ elsif position <= (i * 2) and (i * 2) < (position + bar_height)
701
+ @output.write '▀'
702
+ str += ''
703
+ elsif position <= (i * 2 + 1) and (i * 2) < (position + bar_height)
704
+ @output.write '▄'
705
+ else
706
+ @output.write ' '
707
+ end
708
+ @output.write "\e[39m"
709
+ end
710
+ @output.write "\e[49m"
646
711
  Reline::IOGate.move_cursor_column(dialog.column)
647
712
  move_cursor_down(1) if i < (dialog.contents.size - 1)
648
713
  end
714
+ dialog.width += 1 if dialog_render_info.scrollbar and dialog_render_info.contents.size > height
649
715
  Reline::IOGate.move_cursor_column(cursor_column)
650
716
  move_cursor_up(dialog.vertical_offset + dialog.contents.size - 1)
651
717
  Reline::IOGate.show_cursor
@@ -658,8 +724,8 @@ class Reline::LineEditor
658
724
  }
659
725
  end
660
726
 
661
- private def reset_dialog(dialog, old_dialog_contents, old_dialog_column, old_dialog_vertical_offset)
662
- return if dialog.lines_backup.nil? or old_dialog_contents.nil?
727
+ private def reset_dialog(dialog, old_dialog)
728
+ return if dialog.lines_backup.nil? or old_dialog.contents.nil?
663
729
  prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
664
730
  visual_lines = []
665
731
  visual_start = nil
@@ -675,76 +741,80 @@ class Reline::LineEditor
675
741
  old_y = dialog.lines_backup[:first_line_started_from] + dialog.lines_backup[:started_from]
676
742
  y = @first_line_started_from + @started_from
677
743
  y_diff = y - old_y
678
- if (old_y + old_dialog_vertical_offset) < (y + dialog.vertical_offset)
744
+ if (old_y + old_dialog.vertical_offset) < (y + dialog.vertical_offset)
679
745
  # rerender top
680
- move_cursor_down(old_dialog_vertical_offset - y_diff)
681
- start = visual_start + old_dialog_vertical_offset
682
- line_num = dialog.vertical_offset - old_dialog_vertical_offset
746
+ move_cursor_down(old_dialog.vertical_offset - y_diff)
747
+ start = visual_start + old_dialog.vertical_offset
748
+ line_num = dialog.vertical_offset - old_dialog.vertical_offset
683
749
  line_num.times do |i|
684
- Reline::IOGate.move_cursor_column(old_dialog_column)
750
+ Reline::IOGate.move_cursor_column(old_dialog.column)
685
751
  if visual_lines[start + i].nil?
686
- s = ' ' * DIALOG_WIDTH
752
+ s = ' ' * dialog.width
687
753
  else
688
- s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, DIALOG_WIDTH)
754
+ s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, dialog.width)
755
+ s = padding_space_with_escape_sequences(s, dialog.width)
689
756
  end
690
- @output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % s
757
+ @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
691
758
  move_cursor_down(1) if i < (line_num - 1)
692
759
  end
693
- move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
760
+ move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
694
761
  end
695
- if (old_y + old_dialog_vertical_offset + old_dialog_contents.size) > (y + dialog.vertical_offset + dialog.contents.size)
762
+ if (old_y + old_dialog.vertical_offset + old_dialog.contents.size) > (y + dialog.vertical_offset + dialog.contents.size)
696
763
  # rerender bottom
697
764
  move_cursor_down(dialog.vertical_offset + dialog.contents.size - y_diff)
698
765
  start = visual_start + dialog.vertical_offset + dialog.contents.size
699
- line_num = (old_dialog_vertical_offset + old_dialog_contents.size) - (dialog.vertical_offset + dialog.contents.size)
766
+ line_num = (old_dialog.vertical_offset + old_dialog.contents.size) - (dialog.vertical_offset + dialog.contents.size)
700
767
  line_num.times do |i|
701
- Reline::IOGate.move_cursor_column(old_dialog_column)
768
+ Reline::IOGate.move_cursor_column(old_dialog.column)
702
769
  if visual_lines[start + i].nil?
703
- s = ' ' * DIALOG_WIDTH
770
+ s = ' ' * dialog.width
704
771
  else
705
- s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, DIALOG_WIDTH)
772
+ s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, dialog.width)
773
+ s = padding_space_with_escape_sequences(s, dialog.width)
706
774
  end
707
- @output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % s
775
+ @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
708
776
  move_cursor_down(1) if i < (line_num - 1)
709
777
  end
710
778
  move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
711
779
  end
712
- if old_dialog_column < dialog.column
780
+ if old_dialog.column < dialog.column
713
781
  # rerender left
714
- move_cursor_down(old_dialog_vertical_offset - y_diff)
715
- width = dialog.column - old_dialog_column
716
- start = visual_start + old_dialog_vertical_offset
717
- line_num = old_dialog_contents.size
782
+ move_cursor_down(old_dialog.vertical_offset - y_diff)
783
+ width = dialog.column - old_dialog.column
784
+ start = visual_start + old_dialog.vertical_offset
785
+ line_num = old_dialog.contents.size
718
786
  line_num.times do |i|
719
- Reline::IOGate.move_cursor_column(old_dialog_column)
787
+ Reline::IOGate.move_cursor_column(old_dialog.column)
720
788
  if visual_lines[start + i].nil?
721
789
  s = ' ' * width
722
790
  else
723
- s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, width)
791
+ s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, width)
792
+ s = padding_space_with_escape_sequences(s, dialog.width)
724
793
  end
725
- @output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
794
+ @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
726
795
  move_cursor_down(1) if i < (line_num - 1)
727
796
  end
728
- move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
797
+ move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
729
798
  end
730
- if (old_dialog_column + DIALOG_WIDTH) > (dialog.column + DIALOG_WIDTH)
799
+ if (old_dialog.column + old_dialog.width) > (dialog.column + dialog.width)
731
800
  # rerender right
732
- move_cursor_down(old_dialog_vertical_offset + y_diff)
733
- width = (old_dialog_column + DIALOG_WIDTH) - (dialog.column + DIALOG_WIDTH)
734
- start = visual_start + old_dialog_vertical_offset
735
- line_num = old_dialog_contents.size
801
+ move_cursor_down(old_dialog.vertical_offset + y_diff)
802
+ width = (old_dialog.column + old_dialog.width) - (dialog.column + dialog.width)
803
+ start = visual_start + old_dialog.vertical_offset
804
+ line_num = old_dialog.contents.size
736
805
  line_num.times do |i|
737
- Reline::IOGate.move_cursor_column(old_dialog_column + DIALOG_WIDTH)
806
+ Reline::IOGate.move_cursor_column(old_dialog.column + dialog.width)
738
807
  if visual_lines[start + i].nil?
739
808
  s = ' ' * width
740
809
  else
741
- s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column + DIALOG_WIDTH, width)
810
+ s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column + dialog.width, width)
811
+ s = padding_space_with_escape_sequences(s, dialog.width)
742
812
  end
743
- Reline::IOGate.move_cursor_column(dialog.column + DIALOG_WIDTH)
744
- @output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
813
+ Reline::IOGate.move_cursor_column(dialog.column + dialog.width)
814
+ @output.write "\e[39m\e[49m#{s}\e[39m\e[49m"
745
815
  move_cursor_down(1) if i < (line_num - 1)
746
816
  end
747
- move_cursor_up(old_dialog_vertical_offset + line_num - 1 + y_diff)
817
+ move_cursor_up(old_dialog.vertical_offset + line_num - 1 + y_diff)
748
818
  end
749
819
  Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
750
820
  end
@@ -756,6 +826,7 @@ class Reline::LineEditor
756
826
  end
757
827
 
758
828
  private def clear_each_dialog(dialog)
829
+ dialog.trap_key = nil
759
830
  return unless dialog.contents
760
831
  prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
761
832
  visual_lines = []
@@ -777,13 +848,14 @@ class Reline::LineEditor
777
848
  dialog_vertical_size = dialog.contents.size
778
849
  dialog_vertical_size.times do |i|
779
850
  if i < visual_lines_under_dialog.size
780
- Reline::IOGate.move_cursor_column(0)
781
- @output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % visual_lines_under_dialog[i]
851
+ Reline::IOGate.move_cursor_column(dialog.column)
852
+ str = Reline::Unicode.take_range(visual_lines_under_dialog[i], dialog.column, dialog.width)
853
+ str = padding_space_with_escape_sequences(str, dialog.width)
854
+ @output.write "\e[39m\e[49m#{str}\e[39m\e[49m"
782
855
  else
783
856
  Reline::IOGate.move_cursor_column(dialog.column)
784
- @output.write "\e[39m\e[49m#{' ' * DIALOG_WIDTH}\e[39m\e[49m"
857
+ @output.write "\e[39m\e[49m#{' ' * dialog.width}\e[39m\e[49m"
785
858
  end
786
- Reline::IOGate.erase_after_cursor
787
859
  move_cursor_down(1) if i < (dialog_vertical_size - 1)
788
860
  end
789
861
  move_cursor_up(dialog_vertical_size - 1 + dialog.vertical_offset)
@@ -1277,8 +1349,10 @@ class Reline::LineEditor
1277
1349
  end
1278
1350
  end
1279
1351
  completed = @completion_journey_data.list[@completion_journey_data.pointer]
1280
- @line = @completion_journey_data.preposing + completed + @completion_journey_data.postposing
1281
- line_to_pointer = @completion_journey_data.preposing + completed
1352
+ new_line = (@completion_journey_data.preposing + completed + @completion_journey_data.postposing).split("\n")[@line_index]
1353
+ @line = new_line.nil? ? String.new(encoding: @encoding) : new_line
1354
+ line_to_pointer = (@completion_journey_data.preposing + completed).split("\n").last
1355
+ line_to_pointer = String.new(encoding: @encoding) if line_to_pointer.nil?
1282
1356
  @cursor_max = calculate_width(@line)
1283
1357
  @cursor = calculate_width(line_to_pointer)
1284
1358
  @byte_pointer = line_to_pointer.bytesize
@@ -1439,6 +1513,13 @@ class Reline::LineEditor
1439
1513
  end
1440
1514
 
1441
1515
  def input_key(key)
1516
+ @last_key = key
1517
+ @config.reset_oneshot_key_bindings
1518
+ @dialogs.each do |dialog|
1519
+ if key.char.instance_of?(Symbol) and key.char == dialog.name
1520
+ return
1521
+ end
1522
+ end
1442
1523
  @just_cursor_moving = nil
1443
1524
  if key.char.nil?
1444
1525
  if @first_char