reline 0.0.1 → 0.0.6
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/README.md +2 -0
- data/lib/reline.rb +329 -334
- data/lib/reline/ansi.rb +28 -11
- data/lib/reline/config.rb +4 -5
- data/lib/reline/general_io.rb +4 -1
- data/lib/reline/key_actor/vi_command.rb +1 -1
- data/lib/reline/key_actor/vi_insert.rb +1 -1
- data/lib/reline/key_stroke.rb +1 -1
- data/lib/reline/line_editor.rb +181 -76
- data/lib/reline/unicode/east_asian_width.rb +1 -1
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +109 -65
- metadata +4 -4
data/lib/reline/ansi.rb
CHANGED
@@ -7,7 +7,11 @@ class Reline::ANSI
|
|
7
7
|
[27, 91, 51, 126] => :key_delete, # Del
|
8
8
|
[27, 91, 49, 126] => :ed_move_to_beg, # Home
|
9
9
|
[27, 91, 52, 126] => :ed_move_to_end, # End
|
10
|
-
|
10
|
+
[27, 91, 72] => :ed_move_to_beg, # Home
|
11
|
+
[27, 91, 70] => :ed_move_to_end, # End
|
12
|
+
[27, 32] => :em_set_mark, # M-<space>
|
13
|
+
[24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows
|
14
|
+
}
|
11
15
|
|
12
16
|
@@input = STDIN
|
13
17
|
def self.input=(val)
|
@@ -24,20 +28,22 @@ class Reline::ANSI
|
|
24
28
|
unless @@buf.empty?
|
25
29
|
return @@buf.shift
|
26
30
|
end
|
27
|
-
|
28
|
-
loop do
|
29
|
-
result = select([@@input], [], [], 0.1)
|
30
|
-
next if result.nil?
|
31
|
-
c = @@input.read(1)
|
32
|
-
break
|
33
|
-
end
|
34
|
-
c&.ord
|
31
|
+
@@input.getbyte
|
35
32
|
end
|
36
33
|
|
37
34
|
def self.ungetc(c)
|
38
35
|
@@buf.unshift(c)
|
39
36
|
end
|
40
37
|
|
38
|
+
def self.retrieve_keybuffer
|
39
|
+
result = select([@@input], [], [], 0.001)
|
40
|
+
return if result.nil?
|
41
|
+
str = @@input.read_nonblock(1024)
|
42
|
+
str.bytes.each do |c|
|
43
|
+
@@buf.push(c)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
41
47
|
def self.get_screen_size
|
42
48
|
@@input.winsize
|
43
49
|
rescue Errno::ENOTTY
|
@@ -106,13 +112,23 @@ class Reline::ANSI
|
|
106
112
|
print "\e[1;1H"
|
107
113
|
end
|
108
114
|
|
115
|
+
@@old_winch_handler = nil
|
116
|
+
def self.set_winch_handler(&handler)
|
117
|
+
@@old_winch_handler = Signal.trap('WINCH', &handler)
|
118
|
+
end
|
119
|
+
|
109
120
|
def self.prep
|
121
|
+
retrieve_keybuffer
|
110
122
|
int_handle = Signal.trap('INT', 'IGNORE')
|
111
123
|
otio = `stty -g`.chomp
|
112
124
|
setting = ' -echo -icrnl cbreak'
|
113
|
-
|
125
|
+
stty = `stty -a`
|
126
|
+
if /-parenb\b/ =~ stty
|
114
127
|
setting << ' pass8'
|
115
128
|
end
|
129
|
+
if /\bdsusp *=/ =~ stty
|
130
|
+
setting << ' dsusp undef'
|
131
|
+
end
|
116
132
|
setting << ' -ixoff'
|
117
133
|
`stty #{setting}`
|
118
134
|
Signal.trap('INT', int_handle)
|
@@ -121,7 +137,8 @@ class Reline::ANSI
|
|
121
137
|
|
122
138
|
def self.deprep(otio)
|
123
139
|
int_handle = Signal.trap('INT', 'IGNORE')
|
124
|
-
|
140
|
+
system("stty #{otio}", err: File::NULL)
|
125
141
|
Signal.trap('INT', int_handle)
|
142
|
+
Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
|
126
143
|
end
|
127
144
|
end
|
data/lib/reline/config.rb
CHANGED
@@ -126,20 +126,19 @@ class Reline::Config
|
|
126
126
|
no += 1
|
127
127
|
|
128
128
|
line = line.chomp.lstrip
|
129
|
-
if line
|
129
|
+
if line.start_with?('$')
|
130
130
|
handle_directive(line[1..-1], file, no)
|
131
131
|
next
|
132
132
|
end
|
133
133
|
|
134
134
|
next if @skip_section
|
135
135
|
|
136
|
-
|
136
|
+
case line
|
137
|
+
when /^set +([^ ]+) +([^ ]+)/i
|
137
138
|
var, value = $1.downcase, $2.downcase
|
138
139
|
bind_variable(var, value)
|
139
140
|
next
|
140
|
-
|
141
|
-
|
142
|
-
if line =~ /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/
|
141
|
+
when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
|
143
142
|
key, func_name = $1, $2
|
144
143
|
keystroke, func = bind_key(key, func_name)
|
145
144
|
next unless keystroke
|
data/lib/reline/general_io.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'timeout'
|
2
2
|
|
3
3
|
class Reline::GeneralIO
|
4
|
-
RAW_KEYSTROKE_CONFIG = {}
|
4
|
+
RAW_KEYSTROKE_CONFIG = {}
|
5
5
|
|
6
6
|
@@buf = []
|
7
7
|
|
@@ -56,6 +56,9 @@ class Reline::GeneralIO
|
|
56
56
|
def self.set_screen_size(rows, columns)
|
57
57
|
end
|
58
58
|
|
59
|
+
def self.set_winch_handler(&handler)
|
60
|
+
end
|
61
|
+
|
59
62
|
def self.prep
|
60
63
|
end
|
61
64
|
|
data/lib/reline/key_stroke.rb
CHANGED
@@ -32,7 +32,7 @@ class Reline::KeyStroke
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def expand(input)
|
35
|
-
lhs = key_mapping.keys.select { |
|
35
|
+
lhs = key_mapping.keys.select { |item| input.start_with? item }.sort_by(&:size).reverse.first
|
36
36
|
return input unless lhs
|
37
37
|
rhs = key_mapping[lhs]
|
38
38
|
|
data/lib/reline/line_editor.rb
CHANGED
@@ -60,15 +60,72 @@ class Reline::LineEditor
|
|
60
60
|
reset_variables
|
61
61
|
end
|
62
62
|
|
63
|
+
private def check_multiline_prompt(buffer, prompt)
|
64
|
+
if @vi_arg
|
65
|
+
prompt = "(arg: #{@vi_arg}) "
|
66
|
+
@rerender_all = true
|
67
|
+
elsif @searching_prompt
|
68
|
+
prompt = @searching_prompt
|
69
|
+
@rerender_all = true
|
70
|
+
else
|
71
|
+
prompt = @prompt
|
72
|
+
end
|
73
|
+
if @prompt_proc
|
74
|
+
prompt_list = @prompt_proc.(buffer)
|
75
|
+
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
76
|
+
prompt = prompt_list[@line_index]
|
77
|
+
prompt_width = calculate_width(prompt, true)
|
78
|
+
[prompt, prompt_width, prompt_list]
|
79
|
+
else
|
80
|
+
prompt_width = calculate_width(prompt, true)
|
81
|
+
[prompt, prompt_width, nil]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
63
85
|
def reset(prompt = '', encoding = Encoding.default_external)
|
64
86
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
65
87
|
@screen_size = Reline::IOGate.get_screen_size
|
66
88
|
reset_variables(prompt, encoding)
|
67
89
|
@old_trap = Signal.trap('SIGINT') {
|
68
|
-
scroll_down(@highest_in_all - @first_line_started_from)
|
69
|
-
Reline::IOGate.move_cursor_column(0)
|
70
90
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
91
|
+
raise Interrupt
|
71
92
|
}
|
93
|
+
Reline::IOGate.set_winch_handler do
|
94
|
+
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
95
|
+
old_screen_size = @screen_size
|
96
|
+
@screen_size = Reline::IOGate.get_screen_size
|
97
|
+
if old_screen_size.last < @screen_size.last # columns increase
|
98
|
+
@rerender_all = true
|
99
|
+
rerender
|
100
|
+
else
|
101
|
+
back = 0
|
102
|
+
new_buffer = whole_lines
|
103
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
104
|
+
new_buffer.each_with_index do |line, index|
|
105
|
+
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
106
|
+
width = prompt_width + calculate_width(line)
|
107
|
+
height = calculate_height_by_width(width)
|
108
|
+
back += height
|
109
|
+
end
|
110
|
+
@highest_in_all = back
|
111
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
112
|
+
@first_line_started_from =
|
113
|
+
if @line_index.zero?
|
114
|
+
0
|
115
|
+
else
|
116
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
117
|
+
end
|
118
|
+
if @prompt_proc
|
119
|
+
prompt = prompt_list[@line_index]
|
120
|
+
prompt_width = calculate_width(prompt, true)
|
121
|
+
end
|
122
|
+
calculate_nearest_cursor
|
123
|
+
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
124
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
125
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
126
|
+
@rerender_all = true
|
127
|
+
end
|
128
|
+
end
|
72
129
|
end
|
73
130
|
|
74
131
|
def finalize
|
@@ -81,6 +138,7 @@ class Reline::LineEditor
|
|
81
138
|
|
82
139
|
def reset_variables(prompt = '', encoding = Encoding.default_external)
|
83
140
|
@prompt = prompt
|
141
|
+
@mark_pointer = nil
|
84
142
|
@encoding = encoding
|
85
143
|
@is_multiline = false
|
86
144
|
@finished = false
|
@@ -129,6 +187,16 @@ class Reline::LineEditor
|
|
129
187
|
@is_multiline = false
|
130
188
|
end
|
131
189
|
|
190
|
+
private def calculate_height_by_lines(lines, prompt_list)
|
191
|
+
result = 0
|
192
|
+
lines.each_with_index { |line, i|
|
193
|
+
prompt = ''
|
194
|
+
prompt = prompt_list[i] if prompt_list and prompt_list[i]
|
195
|
+
result += calculate_height_by_width(calculate_width(prompt + line))
|
196
|
+
}
|
197
|
+
result
|
198
|
+
end
|
199
|
+
|
132
200
|
private def insert_new_line(cursor_line, next_line)
|
133
201
|
@line = cursor_line
|
134
202
|
@buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
|
@@ -241,7 +309,7 @@ class Reline::LineEditor
|
|
241
309
|
@byte_pointer = new_byte_pointer
|
242
310
|
end
|
243
311
|
|
244
|
-
def rerender
|
312
|
+
def rerender
|
245
313
|
return if @line.nil?
|
246
314
|
if @menu_info
|
247
315
|
scroll_down(@highest_in_all - @first_line_started_from)
|
@@ -249,32 +317,18 @@ class Reline::LineEditor
|
|
249
317
|
@menu_info.list.each do |item|
|
250
318
|
Reline::IOGate.move_cursor_column(0)
|
251
319
|
@output.print item
|
320
|
+
@output.flush
|
252
321
|
scroll_down(1)
|
253
322
|
end
|
254
323
|
scroll_down(@highest_in_all - 1)
|
255
324
|
move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
|
256
325
|
@menu_info = nil
|
257
326
|
end
|
258
|
-
|
259
|
-
prompt = "(arg: #{@vi_arg}) "
|
260
|
-
prompt_width = calculate_width(prompt)
|
261
|
-
elsif @searching_prompt
|
262
|
-
prompt = @searching_prompt
|
263
|
-
prompt_width = calculate_width(prompt)
|
264
|
-
else
|
265
|
-
prompt = @prompt
|
266
|
-
prompt_width = calculate_width(prompt, true)
|
267
|
-
end
|
327
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
268
328
|
if @cleared
|
269
329
|
Reline::IOGate.clear_screen
|
270
330
|
@cleared = false
|
271
331
|
back = 0
|
272
|
-
prompt_list = nil
|
273
|
-
if @prompt_proc
|
274
|
-
prompt_list = @prompt_proc.(whole_lines)
|
275
|
-
prompt = prompt_list[@line_index]
|
276
|
-
prompt_width = calculate_width(prompt, true)
|
277
|
-
end
|
278
332
|
modify_lines(whole_lines).each_with_index do |line, index|
|
279
333
|
if @prompt_proc
|
280
334
|
pr = prompt_list[index]
|
@@ -300,15 +354,8 @@ class Reline::LineEditor
|
|
300
354
|
else
|
301
355
|
new_lines = whole_lines
|
302
356
|
end
|
303
|
-
prompt_list =
|
304
|
-
|
305
|
-
prompt_list = @prompt_proc.(new_lines)
|
306
|
-
prompt = prompt_list[@line_index]
|
307
|
-
prompt_width = calculate_width(prompt, true)
|
308
|
-
end
|
309
|
-
all_height = new_lines.inject(0) { |result, line|
|
310
|
-
result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
|
311
|
-
}
|
357
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
358
|
+
all_height = calculate_height_by_lines(new_lines, prompt_list)
|
312
359
|
diff = all_height - @highest_in_all
|
313
360
|
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
314
361
|
if diff > 0
|
@@ -348,9 +395,7 @@ class Reline::LineEditor
|
|
348
395
|
if @line_index.zero?
|
349
396
|
0
|
350
397
|
else
|
351
|
-
@buffer_of_lines[0..(@line_index - 1)]
|
352
|
-
result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
|
353
|
-
}
|
398
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
354
399
|
end
|
355
400
|
if @prompt_proc
|
356
401
|
prompt = prompt_list[@line_index]
|
@@ -369,12 +414,7 @@ class Reline::LineEditor
|
|
369
414
|
Reline::IOGate.move_cursor_column(0)
|
370
415
|
back = 0
|
371
416
|
new_buffer = whole_lines
|
372
|
-
prompt_list =
|
373
|
-
if @prompt_proc
|
374
|
-
prompt_list = @prompt_proc.(new_buffer)
|
375
|
-
prompt = prompt_list[@line_index]
|
376
|
-
prompt_width = calculate_width(prompt, true)
|
377
|
-
end
|
417
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
378
418
|
new_buffer.each_with_index do |line, index|
|
379
419
|
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
380
420
|
width = prompt_width + calculate_width(line)
|
@@ -414,9 +454,7 @@ class Reline::LineEditor
|
|
414
454
|
if @line_index.zero?
|
415
455
|
0
|
416
456
|
else
|
417
|
-
new_buffer[0..(@line_index - 1)]
|
418
|
-
result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
|
419
|
-
}
|
457
|
+
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list)
|
420
458
|
end
|
421
459
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
422
460
|
move_cursor_down(@first_line_started_from + @started_from)
|
@@ -426,12 +464,7 @@ class Reline::LineEditor
|
|
426
464
|
end
|
427
465
|
line = modify_lines(whole_lines)[@line_index]
|
428
466
|
if @is_multiline
|
429
|
-
prompt_list =
|
430
|
-
if @prompt_proc
|
431
|
-
prompt_list = @prompt_proc.(whole_lines)
|
432
|
-
prompt = prompt_list[@line_index]
|
433
|
-
prompt_width = calculate_width(prompt, true)
|
434
|
-
end
|
467
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
435
468
|
if finished?
|
436
469
|
# Always rerender on finish because output_modifier_proc may return a different output.
|
437
470
|
render_partial(prompt, prompt_width, line)
|
@@ -477,6 +510,7 @@ class Reline::LineEditor
|
|
477
510
|
next
|
478
511
|
end
|
479
512
|
@output.print line
|
513
|
+
@output.flush
|
480
514
|
if @first_prompt
|
481
515
|
@first_prompt = false
|
482
516
|
@pre_input_hook&.call
|
@@ -619,9 +653,9 @@ class Reline::LineEditor
|
|
619
653
|
else
|
620
654
|
old_waiting_proc = @waiting_proc
|
621
655
|
old_waiting_operator_proc = @waiting_operator_proc
|
622
|
-
@waiting_proc = proc { |
|
656
|
+
@waiting_proc = proc { |k|
|
623
657
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
624
|
-
old_waiting_proc.(
|
658
|
+
old_waiting_proc.(k)
|
625
659
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
626
660
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
627
661
|
@waiting_operator_proc.(cursor_diff, byte_pointer_diff)
|
@@ -732,7 +766,7 @@ class Reline::LineEditor
|
|
732
766
|
end
|
733
767
|
|
734
768
|
def input_key(key)
|
735
|
-
if key.
|
769
|
+
if key.char.nil?
|
736
770
|
if @first_char
|
737
771
|
@line = nil
|
738
772
|
end
|
@@ -772,6 +806,26 @@ class Reline::LineEditor
|
|
772
806
|
|
773
807
|
private def process_auto_indent
|
774
808
|
return if not @check_new_auto_indent and @previous_line_index # move cursor up or down
|
809
|
+
if @check_new_auto_indent and @previous_line_index and @previous_line_index > 0 and @line_index > @previous_line_index
|
810
|
+
# Fix indent of a line when a newline is inserted to the next
|
811
|
+
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
812
|
+
new_indent = @auto_indent_proc.(new_lines[0..-3].push(''), @line_index - 1, 0, true)
|
813
|
+
md = @line.match(/\A */)
|
814
|
+
prev_indent = md[0].count(' ')
|
815
|
+
@line = ' ' * new_indent + @line.lstrip
|
816
|
+
|
817
|
+
new_indent = nil
|
818
|
+
(new_lines[-2].size + 1).times do |n|
|
819
|
+
result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, n, false)
|
820
|
+
if result
|
821
|
+
new_indent = result
|
822
|
+
break
|
823
|
+
end
|
824
|
+
end
|
825
|
+
if new_indent&.>= 0
|
826
|
+
@line = ' ' * new_indent + @line.lstrip
|
827
|
+
end
|
828
|
+
end
|
775
829
|
if @previous_line_index
|
776
830
|
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
777
831
|
else
|
@@ -801,17 +855,25 @@ class Reline::LineEditor
|
|
801
855
|
rest = nil
|
802
856
|
break_pointer = nil
|
803
857
|
quote = nil
|
858
|
+
closing_quote = nil
|
859
|
+
escaped_quote = nil
|
804
860
|
i = 0
|
805
861
|
while i < @byte_pointer do
|
806
862
|
slice = @line.byteslice(i, @byte_pointer - i)
|
807
|
-
|
863
|
+
unless slice.valid_encoding?
|
864
|
+
i += 1
|
865
|
+
next
|
866
|
+
end
|
867
|
+
if quote and slice.start_with?(closing_quote)
|
808
868
|
quote = nil
|
809
869
|
i += 1
|
810
|
-
elsif quote and slice.start_with?(
|
870
|
+
elsif quote and slice.start_with?(escaped_quote)
|
811
871
|
# skip
|
812
872
|
i += 2
|
813
873
|
elsif slice =~ quote_characters_regexp # find new "
|
814
874
|
quote = $&
|
875
|
+
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
|
876
|
+
escaped_quote = /\\#{Regexp.escape(quote)}/
|
815
877
|
i += 1
|
816
878
|
elsif not quote and slice =~ word_break_regexp
|
817
879
|
rest = $'
|
@@ -839,11 +901,7 @@ class Reline::LineEditor
|
|
839
901
|
else
|
840
902
|
temp_buffer[@line_index] = @line
|
841
903
|
end
|
842
|
-
|
843
|
-
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
|
844
|
-
else
|
845
|
-
false
|
846
|
-
end
|
904
|
+
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
|
847
905
|
end
|
848
906
|
|
849
907
|
def insert_text(text)
|
@@ -959,8 +1017,8 @@ class Reline::LineEditor
|
|
959
1017
|
end
|
960
1018
|
width
|
961
1019
|
else
|
962
|
-
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |
|
963
|
-
|
1020
|
+
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
|
1021
|
+
w + Reline::Unicode.get_mbchar_width(gc)
|
964
1022
|
}
|
965
1023
|
end
|
966
1024
|
end
|
@@ -981,6 +1039,8 @@ class Reline::LineEditor
|
|
981
1039
|
end
|
982
1040
|
end
|
983
1041
|
|
1042
|
+
private def ed_unassigned(key) end # do nothing
|
1043
|
+
|
984
1044
|
private def ed_insert(key)
|
985
1045
|
if key.instance_of?(String)
|
986
1046
|
width = Reline::Unicode.get_mbchar_width(key)
|
@@ -1085,7 +1145,11 @@ class Reline::LineEditor
|
|
1085
1145
|
alias_method :end_of_line, :ed_move_to_end
|
1086
1146
|
|
1087
1147
|
private def ed_search_prev_history(key)
|
1088
|
-
|
1148
|
+
if @is_multiline
|
1149
|
+
@line_backup_in_history = whole_buffer
|
1150
|
+
else
|
1151
|
+
@line_backup_in_history = @line
|
1152
|
+
end
|
1089
1153
|
searcher = Fiber.new do
|
1090
1154
|
search_word = String.new(encoding: @encoding)
|
1091
1155
|
multibyte_buf = String.new(encoding: 'ASCII-8BIT')
|
@@ -1120,18 +1184,32 @@ class Reline::LineEditor
|
|
1120
1184
|
end
|
1121
1185
|
end
|
1122
1186
|
if hit
|
1123
|
-
@
|
1124
|
-
|
1187
|
+
if @is_multiline
|
1188
|
+
@buffer_of_lines = hit.split("\n")
|
1189
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1190
|
+
@line_index = @buffer_of_lines.size - 1
|
1191
|
+
@line = @buffer_of_lines.last
|
1192
|
+
@rerender_all = true
|
1193
|
+
@searching_prompt = "(reverse-i-search)`%s'" % [search_word]
|
1194
|
+
else
|
1195
|
+
@line = hit
|
1196
|
+
@searching_prompt = "(reverse-i-search)`%s': %s" % [search_word, hit]
|
1197
|
+
end
|
1125
1198
|
last_hit = hit
|
1126
1199
|
else
|
1127
|
-
@
|
1200
|
+
if @is_multiline
|
1201
|
+
@rerender_all = true
|
1202
|
+
@searching_prompt = "(failed reverse-i-search)`%s'" % [search_word]
|
1203
|
+
else
|
1204
|
+
@searching_prompt = "(failed reverse-i-search)`%s': %s" % [search_word, last_hit]
|
1205
|
+
end
|
1128
1206
|
end
|
1129
1207
|
end
|
1130
1208
|
end
|
1131
1209
|
searcher.resume
|
1132
1210
|
@searching_prompt = "(reverse-i-search)`': "
|
1133
|
-
@waiting_proc = ->(
|
1134
|
-
case
|
1211
|
+
@waiting_proc = ->(k) {
|
1212
|
+
case k
|
1135
1213
|
when "\C-j".ord, "\C-?".ord
|
1136
1214
|
if @history_pointer
|
1137
1215
|
@line = Reline::HISTORY[@history_pointer]
|
@@ -1151,14 +1229,25 @@ class Reline::LineEditor
|
|
1151
1229
|
@cursor_max = calculate_width(@line)
|
1152
1230
|
@cursor = @byte_pointer = 0
|
1153
1231
|
else
|
1154
|
-
chr =
|
1155
|
-
if chr.match?(/[[:print:]]/)
|
1156
|
-
searcher.resume(
|
1232
|
+
chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
|
1233
|
+
if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == 127
|
1234
|
+
searcher.resume(k)
|
1157
1235
|
else
|
1158
1236
|
if @history_pointer
|
1159
|
-
|
1237
|
+
line = Reline::HISTORY[@history_pointer]
|
1238
|
+
else
|
1239
|
+
line = @line_backup_in_history
|
1240
|
+
end
|
1241
|
+
if @is_multiline
|
1242
|
+
@line_backup_in_history = whole_buffer
|
1243
|
+
@buffer_of_lines = line.split("\n")
|
1244
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1245
|
+
@line_index = @buffer_of_lines.size - 1
|
1246
|
+
@line = @buffer_of_lines.last
|
1247
|
+
@rerender_all = true
|
1160
1248
|
else
|
1161
|
-
@
|
1249
|
+
@line_backup_in_history = @line
|
1250
|
+
@line = line
|
1162
1251
|
end
|
1163
1252
|
@searching_prompt = nil
|
1164
1253
|
@waiting_proc = nil
|
@@ -1211,7 +1300,7 @@ class Reline::LineEditor
|
|
1211
1300
|
@line = Reline::HISTORY[@history_pointer]
|
1212
1301
|
end
|
1213
1302
|
end
|
1214
|
-
if @config.editing_mode_is?(:emacs)
|
1303
|
+
if @config.editing_mode_is?(:emacs, :vi_insert)
|
1215
1304
|
@cursor_max = @cursor = calculate_width(@line)
|
1216
1305
|
@byte_pointer = @line.bytesize
|
1217
1306
|
elsif @config.editing_mode_is?(:vi_command)
|
@@ -1258,7 +1347,7 @@ class Reline::LineEditor
|
|
1258
1347
|
end
|
1259
1348
|
end
|
1260
1349
|
@line = '' unless @line
|
1261
|
-
if @config.editing_mode_is?(:emacs)
|
1350
|
+
if @config.editing_mode_is?(:emacs, :vi_insert)
|
1262
1351
|
@cursor_max = @cursor = calculate_width(@line)
|
1263
1352
|
@byte_pointer = @line.bytesize
|
1264
1353
|
elsif @config.editing_mode_is?(:vi_command)
|
@@ -1697,8 +1786,8 @@ class Reline::LineEditor
|
|
1697
1786
|
end
|
1698
1787
|
|
1699
1788
|
private def ed_delete_next_char(key, arg: 1)
|
1700
|
-
|
1701
|
-
|
1789
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
1790
|
+
unless @line.empty? || byte_size == 0
|
1702
1791
|
@line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
|
1703
1792
|
copy_for_vi(mbchar)
|
1704
1793
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
@@ -1795,13 +1884,13 @@ class Reline::LineEditor
|
|
1795
1884
|
end
|
1796
1885
|
|
1797
1886
|
private def vi_replace_char(key, arg: 1)
|
1798
|
-
@waiting_proc = ->(
|
1887
|
+
@waiting_proc = ->(k) {
|
1799
1888
|
if arg == 1
|
1800
1889
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
1801
1890
|
before = @line.byteslice(0, @byte_pointer)
|
1802
1891
|
remaining_point = @byte_pointer + byte_size
|
1803
1892
|
after = @line.byteslice(remaining_point, @line.size - remaining_point)
|
1804
|
-
@line = before +
|
1893
|
+
@line = before + k.chr + after
|
1805
1894
|
@cursor_max = calculate_width(@line)
|
1806
1895
|
@waiting_proc = nil
|
1807
1896
|
elsif arg > 1
|
@@ -1812,7 +1901,7 @@ class Reline::LineEditor
|
|
1812
1901
|
before = @line.byteslice(0, @byte_pointer)
|
1813
1902
|
remaining_point = @byte_pointer + byte_size
|
1814
1903
|
after = @line.byteslice(remaining_point, @line.size - remaining_point)
|
1815
|
-
replaced =
|
1904
|
+
replaced = k.chr * arg
|
1816
1905
|
@line = before + replaced + after
|
1817
1906
|
@byte_pointer += replaced.bytesize
|
1818
1907
|
@cursor += calculate_width(replaced)
|
@@ -1873,4 +1962,20 @@ class Reline::LineEditor
|
|
1873
1962
|
arg -= 1
|
1874
1963
|
vi_join_lines(key, arg: arg) if arg > 0
|
1875
1964
|
end
|
1965
|
+
|
1966
|
+
private def em_set_mark(key)
|
1967
|
+
@mark_pointer = [@byte_pointer, @line_index]
|
1968
|
+
end
|
1969
|
+
alias_method :set_mark, :em_set_mark
|
1970
|
+
|
1971
|
+
private def em_exchange_mark(key)
|
1972
|
+
new_pointer = [@byte_pointer, @line_index]
|
1973
|
+
@previous_line_index = @line_index
|
1974
|
+
@byte_pointer, @line_index = @mark_pointer
|
1975
|
+
@byte_pointer, @line_index = @mark_pointer
|
1976
|
+
@cursor = calculate_width(@line.byteslice(0, @byte_pointer))
|
1977
|
+
@cursor_max = calculate_width(@line)
|
1978
|
+
@mark_pointer = new_pointer
|
1979
|
+
end
|
1980
|
+
alias_method :exchange_point_and_mark, :em_exchange_mark
|
1876
1981
|
end
|