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 +4 -4
- data/lib/reline/config.rb +20 -3
- data/lib/reline/general_io.rb +1 -0
- data/lib/reline/key_stroke.rb +56 -4
- data/lib/reline/line_editor.rb +131 -45
- data/lib/reline/unicode.rb +3 -3
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +2 -0
- data/lib/reline.rb +61 -26
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b41b69340a1422f198eda02baddc77ce7ff82ed3ba1f61a8101d179e7700da23
|
4
|
+
data.tar.gz: a396bceef149a9436aba896305172bb68925e2302cb7a2b1074d3c2a0ebb300a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 ||=
|
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
|
-
#
|
152
|
-
@key_actors[@editing_mode_label].default_key_bindings.
|
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)
|
data/lib/reline/general_io.rb
CHANGED
data/lib/reline/key_stroke.rb
CHANGED
@@ -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
|
-
|
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
|
23
|
-
return :matching if it.size == 1 && (it
|
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
|
-
|
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
|
|
data/lib/reline/line_editor.rb
CHANGED
@@ -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(
|
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('
|
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.
|
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
|
-
|
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
|
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
|
-
|
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 = ' ' *
|
767
|
+
s = ' ' * old_dialog.width
|
697
768
|
else
|
698
|
-
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column,
|
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
|
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 = ' ' *
|
785
|
+
s = ' ' * old_dialog.width
|
714
786
|
else
|
715
|
-
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column,
|
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
|
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
|
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
|
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(
|
791
|
-
|
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(
|
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
|
-
|
1291
|
-
|
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
|
data/lib/reline/unicode.rb
CHANGED
@@ -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
|
data/lib/reline/version.rb
CHANGED
data/lib/reline/windows.rb
CHANGED
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
|
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
|
-
|
366
|
-
|
367
|
-
|
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.
|
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-
|
11
|
+
date: 2021-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: io-console
|