reline 0.5.8 → 0.5.10
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 +40 -43
- data/lib/reline/face.rb +1 -1
- data/lib/reline/{ansi.rb → io/ansi.rb} +95 -85
- data/lib/reline/io/dumb.rb +106 -0
- data/lib/reline/{windows.rb → io/windows.rb} +112 -102
- data/lib/reline/io.rb +41 -0
- data/lib/reline/key_actor/base.rb +22 -6
- data/lib/reline/key_actor/composite.rb +17 -0
- data/lib/reline/key_actor/emacs.rb +2 -2
- data/lib/reline/key_actor/vi_command.rb +2 -2
- data/lib/reline/key_actor/vi_insert.rb +2 -2
- data/lib/reline/key_actor.rb +1 -0
- data/lib/reline/key_stroke.rb +65 -104
- data/lib/reline/line_editor.rb +70 -76
- data/lib/reline/terminfo.rb +6 -1
- data/lib/reline/unicode/east_asian_width.rb +1262 -1191
- data/lib/reline/unicode.rb +14 -39
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +42 -127
- metadata +8 -9
- data/lib/reline/general_io.rb +0 -111
data/lib/reline/key_stroke.rb
CHANGED
@@ -7,138 +7,99 @@ class Reline::KeyStroke
|
|
7
7
|
@config = config
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
result
|
19
|
-
}
|
20
|
-
end
|
10
|
+
# Input exactly matches to a key sequence
|
11
|
+
MATCHING = :matching
|
12
|
+
# Input partially matches to a key sequence
|
13
|
+
MATCHED = :matched
|
14
|
+
# Input matches to a key sequence and the key sequence is a prefix of another key sequence
|
15
|
+
MATCHING_MATCHED = :matching_matched
|
16
|
+
# Input does not match to any key sequence
|
17
|
+
UNMATCHED = :unmatched
|
21
18
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
i = 0
|
26
|
-
loop do
|
27
|
-
my_c = compressed_me[i]
|
28
|
-
other_c = compressed_other[i]
|
29
|
-
other_is_last = (i + 1) == compressed_other.size
|
30
|
-
me_is_last = (i + 1) == compressed_me.size
|
31
|
-
if my_c != other_c
|
32
|
-
if other_c == "\e".ord and other_is_last and my_c.is_a?(Reline::Key) and my_c.with_meta
|
33
|
-
return true
|
34
|
-
else
|
35
|
-
return false
|
36
|
-
end
|
37
|
-
elsif other_is_last
|
38
|
-
return true
|
39
|
-
elsif me_is_last
|
40
|
-
return false
|
41
|
-
end
|
42
|
-
i += 1
|
43
|
-
end
|
44
|
-
end
|
19
|
+
def match_status(input)
|
20
|
+
matching = key_mapping.matching?(input)
|
21
|
+
matched = key_mapping.get(input)
|
45
22
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
compressed_me = compress_meta_key(me)
|
50
|
-
compressed_other = compress_meta_key(other)
|
51
|
-
compressed_me.size == compressed_other.size and [compressed_me, compressed_other].transpose.all?{ |i| equal?(i[0], i[1]) }
|
52
|
-
when Integer
|
53
|
-
if other.is_a?(Reline::Key)
|
54
|
-
if other.combined_char == "\e".ord
|
55
|
-
false
|
56
|
-
else
|
57
|
-
other.combined_char == me
|
58
|
-
end
|
59
|
-
else
|
60
|
-
me == other
|
61
|
-
end
|
62
|
-
when Reline::Key
|
63
|
-
if other.is_a?(Integer)
|
64
|
-
me.combined_char == other
|
65
|
-
else
|
66
|
-
me == other
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
23
|
+
# FIXME: Workaround for single byte. remove this after MAPPING is merged into KeyActor.
|
24
|
+
matched ||= input.size == 1
|
25
|
+
matching ||= input == [ESC_BYTE]
|
70
26
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
:matched
|
27
|
+
if matching && matched
|
28
|
+
MATCHING_MATCHED
|
29
|
+
elsif matching
|
30
|
+
MATCHING
|
31
|
+
elsif matched
|
32
|
+
MATCHED
|
33
|
+
elsif input[0] == ESC_BYTE
|
34
|
+
match_unknown_escape_sequence(input, vi_mode: @config.editing_mode_is?(:vi_insert, :vi_command))
|
35
|
+
elsif input.size == 1
|
36
|
+
MATCHED
|
82
37
|
else
|
83
|
-
|
38
|
+
UNMATCHED
|
84
39
|
end
|
85
40
|
end
|
86
41
|
|
87
42
|
def expand(input)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
return [:ed_unassigned] + expand(input.drop(size))
|
94
|
-
when :matching
|
95
|
-
return [:ed_unassigned]
|
96
|
-
else
|
97
|
-
return input
|
98
|
-
end
|
43
|
+
matched_bytes = nil
|
44
|
+
(1..input.size).each do |i|
|
45
|
+
bytes = input.take(i)
|
46
|
+
status = match_status(bytes)
|
47
|
+
matched_bytes = bytes if status == MATCHED || status == MATCHING_MATCHED
|
99
48
|
end
|
100
|
-
|
49
|
+
return [[], []] unless matched_bytes
|
101
50
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
51
|
+
func = key_mapping.get(matched_bytes)
|
52
|
+
if func.is_a?(Array)
|
53
|
+
keys = func.map { |c| Reline::Key.new(c, c, false) }
|
54
|
+
elsif func
|
55
|
+
keys = [Reline::Key.new(func, func, false)]
|
56
|
+
elsif matched_bytes.size == 1
|
57
|
+
keys = [Reline::Key.new(matched_bytes.first, matched_bytes.first, false)]
|
58
|
+
elsif matched_bytes.size == 2 && matched_bytes[0] == ESC_BYTE
|
59
|
+
keys = [Reline::Key.new(matched_bytes[1], matched_bytes[1] | 0b10000000, true)]
|
60
|
+
else
|
61
|
+
keys = []
|
110
62
|
end
|
63
|
+
|
64
|
+
[keys, input.drop(matched_bytes.size)]
|
111
65
|
end
|
112
66
|
|
113
67
|
private
|
114
68
|
|
115
69
|
# returns match status of CSI/SS3 sequence and matched length
|
116
|
-
def match_unknown_escape_sequence(input)
|
70
|
+
def match_unknown_escape_sequence(input, vi_mode: false)
|
117
71
|
idx = 0
|
118
|
-
return
|
72
|
+
return UNMATCHED unless input[idx] == ESC_BYTE
|
119
73
|
idx += 1
|
120
74
|
idx += 1 if input[idx] == ESC_BYTE
|
121
75
|
|
122
76
|
case input[idx]
|
123
77
|
when nil
|
124
|
-
|
78
|
+
if idx == 1 # `ESC`
|
79
|
+
return MATCHING_MATCHED
|
80
|
+
else # `ESC ESC`
|
81
|
+
return MATCHING
|
82
|
+
end
|
125
83
|
when 91 # == '['.ord
|
126
|
-
# CSI sequence
|
84
|
+
# CSI sequence `ESC [ ... char`
|
127
85
|
idx += 1
|
128
86
|
idx += 1 while idx < input.size && CSI_PARAMETER_BYTES_RANGE.cover?(input[idx])
|
129
87
|
idx += 1 while idx < input.size && CSI_INTERMEDIATE_BYTES_RANGE.cover?(input[idx])
|
130
|
-
input[idx] ? [:matched, idx + 1] : [:matching, nil]
|
131
88
|
when 79 # == 'O'.ord
|
132
|
-
# SS3 sequence
|
133
|
-
|
89
|
+
# SS3 sequence `ESC O char`
|
90
|
+
idx += 1
|
134
91
|
else
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
92
|
+
# `ESC char` or `ESC ESC char`
|
93
|
+
return UNMATCHED if vi_mode
|
94
|
+
end
|
95
|
+
|
96
|
+
case input.size
|
97
|
+
when idx
|
98
|
+
MATCHING
|
99
|
+
when idx + 1
|
100
|
+
MATCHED
|
101
|
+
else
|
102
|
+
UNMATCHED
|
142
103
|
end
|
143
104
|
end
|
144
105
|
|
data/lib/reline/line_editor.rb
CHANGED
@@ -45,6 +45,7 @@ class Reline::LineEditor
|
|
45
45
|
RenderedScreen = Struct.new(:base_y, :lines, :cursor_y, keyword_init: true)
|
46
46
|
|
47
47
|
CompletionJourneyState = Struct.new(:line_index, :pre, :target, :post, :list, :pointer)
|
48
|
+
NullActionState = [nil, nil].freeze
|
48
49
|
|
49
50
|
class MenuInfo
|
50
51
|
attr_reader :list
|
@@ -175,9 +176,8 @@ class Reline::LineEditor
|
|
175
176
|
scroll_into_view
|
176
177
|
Reline::IOGate.move_cursor_up @rendered_screen.cursor_y
|
177
178
|
@rendered_screen.base_y = Reline::IOGate.cursor_pos.y
|
178
|
-
|
179
|
-
|
180
|
-
render_differential
|
179
|
+
clear_rendered_screen_cache
|
180
|
+
render
|
181
181
|
end
|
182
182
|
|
183
183
|
private def handle_interrupted
|
@@ -185,11 +185,11 @@ class Reline::LineEditor
|
|
185
185
|
|
186
186
|
@interrupted = false
|
187
187
|
clear_dialogs
|
188
|
-
|
189
|
-
|
188
|
+
render
|
189
|
+
cursor_to_bottom_offset = @rendered_screen.lines.size - @rendered_screen.cursor_y
|
190
|
+
Reline::IOGate.scroll_down cursor_to_bottom_offset
|
190
191
|
Reline::IOGate.move_cursor_column 0
|
191
|
-
|
192
|
-
@rendered_screen.cursor_y = 0
|
192
|
+
clear_rendered_screen_cache
|
193
193
|
case @old_trap
|
194
194
|
when 'DEFAULT', 'SYSTEM_DEFAULT'
|
195
195
|
raise Interrupt
|
@@ -237,7 +237,6 @@ class Reline::LineEditor
|
|
237
237
|
@perfect_matched = nil
|
238
238
|
@menu_info = nil
|
239
239
|
@searching_prompt = nil
|
240
|
-
@first_char = true
|
241
240
|
@just_cursor_moving = false
|
242
241
|
@eof = false
|
243
242
|
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
@@ -253,6 +252,8 @@ class Reline::LineEditor
|
|
253
252
|
@input_lines = [[[""], 0, 0]]
|
254
253
|
@input_lines_position = 0
|
255
254
|
@undoing = false
|
255
|
+
@prev_action_state = NullActionState
|
256
|
+
@next_action_state = NullActionState
|
256
257
|
reset_line
|
257
258
|
end
|
258
259
|
|
@@ -412,7 +413,7 @@ class Reline::LineEditor
|
|
412
413
|
# do nothing
|
413
414
|
elsif level == :blank
|
414
415
|
Reline::IOGate.move_cursor_column base_x
|
415
|
-
@output.write "#{Reline::IOGate
|
416
|
+
@output.write "#{Reline::IOGate.reset_color_sequence}#{' ' * width}"
|
416
417
|
else
|
417
418
|
x, w, content = new_items[level]
|
418
419
|
cover_begin = base_x != 0 && new_levels[base_x - 1] == level
|
@@ -422,7 +423,7 @@ class Reline::LineEditor
|
|
422
423
|
content, pos = Reline::Unicode.take_mbchar_range(content, base_x - x, width, cover_begin: cover_begin, cover_end: cover_end, padding: true)
|
423
424
|
end
|
424
425
|
Reline::IOGate.move_cursor_column x + pos
|
425
|
-
@output.write "#{Reline::IOGate
|
426
|
+
@output.write "#{Reline::IOGate.reset_color_sequence}#{content}#{Reline::IOGate.reset_color_sequence}"
|
426
427
|
end
|
427
428
|
base_x += width
|
428
429
|
end
|
@@ -458,28 +459,7 @@ class Reline::LineEditor
|
|
458
459
|
end
|
459
460
|
|
460
461
|
def render_finished
|
461
|
-
|
462
|
-
render_full_content
|
463
|
-
end
|
464
|
-
|
465
|
-
def clear_rendered_lines
|
466
|
-
Reline::IOGate.move_cursor_up @rendered_screen.cursor_y
|
467
|
-
Reline::IOGate.move_cursor_column 0
|
468
|
-
|
469
|
-
num_lines = @rendered_screen.lines.size
|
470
|
-
return unless num_lines && num_lines >= 1
|
471
|
-
|
472
|
-
Reline::IOGate.move_cursor_down num_lines - 1
|
473
|
-
(num_lines - 1).times do
|
474
|
-
Reline::IOGate.erase_after_cursor
|
475
|
-
Reline::IOGate.move_cursor_up 1
|
476
|
-
end
|
477
|
-
Reline::IOGate.erase_after_cursor
|
478
|
-
@rendered_screen.lines = []
|
479
|
-
@rendered_screen.cursor_y = 0
|
480
|
-
end
|
481
|
-
|
482
|
-
def render_full_content
|
462
|
+
render_differential([], 0, 0)
|
483
463
|
lines = @buffer_of_lines.size.times.map do |i|
|
484
464
|
line = prompt_list[i] + modified_lines[i]
|
485
465
|
wrapped_lines, = split_by_width(line, screen_width)
|
@@ -488,19 +468,13 @@ class Reline::LineEditor
|
|
488
468
|
@output.puts lines.map { |l| "#{l}\r\n" }.join
|
489
469
|
end
|
490
470
|
|
491
|
-
def print_nomultiline_prompt
|
492
|
-
return unless prompt && !@is_multiline
|
493
|
-
|
471
|
+
def print_nomultiline_prompt
|
494
472
|
# Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence.
|
495
|
-
@
|
496
|
-
@rendered_screen.cursor_y = 0
|
497
|
-
@output.write prompt
|
473
|
+
@output.write @prompt if @prompt && !@is_multiline
|
498
474
|
end
|
499
475
|
|
500
|
-
def
|
476
|
+
def render
|
501
477
|
wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
|
502
|
-
|
503
|
-
rendered_lines = @rendered_screen.lines
|
504
478
|
new_lines = wrapped_prompt_and_input_lines.flatten(1)[screen_scroll_top, screen_height].map do |prompt, line|
|
505
479
|
prompt_width = Reline::Unicode.calculate_width(prompt, true)
|
506
480
|
[[0, prompt_width, prompt], [prompt_width, Reline::Unicode.calculate_width(line, true), line]]
|
@@ -518,12 +492,21 @@ class Reline::LineEditor
|
|
518
492
|
x_range, y_range = dialog_range dialog, wrapped_cursor_y - screen_scroll_top
|
519
493
|
y_range.each do |row|
|
520
494
|
next if row < 0 || row >= screen_height
|
495
|
+
|
521
496
|
dialog_rows = new_lines[row] ||= []
|
522
497
|
# index 0 is for prompt, index 1 is for line, index 2.. is for dialog
|
523
498
|
dialog_rows[index + 2] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]]
|
524
499
|
end
|
525
500
|
end
|
526
501
|
|
502
|
+
render_differential new_lines, wrapped_cursor_x, wrapped_cursor_y - screen_scroll_top
|
503
|
+
end
|
504
|
+
|
505
|
+
# Reflects lines to be rendered and new cursor position to the screen
|
506
|
+
# by calculating the difference from the previous render.
|
507
|
+
|
508
|
+
private def render_differential(new_lines, new_cursor_x, new_cursor_y)
|
509
|
+
rendered_lines = @rendered_screen.lines
|
527
510
|
cursor_y = @rendered_screen.cursor_y
|
528
511
|
if new_lines != rendered_lines
|
529
512
|
# Hide cursor while rendering to avoid cursor flickering.
|
@@ -550,11 +533,14 @@ class Reline::LineEditor
|
|
550
533
|
@rendered_screen.lines = new_lines
|
551
534
|
Reline::IOGate.show_cursor
|
552
535
|
end
|
553
|
-
|
554
|
-
Reline::IOGate.
|
555
|
-
|
556
|
-
|
557
|
-
|
536
|
+
Reline::IOGate.move_cursor_column new_cursor_x
|
537
|
+
Reline::IOGate.move_cursor_down new_cursor_y - cursor_y
|
538
|
+
@rendered_screen.cursor_y = new_cursor_y
|
539
|
+
end
|
540
|
+
|
541
|
+
private def clear_rendered_screen_cache
|
542
|
+
@rendered_screen.lines = []
|
543
|
+
@rendered_screen.cursor_y = 0
|
558
544
|
end
|
559
545
|
|
560
546
|
def upper_space_height(wrapped_cursor_y)
|
@@ -566,7 +552,7 @@ class Reline::LineEditor
|
|
566
552
|
end
|
567
553
|
|
568
554
|
def rerender
|
569
|
-
|
555
|
+
render unless @in_pasting
|
570
556
|
end
|
571
557
|
|
572
558
|
class DialogProcScope
|
@@ -684,10 +670,8 @@ class Reline::LineEditor
|
|
684
670
|
@trap_key.each do |t|
|
685
671
|
@config.add_oneshot_key_binding(t, @name)
|
686
672
|
end
|
687
|
-
|
673
|
+
else
|
688
674
|
@config.add_oneshot_key_binding(@trap_key, @name)
|
689
|
-
elsif @trap_key.is_a?(Integer) or @trap_key.is_a?(Reline::Key)
|
690
|
-
@config.add_oneshot_key_binding([@trap_key], @name)
|
691
675
|
end
|
692
676
|
end
|
693
677
|
dialog_render_info
|
@@ -1080,17 +1064,7 @@ class Reline::LineEditor
|
|
1080
1064
|
else # single byte
|
1081
1065
|
return if key.char >= 128 # maybe, first byte of multi byte
|
1082
1066
|
method_symbol = @config.editing_mode.get_method(key.combined_char)
|
1083
|
-
|
1084
|
-
if @config.editing_mode_is?(:vi_command, :vi_insert)
|
1085
|
-
# split ESC + key in vi mode
|
1086
|
-
method_symbol = @config.editing_mode.get_method("\e".ord)
|
1087
|
-
process_key("\e".ord, method_symbol)
|
1088
|
-
method_symbol = @config.editing_mode.get_method(key.char)
|
1089
|
-
process_key(key.char, method_symbol)
|
1090
|
-
end
|
1091
|
-
else
|
1092
|
-
process_key(key.combined_char, method_symbol)
|
1093
|
-
end
|
1067
|
+
process_key(key.combined_char, method_symbol)
|
1094
1068
|
@multibyte_buffer.clear
|
1095
1069
|
end
|
1096
1070
|
if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
|
@@ -1119,13 +1093,10 @@ class Reline::LineEditor
|
|
1119
1093
|
end
|
1120
1094
|
if key.char.nil?
|
1121
1095
|
process_insert(force: true)
|
1122
|
-
|
1123
|
-
@eof = true
|
1124
|
-
end
|
1096
|
+
@eof = buffer_empty?
|
1125
1097
|
finish
|
1126
1098
|
return
|
1127
1099
|
end
|
1128
|
-
@first_char = false
|
1129
1100
|
@completion_occurs = false
|
1130
1101
|
|
1131
1102
|
if key.char.is_a?(Symbol)
|
@@ -1133,6 +1104,9 @@ class Reline::LineEditor
|
|
1133
1104
|
else
|
1134
1105
|
normal_char(key)
|
1135
1106
|
end
|
1107
|
+
|
1108
|
+
@prev_action_state, @next_action_state = @next_action_state, NullActionState
|
1109
|
+
|
1136
1110
|
unless @completion_occurs
|
1137
1111
|
@completion_state = CompletionState::NORMAL
|
1138
1112
|
@completion_journey_state = nil
|
@@ -1347,7 +1321,7 @@ class Reline::LineEditor
|
|
1347
1321
|
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
|
1348
1322
|
end
|
1349
1323
|
|
1350
|
-
def
|
1324
|
+
def insert_multiline_text(text)
|
1351
1325
|
save_old_buffer
|
1352
1326
|
pre = @buffer_of_lines[@line_index].byteslice(0, @byte_pointer)
|
1353
1327
|
post = @buffer_of_lines[@line_index].byteslice(@byte_pointer..)
|
@@ -1415,6 +1389,10 @@ class Reline::LineEditor
|
|
1415
1389
|
whole_lines.join("\n")
|
1416
1390
|
end
|
1417
1391
|
|
1392
|
+
private def buffer_empty?
|
1393
|
+
current_line.empty? and @buffer_of_lines.size == 1
|
1394
|
+
end
|
1395
|
+
|
1418
1396
|
def finished?
|
1419
1397
|
@finished
|
1420
1398
|
end
|
@@ -1763,29 +1741,31 @@ class Reline::LineEditor
|
|
1763
1741
|
end
|
1764
1742
|
|
1765
1743
|
private def ed_search_prev_history(key, arg: 1)
|
1766
|
-
substr = current_line.byteslice(0, @byte_pointer)
|
1744
|
+
substr = prev_action_state_value(:search_history) == :empty ? '' : current_line.byteslice(0, @byte_pointer)
|
1767
1745
|
return if @history_pointer == 0
|
1768
1746
|
return if @history_pointer.nil? && substr.empty? && !current_line.empty?
|
1769
1747
|
|
1770
1748
|
history_range = 0...(@history_pointer || Reline::HISTORY.size)
|
1771
1749
|
h_pointer, line_index = search_history(substr, history_range.reverse_each)
|
1772
1750
|
return unless h_pointer
|
1773
|
-
move_history(h_pointer, line: line_index || :start, cursor: @byte_pointer)
|
1751
|
+
move_history(h_pointer, line: line_index || :start, cursor: substr.empty? ? :end : @byte_pointer)
|
1774
1752
|
arg -= 1
|
1753
|
+
set_next_action_state(:search_history, :empty) if substr.empty?
|
1775
1754
|
ed_search_prev_history(key, arg: arg) if arg > 0
|
1776
1755
|
end
|
1777
1756
|
alias_method :history_search_backward, :ed_search_prev_history
|
1778
1757
|
|
1779
1758
|
private def ed_search_next_history(key, arg: 1)
|
1780
|
-
substr = current_line.byteslice(0, @byte_pointer)
|
1759
|
+
substr = prev_action_state_value(:search_history) == :empty ? '' : current_line.byteslice(0, @byte_pointer)
|
1781
1760
|
return if @history_pointer.nil?
|
1782
1761
|
|
1783
1762
|
history_range = @history_pointer + 1...Reline::HISTORY.size
|
1784
1763
|
h_pointer, line_index = search_history(substr, history_range)
|
1785
1764
|
return if h_pointer.nil? and not substr.empty?
|
1786
1765
|
|
1787
|
-
move_history(h_pointer, line: line_index || :start, cursor: @byte_pointer)
|
1766
|
+
move_history(h_pointer, line: line_index || :start, cursor: substr.empty? ? :end : @byte_pointer)
|
1788
1767
|
arg -= 1
|
1768
|
+
set_next_action_state(:search_history, :empty) if substr.empty?
|
1789
1769
|
ed_search_next_history(key, arg: arg) if arg > 0
|
1790
1770
|
end
|
1791
1771
|
alias_method :history_search_forward, :ed_search_next_history
|
@@ -1941,7 +1921,7 @@ class Reline::LineEditor
|
|
1941
1921
|
alias_method :kill_whole_line, :em_kill_line
|
1942
1922
|
|
1943
1923
|
private def em_delete(key)
|
1944
|
-
if
|
1924
|
+
if buffer_empty? and key == "\C-d".ord
|
1945
1925
|
@eof = true
|
1946
1926
|
finish
|
1947
1927
|
elsif @byte_pointer < current_line.bytesize
|
@@ -1986,9 +1966,8 @@ class Reline::LineEditor
|
|
1986
1966
|
private def ed_clear_screen(key)
|
1987
1967
|
Reline::IOGate.clear_screen
|
1988
1968
|
@screen_size = Reline::IOGate.get_screen_size
|
1989
|
-
@rendered_screen.lines = []
|
1990
1969
|
@rendered_screen.base_y = 0
|
1991
|
-
|
1970
|
+
clear_rendered_screen_cache
|
1992
1971
|
end
|
1993
1972
|
alias_method :clear_screen, :ed_clear_screen
|
1994
1973
|
|
@@ -2263,9 +2242,11 @@ class Reline::LineEditor
|
|
2263
2242
|
line, cut = byteslice!(current_line, @byte_pointer, byte_pointer_diff)
|
2264
2243
|
elsif byte_pointer_diff < 0
|
2265
2244
|
line, cut = byteslice!(current_line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2245
|
+
else
|
2246
|
+
return
|
2266
2247
|
end
|
2267
2248
|
copy_for_vi(cut)
|
2268
|
-
set_current_line(line
|
2249
|
+
set_current_line(line, @byte_pointer + (byte_pointer_diff < 0 ? byte_pointer_diff : 0))
|
2269
2250
|
end
|
2270
2251
|
|
2271
2252
|
private def vi_yank(key, arg: nil)
|
@@ -2284,13 +2265,14 @@ class Reline::LineEditor
|
|
2284
2265
|
cut = current_line.byteslice(@byte_pointer, byte_pointer_diff)
|
2285
2266
|
elsif byte_pointer_diff < 0
|
2286
2267
|
cut = current_line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2268
|
+
else
|
2269
|
+
return
|
2287
2270
|
end
|
2288
2271
|
copy_for_vi(cut)
|
2289
2272
|
end
|
2290
2273
|
|
2291
2274
|
private def vi_list_or_eof(key)
|
2292
|
-
if
|
2293
|
-
set_current_line('', 0)
|
2275
|
+
if buffer_empty?
|
2294
2276
|
@eof = true
|
2295
2277
|
finish
|
2296
2278
|
else
|
@@ -2551,4 +2533,16 @@ class Reline::LineEditor
|
|
2551
2533
|
target_lines, target_cursor_x, target_cursor_y = @input_lines[@input_lines_position]
|
2552
2534
|
set_current_lines(target_lines.dup, target_cursor_x, target_cursor_y)
|
2553
2535
|
end
|
2536
|
+
|
2537
|
+
private def prev_action_state_value(type)
|
2538
|
+
@prev_action_state[0] == type ? @prev_action_state[1] : nil
|
2539
|
+
end
|
2540
|
+
|
2541
|
+
private def set_next_action_state(type, value)
|
2542
|
+
@next_action_state = [type, value]
|
2543
|
+
end
|
2544
|
+
|
2545
|
+
private def re_read_init_file(_key)
|
2546
|
+
@config.reload
|
2547
|
+
end
|
2554
2548
|
end
|
data/lib/reline/terminfo.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
begin
|
2
|
+
# Ignore warning `Add fiddle to your Gemfile or gemspec` in Ruby 3.4.
|
3
|
+
# terminfo.rb and ansi.rb supports fiddle unavailable environment.
|
4
|
+
verbose, $VERBOSE = $VERBOSE, nil
|
2
5
|
require 'fiddle'
|
3
6
|
require 'fiddle/import'
|
4
7
|
rescue LoadError
|
@@ -7,6 +10,8 @@ rescue LoadError
|
|
7
10
|
false
|
8
11
|
end
|
9
12
|
end
|
13
|
+
ensure
|
14
|
+
$VERBOSE = verbose
|
10
15
|
end
|
11
16
|
|
12
17
|
module Reline::Terminfo
|
@@ -78,7 +83,7 @@ module Reline::Terminfo
|
|
78
83
|
end
|
79
84
|
|
80
85
|
def self.setupterm(term, fildes)
|
81
|
-
errret_int = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT)
|
86
|
+
errret_int = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT, Fiddle::RUBY_FREE)
|
82
87
|
ret = @setupterm.(term, fildes, errret_int)
|
83
88
|
case ret
|
84
89
|
when 0 # OK
|