reline 0.2.8.pre.11 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|