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