reline 0.2.8.pre.11 → 0.3.0
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/ansi.rb +43 -21
- data/lib/reline/config.rb +5 -0
- data/lib/reline/general_io.rb +2 -1
- data/lib/reline/key_stroke.rb +1 -0
- data/lib/reline/line_editor.rb +64 -48
- data/lib/reline/terminfo.rb +11 -3
- data/lib/reline/unicode.rb +9 -1
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +115 -46
- data/lib/reline.rb +17 -6
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e8c2181e5eb21934546d6b768715cb47eab6e1ea1edb2fd2300f43af62dc2b1
|
4
|
+
data.tar.gz: 99a61e2729d4b5d150f6f59d99d6de235ea4f1aab6ac262c92fff6a77101b85f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96ceefe0bf71e7e44ccd2a5970b35fc44019af9d7da4d2e04ac1d827eb6bc6a201e679a5e40c4db976554159286f16e39cdeac68bcf69c2c5abeb00a2045e563
|
7
|
+
data.tar.gz: 251c2029df6b3073010ad6a591fb216990124ed4f9e00f533e79461be66c6a2383ea56c1554dd273e4af3cfc9358dee37b76a1b4bff25a1867972201bfebace3
|
data/lib/reline/ansi.rb
CHANGED
@@ -4,6 +4,19 @@ require 'timeout'
|
|
4
4
|
require_relative 'terminfo'
|
5
5
|
|
6
6
|
class Reline::ANSI
|
7
|
+
CAPNAME_KEY_BINDINGS = {
|
8
|
+
'khome' => :ed_move_to_beg,
|
9
|
+
'kend' => :ed_move_to_end,
|
10
|
+
'kcuu1' => :ed_prev_history,
|
11
|
+
'kcud1' => :ed_next_history,
|
12
|
+
'kcuf1' => :ed_next_char,
|
13
|
+
'kcub1' => :ed_prev_char,
|
14
|
+
'cuu' => :ed_prev_history,
|
15
|
+
'cud' => :ed_next_history,
|
16
|
+
'cuf' => :ed_next_char,
|
17
|
+
'cub' => :ed_prev_char,
|
18
|
+
}
|
19
|
+
|
7
20
|
if Reline::Terminfo.enabled?
|
8
21
|
Reline::Terminfo.setupterm(0, 2)
|
9
22
|
end
|
@@ -49,20 +62,23 @@ class Reline::ANSI
|
|
49
62
|
end
|
50
63
|
|
51
64
|
def self.set_default_key_bindings_terminfo(config)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
Reline::Terminfo
|
64
|
-
|
65
|
-
|
65
|
+
key_bindings = CAPNAME_KEY_BINDINGS.map do |capname, key_binding|
|
66
|
+
begin
|
67
|
+
key_code = Reline::Terminfo.tigetstr(capname)
|
68
|
+
case capname
|
69
|
+
# Escape sequences that omit the move distance and are set to defaults
|
70
|
+
# value 1 may be sometimes sent by pressing the arrow-key.
|
71
|
+
when 'cuu', 'cud', 'cuf', 'cub'
|
72
|
+
[ key_code.sub(/%p1%d/, '').bytes, key_binding ]
|
73
|
+
else
|
74
|
+
[ key_code.bytes, key_binding ]
|
75
|
+
end
|
76
|
+
rescue Reline::Terminfo::TerminfoError
|
77
|
+
# capname is undefined
|
78
|
+
end
|
79
|
+
end.compact.to_h
|
80
|
+
|
81
|
+
key_bindings.each_pair do |key, func|
|
66
82
|
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
67
83
|
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
68
84
|
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
@@ -131,7 +147,7 @@ class Reline::ANSI
|
|
131
147
|
unless @@buf.empty?
|
132
148
|
return @@buf.shift
|
133
149
|
end
|
134
|
-
until c = @@input.raw(intr: true) {
|
150
|
+
until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte }
|
135
151
|
Reline.core.line_editor.resize
|
136
152
|
end
|
137
153
|
(c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
|
@@ -266,7 +282,7 @@ class Reline::ANSI
|
|
266
282
|
|
267
283
|
def self.move_cursor_up(x)
|
268
284
|
if x > 0
|
269
|
-
@@output.write "\e[#{x}A"
|
285
|
+
@@output.write "\e[#{x}A"
|
270
286
|
elsif x < 0
|
271
287
|
move_cursor_down(-x)
|
272
288
|
end
|
@@ -274,7 +290,7 @@ class Reline::ANSI
|
|
274
290
|
|
275
291
|
def self.move_cursor_down(x)
|
276
292
|
if x > 0
|
277
|
-
@@output.write "\e[#{x}B"
|
293
|
+
@@output.write "\e[#{x}B"
|
278
294
|
elsif x < 0
|
279
295
|
move_cursor_up(-x)
|
280
296
|
end
|
@@ -282,7 +298,11 @@ class Reline::ANSI
|
|
282
298
|
|
283
299
|
def self.hide_cursor
|
284
300
|
if Reline::Terminfo.enabled?
|
285
|
-
|
301
|
+
begin
|
302
|
+
@@output.write Reline::Terminfo.tigetstr('civis')
|
303
|
+
rescue Reline::Terminfo::TerminfoError
|
304
|
+
# civis is undefined
|
305
|
+
end
|
286
306
|
else
|
287
307
|
# ignored
|
288
308
|
end
|
@@ -290,7 +310,11 @@ class Reline::ANSI
|
|
290
310
|
|
291
311
|
def self.show_cursor
|
292
312
|
if Reline::Terminfo.enabled?
|
293
|
-
|
313
|
+
begin
|
314
|
+
@@output.write Reline::Terminfo.tigetstr('cnorm')
|
315
|
+
rescue Reline::Terminfo::TerminfoError
|
316
|
+
# cnorm is undefined
|
317
|
+
end
|
294
318
|
else
|
295
319
|
# ignored
|
296
320
|
end
|
@@ -321,8 +345,6 @@ class Reline::ANSI
|
|
321
345
|
end
|
322
346
|
|
323
347
|
def self.deprep(otio)
|
324
|
-
int_handle = Signal.trap('INT', 'IGNORE')
|
325
|
-
Signal.trap('INT', int_handle)
|
326
348
|
Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
|
327
349
|
end
|
328
350
|
end
|
data/lib/reline/config.rb
CHANGED
@@ -67,6 +67,7 @@ class Reline::Config
|
|
67
67
|
@keyseq_timeout = 500
|
68
68
|
@test_mode = false
|
69
69
|
@autocompletion = false
|
70
|
+
@convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
|
70
71
|
end
|
71
72
|
|
72
73
|
def reset
|
@@ -387,4 +388,8 @@ class Reline::Config
|
|
387
388
|
end
|
388
389
|
ret
|
389
390
|
end
|
391
|
+
|
392
|
+
private def seven_bit_encoding?(encoding)
|
393
|
+
encoding == Encoding::US_ASCII
|
394
|
+
end
|
390
395
|
end
|
data/lib/reline/general_io.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'timeout'
|
2
|
+
require 'io/wait'
|
2
3
|
|
3
4
|
class Reline::GeneralIO
|
4
5
|
def self.reset(encoding: nil)
|
@@ -36,7 +37,7 @@ class Reline::GeneralIO
|
|
36
37
|
end
|
37
38
|
c = nil
|
38
39
|
loop do
|
39
|
-
result =
|
40
|
+
result = @@input.wait_readable(0.1)
|
40
41
|
next if result.nil?
|
41
42
|
c = @@input.read(1)
|
42
43
|
break
|
data/lib/reline/key_stroke.rb
CHANGED
data/lib/reline/line_editor.rb
CHANGED
@@ -93,7 +93,7 @@ class Reline::LineEditor
|
|
93
93
|
mode_string
|
94
94
|
end
|
95
95
|
|
96
|
-
private def check_multiline_prompt(buffer
|
96
|
+
private def check_multiline_prompt(buffer)
|
97
97
|
if @vi_arg
|
98
98
|
prompt = "(arg: #{@vi_arg}) "
|
99
99
|
@rerender_all = true
|
@@ -121,7 +121,7 @@ class Reline::LineEditor
|
|
121
121
|
if use_cached_prompt_list
|
122
122
|
prompt_list = @cached_prompt_list
|
123
123
|
else
|
124
|
-
prompt_list = @cached_prompt_list = @prompt_proc.(buffer)
|
124
|
+
prompt_list = @cached_prompt_list = @prompt_proc.(buffer).map { |pr| pr.gsub("\n", "\\n") }
|
125
125
|
@prompt_cache_time = Time.now.to_f
|
126
126
|
end
|
127
127
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
@@ -151,33 +151,6 @@ class Reline::LineEditor
|
|
151
151
|
@screen_size = Reline::IOGate.get_screen_size
|
152
152
|
@screen_height = @screen_size.first
|
153
153
|
reset_variables(prompt, encoding: encoding)
|
154
|
-
@old_trap = Signal.trap('INT') {
|
155
|
-
clear_dialog
|
156
|
-
if @scroll_partial_screen
|
157
|
-
move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
|
158
|
-
else
|
159
|
-
move_cursor_down(@highest_in_all - @line_index - 1)
|
160
|
-
end
|
161
|
-
Reline::IOGate.move_cursor_column(0)
|
162
|
-
scroll_down(1)
|
163
|
-
case @old_trap
|
164
|
-
when 'DEFAULT', 'SYSTEM_DEFAULT'
|
165
|
-
raise Interrupt
|
166
|
-
when 'IGNORE'
|
167
|
-
# Do nothing
|
168
|
-
when 'EXIT'
|
169
|
-
exit
|
170
|
-
else
|
171
|
-
@old_trap.call
|
172
|
-
end
|
173
|
-
}
|
174
|
-
begin
|
175
|
-
@old_tstp_trap = Signal.trap('TSTP') {
|
176
|
-
Reline::IOGate.ungetc("\C-z".ord)
|
177
|
-
@old_tstp_trap.call if @old_tstp_trap.respond_to?(:call)
|
178
|
-
}
|
179
|
-
rescue ArgumentError
|
180
|
-
end
|
181
154
|
Reline::IOGate.set_winch_handler do
|
182
155
|
@resized = true
|
183
156
|
end
|
@@ -217,7 +190,7 @@ class Reline::LineEditor
|
|
217
190
|
else
|
218
191
|
back = 0
|
219
192
|
new_buffer = whole_lines
|
220
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer
|
193
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer)
|
221
194
|
new_buffer.each_with_index do |line, index|
|
222
195
|
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
223
196
|
width = prompt_width + calculate_width(line)
|
@@ -244,6 +217,36 @@ class Reline::LineEditor
|
|
244
217
|
end
|
245
218
|
end
|
246
219
|
|
220
|
+
def set_signal_handlers
|
221
|
+
@old_trap = Signal.trap('INT') {
|
222
|
+
clear_dialog
|
223
|
+
if @scroll_partial_screen
|
224
|
+
move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
|
225
|
+
else
|
226
|
+
move_cursor_down(@highest_in_all - @line_index - 1)
|
227
|
+
end
|
228
|
+
Reline::IOGate.move_cursor_column(0)
|
229
|
+
scroll_down(1)
|
230
|
+
case @old_trap
|
231
|
+
when 'DEFAULT', 'SYSTEM_DEFAULT'
|
232
|
+
raise Interrupt
|
233
|
+
when 'IGNORE'
|
234
|
+
# Do nothing
|
235
|
+
when 'EXIT'
|
236
|
+
exit
|
237
|
+
else
|
238
|
+
@old_trap.call if @old_trap.respond_to?(:call)
|
239
|
+
end
|
240
|
+
}
|
241
|
+
begin
|
242
|
+
@old_tstp_trap = Signal.trap('TSTP') {
|
243
|
+
Reline::IOGate.ungetc("\C-z".ord)
|
244
|
+
@old_tstp_trap.call if @old_tstp_trap.respond_to?(:call)
|
245
|
+
}
|
246
|
+
rescue ArgumentError
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
247
250
|
def finalize
|
248
251
|
Signal.trap('INT', @old_trap)
|
249
252
|
begin
|
@@ -257,7 +260,7 @@ class Reline::LineEditor
|
|
257
260
|
end
|
258
261
|
|
259
262
|
def reset_variables(prompt = '', encoding:)
|
260
|
-
@prompt = prompt
|
263
|
+
@prompt = prompt.gsub("\n", "\\n")
|
261
264
|
@mark_pointer = nil
|
262
265
|
@encoding = encoding
|
263
266
|
@is_multiline = false
|
@@ -435,7 +438,7 @@ class Reline::LineEditor
|
|
435
438
|
show_menu
|
436
439
|
@menu_info = nil
|
437
440
|
end
|
438
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines
|
441
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
|
439
442
|
if @cleared
|
440
443
|
clear_screen_buffer(prompt, prompt_list, prompt_width)
|
441
444
|
@cleared = false
|
@@ -446,7 +449,7 @@ class Reline::LineEditor
|
|
446
449
|
Reline::IOGate.move_cursor_up(@first_line_started_from + @started_from - @scroll_partial_screen)
|
447
450
|
Reline::IOGate.move_cursor_column(0)
|
448
451
|
@scroll_partial_screen = nil
|
449
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines
|
452
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
|
450
453
|
if @previous_line_index
|
451
454
|
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
452
455
|
else
|
@@ -492,7 +495,7 @@ class Reline::LineEditor
|
|
492
495
|
end
|
493
496
|
line = modify_lines(new_lines)[@line_index]
|
494
497
|
clear_dialog
|
495
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines
|
498
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
|
496
499
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
497
500
|
move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
|
498
501
|
scroll_down(1)
|
@@ -501,7 +504,7 @@ class Reline::LineEditor
|
|
501
504
|
else
|
502
505
|
if not rendered and not @in_pasting
|
503
506
|
line = modify_lines(whole_lines)[@line_index]
|
504
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines
|
507
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
|
505
508
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
506
509
|
end
|
507
510
|
render_dialog((prompt_width + @cursor) % @screen_size.last)
|
@@ -634,8 +637,12 @@ class Reline::LineEditor
|
|
634
637
|
end
|
635
638
|
|
636
639
|
def add_dialog_proc(name, p, context = nil)
|
637
|
-
|
638
|
-
@dialogs
|
640
|
+
dialog = Dialog.new(name, @config, DialogProcScope.new(self, @config, p, context))
|
641
|
+
if index = @dialogs.find_index { |d| d.name == name }
|
642
|
+
@dialogs[index] = dialog
|
643
|
+
else
|
644
|
+
@dialogs << dialog
|
645
|
+
end
|
639
646
|
end
|
640
647
|
|
641
648
|
DIALOG_DEFAULT_HEIGHT = 20
|
@@ -651,6 +658,7 @@ class Reline::LineEditor
|
|
651
658
|
|
652
659
|
private def render_each_dialog(dialog, cursor_column)
|
653
660
|
if @in_pasting
|
661
|
+
clear_each_dialog(dialog)
|
654
662
|
dialog.contents = nil
|
655
663
|
dialog.trap_key = nil
|
656
664
|
return
|
@@ -706,19 +714,18 @@ class Reline::LineEditor
|
|
706
714
|
dialog.scrollbar_pos = nil
|
707
715
|
end
|
708
716
|
upper_space = @first_line_started_from - @started_from
|
709
|
-
lower_space = @highest_in_all - @first_line_started_from - @started_from - 1
|
710
717
|
dialog.column = dialog_render_info.pos.x
|
711
718
|
dialog.width += @block_elem_width if dialog.scrollbar_pos
|
712
719
|
diff = (dialog.column + dialog.width) - (@screen_size.last)
|
713
720
|
if diff > 0
|
714
721
|
dialog.column -= diff
|
715
722
|
end
|
716
|
-
if (
|
723
|
+
if (@rest_height - dialog_render_info.pos.y) >= height
|
717
724
|
dialog.vertical_offset = dialog_render_info.pos.y + 1
|
718
725
|
elsif upper_space >= height
|
719
726
|
dialog.vertical_offset = dialog_render_info.pos.y - height
|
720
727
|
else
|
721
|
-
if (
|
728
|
+
if (@rest_height - dialog_render_info.pos.y) < height
|
722
729
|
scroll_down(height + dialog_render_info.pos.y)
|
723
730
|
move_cursor_up(height + dialog_render_info.pos.y)
|
724
731
|
end
|
@@ -776,7 +783,7 @@ class Reline::LineEditor
|
|
776
783
|
|
777
784
|
private def reset_dialog(dialog, old_dialog)
|
778
785
|
return if dialog.lines_backup.nil? or old_dialog.contents.nil?
|
779
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines]
|
786
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines])
|
780
787
|
visual_lines = []
|
781
788
|
visual_start = nil
|
782
789
|
dialog.lines_backup[:lines].each_with_index { |l, i|
|
@@ -858,7 +865,8 @@ class Reline::LineEditor
|
|
858
865
|
s = ' ' * width
|
859
866
|
else
|
860
867
|
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column + dialog.width, width)
|
861
|
-
|
868
|
+
rerender_width = old_dialog.width - dialog.width
|
869
|
+
s = padding_space_with_escape_sequences(s, rerender_width)
|
862
870
|
end
|
863
871
|
Reline::IOGate.move_cursor_column(dialog.column + dialog.width)
|
864
872
|
@output.write "\e[0m#{s}\e[0m"
|
@@ -878,7 +886,7 @@ class Reline::LineEditor
|
|
878
886
|
private def clear_each_dialog(dialog)
|
879
887
|
dialog.trap_key = nil
|
880
888
|
return unless dialog.contents
|
881
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines]
|
889
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines])
|
882
890
|
visual_lines = []
|
883
891
|
visual_lines_under_dialog = []
|
884
892
|
visual_start = nil
|
@@ -963,7 +971,7 @@ class Reline::LineEditor
|
|
963
971
|
end
|
964
972
|
|
965
973
|
def just_move_cursor
|
966
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines
|
974
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines)
|
967
975
|
move_cursor_up(@started_from)
|
968
976
|
new_first_line_started_from =
|
969
977
|
if @line_index.zero?
|
@@ -1000,7 +1008,7 @@ class Reline::LineEditor
|
|
1000
1008
|
else
|
1001
1009
|
new_lines = whole_lines
|
1002
1010
|
end
|
1003
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines
|
1011
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
|
1004
1012
|
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
1005
1013
|
diff = all_height - @highest_in_all
|
1006
1014
|
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
@@ -1047,7 +1055,7 @@ class Reline::LineEditor
|
|
1047
1055
|
Reline::IOGate.move_cursor_column(0)
|
1048
1056
|
back = 0
|
1049
1057
|
new_buffer = whole_lines
|
1050
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer
|
1058
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer)
|
1051
1059
|
new_buffer.each_with_index do |line, index|
|
1052
1060
|
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
1053
1061
|
width = prompt_width + calculate_width(line)
|
@@ -2021,8 +2029,16 @@ class Reline::LineEditor
|
|
2021
2029
|
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
2022
2030
|
@byte_pointer += bytesize
|
2023
2031
|
last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
|
2024
|
-
|
2025
|
-
|
2032
|
+
combined_char = last_mbchar + str
|
2033
|
+
if last_byte_size != 0 and combined_char.grapheme_clusters.size == 1
|
2034
|
+
# combined char
|
2035
|
+
last_mbchar_width = Reline::Unicode.get_mbchar_width(last_mbchar)
|
2036
|
+
combined_char_width = Reline::Unicode.get_mbchar_width(combined_char)
|
2037
|
+
if combined_char_width > last_mbchar_width
|
2038
|
+
width = combined_char_width - last_mbchar_width
|
2039
|
+
else
|
2040
|
+
width = 0
|
2041
|
+
end
|
2026
2042
|
end
|
2027
2043
|
@cursor += width
|
2028
2044
|
@cursor_max += width
|
data/lib/reline/terminfo.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
|
-
|
2
|
-
require 'fiddle
|
1
|
+
begin
|
2
|
+
require 'fiddle'
|
3
|
+
require 'fiddle/import'
|
4
|
+
rescue LoadError
|
5
|
+
module Reline::Terminfo
|
6
|
+
def self.curses_dl
|
7
|
+
false
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
3
11
|
|
4
12
|
module Reline::Terminfo
|
5
13
|
extend Fiddle::Importer
|
@@ -50,7 +58,7 @@ module Reline::Terminfo
|
|
50
58
|
@curses_dl = nil if @curses_dl == false
|
51
59
|
@curses_dl
|
52
60
|
end
|
53
|
-
end
|
61
|
+
end if not Reline.const_defined?(:Terminfo) or not Reline::Terminfo.respond_to?(:curses_dl)
|
54
62
|
|
55
63
|
module Reline::Terminfo
|
56
64
|
dlload curses_dl
|
data/lib/reline/unicode.rb
CHANGED
@@ -79,6 +79,8 @@ class Reline::Unicode
|
|
79
79
|
|
80
80
|
require 'reline/unicode/east_asian_width'
|
81
81
|
|
82
|
+
HalfwidthDakutenHandakuten = /[\u{FF9E}\u{FF9F}]/
|
83
|
+
|
82
84
|
MBCharWidthRE = /
|
83
85
|
(?<width_2_1>
|
84
86
|
[#{ EscapedChars.map {|c| "\\x%02x" % c.ord }.join }] (?# ^ + char, such as ^M, ^H, ^[, ...)
|
@@ -93,6 +95,12 @@ class Reline::Unicode
|
|
93
95
|
#{ EastAsianWidth::TYPE_H }
|
94
96
|
| #{ EastAsianWidth::TYPE_NA }
|
95
97
|
| #{ EastAsianWidth::TYPE_N }
|
98
|
+
)(?!#{ HalfwidthDakutenHandakuten })
|
99
|
+
| (?<width_2_3>
|
100
|
+
(?: #{ EastAsianWidth::TYPE_H }
|
101
|
+
| #{ EastAsianWidth::TYPE_NA }
|
102
|
+
| #{ EastAsianWidth::TYPE_N })
|
103
|
+
#{ HalfwidthDakutenHandakuten }
|
96
104
|
)
|
97
105
|
| (?<ambiguous_width>
|
98
106
|
#{EastAsianWidth::TYPE_A}
|
@@ -109,7 +117,7 @@ class Reline::Unicode
|
|
109
117
|
m = mbchar.encode(Encoding::UTF_8).match(MBCharWidthRE)
|
110
118
|
case
|
111
119
|
when m.nil? then 1 # TODO should be U+FFFD � REPLACEMENT CHARACTER
|
112
|
-
when m[:width_2_1], m[:width_2_2] then 2
|
120
|
+
when m[:width_2_1], m[:width_2_2], m[:width_2_3] then 2
|
113
121
|
when m[:width_3] then 3
|
114
122
|
when m[:width_0] then 0
|
115
123
|
when m[:width_1] then 1
|
data/lib/reline/version.rb
CHANGED
data/lib/reline/windows.rb
CHANGED
@@ -168,7 +168,9 @@ class Reline::Windows
|
|
168
168
|
@@input_buf = []
|
169
169
|
@@output_buf = []
|
170
170
|
|
171
|
-
|
171
|
+
@@output = STDOUT
|
172
|
+
|
173
|
+
def self.msys_tty?(io = @@hConsoleInputHandle)
|
172
174
|
# check if fd is a pipe
|
173
175
|
if @@GetFileType.call(io) != FILE_TYPE_PIPE
|
174
176
|
return false
|
@@ -213,8 +215,29 @@ class Reline::Windows
|
|
213
215
|
[ { control_keys: :SHIFT, virtual_key_code: VK_TAB }, [27, 91, 90] ],
|
214
216
|
]
|
215
217
|
|
218
|
+
@@hsg = nil
|
219
|
+
|
216
220
|
def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
217
221
|
|
222
|
+
# high-surrogate
|
223
|
+
if 0xD800 <= char_code and char_code <= 0xDBFF
|
224
|
+
@@hsg = char_code
|
225
|
+
return
|
226
|
+
end
|
227
|
+
# low-surrogate
|
228
|
+
if 0xDC00 <= char_code and char_code <= 0xDFFF
|
229
|
+
if @@hsg
|
230
|
+
char_code = 0x10000 + (@@hsg - 0xD800) * 0x400 + char_code - 0xDC00
|
231
|
+
@@hsg = nil
|
232
|
+
else
|
233
|
+
# no high-surrogate. ignored.
|
234
|
+
return
|
235
|
+
end
|
236
|
+
else
|
237
|
+
# ignore high-surrogate without low-surrogate if there
|
238
|
+
@@hsg = nil
|
239
|
+
end
|
240
|
+
|
218
241
|
key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state)
|
219
242
|
|
220
243
|
match = KEY_MAP.find { |args,| key.matches?(**args) }
|
@@ -233,27 +256,35 @@ class Reline::Windows
|
|
233
256
|
|
234
257
|
def self.check_input_event
|
235
258
|
num_of_events = 0.chr * 8
|
236
|
-
while @@output_buf.empty?
|
259
|
+
while @@output_buf.empty?
|
237
260
|
Reline.core.line_editor.resize
|
238
|
-
|
261
|
+
if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
|
262
|
+
# prevent for background consolemode change
|
263
|
+
@@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
|
264
|
+
next
|
265
|
+
end
|
239
266
|
next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack1('L') == 0
|
240
|
-
|
267
|
+
input_records = 0.chr * 20 * 80
|
241
268
|
read_event = 0.chr * 4
|
242
|
-
if @@ReadConsoleInputW.(@@hConsoleInputHandle,
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
269
|
+
if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_records, 80, read_event) != 0
|
270
|
+
read_events = read_event.unpack1('L')
|
271
|
+
0.upto(read_events) do |idx|
|
272
|
+
input_record = input_records[idx * 20, 20]
|
273
|
+
event = input_record[0, 2].unpack1('s*')
|
274
|
+
case event
|
275
|
+
when WINDOW_BUFFER_SIZE_EVENT
|
276
|
+
@@winch_handler.()
|
277
|
+
when KEY_EVENT
|
278
|
+
key_down = input_record[4, 4].unpack1('l*')
|
279
|
+
repeat_count = input_record[8, 2].unpack1('s*')
|
280
|
+
virtual_key_code = input_record[10, 2].unpack1('s*')
|
281
|
+
virtual_scan_code = input_record[12, 2].unpack1('s*')
|
282
|
+
char_code = input_record[14, 2].unpack1('S*')
|
283
|
+
control_key_state = input_record[16, 2].unpack1('S*')
|
284
|
+
is_key_down = key_down.zero? ? false : true
|
285
|
+
if is_key_down
|
286
|
+
process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
287
|
+
end
|
257
288
|
end
|
258
289
|
end
|
259
290
|
end
|
@@ -274,7 +305,7 @@ class Reline::Windows
|
|
274
305
|
end
|
275
306
|
|
276
307
|
def self.empty_buffer?
|
277
|
-
if not @@
|
308
|
+
if not @@output_buf.empty?
|
278
309
|
false
|
279
310
|
elsif @@kbhit.call == 0
|
280
311
|
true
|
@@ -283,17 +314,37 @@ class Reline::Windows
|
|
283
314
|
end
|
284
315
|
end
|
285
316
|
|
286
|
-
def self.
|
317
|
+
def self.get_console_screen_buffer_info
|
318
|
+
# CONSOLE_SCREEN_BUFFER_INFO
|
319
|
+
# [ 0,2] dwSize.X
|
320
|
+
# [ 2,2] dwSize.Y
|
321
|
+
# [ 4,2] dwCursorPositions.X
|
322
|
+
# [ 6,2] dwCursorPositions.Y
|
323
|
+
# [ 8,2] wAttributes
|
324
|
+
# [10,2] srWindow.Left
|
325
|
+
# [12,2] srWindow.Top
|
326
|
+
# [14,2] srWindow.Right
|
327
|
+
# [16,2] srWindow.Bottom
|
328
|
+
# [18,2] dwMaximumWindowSize.X
|
329
|
+
# [20,2] dwMaximumWindowSize.Y
|
287
330
|
csbi = 0.chr * 22
|
288
|
-
@@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
|
331
|
+
return if @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi) == 0
|
332
|
+
csbi
|
333
|
+
end
|
334
|
+
|
335
|
+
def self.get_screen_size
|
336
|
+
unless csbi = get_console_screen_buffer_info
|
337
|
+
return [1, 1]
|
338
|
+
end
|
289
339
|
csbi[0, 4].unpack('SS').reverse
|
290
340
|
end
|
291
341
|
|
292
342
|
def self.cursor_pos
|
293
|
-
csbi =
|
294
|
-
|
295
|
-
|
296
|
-
|
343
|
+
unless csbi = get_console_screen_buffer_info
|
344
|
+
return Reline::CursorPos.new(0, 0)
|
345
|
+
end
|
346
|
+
x = csbi[4, 2].unpack1('s')
|
347
|
+
y = csbi[6, 2].unpack1('s')
|
297
348
|
Reline::CursorPos.new(x, y)
|
298
349
|
end
|
299
350
|
|
@@ -313,6 +364,7 @@ class Reline::Windows
|
|
313
364
|
|
314
365
|
def self.move_cursor_down(val)
|
315
366
|
if val > 0
|
367
|
+
return unless csbi = get_console_screen_buffer_info
|
316
368
|
screen_height = get_screen_size.first
|
317
369
|
y = cursor_pos.y + val
|
318
370
|
y = screen_height - 1 if y > (screen_height - 1)
|
@@ -323,8 +375,7 @@ class Reline::Windows
|
|
323
375
|
end
|
324
376
|
|
325
377
|
def self.erase_after_cursor
|
326
|
-
csbi =
|
327
|
-
@@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
|
378
|
+
return unless csbi = get_console_screen_buffer_info
|
328
379
|
attributes = csbi[8, 2].unpack1('S')
|
329
380
|
cursor = csbi[4, 4].unpack1('L')
|
330
381
|
written = 0.chr * 4
|
@@ -333,27 +384,45 @@ class Reline::Windows
|
|
333
384
|
end
|
334
385
|
|
335
386
|
def self.scroll_down(val)
|
336
|
-
return if val
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
@@
|
387
|
+
return if val < 0
|
388
|
+
return unless csbi = get_console_screen_buffer_info
|
389
|
+
buffer_width, x, y, buffer_lines, attributes, window_left, window_top, window_bottom = csbi.unpack('ssssSssx2s')
|
390
|
+
screen_height = window_bottom - window_top + 1
|
391
|
+
val = screen_height if val > screen_height
|
392
|
+
|
393
|
+
if @@legacy_console || window_left != 0
|
394
|
+
# unless ENABLE_VIRTUAL_TERMINAL,
|
395
|
+
# if srWindow.Left != 0 then it's conhost.exe hosted console
|
396
|
+
# and puts "\n" causes horizontal scroll. its glitch.
|
397
|
+
# FYI irb write from culumn 1, so this gives no gain.
|
398
|
+
scroll_rectangle = [0, val, buffer_width, buffer_lines - val].pack('s4')
|
399
|
+
destination_origin = 0 # y * 65536 + x
|
400
|
+
fill = [' '.ord, attributes].pack('SS')
|
401
|
+
@@ScrollConsoleScreenBuffer.call(@@hConsoleHandle, scroll_rectangle, nil, destination_origin, fill)
|
402
|
+
else
|
403
|
+
origin_x = x + 1
|
404
|
+
origin_y = y - window_top + 1
|
405
|
+
@@output.write [
|
406
|
+
(origin_y != screen_height) ? "\e[#{screen_height};H" : nil,
|
407
|
+
"\n" * val,
|
408
|
+
(origin_y != screen_height or !x.zero?) ? "\e[#{origin_y};#{origin_x}H" : nil
|
409
|
+
].join
|
410
|
+
end
|
343
411
|
end
|
344
412
|
|
345
413
|
def self.clear_screen
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
414
|
+
if @@legacy_console
|
415
|
+
return unless csbi = get_console_screen_buffer_info
|
416
|
+
buffer_width, _buffer_lines, attributes, window_top, window_bottom = csbi.unpack('ss@8S@12sx2s')
|
417
|
+
fill_length = buffer_width * (window_bottom - window_top + 1)
|
418
|
+
screen_topleft = window_top * 65536
|
419
|
+
written = 0.chr * 4
|
420
|
+
@@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, fill_length, screen_topleft, written)
|
421
|
+
@@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, fill_length, screen_topleft, written)
|
422
|
+
@@SetConsoleCursorPosition.call(@@hConsoleHandle, screen_topleft)
|
423
|
+
else
|
424
|
+
@@output.write "\e[2J" "\e[H"
|
425
|
+
end
|
357
426
|
end
|
358
427
|
|
359
428
|
def self.set_screen_size(rows, columns)
|
data/lib/reline.rb
CHANGED
@@ -60,7 +60,7 @@ module Reline
|
|
60
60
|
|
61
61
|
def initialize
|
62
62
|
self.output = STDOUT
|
63
|
-
@dialog_proc_list =
|
63
|
+
@dialog_proc_list = {}
|
64
64
|
yield self
|
65
65
|
@completion_quote_character = nil
|
66
66
|
@bracketed_paste_finished = false
|
@@ -155,10 +155,15 @@ module Reline
|
|
155
155
|
@dig_perfect_match_proc = p
|
156
156
|
end
|
157
157
|
|
158
|
+
DialogProc = Struct.new(:dialog_proc, :context)
|
158
159
|
def add_dialog_proc(name_sym, p, context = nil)
|
159
160
|
raise ArgumentError unless p.respond_to?(:call) or p.nil?
|
160
161
|
raise ArgumentError unless name_sym.instance_of?(Symbol)
|
161
|
-
@dialog_proc_list
|
162
|
+
@dialog_proc_list[name_sym] = DialogProc.new(p, context)
|
163
|
+
end
|
164
|
+
|
165
|
+
def dialog_proc(name_sym)
|
166
|
+
@dialog_proc_list[name_sym]
|
162
167
|
end
|
163
168
|
|
164
169
|
def input=(val)
|
@@ -301,9 +306,8 @@ module Reline
|
|
301
306
|
line_editor.auto_indent_proc = auto_indent_proc
|
302
307
|
line_editor.dig_perfect_match_proc = dig_perfect_match_proc
|
303
308
|
line_editor.pre_input_hook = pre_input_hook
|
304
|
-
@dialog_proc_list.
|
305
|
-
name_sym, dialog_proc, context
|
306
|
-
line_editor.add_dialog_proc(name_sym, dialog_proc, context)
|
309
|
+
@dialog_proc_list.each_pair do |name_sym, d|
|
310
|
+
line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
|
307
311
|
end
|
308
312
|
|
309
313
|
unless config.test_mode
|
@@ -315,6 +319,7 @@ module Reline
|
|
315
319
|
line_editor.rerender
|
316
320
|
|
317
321
|
begin
|
322
|
+
line_editor.set_signal_handlers
|
318
323
|
prev_pasting_state = false
|
319
324
|
loop do
|
320
325
|
prev_pasting_state = Reline::IOGate.in_pasting?
|
@@ -343,6 +348,11 @@ module Reline
|
|
343
348
|
line_editor.finalize
|
344
349
|
Reline::IOGate.deprep(otio)
|
345
350
|
raise e
|
351
|
+
rescue Exception
|
352
|
+
# Including Interrupt
|
353
|
+
line_editor.finalize
|
354
|
+
Reline::IOGate.deprep(otio)
|
355
|
+
raise
|
346
356
|
end
|
347
357
|
|
348
358
|
line_editor.finalize
|
@@ -457,7 +467,7 @@ module Reline
|
|
457
467
|
|
458
468
|
private def may_req_ambiguous_char_width
|
459
469
|
@ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
|
460
|
-
return if @ambiguous_width
|
470
|
+
return if defined? @ambiguous_width
|
461
471
|
Reline::IOGate.move_cursor_column(0)
|
462
472
|
begin
|
463
473
|
output.write "\u{25bd}"
|
@@ -516,6 +526,7 @@ module Reline
|
|
516
526
|
def_single_delegators :core, :last_incremental_search
|
517
527
|
def_single_delegators :core, :last_incremental_search=
|
518
528
|
def_single_delegators :core, :add_dialog_proc
|
529
|
+
def_single_delegators :core, :dialog_proc
|
519
530
|
def_single_delegators :core, :autocompletion, :autocompletion=
|
520
531
|
|
521
532
|
def_single_delegators :core, :readmultiline
|
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.
|
4
|
+
version: 0.3.0
|
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-12-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: io-console
|
@@ -69,11 +69,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
69
|
version: '2.5'
|
70
70
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
|
-
- - "
|
72
|
+
- - ">="
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version:
|
74
|
+
version: '0'
|
75
75
|
requirements: []
|
76
|
-
rubygems_version: 3.
|
76
|
+
rubygems_version: 3.2.22
|
77
77
|
signing_key:
|
78
78
|
specification_version: 4
|
79
79
|
summary: Alternative GNU Readline or Editline implementation by pure Ruby.
|