reline 0.5.8 → 0.5.10
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 +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
|