reline 0.1.2 → 0.1.7
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 +4 -0
- data/lib/reline.rb +47 -25
- data/lib/reline/ansi.rb +95 -18
- data/lib/reline/config.rb +69 -10
- data/lib/reline/general_io.rb +12 -0
- data/lib/reline/history.rb +33 -13
- data/lib/reline/key_actor/emacs.rb +6 -6
- data/lib/reline/key_actor/vi_command.rb +2 -2
- data/lib/reline/key_actor/vi_insert.rb +2 -2
- data/lib/reline/key_stroke.rb +2 -0
- data/lib/reline/line_editor.rb +350 -126
- data/lib/reline/line_editor.rb.orig +2384 -0
- data/lib/reline/line_editor.rb.rej +81 -0
- data/lib/reline/sibori.rb +170 -0
- data/lib/reline/unicode.rb +93 -15
- data/lib/reline/unicode/east_asian_width.rb +1149 -1130
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +65 -5
- metadata +24 -7
data/lib/reline/general_io.rb
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
require 'timeout'
|
2
2
|
|
3
3
|
class Reline::GeneralIO
|
4
|
+
def self.encoding
|
5
|
+
RUBY_PLATFORM =~ /mswin|mingw/ ? Encoding::UTF_8 : Encoding::default_external
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.win?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
4
12
|
RAW_KEYSTROKE_CONFIG = {}
|
5
13
|
|
6
14
|
@@buf = []
|
@@ -59,6 +67,10 @@ class Reline::GeneralIO
|
|
59
67
|
def self.set_winch_handler(&handler)
|
60
68
|
end
|
61
69
|
|
70
|
+
def self.in_pasting?
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
62
74
|
def self.prep
|
63
75
|
end
|
64
76
|
|
data/lib/reline/history.rb
CHANGED
@@ -19,7 +19,7 @@ class Reline::History < Array
|
|
19
19
|
|
20
20
|
def []=(index, val)
|
21
21
|
index = check_index(index)
|
22
|
-
super(index, String.new(val, encoding:
|
22
|
+
super(index, String.new(val, encoding: Reline.encoding_system_needs))
|
23
23
|
end
|
24
24
|
|
25
25
|
def concat(*val)
|
@@ -29,27 +29,47 @@ class Reline::History < Array
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def push(*val)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
32
|
+
# If history_size is zero, all histories are dropped.
|
33
|
+
return self if @config.history_size.zero?
|
34
|
+
# If history_size is negative, history size is unlimited.
|
35
|
+
if @config.history_size.positive?
|
36
|
+
diff = size + val.size - @config.history_size
|
37
|
+
if diff > 0
|
38
|
+
if diff <= size
|
39
|
+
shift(diff)
|
40
|
+
else
|
41
|
+
diff -= size
|
42
|
+
clear
|
43
|
+
val.shift(diff)
|
44
|
+
end
|
40
45
|
end
|
41
46
|
end
|
42
|
-
super(*(val.map{ |v|
|
47
|
+
super(*(val.map{ |v|
|
48
|
+
String.new(v, encoding: Reline.encoding_system_needs)
|
49
|
+
}))
|
43
50
|
end
|
44
51
|
|
45
52
|
def <<(val)
|
46
|
-
|
47
|
-
|
53
|
+
# If history_size is zero, all histories are dropped.
|
54
|
+
return self if @config.history_size.zero?
|
55
|
+
# If history_size is negative, history size is unlimited.
|
56
|
+
if @config.history_size.positive?
|
57
|
+
shift if size + 1 > @config.history_size
|
58
|
+
end
|
59
|
+
super(String.new(val, encoding: Reline.encoding_system_needs))
|
48
60
|
end
|
49
61
|
|
50
62
|
private def check_index(index)
|
51
63
|
index += size if index < 0
|
52
|
-
|
64
|
+
if index < -2147483648 or 2147483647 < index
|
65
|
+
raise RangeError.new("integer #{index} too big to convert to `int'")
|
66
|
+
end
|
67
|
+
# If history_size is negative, history size is unlimited.
|
68
|
+
if @config.history_size.positive?
|
69
|
+
if index < -@config.history_size or @config.history_size < index
|
70
|
+
raise RangeError.new("index=<#{index}>")
|
71
|
+
end
|
72
|
+
end
|
53
73
|
raise IndexError.new("index=<#{index}>") if index < 0 or size <= index
|
54
74
|
index
|
55
75
|
end
|
@@ -37,9 +37,9 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
|
|
37
37
|
# 17 ^Q
|
38
38
|
:ed_quoted_insert,
|
39
39
|
# 18 ^R
|
40
|
-
:
|
40
|
+
:vi_search_prev,
|
41
41
|
# 19 ^S
|
42
|
-
:
|
42
|
+
:vi_search_next,
|
43
43
|
# 20 ^T
|
44
44
|
:ed_transpose_chars,
|
45
45
|
# 21 ^U
|
@@ -413,11 +413,11 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
|
|
413
413
|
# 205 M-M
|
414
414
|
:ed_unassigned,
|
415
415
|
# 206 M-N
|
416
|
-
:
|
416
|
+
:vi_search_next,
|
417
417
|
# 207 M-O
|
418
418
|
:ed_sequence_lead_in,
|
419
419
|
# 208 M-P
|
420
|
-
:
|
420
|
+
:vi_search_prev,
|
421
421
|
# 209 M-Q
|
422
422
|
:ed_unassigned,
|
423
423
|
# 210 M-R
|
@@ -477,11 +477,11 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
|
|
477
477
|
# 237 M-m
|
478
478
|
:ed_unassigned,
|
479
479
|
# 238 M-n
|
480
|
-
:
|
480
|
+
:vi_search_next,
|
481
481
|
# 239 M-o
|
482
482
|
:ed_unassigned,
|
483
483
|
# 240 M-p
|
484
|
-
:
|
484
|
+
:vi_search_prev,
|
485
485
|
# 241 M-q
|
486
486
|
:ed_unassigned,
|
487
487
|
# 242 M-r
|
@@ -37,7 +37,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
|
|
37
37
|
# 17 ^Q
|
38
38
|
:ed_ignore,
|
39
39
|
# 18 ^R
|
40
|
-
:
|
40
|
+
:vi_search_prev,
|
41
41
|
# 19 ^S
|
42
42
|
:ed_ignore,
|
43
43
|
# 20 ^T
|
@@ -151,7 +151,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
|
|
151
151
|
# 74 J
|
152
152
|
:vi_join_lines,
|
153
153
|
# 75 K
|
154
|
-
:
|
154
|
+
:vi_search_prev,
|
155
155
|
# 76 L
|
156
156
|
:ed_unassigned,
|
157
157
|
# 77 M
|
data/lib/reline/key_stroke.rb
CHANGED
data/lib/reline/line_editor.rb
CHANGED
@@ -2,7 +2,6 @@ require 'reline/kill_ring'
|
|
2
2
|
require 'reline/unicode'
|
3
3
|
|
4
4
|
require 'tempfile'
|
5
|
-
require 'pathname'
|
6
5
|
|
7
6
|
class Reline::LineEditor
|
8
7
|
# TODO: undo
|
@@ -51,16 +50,18 @@ class Reline::LineEditor
|
|
51
50
|
CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
|
52
51
|
MenuInfo = Struct.new('MenuInfo', :target, :list)
|
53
52
|
|
54
|
-
|
55
|
-
OSC_REGEXP = /\e\]\d+(?:;[^;]+)*\a/
|
56
|
-
NON_PRINTING_START = "\1"
|
57
|
-
NON_PRINTING_END = "\2"
|
58
|
-
WIDTH_SCANNER = /\G(?:#{NON_PRINTING_START}|#{NON_PRINTING_END}|#{CSI_REGEXP}|#{OSC_REGEXP}|\X)/
|
59
|
-
|
60
|
-
def initialize(config)
|
53
|
+
def initialize(config, encoding)
|
61
54
|
@config = config
|
62
55
|
@completion_append_character = ''
|
63
|
-
reset_variables
|
56
|
+
reset_variables(encoding: encoding)
|
57
|
+
end
|
58
|
+
|
59
|
+
def simplified_rendering?
|
60
|
+
if finished?
|
61
|
+
false
|
62
|
+
else
|
63
|
+
not @rerender_all and not finished? and Reline::IOGate.in_pasting?
|
64
|
+
end
|
64
65
|
end
|
65
66
|
|
66
67
|
private def check_multiline_prompt(buffer, prompt)
|
@@ -73,22 +74,47 @@ class Reline::LineEditor
|
|
73
74
|
else
|
74
75
|
prompt = @prompt
|
75
76
|
end
|
77
|
+
return [prompt, calculate_width(prompt, true), [prompt] * buffer.size] if simplified_rendering?
|
76
78
|
if @prompt_proc
|
77
79
|
prompt_list = @prompt_proc.(buffer)
|
78
80
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
81
|
+
if @config.show_mode_in_prompt
|
82
|
+
if @config.editing_mode_is?(:vi_command)
|
83
|
+
mode_icon = @config.vi_cmd_mode_icon
|
84
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
85
|
+
mode_icon = @config.vi_ins_mode_icon
|
86
|
+
elsif @config.editing_mode_is?(:emacs)
|
87
|
+
mode_icon = @config.emacs_mode_string
|
88
|
+
else
|
89
|
+
mode_icon = '?'
|
90
|
+
end
|
91
|
+
prompt_list.map!{ |pr| mode_icon + pr }
|
92
|
+
end
|
79
93
|
prompt = prompt_list[@line_index]
|
80
94
|
prompt_width = calculate_width(prompt, true)
|
81
95
|
[prompt, prompt_width, prompt_list]
|
82
96
|
else
|
83
97
|
prompt_width = calculate_width(prompt, true)
|
98
|
+
if @config.show_mode_in_prompt
|
99
|
+
if @config.editing_mode_is?(:vi_command)
|
100
|
+
mode_icon = @config.vi_cmd_mode_icon
|
101
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
102
|
+
mode_icon = @config.vi_ins_mode_icon
|
103
|
+
elsif @config.editing_mode_is?(:emacs)
|
104
|
+
mode_icon = @config.emacs_mode_string
|
105
|
+
else
|
106
|
+
mode_icon = '?'
|
107
|
+
end
|
108
|
+
prompt = mode_icon + prompt
|
109
|
+
end
|
84
110
|
[prompt, prompt_width, nil]
|
85
111
|
end
|
86
112
|
end
|
87
113
|
|
88
|
-
def reset(prompt = '', encoding
|
114
|
+
def reset(prompt = '', encoding:)
|
89
115
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
90
116
|
@screen_size = Reline::IOGate.get_screen_size
|
91
|
-
reset_variables(prompt, encoding)
|
117
|
+
reset_variables(prompt, encoding: encoding)
|
92
118
|
@old_trap = Signal.trap('SIGINT') {
|
93
119
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
94
120
|
raise Interrupt
|
@@ -116,7 +142,7 @@ class Reline::LineEditor
|
|
116
142
|
if @line_index.zero?
|
117
143
|
0
|
118
144
|
else
|
119
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
145
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
120
146
|
end
|
121
147
|
if @prompt_proc
|
122
148
|
prompt = prompt_list[@line_index]
|
@@ -139,7 +165,7 @@ class Reline::LineEditor
|
|
139
165
|
@eof
|
140
166
|
end
|
141
167
|
|
142
|
-
def reset_variables(prompt = '', encoding
|
168
|
+
def reset_variables(prompt = '', encoding:)
|
143
169
|
@prompt = prompt
|
144
170
|
@mark_pointer = nil
|
145
171
|
@encoding = encoding
|
@@ -161,6 +187,7 @@ class Reline::LineEditor
|
|
161
187
|
@searching_prompt = nil
|
162
188
|
@first_char = true
|
163
189
|
@eof = false
|
190
|
+
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
164
191
|
reset_line
|
165
192
|
end
|
166
193
|
|
@@ -190,10 +217,10 @@ class Reline::LineEditor
|
|
190
217
|
@is_multiline = false
|
191
218
|
end
|
192
219
|
|
193
|
-
private def calculate_height_by_lines(lines,
|
220
|
+
private def calculate_height_by_lines(lines, prompt)
|
194
221
|
result = 0
|
222
|
+
prompt_list = prompt.is_a?(Array) ? prompt : nil
|
195
223
|
lines.each_with_index { |line, i|
|
196
|
-
prompt = ''
|
197
224
|
prompt = prompt_list[i] if prompt_list and prompt_list[i]
|
198
225
|
result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
|
199
226
|
}
|
@@ -211,40 +238,8 @@ class Reline::LineEditor
|
|
211
238
|
width.div(@screen_size.last) + 1
|
212
239
|
end
|
213
240
|
|
214
|
-
private def split_by_width(
|
215
|
-
|
216
|
-
height = 1
|
217
|
-
width = 0
|
218
|
-
rest = "#{prompt}#{str}".encode(Encoding::UTF_8)
|
219
|
-
in_zero_width = false
|
220
|
-
rest.scan(WIDTH_SCANNER) do |gc|
|
221
|
-
case gc
|
222
|
-
when NON_PRINTING_START
|
223
|
-
in_zero_width = true
|
224
|
-
when NON_PRINTING_END
|
225
|
-
in_zero_width = false
|
226
|
-
when CSI_REGEXP, OSC_REGEXP
|
227
|
-
lines.last << gc
|
228
|
-
else
|
229
|
-
unless in_zero_width
|
230
|
-
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
|
231
|
-
if (width += mbchar_width) > max_width
|
232
|
-
width = mbchar_width
|
233
|
-
lines << nil
|
234
|
-
lines << String.new(encoding: @encoding)
|
235
|
-
height += 1
|
236
|
-
end
|
237
|
-
end
|
238
|
-
lines.last << gc
|
239
|
-
end
|
240
|
-
end
|
241
|
-
# The cursor moves to next line in first
|
242
|
-
if width == max_width
|
243
|
-
lines << nil
|
244
|
-
lines << String.new(encoding: @encoding)
|
245
|
-
height += 1
|
246
|
-
end
|
247
|
-
[lines, height]
|
241
|
+
private def split_by_width(str, max_width)
|
242
|
+
Reline::Unicode.split_by_width(str, max_width, @encoding)
|
248
243
|
end
|
249
244
|
|
250
245
|
private def scroll_down(val)
|
@@ -312,14 +307,19 @@ class Reline::LineEditor
|
|
312
307
|
@byte_pointer = new_byte_pointer
|
313
308
|
end
|
314
309
|
|
310
|
+
def rerender_all
|
311
|
+
@rerender_all = true
|
312
|
+
rerender
|
313
|
+
end
|
314
|
+
|
315
315
|
def rerender
|
316
316
|
return if @line.nil?
|
317
317
|
if @menu_info
|
318
318
|
scroll_down(@highest_in_all - @first_line_started_from)
|
319
319
|
@rerender_all = true
|
320
|
-
@menu_info.list.each do |item|
|
320
|
+
@menu_info.list.sort!.each do |item|
|
321
321
|
Reline::IOGate.move_cursor_column(0)
|
322
|
-
@output.
|
322
|
+
@output.write item
|
323
323
|
@output.flush
|
324
324
|
scroll_down(1)
|
325
325
|
end
|
@@ -358,7 +358,7 @@ class Reline::LineEditor
|
|
358
358
|
new_lines = whole_lines
|
359
359
|
end
|
360
360
|
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
361
|
-
all_height = calculate_height_by_lines(new_lines, prompt_list)
|
361
|
+
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
362
362
|
diff = all_height - @highest_in_all
|
363
363
|
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
364
364
|
if diff > 0
|
@@ -398,7 +398,7 @@ class Reline::LineEditor
|
|
398
398
|
if @line_index.zero?
|
399
399
|
0
|
400
400
|
else
|
401
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
401
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
402
402
|
end
|
403
403
|
if @prompt_proc
|
404
404
|
prompt = prompt_list[@line_index]
|
@@ -457,7 +457,7 @@ class Reline::LineEditor
|
|
457
457
|
if @line_index.zero?
|
458
458
|
0
|
459
459
|
else
|
460
|
-
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list)
|
460
|
+
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
461
461
|
end
|
462
462
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
463
463
|
move_cursor_down(@first_line_started_from + @started_from)
|
@@ -488,7 +488,7 @@ class Reline::LineEditor
|
|
488
488
|
end
|
489
489
|
|
490
490
|
private def render_partial(prompt, prompt_width, line_to_render, with_control = true)
|
491
|
-
visual_lines, height = split_by_width(
|
491
|
+
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
|
492
492
|
if with_control
|
493
493
|
if height > @highest_in_this
|
494
494
|
diff = height - @highest_in_this
|
@@ -507,12 +507,30 @@ class Reline::LineEditor
|
|
507
507
|
Reline::IOGate.move_cursor_column(0)
|
508
508
|
visual_lines.each_with_index do |line, index|
|
509
509
|
if line.nil?
|
510
|
-
Reline::IOGate.
|
511
|
-
|
512
|
-
|
510
|
+
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
511
|
+
# reaches the end of line
|
512
|
+
if Reline::IOGate.win?
|
513
|
+
# A newline is automatically inserted if a character is rendered at
|
514
|
+
# eol on command prompt.
|
515
|
+
else
|
516
|
+
# When the cursor is at the end of the line and erases characters
|
517
|
+
# after the cursor, some terminals delete the character at the
|
518
|
+
# cursor position.
|
519
|
+
move_cursor_down(1)
|
520
|
+
Reline::IOGate.move_cursor_column(0)
|
521
|
+
end
|
522
|
+
else
|
523
|
+
Reline::IOGate.erase_after_cursor
|
524
|
+
move_cursor_down(1)
|
525
|
+
Reline::IOGate.move_cursor_column(0)
|
526
|
+
end
|
513
527
|
next
|
514
528
|
end
|
515
|
-
@output.
|
529
|
+
@output.write line
|
530
|
+
if Reline::IOGate.win? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
|
531
|
+
# A newline is automatically inserted if a character is rendered at eol on command prompt.
|
532
|
+
@rest_height -= 1 if @rest_height > 0
|
533
|
+
end
|
516
534
|
@output.flush
|
517
535
|
if @first_prompt
|
518
536
|
@first_prompt = false
|
@@ -520,22 +538,25 @@ class Reline::LineEditor
|
|
520
538
|
end
|
521
539
|
end
|
522
540
|
Reline::IOGate.erase_after_cursor
|
541
|
+
Reline::IOGate.move_cursor_column(0)
|
523
542
|
if with_control
|
524
|
-
|
543
|
+
# Just after rendring, so the cursor is on the last line.
|
525
544
|
if finished?
|
526
|
-
|
545
|
+
Reline::IOGate.move_cursor_column(0)
|
546
|
+
else
|
547
|
+
# Moves up from bottom of lines to the cursor position.
|
548
|
+
move_cursor_up(height - 1 - @started_from)
|
549
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
527
550
|
end
|
528
|
-
move_cursor_down(@started_from)
|
529
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
530
551
|
end
|
531
552
|
height
|
532
553
|
end
|
533
554
|
|
534
555
|
private def modify_lines(before)
|
535
|
-
return before if before.nil? || before.empty?
|
556
|
+
return before if before.nil? || before.empty? || simplified_rendering?
|
536
557
|
|
537
558
|
if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?)
|
538
|
-
after.lines(chomp
|
559
|
+
after.lines("\n").map { |l| l.chomp('') }
|
539
560
|
else
|
540
561
|
before
|
541
562
|
end
|
@@ -560,7 +581,7 @@ class Reline::LineEditor
|
|
560
581
|
else
|
561
582
|
i&.start_with?(target)
|
562
583
|
end
|
563
|
-
}
|
584
|
+
}.uniq
|
564
585
|
if is_menu
|
565
586
|
menu(target, list)
|
566
587
|
return nil
|
@@ -708,6 +729,18 @@ class Reline::LineEditor
|
|
708
729
|
method_obj and method_obj.parameters.length != 1
|
709
730
|
end
|
710
731
|
|
732
|
+
def wrap_method_call(method_symbol, method_obj, key)
|
733
|
+
if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
|
734
|
+
not_insertion = method_symbol != :ed_insert
|
735
|
+
process_insert(force: not_insertion)
|
736
|
+
end
|
737
|
+
if @vi_arg
|
738
|
+
method_obj.(key, arg: @vi_arg)
|
739
|
+
else
|
740
|
+
method_obj.(key)
|
741
|
+
end
|
742
|
+
end
|
743
|
+
|
711
744
|
private def process_key(key, method_symbol)
|
712
745
|
if method_symbol and respond_to?(method_symbol, true)
|
713
746
|
method_obj = method(method_symbol)
|
@@ -717,10 +750,10 @@ class Reline::LineEditor
|
|
717
750
|
if method_symbol and key.is_a?(Symbol)
|
718
751
|
if @vi_arg and argumentable?(method_obj)
|
719
752
|
run_for_operators(key, method_symbol) do
|
720
|
-
|
753
|
+
wrap_method_call(method_symbol, method_obj, key)
|
721
754
|
end
|
722
755
|
else
|
723
|
-
method_obj
|
756
|
+
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
724
757
|
end
|
725
758
|
@kill_ring.process
|
726
759
|
@vi_arg = nil
|
@@ -730,12 +763,12 @@ class Reline::LineEditor
|
|
730
763
|
else
|
731
764
|
if argumentable?(method_obj)
|
732
765
|
run_for_operators(key, method_symbol) do
|
733
|
-
|
766
|
+
wrap_method_call(method_symbol, method_obj, key)
|
734
767
|
end
|
735
768
|
elsif @waiting_proc
|
736
769
|
@waiting_proc.(key)
|
737
770
|
elsif method_obj
|
738
|
-
method_obj
|
771
|
+
wrap_method_call(method_symbol, method_obj, key)
|
739
772
|
else
|
740
773
|
ed_insert(key)
|
741
774
|
end
|
@@ -747,10 +780,10 @@ class Reline::LineEditor
|
|
747
780
|
@kill_ring.process
|
748
781
|
elsif method_obj
|
749
782
|
if method_symbol == :ed_argument_digit
|
750
|
-
method_obj
|
783
|
+
wrap_method_call(method_symbol, method_obj, key)
|
751
784
|
else
|
752
785
|
run_for_operators(key, method_symbol) do
|
753
|
-
method_obj
|
786
|
+
wrap_method_call(method_symbol, method_obj, key)
|
754
787
|
end
|
755
788
|
end
|
756
789
|
@kill_ring.process
|
@@ -812,6 +845,7 @@ class Reline::LineEditor
|
|
812
845
|
result = call_completion_proc
|
813
846
|
if result.is_a?(Array)
|
814
847
|
completion_occurs = true
|
848
|
+
process_insert
|
815
849
|
complete(result)
|
816
850
|
end
|
817
851
|
end
|
@@ -820,6 +854,7 @@ class Reline::LineEditor
|
|
820
854
|
result = call_completion_proc
|
821
855
|
if result.is_a?(Array)
|
822
856
|
completion_occurs = true
|
857
|
+
process_insert
|
823
858
|
move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
|
824
859
|
end
|
825
860
|
end
|
@@ -831,7 +866,7 @@ class Reline::LineEditor
|
|
831
866
|
unless completion_occurs
|
832
867
|
@completion_state = CompletionState::NORMAL
|
833
868
|
end
|
834
|
-
if @is_multiline and @auto_indent_proc
|
869
|
+
if @is_multiline and @auto_indent_proc and not simplified_rendering?
|
835
870
|
process_auto_indent
|
836
871
|
end
|
837
872
|
end
|
@@ -905,7 +940,6 @@ class Reline::LineEditor
|
|
905
940
|
quote = nil
|
906
941
|
i += 1
|
907
942
|
rest = nil
|
908
|
-
break_pointer = nil
|
909
943
|
elsif quote and slice.start_with?(escaped_quote)
|
910
944
|
# skip
|
911
945
|
i += 2
|
@@ -915,7 +949,7 @@ class Reline::LineEditor
|
|
915
949
|
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
|
916
950
|
escaped_quote = /\\#{Regexp.escape(quote)}/
|
917
951
|
i += 1
|
918
|
-
break_pointer = i
|
952
|
+
break_pointer = i - 1
|
919
953
|
elsif not quote and slice =~ word_break_regexp
|
920
954
|
rest = $'
|
921
955
|
i += 1
|
@@ -937,6 +971,11 @@ class Reline::LineEditor
|
|
937
971
|
end
|
938
972
|
else
|
939
973
|
preposing = ''
|
974
|
+
if break_pointer
|
975
|
+
preposing = @line.byteslice(0, break_pointer)
|
976
|
+
else
|
977
|
+
preposing = ''
|
978
|
+
end
|
940
979
|
target = before
|
941
980
|
end
|
942
981
|
[preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
|
@@ -1029,6 +1068,7 @@ class Reline::LineEditor
|
|
1029
1068
|
|
1030
1069
|
def finish
|
1031
1070
|
@finished = true
|
1071
|
+
@rerender_all = true
|
1032
1072
|
@config.reset
|
1033
1073
|
end
|
1034
1074
|
|
@@ -1046,29 +1086,7 @@ class Reline::LineEditor
|
|
1046
1086
|
end
|
1047
1087
|
|
1048
1088
|
private def calculate_width(str, allow_escape_code = false)
|
1049
|
-
|
1050
|
-
width = 0
|
1051
|
-
rest = str.encode(Encoding::UTF_8)
|
1052
|
-
in_zero_width = false
|
1053
|
-
rest.scan(WIDTH_SCANNER) do |gc|
|
1054
|
-
case gc
|
1055
|
-
when NON_PRINTING_START
|
1056
|
-
in_zero_width = true
|
1057
|
-
when NON_PRINTING_END
|
1058
|
-
in_zero_width = false
|
1059
|
-
when CSI_REGEXP, OSC_REGEXP
|
1060
|
-
else
|
1061
|
-
unless in_zero_width
|
1062
|
-
width += Reline::Unicode.get_mbchar_width(gc)
|
1063
|
-
end
|
1064
|
-
end
|
1065
|
-
end
|
1066
|
-
width
|
1067
|
-
else
|
1068
|
-
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
|
1069
|
-
w + Reline::Unicode.get_mbchar_width(gc)
|
1070
|
-
}
|
1071
|
-
end
|
1089
|
+
Reline::Unicode.calculate_width(str, allow_escape_code)
|
1072
1090
|
end
|
1073
1091
|
|
1074
1092
|
private def key_delete(key)
|
@@ -1089,28 +1107,55 @@ class Reline::LineEditor
|
|
1089
1107
|
|
1090
1108
|
private def ed_unassigned(key) end # do nothing
|
1091
1109
|
|
1110
|
+
private def process_insert(force: false)
|
1111
|
+
return if @continuous_insertion_buffer.empty? or (Reline::IOGate.in_pasting? and not force)
|
1112
|
+
width = Reline::Unicode.calculate_width(@continuous_insertion_buffer)
|
1113
|
+
bytesize = @continuous_insertion_buffer.bytesize
|
1114
|
+
if @cursor == @cursor_max
|
1115
|
+
@line += @continuous_insertion_buffer
|
1116
|
+
else
|
1117
|
+
@line = byteinsert(@line, @byte_pointer, @continuous_insertion_buffer)
|
1118
|
+
end
|
1119
|
+
@byte_pointer += bytesize
|
1120
|
+
@cursor += width
|
1121
|
+
@cursor_max += width
|
1122
|
+
@continuous_insertion_buffer.clear
|
1123
|
+
end
|
1124
|
+
|
1092
1125
|
private def ed_insert(key)
|
1126
|
+
str = nil
|
1127
|
+
width = nil
|
1128
|
+
bytesize = nil
|
1093
1129
|
if key.instance_of?(String)
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
@line = byteinsert(@line, @byte_pointer, key)
|
1130
|
+
begin
|
1131
|
+
key.encode(Encoding::UTF_8)
|
1132
|
+
rescue Encoding::UndefinedConversionError
|
1133
|
+
return
|
1099
1134
|
end
|
1100
|
-
|
1101
|
-
|
1102
|
-
@cursor_max += width
|
1135
|
+
str = key
|
1136
|
+
bytesize = key.bytesize
|
1103
1137
|
else
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1138
|
+
begin
|
1139
|
+
key.chr.encode(Encoding::UTF_8)
|
1140
|
+
rescue Encoding::UndefinedConversionError
|
1141
|
+
return
|
1108
1142
|
end
|
1109
|
-
|
1110
|
-
|
1111
|
-
@cursor += width
|
1112
|
-
@cursor_max += width
|
1143
|
+
str = key.chr
|
1144
|
+
bytesize = 1
|
1113
1145
|
end
|
1146
|
+
if Reline::IOGate.in_pasting?
|
1147
|
+
@continuous_insertion_buffer << str
|
1148
|
+
return
|
1149
|
+
end
|
1150
|
+
width = Reline::Unicode.get_mbchar_width(str)
|
1151
|
+
if @cursor == @cursor_max
|
1152
|
+
@line += str
|
1153
|
+
else
|
1154
|
+
@line = byteinsert(@line, @byte_pointer, str)
|
1155
|
+
end
|
1156
|
+
@byte_pointer += bytesize
|
1157
|
+
@cursor += width
|
1158
|
+
@cursor_max += width
|
1114
1159
|
end
|
1115
1160
|
alias_method :ed_digit, :ed_insert
|
1116
1161
|
alias_method :self_insert, :ed_insert
|
@@ -1236,7 +1281,7 @@ class Reline::LineEditor
|
|
1236
1281
|
if search_word.empty? and Reline.last_incremental_search
|
1237
1282
|
search_word = Reline.last_incremental_search
|
1238
1283
|
end
|
1239
|
-
if @history_pointer
|
1284
|
+
if @history_pointer
|
1240
1285
|
case prev_search_key
|
1241
1286
|
when "\C-r".ord
|
1242
1287
|
history_pointer_base = 0
|
@@ -1308,7 +1353,7 @@ class Reline::LineEditor
|
|
1308
1353
|
end
|
1309
1354
|
end
|
1310
1355
|
|
1311
|
-
private def
|
1356
|
+
private def incremental_search_history(key)
|
1312
1357
|
unless @history_pointer
|
1313
1358
|
if @is_multiline
|
1314
1359
|
@line_backup_in_history = whole_buffer
|
@@ -1389,15 +1434,114 @@ class Reline::LineEditor
|
|
1389
1434
|
}
|
1390
1435
|
end
|
1391
1436
|
|
1392
|
-
private def
|
1393
|
-
|
1437
|
+
private def vi_search_prev(key)
|
1438
|
+
incremental_search_history(key)
|
1439
|
+
end
|
1440
|
+
alias_method :reverse_search_history, :vi_search_prev
|
1441
|
+
|
1442
|
+
private def vi_search_next(key)
|
1443
|
+
incremental_search_history(key)
|
1444
|
+
end
|
1445
|
+
alias_method :forward_search_history, :vi_search_next
|
1446
|
+
|
1447
|
+
private def ed_search_prev_history(key, arg: 1)
|
1448
|
+
history = nil
|
1449
|
+
h_pointer = nil
|
1450
|
+
line_no = nil
|
1451
|
+
substr = @line.slice(0, @byte_pointer)
|
1452
|
+
if @history_pointer.nil?
|
1453
|
+
return if not @line.empty? and substr.empty?
|
1454
|
+
history = Reline::HISTORY
|
1455
|
+
elsif @history_pointer.zero?
|
1456
|
+
history = nil
|
1457
|
+
h_pointer = nil
|
1458
|
+
else
|
1459
|
+
history = Reline::HISTORY.slice(0, @history_pointer)
|
1460
|
+
end
|
1461
|
+
return if history.nil?
|
1462
|
+
if @is_multiline
|
1463
|
+
h_pointer = history.rindex { |h|
|
1464
|
+
h.split("\n").each_with_index { |l, i|
|
1465
|
+
if l.start_with?(substr)
|
1466
|
+
line_no = i
|
1467
|
+
break
|
1468
|
+
end
|
1469
|
+
}
|
1470
|
+
not line_no.nil?
|
1471
|
+
}
|
1472
|
+
else
|
1473
|
+
h_pointer = history.rindex { |l|
|
1474
|
+
l.start_with?(substr)
|
1475
|
+
}
|
1476
|
+
end
|
1477
|
+
return if h_pointer.nil?
|
1478
|
+
@history_pointer = h_pointer
|
1479
|
+
if @is_multiline
|
1480
|
+
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1481
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1482
|
+
@line_index = line_no
|
1483
|
+
@line = @buffer_of_lines.last
|
1484
|
+
@rerender_all = true
|
1485
|
+
else
|
1486
|
+
@line = Reline::HISTORY[@history_pointer]
|
1487
|
+
end
|
1488
|
+
@cursor_max = calculate_width(@line)
|
1489
|
+
arg -= 1
|
1490
|
+
ed_search_prev_history(key, arg: arg) if arg > 0
|
1394
1491
|
end
|
1395
|
-
alias_method :
|
1492
|
+
alias_method :history_search_backward, :ed_search_prev_history
|
1396
1493
|
|
1397
|
-
private def ed_search_next_history(key)
|
1398
|
-
|
1494
|
+
private def ed_search_next_history(key, arg: 1)
|
1495
|
+
substr = @line.slice(0, @byte_pointer)
|
1496
|
+
if @history_pointer.nil?
|
1497
|
+
return
|
1498
|
+
elsif @history_pointer == (Reline::HISTORY.size - 1) and not substr.empty?
|
1499
|
+
return
|
1500
|
+
end
|
1501
|
+
history = Reline::HISTORY.slice((@history_pointer + 1)..-1)
|
1502
|
+
h_pointer = nil
|
1503
|
+
line_no = nil
|
1504
|
+
if @is_multiline
|
1505
|
+
h_pointer = history.index { |h|
|
1506
|
+
h.split("\n").each_with_index { |l, i|
|
1507
|
+
if l.start_with?(substr)
|
1508
|
+
line_no = i
|
1509
|
+
break
|
1510
|
+
end
|
1511
|
+
}
|
1512
|
+
not line_no.nil?
|
1513
|
+
}
|
1514
|
+
else
|
1515
|
+
h_pointer = history.index { |l|
|
1516
|
+
l.start_with?(substr)
|
1517
|
+
}
|
1518
|
+
end
|
1519
|
+
h_pointer += @history_pointer + 1 if h_pointer and @history_pointer
|
1520
|
+
return if h_pointer.nil? and not substr.empty?
|
1521
|
+
@history_pointer = h_pointer
|
1522
|
+
if @is_multiline
|
1523
|
+
if @history_pointer.nil? and substr.empty?
|
1524
|
+
@buffer_of_lines = []
|
1525
|
+
@line_index = 0
|
1526
|
+
else
|
1527
|
+
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1528
|
+
@line_index = line_no
|
1529
|
+
end
|
1530
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1531
|
+
@line = @buffer_of_lines.last
|
1532
|
+
@rerender_all = true
|
1533
|
+
else
|
1534
|
+
if @history_pointer.nil? and substr.empty?
|
1535
|
+
@line = ''
|
1536
|
+
else
|
1537
|
+
@line = Reline::HISTORY[@history_pointer]
|
1538
|
+
end
|
1539
|
+
end
|
1540
|
+
@cursor_max = calculate_width(@line)
|
1541
|
+
arg -= 1
|
1542
|
+
ed_search_next_history(key, arg: arg) if arg > 0
|
1399
1543
|
end
|
1400
|
-
alias_method :
|
1544
|
+
alias_method :history_search_forward, :ed_search_next_history
|
1401
1545
|
|
1402
1546
|
private def ed_prev_history(key, arg: 1)
|
1403
1547
|
if @is_multiline and @line_index > 0
|
@@ -1497,6 +1641,7 @@ class Reline::LineEditor
|
|
1497
1641
|
end
|
1498
1642
|
|
1499
1643
|
private def ed_newline(key)
|
1644
|
+
process_insert(force: true)
|
1500
1645
|
if @is_multiline
|
1501
1646
|
if @config.editing_mode_is?(:vi_command)
|
1502
1647
|
if @line_index < (@buffer_of_lines.size - 1)
|
@@ -1876,6 +2021,16 @@ class Reline::LineEditor
|
|
1876
2021
|
end
|
1877
2022
|
end
|
1878
2023
|
|
2024
|
+
private def vi_insert_at_bol(key)
|
2025
|
+
ed_move_to_beg(key)
|
2026
|
+
@config.editing_mode = :vi_insert
|
2027
|
+
end
|
2028
|
+
|
2029
|
+
private def vi_add_at_eol(key)
|
2030
|
+
ed_move_to_end(key)
|
2031
|
+
@config.editing_mode = :vi_insert
|
2032
|
+
end
|
2033
|
+
|
1879
2034
|
private def ed_delete_prev_char(key, arg: 1)
|
1880
2035
|
deleted = ''
|
1881
2036
|
arg.times do
|
@@ -1898,6 +2053,18 @@ class Reline::LineEditor
|
|
1898
2053
|
end
|
1899
2054
|
|
1900
2055
|
private def vi_change_meta(key)
|
2056
|
+
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2057
|
+
if byte_pointer_diff > 0
|
2058
|
+
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
2059
|
+
elsif byte_pointer_diff < 0
|
2060
|
+
@line, cut = byteslice!(@line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2061
|
+
end
|
2062
|
+
copy_for_vi(cut)
|
2063
|
+
@cursor += cursor_diff if cursor_diff < 0
|
2064
|
+
@cursor_max -= cursor_diff.abs
|
2065
|
+
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2066
|
+
@config.editing_mode = :vi_insert
|
2067
|
+
}
|
1901
2068
|
end
|
1902
2069
|
|
1903
2070
|
private def vi_delete_meta(key)
|
@@ -1978,7 +2145,7 @@ class Reline::LineEditor
|
|
1978
2145
|
fp.path
|
1979
2146
|
}
|
1980
2147
|
system("#{ENV['EDITOR']} #{path}")
|
1981
|
-
@line =
|
2148
|
+
@line = File.read(path)
|
1982
2149
|
finish
|
1983
2150
|
end
|
1984
2151
|
|
@@ -2063,12 +2230,17 @@ class Reline::LineEditor
|
|
2063
2230
|
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) }
|
2064
2231
|
end
|
2065
2232
|
|
2066
|
-
private def
|
2233
|
+
private def vi_to_next_char(key, arg: 1)
|
2234
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, true) }
|
2235
|
+
end
|
2236
|
+
|
2237
|
+
private def search_next_char(key, arg, need_prev_char = false)
|
2067
2238
|
if key.instance_of?(String)
|
2068
2239
|
inputed_char = key
|
2069
2240
|
else
|
2070
2241
|
inputed_char = key.chr
|
2071
2242
|
end
|
2243
|
+
prev_total = nil
|
2072
2244
|
total = nil
|
2073
2245
|
found = false
|
2074
2246
|
@line.byteslice(@byte_pointer..-1).grapheme_clusters.each do |mbchar|
|
@@ -2086,13 +2258,66 @@ class Reline::LineEditor
|
|
2086
2258
|
end
|
2087
2259
|
end
|
2088
2260
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2261
|
+
prev_total = total
|
2089
2262
|
total = [total.first + mbchar.bytesize, total.last + width]
|
2090
2263
|
end
|
2091
2264
|
end
|
2092
|
-
if found and total
|
2265
|
+
if not need_prev_char and found and total
|
2093
2266
|
byte_size, width = total
|
2094
2267
|
@byte_pointer += byte_size
|
2095
2268
|
@cursor += width
|
2269
|
+
elsif need_prev_char and found and prev_total
|
2270
|
+
byte_size, width = prev_total
|
2271
|
+
@byte_pointer += byte_size
|
2272
|
+
@cursor += width
|
2273
|
+
end
|
2274
|
+
@waiting_proc = nil
|
2275
|
+
end
|
2276
|
+
|
2277
|
+
private def vi_prev_char(key, arg: 1)
|
2278
|
+
@waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg) }
|
2279
|
+
end
|
2280
|
+
|
2281
|
+
private def vi_to_prev_char(key, arg: 1)
|
2282
|
+
@waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg, true) }
|
2283
|
+
end
|
2284
|
+
|
2285
|
+
private def search_prev_char(key, arg, need_next_char = false)
|
2286
|
+
if key.instance_of?(String)
|
2287
|
+
inputed_char = key
|
2288
|
+
else
|
2289
|
+
inputed_char = key.chr
|
2290
|
+
end
|
2291
|
+
prev_total = nil
|
2292
|
+
total = nil
|
2293
|
+
found = false
|
2294
|
+
@line.byteslice(0..@byte_pointer).grapheme_clusters.reverse_each do |mbchar|
|
2295
|
+
# total has [byte_size, cursor]
|
2296
|
+
unless total
|
2297
|
+
# skip cursor point
|
2298
|
+
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2299
|
+
total = [mbchar.bytesize, width]
|
2300
|
+
else
|
2301
|
+
if inputed_char == mbchar
|
2302
|
+
arg -= 1
|
2303
|
+
if arg.zero?
|
2304
|
+
found = true
|
2305
|
+
break
|
2306
|
+
end
|
2307
|
+
end
|
2308
|
+
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2309
|
+
prev_total = total
|
2310
|
+
total = [total.first + mbchar.bytesize, total.last + width]
|
2311
|
+
end
|
2312
|
+
end
|
2313
|
+
if not need_next_char and found and total
|
2314
|
+
byte_size, width = total
|
2315
|
+
@byte_pointer -= byte_size
|
2316
|
+
@cursor -= width
|
2317
|
+
elsif need_next_char and found and prev_total
|
2318
|
+
byte_size, width = prev_total
|
2319
|
+
@byte_pointer -= byte_size
|
2320
|
+
@cursor -= width
|
2096
2321
|
end
|
2097
2322
|
@waiting_proc = nil
|
2098
2323
|
end
|
@@ -2120,7 +2345,6 @@ class Reline::LineEditor
|
|
2120
2345
|
new_pointer = [@byte_pointer, @line_index]
|
2121
2346
|
@previous_line_index = @line_index
|
2122
2347
|
@byte_pointer, @line_index = @mark_pointer
|
2123
|
-
@byte_pointer, @line_index = @mark_pointer
|
2124
2348
|
@cursor = calculate_width(@line.byteslice(0, @byte_pointer))
|
2125
2349
|
@cursor_max = calculate_width(@line)
|
2126
2350
|
@mark_pointer = new_pointer
|