reline 0.1.0 → 0.1.5
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 +41 -25
- data/lib/reline/ansi.rb +79 -18
- data/lib/reline/config.rb +69 -10
- data/lib/reline/general_io.rb +8 -0
- data/lib/reline/history.rb +33 -13
- data/lib/reline/key_actor/emacs.rb +7 -7
- 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 +305 -119
- data/lib/reline/unicode.rb +68 -0
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +51 -5
- metadata +7 -7
data/lib/reline/general_io.rb
CHANGED
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
|
@@ -9,7 +9,7 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
|
|
9
9
|
# 3 ^C
|
10
10
|
:ed_ignore,
|
11
11
|
# 4 ^D
|
12
|
-
:
|
12
|
+
:em_delete,
|
13
13
|
# 5 ^E
|
14
14
|
:ed_move_to_end,
|
15
15
|
# 6 ^F
|
@@ -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,10 @@ 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)
|
64
57
|
end
|
65
58
|
|
66
59
|
private def check_multiline_prompt(buffer, prompt)
|
@@ -76,19 +69,43 @@ class Reline::LineEditor
|
|
76
69
|
if @prompt_proc
|
77
70
|
prompt_list = @prompt_proc.(buffer)
|
78
71
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
72
|
+
if @config.show_mode_in_prompt
|
73
|
+
if @config.editing_mode_is?(:vi_command)
|
74
|
+
mode_icon = @config.vi_cmd_mode_icon
|
75
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
76
|
+
mode_icon = @config.vi_ins_mode_icon
|
77
|
+
elsif @config.editing_mode_is?(:emacs)
|
78
|
+
mode_icon = @config.emacs_mode_string
|
79
|
+
else
|
80
|
+
mode_icon = '?'
|
81
|
+
end
|
82
|
+
prompt_list.map!{ |pr| mode_icon + pr }
|
83
|
+
end
|
79
84
|
prompt = prompt_list[@line_index]
|
80
85
|
prompt_width = calculate_width(prompt, true)
|
81
86
|
[prompt, prompt_width, prompt_list]
|
82
87
|
else
|
83
88
|
prompt_width = calculate_width(prompt, true)
|
89
|
+
if @config.show_mode_in_prompt
|
90
|
+
if @config.editing_mode_is?(:vi_command)
|
91
|
+
mode_icon = @config.vi_cmd_mode_icon
|
92
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
93
|
+
mode_icon = @config.vi_ins_mode_icon
|
94
|
+
elsif @config.editing_mode_is?(:emacs)
|
95
|
+
mode_icon = @config.emacs_mode_string
|
96
|
+
else
|
97
|
+
mode_icon = '?'
|
98
|
+
end
|
99
|
+
prompt = mode_icon + prompt
|
100
|
+
end
|
84
101
|
[prompt, prompt_width, nil]
|
85
102
|
end
|
86
103
|
end
|
87
104
|
|
88
|
-
def reset(prompt = '', encoding
|
105
|
+
def reset(prompt = '', encoding:)
|
89
106
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
90
107
|
@screen_size = Reline::IOGate.get_screen_size
|
91
|
-
reset_variables(prompt, encoding)
|
108
|
+
reset_variables(prompt, encoding: encoding)
|
92
109
|
@old_trap = Signal.trap('SIGINT') {
|
93
110
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
94
111
|
raise Interrupt
|
@@ -116,7 +133,7 @@ class Reline::LineEditor
|
|
116
133
|
if @line_index.zero?
|
117
134
|
0
|
118
135
|
else
|
119
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
136
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
120
137
|
end
|
121
138
|
if @prompt_proc
|
122
139
|
prompt = prompt_list[@line_index]
|
@@ -139,7 +156,7 @@ class Reline::LineEditor
|
|
139
156
|
@eof
|
140
157
|
end
|
141
158
|
|
142
|
-
def reset_variables(prompt = '', encoding
|
159
|
+
def reset_variables(prompt = '', encoding:)
|
143
160
|
@prompt = prompt
|
144
161
|
@mark_pointer = nil
|
145
162
|
@encoding = encoding
|
@@ -190,10 +207,10 @@ class Reline::LineEditor
|
|
190
207
|
@is_multiline = false
|
191
208
|
end
|
192
209
|
|
193
|
-
private def calculate_height_by_lines(lines,
|
210
|
+
private def calculate_height_by_lines(lines, prompt)
|
194
211
|
result = 0
|
212
|
+
prompt_list = prompt.is_a?(Array) ? prompt : nil
|
195
213
|
lines.each_with_index { |line, i|
|
196
|
-
prompt = ''
|
197
214
|
prompt = prompt_list[i] if prompt_list and prompt_list[i]
|
198
215
|
result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
|
199
216
|
}
|
@@ -211,40 +228,8 @@ class Reline::LineEditor
|
|
211
228
|
width.div(@screen_size.last) + 1
|
212
229
|
end
|
213
230
|
|
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]
|
231
|
+
private def split_by_width(str, max_width)
|
232
|
+
Reline::Unicode.split_by_width(str, max_width, @encoding)
|
248
233
|
end
|
249
234
|
|
250
235
|
private def scroll_down(val)
|
@@ -317,9 +302,9 @@ class Reline::LineEditor
|
|
317
302
|
if @menu_info
|
318
303
|
scroll_down(@highest_in_all - @first_line_started_from)
|
319
304
|
@rerender_all = true
|
320
|
-
@menu_info.list.each do |item|
|
305
|
+
@menu_info.list.sort!.each do |item|
|
321
306
|
Reline::IOGate.move_cursor_column(0)
|
322
|
-
@output.
|
307
|
+
@output.write item
|
323
308
|
@output.flush
|
324
309
|
scroll_down(1)
|
325
310
|
end
|
@@ -358,7 +343,7 @@ class Reline::LineEditor
|
|
358
343
|
new_lines = whole_lines
|
359
344
|
end
|
360
345
|
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
361
|
-
all_height = calculate_height_by_lines(new_lines, prompt_list)
|
346
|
+
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
362
347
|
diff = all_height - @highest_in_all
|
363
348
|
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
364
349
|
if diff > 0
|
@@ -398,7 +383,7 @@ class Reline::LineEditor
|
|
398
383
|
if @line_index.zero?
|
399
384
|
0
|
400
385
|
else
|
401
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
386
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
402
387
|
end
|
403
388
|
if @prompt_proc
|
404
389
|
prompt = prompt_list[@line_index]
|
@@ -457,7 +442,7 @@ class Reline::LineEditor
|
|
457
442
|
if @line_index.zero?
|
458
443
|
0
|
459
444
|
else
|
460
|
-
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list)
|
445
|
+
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
461
446
|
end
|
462
447
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
463
448
|
move_cursor_down(@first_line_started_from + @started_from)
|
@@ -488,7 +473,7 @@ class Reline::LineEditor
|
|
488
473
|
end
|
489
474
|
|
490
475
|
private def render_partial(prompt, prompt_width, line_to_render, with_control = true)
|
491
|
-
visual_lines, height = split_by_width(
|
476
|
+
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
|
492
477
|
if with_control
|
493
478
|
if height > @highest_in_this
|
494
479
|
diff = height - @highest_in_this
|
@@ -507,12 +492,30 @@ class Reline::LineEditor
|
|
507
492
|
Reline::IOGate.move_cursor_column(0)
|
508
493
|
visual_lines.each_with_index do |line, index|
|
509
494
|
if line.nil?
|
510
|
-
Reline::IOGate.
|
511
|
-
|
512
|
-
|
495
|
+
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
496
|
+
# reaches the end of line
|
497
|
+
if Reline::IOGate.win?
|
498
|
+
# A newline is automatically inserted if a character is rendered at
|
499
|
+
# eol on command prompt.
|
500
|
+
else
|
501
|
+
# When the cursor is at the end of the line and erases characters
|
502
|
+
# after the cursor, some terminals delete the character at the
|
503
|
+
# cursor position.
|
504
|
+
move_cursor_down(1)
|
505
|
+
Reline::IOGate.move_cursor_column(0)
|
506
|
+
end
|
507
|
+
else
|
508
|
+
Reline::IOGate.erase_after_cursor
|
509
|
+
move_cursor_down(1)
|
510
|
+
Reline::IOGate.move_cursor_column(0)
|
511
|
+
end
|
513
512
|
next
|
514
513
|
end
|
515
|
-
@output.
|
514
|
+
@output.write line
|
515
|
+
if Reline::IOGate.win? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
|
516
|
+
# A newline is automatically inserted if a character is rendered at eol on command prompt.
|
517
|
+
@rest_height -= 1 if @rest_height > 0
|
518
|
+
end
|
516
519
|
@output.flush
|
517
520
|
if @first_prompt
|
518
521
|
@first_prompt = false
|
@@ -521,12 +524,14 @@ class Reline::LineEditor
|
|
521
524
|
end
|
522
525
|
Reline::IOGate.erase_after_cursor
|
523
526
|
if with_control
|
524
|
-
|
527
|
+
# Just after rendring, so the cursor is on the last line.
|
525
528
|
if finished?
|
526
|
-
|
529
|
+
Reline::IOGate.move_cursor_column(0)
|
530
|
+
else
|
531
|
+
# Moves up from bottom of lines to the cursor position.
|
532
|
+
move_cursor_up(height - 1 - @started_from)
|
533
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
527
534
|
end
|
528
|
-
move_cursor_down(@started_from)
|
529
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
530
535
|
end
|
531
536
|
height
|
532
537
|
end
|
@@ -535,7 +540,7 @@ class Reline::LineEditor
|
|
535
540
|
return before if before.nil? || before.empty?
|
536
541
|
|
537
542
|
if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?)
|
538
|
-
after.lines(chomp
|
543
|
+
after.lines("\n").map { |l| l.chomp('') }
|
539
544
|
else
|
540
545
|
before
|
541
546
|
end
|
@@ -595,14 +600,22 @@ class Reline::LineEditor
|
|
595
600
|
[target, preposing, completed, postposing]
|
596
601
|
end
|
597
602
|
|
598
|
-
private def complete(list)
|
603
|
+
private def complete(list, just_show_list = false)
|
599
604
|
case @completion_state
|
600
605
|
when CompletionState::NORMAL, CompletionState::JOURNEY
|
601
606
|
@completion_state = CompletionState::COMPLETION
|
602
607
|
when CompletionState::PERFECT_MATCH
|
603
608
|
@dig_perfect_match_proc&.(@perfect_matched)
|
604
609
|
end
|
605
|
-
|
610
|
+
if just_show_list
|
611
|
+
is_menu = true
|
612
|
+
elsif @completion_state == CompletionState::MENU
|
613
|
+
is_menu = true
|
614
|
+
elsif @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
|
615
|
+
is_menu = true
|
616
|
+
else
|
617
|
+
is_menu = false
|
618
|
+
end
|
606
619
|
result = complete_internal_proc(list, is_menu)
|
607
620
|
if @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
|
608
621
|
@completion_state = CompletionState::PERFECT_MATCH
|
@@ -621,7 +634,7 @@ class Reline::LineEditor
|
|
621
634
|
else
|
622
635
|
@completion_state = CompletionState::MENU
|
623
636
|
end
|
624
|
-
if target < completed
|
637
|
+
if not just_show_list and target < completed
|
625
638
|
@line = preposing + completed + completion_append_character.to_s + postposing
|
626
639
|
line_to_pointer = preposing + completed + completion_append_character.to_s
|
627
640
|
@cursor_max = calculate_width(@line)
|
@@ -897,7 +910,6 @@ class Reline::LineEditor
|
|
897
910
|
quote = nil
|
898
911
|
i += 1
|
899
912
|
rest = nil
|
900
|
-
break_pointer = nil
|
901
913
|
elsif quote and slice.start_with?(escaped_quote)
|
902
914
|
# skip
|
903
915
|
i += 2
|
@@ -907,10 +919,11 @@ class Reline::LineEditor
|
|
907
919
|
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
|
908
920
|
escaped_quote = /\\#{Regexp.escape(quote)}/
|
909
921
|
i += 1
|
910
|
-
break_pointer = i
|
922
|
+
break_pointer = i - 1
|
911
923
|
elsif not quote and slice =~ word_break_regexp
|
912
924
|
rest = $'
|
913
925
|
i += 1
|
926
|
+
before = @line.byteslice(i, @byte_pointer - i)
|
914
927
|
break_pointer = i
|
915
928
|
else
|
916
929
|
i += 1
|
@@ -928,6 +941,11 @@ class Reline::LineEditor
|
|
928
941
|
end
|
929
942
|
else
|
930
943
|
preposing = ''
|
944
|
+
if break_pointer
|
945
|
+
preposing = @line.byteslice(0, break_pointer)
|
946
|
+
else
|
947
|
+
preposing = ''
|
948
|
+
end
|
931
949
|
target = before
|
932
950
|
end
|
933
951
|
[preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
|
@@ -1037,29 +1055,7 @@ class Reline::LineEditor
|
|
1037
1055
|
end
|
1038
1056
|
|
1039
1057
|
private def calculate_width(str, allow_escape_code = false)
|
1040
|
-
|
1041
|
-
width = 0
|
1042
|
-
rest = str.encode(Encoding::UTF_8)
|
1043
|
-
in_zero_width = false
|
1044
|
-
rest.scan(WIDTH_SCANNER) do |gc|
|
1045
|
-
case gc
|
1046
|
-
when NON_PRINTING_START
|
1047
|
-
in_zero_width = true
|
1048
|
-
when NON_PRINTING_END
|
1049
|
-
in_zero_width = false
|
1050
|
-
when CSI_REGEXP, OSC_REGEXP
|
1051
|
-
else
|
1052
|
-
unless in_zero_width
|
1053
|
-
width += Reline::Unicode.get_mbchar_width(gc)
|
1054
|
-
end
|
1055
|
-
end
|
1056
|
-
end
|
1057
|
-
width
|
1058
|
-
else
|
1059
|
-
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
|
1060
|
-
w + Reline::Unicode.get_mbchar_width(gc)
|
1061
|
-
}
|
1062
|
-
end
|
1058
|
+
Reline::Unicode.calculate_width(str, allow_escape_code)
|
1063
1059
|
end
|
1064
1060
|
|
1065
1061
|
private def key_delete(key)
|
@@ -1082,6 +1078,11 @@ class Reline::LineEditor
|
|
1082
1078
|
|
1083
1079
|
private def ed_insert(key)
|
1084
1080
|
if key.instance_of?(String)
|
1081
|
+
begin
|
1082
|
+
key.encode(Encoding::UTF_8)
|
1083
|
+
rescue Encoding::UndefinedConversionError
|
1084
|
+
return
|
1085
|
+
end
|
1085
1086
|
width = Reline::Unicode.get_mbchar_width(key)
|
1086
1087
|
if @cursor == @cursor_max
|
1087
1088
|
@line += key
|
@@ -1092,6 +1093,11 @@ class Reline::LineEditor
|
|
1092
1093
|
@cursor += width
|
1093
1094
|
@cursor_max += width
|
1094
1095
|
else
|
1096
|
+
begin
|
1097
|
+
key.chr.encode(Encoding::UTF_8)
|
1098
|
+
rescue Encoding::UndefinedConversionError
|
1099
|
+
return
|
1100
|
+
end
|
1095
1101
|
if @cursor == @cursor_max
|
1096
1102
|
@line += key.chr
|
1097
1103
|
else
|
@@ -1227,7 +1233,7 @@ class Reline::LineEditor
|
|
1227
1233
|
if search_word.empty? and Reline.last_incremental_search
|
1228
1234
|
search_word = Reline.last_incremental_search
|
1229
1235
|
end
|
1230
|
-
if @history_pointer
|
1236
|
+
if @history_pointer
|
1231
1237
|
case prev_search_key
|
1232
1238
|
when "\C-r".ord
|
1233
1239
|
history_pointer_base = 0
|
@@ -1299,7 +1305,7 @@ class Reline::LineEditor
|
|
1299
1305
|
end
|
1300
1306
|
end
|
1301
1307
|
|
1302
|
-
private def
|
1308
|
+
private def incremental_search_history(key)
|
1303
1309
|
unless @history_pointer
|
1304
1310
|
if @is_multiline
|
1305
1311
|
@line_backup_in_history = whole_buffer
|
@@ -1380,15 +1386,114 @@ class Reline::LineEditor
|
|
1380
1386
|
}
|
1381
1387
|
end
|
1382
1388
|
|
1383
|
-
private def
|
1384
|
-
|
1389
|
+
private def vi_search_prev(key)
|
1390
|
+
incremental_search_history(key)
|
1391
|
+
end
|
1392
|
+
alias_method :reverse_search_history, :vi_search_prev
|
1393
|
+
|
1394
|
+
private def vi_search_next(key)
|
1395
|
+
incremental_search_history(key)
|
1396
|
+
end
|
1397
|
+
alias_method :forward_search_history, :vi_search_next
|
1398
|
+
|
1399
|
+
private def ed_search_prev_history(key, arg: 1)
|
1400
|
+
history = nil
|
1401
|
+
h_pointer = nil
|
1402
|
+
line_no = nil
|
1403
|
+
substr = @line.slice(0, @byte_pointer)
|
1404
|
+
if @history_pointer.nil?
|
1405
|
+
return if not @line.empty? and substr.empty?
|
1406
|
+
history = Reline::HISTORY
|
1407
|
+
elsif @history_pointer.zero?
|
1408
|
+
history = nil
|
1409
|
+
h_pointer = nil
|
1410
|
+
else
|
1411
|
+
history = Reline::HISTORY.slice(0, @history_pointer)
|
1412
|
+
end
|
1413
|
+
return if history.nil?
|
1414
|
+
if @is_multiline
|
1415
|
+
h_pointer = history.rindex { |h|
|
1416
|
+
h.split("\n").each_with_index { |l, i|
|
1417
|
+
if l.start_with?(substr)
|
1418
|
+
line_no = i
|
1419
|
+
break
|
1420
|
+
end
|
1421
|
+
}
|
1422
|
+
not line_no.nil?
|
1423
|
+
}
|
1424
|
+
else
|
1425
|
+
h_pointer = history.rindex { |l|
|
1426
|
+
l.start_with?(substr)
|
1427
|
+
}
|
1428
|
+
end
|
1429
|
+
return if h_pointer.nil?
|
1430
|
+
@history_pointer = h_pointer
|
1431
|
+
if @is_multiline
|
1432
|
+
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1433
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1434
|
+
@line_index = line_no
|
1435
|
+
@line = @buffer_of_lines.last
|
1436
|
+
@rerender_all = true
|
1437
|
+
else
|
1438
|
+
@line = Reline::HISTORY[@history_pointer]
|
1439
|
+
end
|
1440
|
+
@cursor_max = calculate_width(@line)
|
1441
|
+
arg -= 1
|
1442
|
+
ed_search_prev_history(key, arg: arg) if arg > 0
|
1385
1443
|
end
|
1386
|
-
alias_method :
|
1444
|
+
alias_method :history_search_backward, :ed_search_prev_history
|
1387
1445
|
|
1388
|
-
private def ed_search_next_history(key)
|
1389
|
-
|
1446
|
+
private def ed_search_next_history(key, arg: 1)
|
1447
|
+
substr = @line.slice(0, @byte_pointer)
|
1448
|
+
if @history_pointer.nil?
|
1449
|
+
return
|
1450
|
+
elsif @history_pointer == (Reline::HISTORY.size - 1) and not substr.empty?
|
1451
|
+
return
|
1452
|
+
end
|
1453
|
+
history = Reline::HISTORY.slice((@history_pointer + 1)..-1)
|
1454
|
+
h_pointer = nil
|
1455
|
+
line_no = nil
|
1456
|
+
if @is_multiline
|
1457
|
+
h_pointer = history.index { |h|
|
1458
|
+
h.split("\n").each_with_index { |l, i|
|
1459
|
+
if l.start_with?(substr)
|
1460
|
+
line_no = i
|
1461
|
+
break
|
1462
|
+
end
|
1463
|
+
}
|
1464
|
+
not line_no.nil?
|
1465
|
+
}
|
1466
|
+
else
|
1467
|
+
h_pointer = history.index { |l|
|
1468
|
+
l.start_with?(substr)
|
1469
|
+
}
|
1470
|
+
end
|
1471
|
+
h_pointer += @history_pointer + 1 if h_pointer and @history_pointer
|
1472
|
+
return if h_pointer.nil? and not substr.empty?
|
1473
|
+
@history_pointer = h_pointer
|
1474
|
+
if @is_multiline
|
1475
|
+
if @history_pointer.nil? and substr.empty?
|
1476
|
+
@buffer_of_lines = []
|
1477
|
+
@line_index = 0
|
1478
|
+
else
|
1479
|
+
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1480
|
+
@line_index = line_no
|
1481
|
+
end
|
1482
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1483
|
+
@line = @buffer_of_lines.last
|
1484
|
+
@rerender_all = true
|
1485
|
+
else
|
1486
|
+
if @history_pointer.nil? and substr.empty?
|
1487
|
+
@line = ''
|
1488
|
+
else
|
1489
|
+
@line = Reline::HISTORY[@history_pointer]
|
1490
|
+
end
|
1491
|
+
end
|
1492
|
+
@cursor_max = calculate_width(@line)
|
1493
|
+
arg -= 1
|
1494
|
+
ed_search_next_history(key, arg: arg) if arg > 0
|
1390
1495
|
end
|
1391
|
-
alias_method :
|
1496
|
+
alias_method :history_search_forward, :ed_search_next_history
|
1392
1497
|
|
1393
1498
|
private def ed_prev_history(key, arg: 1)
|
1394
1499
|
if @is_multiline and @line_index > 0
|
@@ -1567,7 +1672,7 @@ class Reline::LineEditor
|
|
1567
1672
|
end
|
1568
1673
|
end
|
1569
1674
|
|
1570
|
-
private def
|
1675
|
+
private def em_delete(key)
|
1571
1676
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
1572
1677
|
@line = nil
|
1573
1678
|
if @buffer_of_lines.size > 1
|
@@ -1592,7 +1697,19 @@ class Reline::LineEditor
|
|
1592
1697
|
@rest_height += 1
|
1593
1698
|
end
|
1594
1699
|
end
|
1595
|
-
alias_method :delete_char, :
|
1700
|
+
alias_method :delete_char, :em_delete
|
1701
|
+
|
1702
|
+
private def em_delete_or_list(key)
|
1703
|
+
if @line.empty? or @byte_pointer < @line.bytesize
|
1704
|
+
em_delete(key)
|
1705
|
+
else # show completed list
|
1706
|
+
result = call_completion_proc
|
1707
|
+
if result.is_a?(Array)
|
1708
|
+
complete(result, true)
|
1709
|
+
end
|
1710
|
+
end
|
1711
|
+
end
|
1712
|
+
alias_method :delete_char_or_list, :em_delete_or_list
|
1596
1713
|
|
1597
1714
|
private def em_yank(key)
|
1598
1715
|
yanked = @kill_ring.yank
|
@@ -1855,6 +1972,16 @@ class Reline::LineEditor
|
|
1855
1972
|
end
|
1856
1973
|
end
|
1857
1974
|
|
1975
|
+
private def vi_insert_at_bol(key)
|
1976
|
+
ed_move_to_beg(key)
|
1977
|
+
@config.editing_mode = :vi_insert
|
1978
|
+
end
|
1979
|
+
|
1980
|
+
private def vi_add_at_eol(key)
|
1981
|
+
ed_move_to_end(key)
|
1982
|
+
@config.editing_mode = :vi_insert
|
1983
|
+
end
|
1984
|
+
|
1858
1985
|
private def ed_delete_prev_char(key, arg: 1)
|
1859
1986
|
deleted = ''
|
1860
1987
|
arg.times do
|
@@ -1877,6 +2004,18 @@ class Reline::LineEditor
|
|
1877
2004
|
end
|
1878
2005
|
|
1879
2006
|
private def vi_change_meta(key)
|
2007
|
+
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2008
|
+
if byte_pointer_diff > 0
|
2009
|
+
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
2010
|
+
elsif byte_pointer_diff < 0
|
2011
|
+
@line, cut = byteslice!(@line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2012
|
+
end
|
2013
|
+
copy_for_vi(cut)
|
2014
|
+
@cursor += cursor_diff if cursor_diff < 0
|
2015
|
+
@cursor_max -= cursor_diff.abs
|
2016
|
+
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2017
|
+
@config.editing_mode = :vi_insert
|
2018
|
+
}
|
1880
2019
|
end
|
1881
2020
|
|
1882
2021
|
private def vi_delete_meta(key)
|
@@ -1896,18 +2035,6 @@ class Reline::LineEditor
|
|
1896
2035
|
private def vi_yank(key)
|
1897
2036
|
end
|
1898
2037
|
|
1899
|
-
private def vi_end_of_transmission(key)
|
1900
|
-
if @line.empty?
|
1901
|
-
@line = nil
|
1902
|
-
if @buffer_of_lines.size > 1
|
1903
|
-
scroll_down(@highest_in_all - @first_line_started_from)
|
1904
|
-
end
|
1905
|
-
Reline::IOGate.move_cursor_column(0)
|
1906
|
-
@eof = true
|
1907
|
-
finish
|
1908
|
-
end
|
1909
|
-
end
|
1910
|
-
|
1911
2038
|
private def vi_list_or_eof(key)
|
1912
2039
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
1913
2040
|
@line = nil
|
@@ -1918,9 +2045,11 @@ class Reline::LineEditor
|
|
1918
2045
|
@eof = true
|
1919
2046
|
finish
|
1920
2047
|
else
|
1921
|
-
|
2048
|
+
ed_newline(key)
|
1922
2049
|
end
|
1923
2050
|
end
|
2051
|
+
alias_method :vi_end_of_transmission, :vi_list_or_eof
|
2052
|
+
alias_method :vi_eof_maybe, :vi_list_or_eof
|
1924
2053
|
|
1925
2054
|
private def ed_delete_next_char(key, arg: 1)
|
1926
2055
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
@@ -1967,7 +2096,7 @@ class Reline::LineEditor
|
|
1967
2096
|
fp.path
|
1968
2097
|
}
|
1969
2098
|
system("#{ENV['EDITOR']} #{path}")
|
1970
|
-
@line =
|
2099
|
+
@line = File.read(path)
|
1971
2100
|
finish
|
1972
2101
|
end
|
1973
2102
|
|
@@ -2052,12 +2181,17 @@ class Reline::LineEditor
|
|
2052
2181
|
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) }
|
2053
2182
|
end
|
2054
2183
|
|
2055
|
-
private def
|
2184
|
+
private def vi_to_next_char(key, arg: 1)
|
2185
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, true) }
|
2186
|
+
end
|
2187
|
+
|
2188
|
+
private def search_next_char(key, arg, need_prev_char = false)
|
2056
2189
|
if key.instance_of?(String)
|
2057
2190
|
inputed_char = key
|
2058
2191
|
else
|
2059
2192
|
inputed_char = key.chr
|
2060
2193
|
end
|
2194
|
+
prev_total = nil
|
2061
2195
|
total = nil
|
2062
2196
|
found = false
|
2063
2197
|
@line.byteslice(@byte_pointer..-1).grapheme_clusters.each do |mbchar|
|
@@ -2075,13 +2209,66 @@ class Reline::LineEditor
|
|
2075
2209
|
end
|
2076
2210
|
end
|
2077
2211
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2212
|
+
prev_total = total
|
2078
2213
|
total = [total.first + mbchar.bytesize, total.last + width]
|
2079
2214
|
end
|
2080
2215
|
end
|
2081
|
-
if found and total
|
2216
|
+
if not need_prev_char and found and total
|
2082
2217
|
byte_size, width = total
|
2083
2218
|
@byte_pointer += byte_size
|
2084
2219
|
@cursor += width
|
2220
|
+
elsif need_prev_char and found and prev_total
|
2221
|
+
byte_size, width = prev_total
|
2222
|
+
@byte_pointer += byte_size
|
2223
|
+
@cursor += width
|
2224
|
+
end
|
2225
|
+
@waiting_proc = nil
|
2226
|
+
end
|
2227
|
+
|
2228
|
+
private def vi_prev_char(key, arg: 1)
|
2229
|
+
@waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg) }
|
2230
|
+
end
|
2231
|
+
|
2232
|
+
private def vi_to_prev_char(key, arg: 1)
|
2233
|
+
@waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg, true) }
|
2234
|
+
end
|
2235
|
+
|
2236
|
+
private def search_prev_char(key, arg, need_next_char = false)
|
2237
|
+
if key.instance_of?(String)
|
2238
|
+
inputed_char = key
|
2239
|
+
else
|
2240
|
+
inputed_char = key.chr
|
2241
|
+
end
|
2242
|
+
prev_total = nil
|
2243
|
+
total = nil
|
2244
|
+
found = false
|
2245
|
+
@line.byteslice(0..@byte_pointer).grapheme_clusters.reverse_each do |mbchar|
|
2246
|
+
# total has [byte_size, cursor]
|
2247
|
+
unless total
|
2248
|
+
# skip cursor point
|
2249
|
+
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2250
|
+
total = [mbchar.bytesize, width]
|
2251
|
+
else
|
2252
|
+
if inputed_char == mbchar
|
2253
|
+
arg -= 1
|
2254
|
+
if arg.zero?
|
2255
|
+
found = true
|
2256
|
+
break
|
2257
|
+
end
|
2258
|
+
end
|
2259
|
+
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2260
|
+
prev_total = total
|
2261
|
+
total = [total.first + mbchar.bytesize, total.last + width]
|
2262
|
+
end
|
2263
|
+
end
|
2264
|
+
if not need_next_char and found and total
|
2265
|
+
byte_size, width = total
|
2266
|
+
@byte_pointer -= byte_size
|
2267
|
+
@cursor -= width
|
2268
|
+
elsif need_next_char and found and prev_total
|
2269
|
+
byte_size, width = prev_total
|
2270
|
+
@byte_pointer -= byte_size
|
2271
|
+
@cursor -= width
|
2085
2272
|
end
|
2086
2273
|
@waiting_proc = nil
|
2087
2274
|
end
|
@@ -2109,7 +2296,6 @@ class Reline::LineEditor
|
|
2109
2296
|
new_pointer = [@byte_pointer, @line_index]
|
2110
2297
|
@previous_line_index = @line_index
|
2111
2298
|
@byte_pointer, @line_index = @mark_pointer
|
2112
|
-
@byte_pointer, @line_index = @mark_pointer
|
2113
2299
|
@cursor = calculate_width(@line.byteslice(0, @byte_pointer))
|
2114
2300
|
@cursor_max = calculate_width(@line)
|
2115
2301
|
@mark_pointer = new_pointer
|