reline 0.5.9 → 0.6.1
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 +41 -50
- data/lib/reline/face.rb +1 -1
- data/lib/reline/history.rb +3 -3
- data/lib/reline/io/ansi.rb +77 -123
- data/lib/reline/io/dumb.rb +16 -2
- data/lib/reline/io/windows.rb +94 -67
- data/lib/reline/io.rb +14 -0
- data/lib/reline/key_actor/base.rb +10 -4
- data/lib/reline/key_actor/emacs.rb +96 -96
- data/lib/reline/key_actor/vi_command.rb +182 -182
- data/lib/reline/key_actor/vi_insert.rb +137 -137
- data/lib/reline/key_stroke.rb +26 -16
- data/lib/reline/line_editor.rb +331 -534
- data/lib/reline/unicode/east_asian_width.rb +1289 -1192
- data/lib/reline/unicode.rb +155 -436
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +46 -34
- metadata +3 -7
- data/lib/reline/terminfo.rb +0 -158
data/lib/reline/line_editor.rb
CHANGED
@@ -13,7 +13,6 @@ class Reline::LineEditor
|
|
13
13
|
attr_accessor :prompt_proc
|
14
14
|
attr_accessor :auto_indent_proc
|
15
15
|
attr_accessor :dig_perfect_match_proc
|
16
|
-
attr_writer :output
|
17
16
|
|
18
17
|
VI_MOTIONS = %i{
|
19
18
|
ed_prev_char
|
@@ -36,7 +35,6 @@ class Reline::LineEditor
|
|
36
35
|
|
37
36
|
module CompletionState
|
38
37
|
NORMAL = :normal
|
39
|
-
COMPLETION = :completion
|
40
38
|
MENU = :menu
|
41
39
|
MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
|
42
40
|
PERFECT_MATCH = :perfect_match
|
@@ -72,17 +70,21 @@ class Reline::LineEditor
|
|
72
70
|
|
73
71
|
MINIMUM_SCROLLBAR_HEIGHT = 1
|
74
72
|
|
75
|
-
def initialize(config
|
73
|
+
def initialize(config)
|
76
74
|
@config = config
|
77
75
|
@completion_append_character = ''
|
78
76
|
@screen_size = [0, 0] # Should be initialized with actual winsize in LineEditor#reset
|
79
|
-
reset_variables
|
77
|
+
reset_variables
|
80
78
|
end
|
81
79
|
|
82
80
|
def io_gate
|
83
81
|
Reline::IOGate
|
84
82
|
end
|
85
83
|
|
84
|
+
def encoding
|
85
|
+
io_gate.encoding
|
86
|
+
end
|
87
|
+
|
86
88
|
def set_pasting_state(in_pasting)
|
87
89
|
# While pasting, text to be inserted is stored to @continuous_insertion_buffer.
|
88
90
|
# After pasting, this buffer should be force inserted.
|
@@ -136,9 +138,9 @@ class Reline::LineEditor
|
|
136
138
|
end
|
137
139
|
end
|
138
140
|
|
139
|
-
def reset(prompt = ''
|
141
|
+
def reset(prompt = '')
|
140
142
|
@screen_size = Reline::IOGate.get_screen_size
|
141
|
-
reset_variables(prompt
|
143
|
+
reset_variables(prompt)
|
142
144
|
@rendered_screen.base_y = Reline::IOGate.cursor_pos.y
|
143
145
|
if ENV.key?('RELINE_ALT_SCROLLBAR')
|
144
146
|
@full_block = '::'
|
@@ -150,7 +152,7 @@ class Reline::LineEditor
|
|
150
152
|
@upper_half_block = '▀'
|
151
153
|
@lower_half_block = '▄'
|
152
154
|
@block_elem_width = 1
|
153
|
-
elsif
|
155
|
+
elsif encoding == Encoding::UTF_8
|
154
156
|
@full_block = '█'
|
155
157
|
@upper_half_block = '▀'
|
156
158
|
@lower_half_block = '▄'
|
@@ -176,9 +178,8 @@ class Reline::LineEditor
|
|
176
178
|
scroll_into_view
|
177
179
|
Reline::IOGate.move_cursor_up @rendered_screen.cursor_y
|
178
180
|
@rendered_screen.base_y = Reline::IOGate.cursor_pos.y
|
179
|
-
|
180
|
-
|
181
|
-
render_differential
|
181
|
+
clear_rendered_screen_cache
|
182
|
+
render
|
182
183
|
end
|
183
184
|
|
184
185
|
private def handle_interrupted
|
@@ -186,11 +187,11 @@ class Reline::LineEditor
|
|
186
187
|
|
187
188
|
@interrupted = false
|
188
189
|
clear_dialogs
|
189
|
-
|
190
|
-
|
190
|
+
render
|
191
|
+
cursor_to_bottom_offset = @rendered_screen.lines.size - @rendered_screen.cursor_y
|
192
|
+
Reline::IOGate.scroll_down cursor_to_bottom_offset
|
191
193
|
Reline::IOGate.move_cursor_column 0
|
192
|
-
|
193
|
-
@rendered_screen.cursor_y = 0
|
194
|
+
clear_rendered_screen_cache
|
194
195
|
case @old_trap
|
195
196
|
when 'DEFAULT', 'SYSTEM_DEFAULT'
|
196
197
|
raise Interrupt
|
@@ -220,10 +221,9 @@ class Reline::LineEditor
|
|
220
221
|
@eof
|
221
222
|
end
|
222
223
|
|
223
|
-
def reset_variables(prompt = ''
|
224
|
+
def reset_variables(prompt = '')
|
224
225
|
@prompt = prompt.gsub("\n", "\\n")
|
225
226
|
@mark_pointer = nil
|
226
|
-
@encoding = encoding
|
227
227
|
@is_multiline = false
|
228
228
|
@finished = false
|
229
229
|
@history_pointer = nil
|
@@ -240,7 +240,7 @@ class Reline::LineEditor
|
|
240
240
|
@searching_prompt = nil
|
241
241
|
@just_cursor_moving = false
|
242
242
|
@eof = false
|
243
|
-
@continuous_insertion_buffer = String.new(encoding:
|
243
|
+
@continuous_insertion_buffer = String.new(encoding: encoding)
|
244
244
|
@scroll_partial_screen = 0
|
245
245
|
@drop_terminate_spaces = false
|
246
246
|
@in_pasting = false
|
@@ -250,9 +250,9 @@ class Reline::LineEditor
|
|
250
250
|
@resized = false
|
251
251
|
@cache = {}
|
252
252
|
@rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
|
253
|
-
@
|
254
|
-
@
|
255
|
-
@
|
253
|
+
@undo_redo_history = [[[""], 0, 0]]
|
254
|
+
@undo_redo_index = 0
|
255
|
+
@restoring = false
|
256
256
|
@prev_action_state = NullActionState
|
257
257
|
@next_action_state = NullActionState
|
258
258
|
reset_line
|
@@ -260,11 +260,10 @@ class Reline::LineEditor
|
|
260
260
|
|
261
261
|
def reset_line
|
262
262
|
@byte_pointer = 0
|
263
|
-
@buffer_of_lines = [String.new(encoding:
|
263
|
+
@buffer_of_lines = [String.new(encoding: encoding)]
|
264
264
|
@line_index = 0
|
265
265
|
@cache.clear
|
266
266
|
@line_backup_in_history = nil
|
267
|
-
@multibyte_buffer = String.new(encoding: 'ASCII-8BIT')
|
268
267
|
end
|
269
268
|
|
270
269
|
def multiline_on
|
@@ -276,7 +275,7 @@ class Reline::LineEditor
|
|
276
275
|
end
|
277
276
|
|
278
277
|
private def insert_new_line(cursor_line, next_line)
|
279
|
-
@buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding:
|
278
|
+
@buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: encoding))
|
280
279
|
@buffer_of_lines[@line_index] = cursor_line
|
281
280
|
@line_index += 1
|
282
281
|
@byte_pointer = 0
|
@@ -298,8 +297,8 @@ class Reline::LineEditor
|
|
298
297
|
end
|
299
298
|
end
|
300
299
|
|
301
|
-
private def
|
302
|
-
Reline::Unicode.
|
300
|
+
private def split_line_by_width(str, max_width, offset: 0)
|
301
|
+
Reline::Unicode.split_line_by_width(str, max_width, encoding, offset: offset)
|
303
302
|
end
|
304
303
|
|
305
304
|
def current_byte_pointer_cursor
|
@@ -389,8 +388,8 @@ class Reline::LineEditor
|
|
389
388
|
if (cached = cached_wraps[[prompt, line]])
|
390
389
|
next cached
|
391
390
|
end
|
392
|
-
*wrapped_prompts, code_line_prompt =
|
393
|
-
wrapped_lines =
|
391
|
+
*wrapped_prompts, code_line_prompt = split_line_by_width(prompt, width)
|
392
|
+
wrapped_lines = split_line_by_width(line, width, offset: calculate_width(code_line_prompt, true))
|
394
393
|
wrapped_prompts.map { |p| [p, ''] } + [[code_line_prompt, wrapped_lines.first]] + wrapped_lines.drop(1).map { |c| ['', c] }
|
395
394
|
end
|
396
395
|
end
|
@@ -414,7 +413,7 @@ class Reline::LineEditor
|
|
414
413
|
# do nothing
|
415
414
|
elsif level == :blank
|
416
415
|
Reline::IOGate.move_cursor_column base_x
|
417
|
-
|
416
|
+
Reline::IOGate.write "#{Reline::IOGate.reset_color_sequence}#{' ' * width}"
|
418
417
|
else
|
419
418
|
x, w, content = new_items[level]
|
420
419
|
cover_begin = base_x != 0 && new_levels[base_x - 1] == level
|
@@ -424,7 +423,7 @@ class Reline::LineEditor
|
|
424
423
|
content, pos = Reline::Unicode.take_mbchar_range(content, base_x - x, width, cover_begin: cover_begin, cover_end: cover_end, padding: true)
|
425
424
|
end
|
426
425
|
Reline::IOGate.move_cursor_column x + pos
|
427
|
-
|
426
|
+
Reline::IOGate.write "#{Reline::IOGate.reset_color_sequence}#{content}#{Reline::IOGate.reset_color_sequence}"
|
428
427
|
end
|
429
428
|
base_x += width
|
430
429
|
end
|
@@ -437,8 +436,8 @@ class Reline::LineEditor
|
|
437
436
|
# Calculate cursor position in word wrapped content.
|
438
437
|
def wrapped_cursor_position
|
439
438
|
prompt_width = calculate_width(prompt_list[@line_index], true)
|
440
|
-
line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
|
441
|
-
wrapped_line_before_cursor =
|
439
|
+
line_before_cursor = Reline::Unicode.escape_for_print(whole_lines[@line_index].byteslice(0, @byte_pointer))
|
440
|
+
wrapped_line_before_cursor = split_line_by_width(' ' * prompt_width + line_before_cursor, screen_width)
|
442
441
|
wrapped_cursor_y = wrapped_prompt_and_input_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
|
443
442
|
wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
|
444
443
|
[wrapped_cursor_x, wrapped_cursor_y]
|
@@ -460,49 +459,27 @@ class Reline::LineEditor
|
|
460
459
|
end
|
461
460
|
|
462
461
|
def render_finished
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
num_lines = @rendered_screen.lines.size
|
472
|
-
return unless num_lines && num_lines >= 1
|
473
|
-
|
474
|
-
Reline::IOGate.move_cursor_down num_lines - 1
|
475
|
-
(num_lines - 1).times do
|
476
|
-
Reline::IOGate.erase_after_cursor
|
477
|
-
Reline::IOGate.move_cursor_up 1
|
478
|
-
end
|
479
|
-
Reline::IOGate.erase_after_cursor
|
480
|
-
@rendered_screen.lines = []
|
481
|
-
@rendered_screen.cursor_y = 0
|
482
|
-
end
|
483
|
-
|
484
|
-
def render_full_content
|
485
|
-
lines = @buffer_of_lines.size.times.map do |i|
|
486
|
-
line = prompt_list[i] + modified_lines[i]
|
487
|
-
wrapped_lines, = split_by_width(line, screen_width)
|
488
|
-
wrapped_lines.last.empty? ? "#{line} " : line
|
462
|
+
Reline::IOGate.buffered_output do
|
463
|
+
render_differential([], 0, 0)
|
464
|
+
lines = @buffer_of_lines.size.times.map do |i|
|
465
|
+
line = Reline::Unicode.strip_non_printing_start_end(prompt_list[i]) + modified_lines[i]
|
466
|
+
wrapped_lines = split_line_by_width(line, screen_width)
|
467
|
+
wrapped_lines.last.empty? ? "#{line} " : line
|
468
|
+
end
|
469
|
+
Reline::IOGate.write lines.map { |l| "#{l}\r\n" }.join
|
489
470
|
end
|
490
|
-
@output.puts lines.map { |l| "#{l}\r\n" }.join
|
491
471
|
end
|
492
472
|
|
493
|
-
def print_nomultiline_prompt
|
494
|
-
|
495
|
-
|
473
|
+
def print_nomultiline_prompt
|
474
|
+
Reline::IOGate.disable_auto_linewrap(true) if Reline::IOGate.win?
|
496
475
|
# Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence.
|
497
|
-
|
498
|
-
|
499
|
-
|
476
|
+
Reline::IOGate.write Reline::Unicode.strip_non_printing_start_end(@prompt) if @prompt && !@is_multiline
|
477
|
+
ensure
|
478
|
+
Reline::IOGate.disable_auto_linewrap(false) if Reline::IOGate.win?
|
500
479
|
end
|
501
480
|
|
502
|
-
def
|
481
|
+
def render
|
503
482
|
wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
|
504
|
-
|
505
|
-
rendered_lines = @rendered_screen.lines
|
506
483
|
new_lines = wrapped_prompt_and_input_lines.flatten(1)[screen_scroll_top, screen_height].map do |prompt, line|
|
507
484
|
prompt_width = Reline::Unicode.calculate_width(prompt, true)
|
508
485
|
[[0, prompt_width, prompt], [prompt_width, Reline::Unicode.calculate_width(line, true), line]]
|
@@ -520,12 +497,24 @@ class Reline::LineEditor
|
|
520
497
|
x_range, y_range = dialog_range dialog, wrapped_cursor_y - screen_scroll_top
|
521
498
|
y_range.each do |row|
|
522
499
|
next if row < 0 || row >= screen_height
|
500
|
+
|
523
501
|
dialog_rows = new_lines[row] ||= []
|
524
502
|
# index 0 is for prompt, index 1 is for line, index 2.. is for dialog
|
525
503
|
dialog_rows[index + 2] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]]
|
526
504
|
end
|
527
505
|
end
|
528
506
|
|
507
|
+
Reline::IOGate.buffered_output do
|
508
|
+
render_differential new_lines, wrapped_cursor_x, wrapped_cursor_y - screen_scroll_top
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
# Reflects lines to be rendered and new cursor position to the screen
|
513
|
+
# by calculating the difference from the previous render.
|
514
|
+
|
515
|
+
private def render_differential(new_lines, new_cursor_x, new_cursor_y)
|
516
|
+
Reline::IOGate.disable_auto_linewrap(true) if Reline::IOGate.win?
|
517
|
+
rendered_lines = @rendered_screen.lines
|
529
518
|
cursor_y = @rendered_screen.cursor_y
|
530
519
|
if new_lines != rendered_lines
|
531
520
|
# Hide cursor while rendering to avoid cursor flickering.
|
@@ -552,11 +541,17 @@ class Reline::LineEditor
|
|
552
541
|
@rendered_screen.lines = new_lines
|
553
542
|
Reline::IOGate.show_cursor
|
554
543
|
end
|
555
|
-
|
556
|
-
|
557
|
-
Reline::IOGate.move_cursor_down
|
558
|
-
@rendered_screen.cursor_y =
|
559
|
-
|
544
|
+
Reline::IOGate.move_cursor_column new_cursor_x
|
545
|
+
new_cursor_y = new_cursor_y.clamp(0, screen_height - 1)
|
546
|
+
Reline::IOGate.move_cursor_down new_cursor_y - cursor_y
|
547
|
+
@rendered_screen.cursor_y = new_cursor_y
|
548
|
+
ensure
|
549
|
+
Reline::IOGate.disable_auto_linewrap(false) if Reline::IOGate.win?
|
550
|
+
end
|
551
|
+
|
552
|
+
private def clear_rendered_screen_cache
|
553
|
+
@rendered_screen.lines = []
|
554
|
+
@rendered_screen.cursor_y = 0
|
560
555
|
end
|
561
556
|
|
562
557
|
def upper_space_height(wrapped_cursor_y)
|
@@ -568,7 +563,7 @@ class Reline::LineEditor
|
|
568
563
|
end
|
569
564
|
|
570
565
|
def rerender
|
571
|
-
|
566
|
+
render unless @in_pasting
|
572
567
|
end
|
573
568
|
|
574
569
|
class DialogProcScope
|
@@ -586,8 +581,9 @@ class Reline::LineEditor
|
|
586
581
|
@context
|
587
582
|
end
|
588
583
|
|
589
|
-
def retrieve_completion_block(
|
590
|
-
@line_editor.retrieve_completion_block
|
584
|
+
def retrieve_completion_block(_unused = false)
|
585
|
+
preposing, target, postposing, _quote = @line_editor.retrieve_completion_block
|
586
|
+
[preposing, target, postposing]
|
591
587
|
end
|
592
588
|
|
593
589
|
def call_completion_proc_with_checking_args(pre, target, post)
|
@@ -807,98 +803,73 @@ class Reline::LineEditor
|
|
807
803
|
@config.editing_mode
|
808
804
|
end
|
809
805
|
|
810
|
-
private def menu(
|
806
|
+
private def menu(list)
|
811
807
|
@menu_info = MenuInfo.new(list)
|
812
808
|
end
|
813
809
|
|
814
|
-
private def
|
815
|
-
|
816
|
-
list
|
817
|
-
|
818
|
-
|
810
|
+
private def filter_normalize_candidates(target, list)
|
811
|
+
target = target.downcase if @config.completion_ignore_case
|
812
|
+
list.select do |item|
|
813
|
+
next unless item
|
814
|
+
unless Encoding.compatible?(target.encoding, item.encoding)
|
815
|
+
# Workaround for Readline test
|
816
|
+
if defined?(::Readline) && ::Readline == ::Reline
|
817
|
+
raise Encoding::CompatibilityError, "incompatible character encodings: #{target.encoding} and #{item.encoding}"
|
818
|
+
end
|
819
819
|
end
|
820
|
+
|
820
821
|
if @config.completion_ignore_case
|
821
|
-
|
822
|
+
item.downcase.start_with?(target)
|
822
823
|
else
|
823
|
-
|
824
|
-
end
|
825
|
-
}.uniq
|
826
|
-
if is_menu
|
827
|
-
menu(target, list)
|
828
|
-
return nil
|
829
|
-
end
|
830
|
-
completed = list.inject { |memo, item|
|
831
|
-
begin
|
832
|
-
memo_mbchars = memo.unicode_normalize.grapheme_clusters
|
833
|
-
item_mbchars = item.unicode_normalize.grapheme_clusters
|
834
|
-
rescue Encoding::CompatibilityError
|
835
|
-
memo_mbchars = memo.grapheme_clusters
|
836
|
-
item_mbchars = item.grapheme_clusters
|
837
|
-
end
|
838
|
-
size = [memo_mbchars.size, item_mbchars.size].min
|
839
|
-
result = +''
|
840
|
-
size.times do |i|
|
841
|
-
if @config.completion_ignore_case
|
842
|
-
if memo_mbchars[i].casecmp?(item_mbchars[i])
|
843
|
-
result << memo_mbchars[i]
|
844
|
-
else
|
845
|
-
break
|
846
|
-
end
|
847
|
-
else
|
848
|
-
if memo_mbchars[i] == item_mbchars[i]
|
849
|
-
result << memo_mbchars[i]
|
850
|
-
else
|
851
|
-
break
|
852
|
-
end
|
853
|
-
end
|
824
|
+
item.start_with?(target)
|
854
825
|
end
|
855
|
-
|
856
|
-
|
857
|
-
|
826
|
+
end.map do |item|
|
827
|
+
item.unicode_normalize
|
828
|
+
rescue Encoding::CompatibilityError
|
829
|
+
item
|
830
|
+
end.uniq
|
858
831
|
end
|
859
832
|
|
860
|
-
private def perform_completion(
|
833
|
+
private def perform_completion(preposing, target, postposing, quote, list)
|
834
|
+
candidates = filter_normalize_candidates(target, list)
|
835
|
+
|
861
836
|
case @completion_state
|
862
|
-
when CompletionState::NORMAL
|
863
|
-
@completion_state = CompletionState::COMPLETION
|
864
837
|
when CompletionState::PERFECT_MATCH
|
865
|
-
@dig_perfect_match_proc
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
is_menu = false
|
875
|
-
end
|
876
|
-
result = complete_internal_proc(list, is_menu)
|
877
|
-
if @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
|
838
|
+
if @dig_perfect_match_proc
|
839
|
+
@dig_perfect_match_proc.call(@perfect_matched)
|
840
|
+
return
|
841
|
+
end
|
842
|
+
when CompletionState::MENU
|
843
|
+
menu(candidates)
|
844
|
+
return
|
845
|
+
when CompletionState::MENU_WITH_PERFECT_MATCH
|
846
|
+
menu(candidates)
|
878
847
|
@completion_state = CompletionState::PERFECT_MATCH
|
848
|
+
return
|
879
849
|
end
|
880
|
-
|
881
|
-
|
882
|
-
return if completed.
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
@
|
850
|
+
|
851
|
+
completed = Reline::Unicode.common_prefix(candidates, ignore_case: @config.completion_ignore_case)
|
852
|
+
return if completed.empty?
|
853
|
+
|
854
|
+
append_character = ''
|
855
|
+
if candidates.include?(completed)
|
856
|
+
if candidates.one?
|
857
|
+
append_character = quote || completion_append_character.to_s
|
858
|
+
@completion_state = CompletionState::PERFECT_MATCH
|
859
|
+
elsif @config.show_all_if_ambiguous
|
860
|
+
menu(candidates)
|
861
|
+
@completion_state = CompletionState::PERFECT_MATCH
|
892
862
|
else
|
893
|
-
@completion_state = CompletionState::
|
894
|
-
perform_completion(list, true) if @config.show_all_if_ambiguous
|
895
|
-
end
|
896
|
-
if not just_show_list and target < completed
|
897
|
-
@buffer_of_lines[@line_index] = (preposing + completed + completion_append_character.to_s + postposing).split("\n")[@line_index] || String.new(encoding: @encoding)
|
898
|
-
line_to_pointer = (preposing + completed + completion_append_character.to_s).split("\n")[@line_index] || String.new(encoding: @encoding)
|
899
|
-
@byte_pointer = line_to_pointer.bytesize
|
863
|
+
@completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
|
900
864
|
end
|
865
|
+
@perfect_matched = completed
|
866
|
+
else
|
867
|
+
@completion_state = CompletionState::MENU
|
868
|
+
menu(candidates) if @config.show_all_if_ambiguous
|
901
869
|
end
|
870
|
+
@buffer_of_lines[@line_index] = (preposing + completed + append_character + postposing).split("\n")[@line_index] || String.new(encoding: encoding)
|
871
|
+
line_to_pointer = (preposing + completed + append_character).split("\n")[@line_index] || String.new(encoding: encoding)
|
872
|
+
@byte_pointer = line_to_pointer.bytesize
|
902
873
|
end
|
903
874
|
|
904
875
|
def dialog_proc_scope_completion_journey_data
|
@@ -927,8 +898,8 @@ class Reline::LineEditor
|
|
927
898
|
end
|
928
899
|
|
929
900
|
private def retrieve_completion_journey_state
|
930
|
-
preposing, target, postposing = retrieve_completion_block
|
931
|
-
list = call_completion_proc
|
901
|
+
preposing, target, postposing, quote = retrieve_completion_block
|
902
|
+
list = call_completion_proc(preposing, target, postposing, quote)
|
932
903
|
return unless list.is_a?(Array)
|
933
904
|
|
934
905
|
candidates = list.select{ |item| item.start_with?(target) }
|
@@ -941,28 +912,36 @@ class Reline::LineEditor
|
|
941
912
|
)
|
942
913
|
end
|
943
914
|
|
944
|
-
private def run_for_operators(key, method_symbol
|
915
|
+
private def run_for_operators(key, method_symbol)
|
916
|
+
# Reject multibyte input (converted to ed_insert) in vi_command mode
|
917
|
+
return if method_symbol == :ed_insert && @config.editing_mode_is?(:vi_command) && !@waiting_proc
|
918
|
+
|
919
|
+
if ARGUMENT_DIGIT_METHODS.include?(method_symbol) && !@waiting_proc
|
920
|
+
wrap_method_call(method_symbol, key, false)
|
921
|
+
return
|
922
|
+
end
|
923
|
+
|
945
924
|
if @vi_waiting_operator
|
946
|
-
if VI_MOTIONS.include?(method_symbol)
|
925
|
+
if @waiting_proc || VI_MOTIONS.include?(method_symbol)
|
947
926
|
old_byte_pointer = @byte_pointer
|
948
927
|
@vi_arg = (@vi_arg || 1) * @vi_waiting_operator_arg
|
949
|
-
|
928
|
+
wrap_method_call(method_symbol, key, true)
|
950
929
|
unless @waiting_proc
|
951
930
|
byte_pointer_diff = @byte_pointer - old_byte_pointer
|
952
931
|
@byte_pointer = old_byte_pointer
|
953
|
-
|
954
|
-
wrap_method_call(@vi_waiting_operator, method_obj, byte_pointer_diff)
|
932
|
+
__send__(@vi_waiting_operator, byte_pointer_diff)
|
955
933
|
cleanup_waiting
|
956
934
|
end
|
957
935
|
else
|
958
936
|
# Ignores operator when not motion is given.
|
959
|
-
|
937
|
+
wrap_method_call(method_symbol, key, false)
|
960
938
|
cleanup_waiting
|
961
939
|
end
|
962
|
-
@vi_arg = nil
|
963
940
|
else
|
964
|
-
|
941
|
+
wrap_method_call(method_symbol, key, false)
|
965
942
|
end
|
943
|
+
@vi_arg = nil
|
944
|
+
@kill_ring.process
|
966
945
|
end
|
967
946
|
|
968
947
|
private def argumentable?(method_obj)
|
@@ -975,20 +954,23 @@ class Reline::LineEditor
|
|
975
954
|
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :inclusive }
|
976
955
|
end
|
977
956
|
|
978
|
-
def wrap_method_call(method_symbol,
|
979
|
-
if @
|
980
|
-
|
981
|
-
|
957
|
+
def wrap_method_call(method_symbol, key, with_operator)
|
958
|
+
if @waiting_proc
|
959
|
+
@waiting_proc.call(key)
|
960
|
+
return
|
982
961
|
end
|
962
|
+
|
963
|
+
return unless respond_to?(method_symbol, true)
|
964
|
+
method_obj = method(method_symbol)
|
983
965
|
if @vi_arg and argumentable?(method_obj)
|
984
|
-
if
|
985
|
-
method_obj.(key, arg: @vi_arg, inclusive:
|
966
|
+
if inclusive?(method_obj)
|
967
|
+
method_obj.(key, arg: @vi_arg, inclusive: with_operator)
|
986
968
|
else
|
987
969
|
method_obj.(key, arg: @vi_arg)
|
988
970
|
end
|
989
971
|
else
|
990
|
-
if
|
991
|
-
method_obj.(key, inclusive:
|
972
|
+
if inclusive?(method_obj)
|
973
|
+
method_obj.(key, inclusive: with_operator)
|
992
974
|
else
|
993
975
|
method_obj.(key)
|
994
976
|
end
|
@@ -1003,90 +985,20 @@ class Reline::LineEditor
|
|
1003
985
|
@drop_terminate_spaces = false
|
1004
986
|
end
|
1005
987
|
|
1006
|
-
|
1007
|
-
|
1008
|
-
cleanup_waiting
|
1009
|
-
elsif @waiting_proc
|
1010
|
-
old_byte_pointer = @byte_pointer
|
1011
|
-
@waiting_proc.call(key)
|
1012
|
-
if @vi_waiting_operator
|
1013
|
-
byte_pointer_diff = @byte_pointer - old_byte_pointer
|
1014
|
-
@byte_pointer = old_byte_pointer
|
1015
|
-
method_obj = method(@vi_waiting_operator)
|
1016
|
-
wrap_method_call(@vi_waiting_operator, method_obj, byte_pointer_diff)
|
1017
|
-
cleanup_waiting
|
1018
|
-
end
|
1019
|
-
@kill_ring.process
|
1020
|
-
return
|
1021
|
-
end
|
988
|
+
ARGUMENT_DIGIT_METHODS = %i[ed_digit vi_zero ed_argument_digit]
|
989
|
+
VI_WAITING_ACCEPT_METHODS = %i[vi_change_meta vi_delete_meta vi_yank ed_insert ed_argument_digit]
|
1022
990
|
|
1023
|
-
|
1024
|
-
|
991
|
+
private def process_key(key, method_symbol)
|
992
|
+
if @waiting_proc
|
993
|
+
cleanup_waiting unless key.size == 1
|
1025
994
|
end
|
1026
|
-
if
|
1027
|
-
|
1028
|
-
run_for_operators(key, method_symbol) do |with_operator|
|
1029
|
-
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
1030
|
-
end
|
1031
|
-
else
|
1032
|
-
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
1033
|
-
end
|
1034
|
-
@kill_ring.process
|
1035
|
-
if @vi_arg
|
1036
|
-
@vi_arg = nil
|
1037
|
-
end
|
1038
|
-
elsif @vi_arg
|
1039
|
-
if key.chr =~ /[0-9]/
|
1040
|
-
ed_argument_digit(key)
|
1041
|
-
else
|
1042
|
-
if argumentable?(method_obj)
|
1043
|
-
run_for_operators(key, method_symbol) do |with_operator|
|
1044
|
-
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
1045
|
-
end
|
1046
|
-
elsif method_obj
|
1047
|
-
wrap_method_call(method_symbol, method_obj, key)
|
1048
|
-
else
|
1049
|
-
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
1050
|
-
end
|
1051
|
-
@kill_ring.process
|
1052
|
-
if @vi_arg
|
1053
|
-
@vi_arg = nil
|
1054
|
-
end
|
1055
|
-
end
|
1056
|
-
elsif method_obj
|
1057
|
-
if method_symbol == :ed_argument_digit
|
1058
|
-
wrap_method_call(method_symbol, method_obj, key)
|
1059
|
-
else
|
1060
|
-
run_for_operators(key, method_symbol) do |with_operator|
|
1061
|
-
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
1062
|
-
end
|
1063
|
-
end
|
1064
|
-
@kill_ring.process
|
1065
|
-
else
|
1066
|
-
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
995
|
+
if @vi_waiting_operator
|
996
|
+
cleanup_waiting unless VI_WAITING_ACCEPT_METHODS.include?(method_symbol) || VI_MOTIONS.include?(method_symbol)
|
1067
997
|
end
|
1068
|
-
end
|
1069
998
|
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
if @multibyte_buffer.dup.force_encoding(@encoding).valid_encoding?
|
1074
|
-
process_key(@multibyte_buffer.dup.force_encoding(@encoding), nil)
|
1075
|
-
@multibyte_buffer.clear
|
1076
|
-
else
|
1077
|
-
# invalid
|
1078
|
-
return
|
1079
|
-
end
|
1080
|
-
else # single byte
|
1081
|
-
return if key.char >= 128 # maybe, first byte of multi byte
|
1082
|
-
method_symbol = @config.editing_mode.get_method(key.combined_char)
|
1083
|
-
process_key(key.combined_char, method_symbol)
|
1084
|
-
@multibyte_buffer.clear
|
1085
|
-
end
|
1086
|
-
if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
|
1087
|
-
byte_size = Reline::Unicode.get_prev_mbchar_size(@buffer_of_lines[@line_index], @byte_pointer)
|
1088
|
-
@byte_pointer -= byte_size
|
1089
|
-
end
|
999
|
+
process_insert(force: method_symbol != :ed_insert)
|
1000
|
+
|
1001
|
+
run_for_operators(key, method_symbol)
|
1090
1002
|
end
|
1091
1003
|
|
1092
1004
|
def update(key)
|
@@ -1100,25 +1012,22 @@ class Reline::LineEditor
|
|
1100
1012
|
end
|
1101
1013
|
|
1102
1014
|
def input_key(key)
|
1103
|
-
|
1015
|
+
old_buffer_of_lines = @buffer_of_lines.dup
|
1104
1016
|
@config.reset_oneshot_key_bindings
|
1105
|
-
@dialogs.each do |dialog|
|
1106
|
-
if key.char.instance_of?(Symbol) and key.char == dialog.name
|
1107
|
-
return
|
1108
|
-
end
|
1109
|
-
end
|
1110
1017
|
if key.char.nil?
|
1111
1018
|
process_insert(force: true)
|
1112
1019
|
@eof = buffer_empty?
|
1113
1020
|
finish
|
1114
1021
|
return
|
1115
1022
|
end
|
1023
|
+
return if @dialogs.any? { |dialog| dialog.name == key.method_symbol }
|
1024
|
+
|
1116
1025
|
@completion_occurs = false
|
1117
1026
|
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1027
|
+
process_key(key.char, key.method_symbol)
|
1028
|
+
if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
|
1029
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(@buffer_of_lines[@line_index], @byte_pointer)
|
1030
|
+
@byte_pointer -= byte_size
|
1122
1031
|
end
|
1123
1032
|
|
1124
1033
|
@prev_action_state, @next_action_state = @next_action_state, NullActionState
|
@@ -1128,15 +1037,16 @@ class Reline::LineEditor
|
|
1128
1037
|
@completion_journey_state = nil
|
1129
1038
|
end
|
1130
1039
|
|
1131
|
-
|
1132
|
-
|
1040
|
+
modified = old_buffer_of_lines != @buffer_of_lines
|
1041
|
+
|
1042
|
+
push_undo_redo(modified) unless @restoring
|
1043
|
+
@restoring = false
|
1133
1044
|
|
1134
1045
|
if @in_pasting
|
1135
1046
|
clear_dialogs
|
1136
1047
|
return
|
1137
1048
|
end
|
1138
1049
|
|
1139
|
-
modified = @old_buffer_of_lines != @buffer_of_lines
|
1140
1050
|
if !@completion_occurs && modified && !@config.disable_completion && @config.autocompletion
|
1141
1051
|
# Auto complete starts only when edited
|
1142
1052
|
process_insert(force: true)
|
@@ -1145,26 +1055,17 @@ class Reline::LineEditor
|
|
1145
1055
|
modified
|
1146
1056
|
end
|
1147
1057
|
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1058
|
+
MAX_UNDO_REDO_HISTORY_SIZE = 100
|
1059
|
+
def push_undo_redo(modified)
|
1060
|
+
if modified
|
1061
|
+
@undo_redo_history = @undo_redo_history[0..@undo_redo_index]
|
1062
|
+
@undo_redo_history.push([@buffer_of_lines.dup, @byte_pointer, @line_index])
|
1063
|
+
if @undo_redo_history.size > MAX_UNDO_REDO_HISTORY_SIZE
|
1064
|
+
@undo_redo_history.shift
|
1065
|
+
end
|
1066
|
+
@undo_redo_index = @undo_redo_history.size - 1
|
1155
1067
|
else
|
1156
|
-
@
|
1157
|
-
@input_lines_position += 1
|
1158
|
-
@input_lines.push([@buffer_of_lines.dup, @byte_pointer, @line_index])
|
1159
|
-
end
|
1160
|
-
trim_input_lines
|
1161
|
-
end
|
1162
|
-
|
1163
|
-
MAX_INPUT_LINES = 100
|
1164
|
-
def trim_input_lines
|
1165
|
-
if @input_lines.size > MAX_INPUT_LINES
|
1166
|
-
@input_lines.shift
|
1167
|
-
@input_lines_position -= 1
|
1068
|
+
@undo_redo_history[@undo_redo_index] = [@buffer_of_lines.dup, @byte_pointer, @line_index]
|
1168
1069
|
end
|
1169
1070
|
end
|
1170
1071
|
|
@@ -1178,9 +1079,8 @@ class Reline::LineEditor
|
|
1178
1079
|
end
|
1179
1080
|
end
|
1180
1081
|
|
1181
|
-
def call_completion_proc
|
1182
|
-
|
1183
|
-
pre, target, post = result
|
1082
|
+
def call_completion_proc(pre, target, post, quote)
|
1083
|
+
Reline.core.instance_variable_set(:@completion_quote_character, quote)
|
1184
1084
|
result = call_completion_proc_with_checking_args(pre, target, post)
|
1185
1085
|
Reline.core.instance_variable_set(:@completion_quote_character, nil)
|
1186
1086
|
result
|
@@ -1244,84 +1144,32 @@ class Reline::LineEditor
|
|
1244
1144
|
process_auto_indent
|
1245
1145
|
end
|
1246
1146
|
|
1247
|
-
def
|
1248
|
-
|
1249
|
-
|
1250
|
-
@line_index = line_index
|
1251
|
-
if byte_pointer
|
1252
|
-
@byte_pointer = byte_pointer
|
1253
|
-
else
|
1254
|
-
calculate_nearest_cursor(cursor)
|
1255
|
-
end
|
1256
|
-
process_auto_indent
|
1257
|
-
end
|
1258
|
-
|
1259
|
-
def retrieve_completion_block(set_completion_quote_character = false)
|
1260
|
-
if Reline.completer_word_break_characters.empty?
|
1261
|
-
word_break_regexp = nil
|
1262
|
-
else
|
1263
|
-
word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
|
1264
|
-
end
|
1265
|
-
if Reline.completer_quote_characters.empty?
|
1266
|
-
quote_characters_regexp = nil
|
1267
|
-
else
|
1268
|
-
quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
|
1269
|
-
end
|
1270
|
-
before = current_line.byteslice(0, @byte_pointer)
|
1271
|
-
rest = nil
|
1272
|
-
break_pointer = nil
|
1147
|
+
def retrieve_completion_block
|
1148
|
+
quote_characters = Reline.completer_quote_characters
|
1149
|
+
before = current_line.byteslice(0, @byte_pointer).grapheme_clusters
|
1273
1150
|
quote = nil
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
elsif quote and slice.start_with?(escaped_quote)
|
1288
|
-
# skip
|
1289
|
-
i += 2
|
1290
|
-
elsif quote_characters_regexp and slice =~ quote_characters_regexp # find new "
|
1291
|
-
rest = $'
|
1292
|
-
quote = $&
|
1293
|
-
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
|
1294
|
-
escaped_quote = /\\#{Regexp.escape(quote)}/
|
1295
|
-
i += 1
|
1296
|
-
break_pointer = i - 1
|
1297
|
-
elsif word_break_regexp and not quote and slice =~ word_break_regexp
|
1298
|
-
rest = $'
|
1299
|
-
i += 1
|
1300
|
-
before = current_line.byteslice(i, @byte_pointer - i)
|
1301
|
-
break_pointer = i
|
1302
|
-
else
|
1303
|
-
i += 1
|
1304
|
-
end
|
1305
|
-
end
|
1306
|
-
postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
|
1307
|
-
if rest
|
1308
|
-
preposing = current_line.byteslice(0, break_pointer)
|
1309
|
-
target = rest
|
1310
|
-
if set_completion_quote_character and quote
|
1311
|
-
Reline.core.instance_variable_set(:@completion_quote_character, quote)
|
1312
|
-
if postposing !~ /(?!\\)#{Regexp.escape(quote)}/ # closing quote
|
1313
|
-
insert_text(quote)
|
1151
|
+
# Calculate closing quote when cursor is at the end of the line
|
1152
|
+
if current_line.bytesize == @byte_pointer && !quote_characters.empty?
|
1153
|
+
escaped = false
|
1154
|
+
before.each do |c|
|
1155
|
+
if escaped
|
1156
|
+
escaped = false
|
1157
|
+
next
|
1158
|
+
elsif c == '\\'
|
1159
|
+
escaped = true
|
1160
|
+
elsif quote
|
1161
|
+
quote = nil if c == quote
|
1162
|
+
elsif quote_characters.include?(c)
|
1163
|
+
quote = c
|
1314
1164
|
end
|
1315
1165
|
end
|
1316
|
-
else
|
1317
|
-
preposing = ''
|
1318
|
-
if break_pointer
|
1319
|
-
preposing = current_line.byteslice(0, break_pointer)
|
1320
|
-
else
|
1321
|
-
preposing = ''
|
1322
|
-
end
|
1323
|
-
target = before
|
1324
1166
|
end
|
1167
|
+
|
1168
|
+
word_break_characters = quote_characters + Reline.completer_word_break_characters
|
1169
|
+
break_index = before.rindex { |c| word_break_characters.include?(c) || quote_characters.include?(c) } || -1
|
1170
|
+
preposing = before.take(break_index + 1).join
|
1171
|
+
target = before.drop(break_index + 1).join
|
1172
|
+
postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
|
1325
1173
|
lines = whole_lines
|
1326
1174
|
if @line_index > 0
|
1327
1175
|
preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
|
@@ -1329,7 +1177,7 @@ class Reline::LineEditor
|
|
1329
1177
|
if (lines.size - 1) > @line_index
|
1330
1178
|
postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
|
1331
1179
|
end
|
1332
|
-
[preposing.encode(
|
1180
|
+
[preposing.encode(encoding), target.encode(encoding), postposing.encode(encoding), quote&.encode(encoding)]
|
1333
1181
|
end
|
1334
1182
|
|
1335
1183
|
def confirm_multiline_termination
|
@@ -1337,16 +1185,14 @@ class Reline::LineEditor
|
|
1337
1185
|
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
|
1338
1186
|
end
|
1339
1187
|
|
1340
|
-
def
|
1341
|
-
save_old_buffer
|
1188
|
+
def insert_multiline_text(text)
|
1342
1189
|
pre = @buffer_of_lines[@line_index].byteslice(0, @byte_pointer)
|
1343
1190
|
post = @buffer_of_lines[@line_index].byteslice(@byte_pointer..)
|
1344
|
-
lines = (pre + text.gsub(/\r\n?/, "\n") + post).split("\n", -1)
|
1191
|
+
lines = (pre + Reline::Unicode.safe_encode(text, encoding).gsub(/\r\n?/, "\n") + post).split("\n", -1)
|
1345
1192
|
lines << '' if lines.empty?
|
1346
1193
|
@buffer_of_lines[@line_index, 1] = lines
|
1347
1194
|
@line_index += lines.size - 1
|
1348
1195
|
@byte_pointer = @buffer_of_lines[@line_index].bytesize - post.bytesize
|
1349
|
-
push_input_lines
|
1350
1196
|
end
|
1351
1197
|
|
1352
1198
|
def insert_text(text)
|
@@ -1386,7 +1232,7 @@ class Reline::LineEditor
|
|
1386
1232
|
last += current_line.bytesize if last < 0
|
1387
1233
|
first += current_line.bytesize if first < 0
|
1388
1234
|
range = range.exclude_end? ? first...last : first..last
|
1389
|
-
line = current_line.bytes.reject.with_index{ |c, i| range.include?(i) }.map{ |c| c.chr(Encoding::ASCII_8BIT) }.join.force_encoding(
|
1235
|
+
line = current_line.bytes.reject.with_index{ |c, i| range.include?(i) }.map{ |c| c.chr(Encoding::ASCII_8BIT) }.join.force_encoding(encoding)
|
1390
1236
|
set_current_line(line)
|
1391
1237
|
else
|
1392
1238
|
set_current_line(current_line.byteslice(0, start))
|
@@ -1460,10 +1306,11 @@ class Reline::LineEditor
|
|
1460
1306
|
@completion_occurs = move_completed_list(:down)
|
1461
1307
|
else
|
1462
1308
|
@completion_journey_state = nil
|
1463
|
-
|
1309
|
+
pre, target, post, quote = retrieve_completion_block
|
1310
|
+
result = call_completion_proc(pre, target, post, quote)
|
1464
1311
|
if result.is_a?(Array)
|
1465
1312
|
@completion_occurs = true
|
1466
|
-
perform_completion(
|
1313
|
+
perform_completion(pre, target, post, quote, result)
|
1467
1314
|
end
|
1468
1315
|
end
|
1469
1316
|
end
|
@@ -1511,21 +1358,11 @@ class Reline::LineEditor
|
|
1511
1358
|
# digit or if the existing argument is already greater than a
|
1512
1359
|
# million.
|
1513
1360
|
# GNU Readline:: +self-insert+ (a, b, A, 1, !, …) Insert yourself.
|
1514
|
-
private def ed_insert(
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
return
|
1520
|
-
end
|
1521
|
-
str = key
|
1522
|
-
else
|
1523
|
-
begin
|
1524
|
-
key.chr.encode(Encoding::UTF_8)
|
1525
|
-
rescue Encoding::UndefinedConversionError
|
1526
|
-
return
|
1527
|
-
end
|
1528
|
-
str = key.chr
|
1361
|
+
private def ed_insert(str)
|
1362
|
+
begin
|
1363
|
+
str.encode(Encoding::UTF_8)
|
1364
|
+
rescue Encoding::UndefinedConversionError
|
1365
|
+
return
|
1529
1366
|
end
|
1530
1367
|
if @in_pasting
|
1531
1368
|
@continuous_insertion_buffer << str
|
@@ -1536,24 +1373,26 @@ class Reline::LineEditor
|
|
1536
1373
|
|
1537
1374
|
insert_text(str)
|
1538
1375
|
end
|
1539
|
-
alias_method :ed_digit, :ed_insert
|
1540
1376
|
alias_method :self_insert, :ed_insert
|
1541
1377
|
|
1542
|
-
private def
|
1543
|
-
@
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1378
|
+
private def ed_digit(key)
|
1379
|
+
if @vi_arg
|
1380
|
+
ed_argument_digit(key)
|
1381
|
+
else
|
1382
|
+
ed_insert(key)
|
1383
|
+
end
|
1384
|
+
end
|
1385
|
+
|
1386
|
+
private def insert_raw_char(str, arg: 1)
|
1387
|
+
arg.times do
|
1388
|
+
if str == "\C-j" or str == "\C-m"
|
1389
|
+
key_newline(str)
|
1390
|
+
elsif str != "\0"
|
1391
|
+
# Ignore NUL.
|
1392
|
+
ed_insert(str)
|
1552
1393
|
end
|
1553
|
-
|
1554
|
-
}
|
1394
|
+
end
|
1555
1395
|
end
|
1556
|
-
alias_method :quoted_insert, :ed_quoted_insert
|
1557
1396
|
|
1558
1397
|
private def ed_next_char(key, arg: 1)
|
1559
1398
|
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
@@ -1582,14 +1421,21 @@ class Reline::LineEditor
|
|
1582
1421
|
alias_method :backward_char, :ed_prev_char
|
1583
1422
|
|
1584
1423
|
private def vi_first_print(key)
|
1585
|
-
@byte_pointer
|
1424
|
+
@byte_pointer = Reline::Unicode.vi_first_print(current_line)
|
1586
1425
|
end
|
1587
1426
|
|
1588
1427
|
private def ed_move_to_beg(key)
|
1589
1428
|
@byte_pointer = 0
|
1590
1429
|
end
|
1591
1430
|
alias_method :beginning_of_line, :ed_move_to_beg
|
1592
|
-
|
1431
|
+
|
1432
|
+
private def vi_zero(key)
|
1433
|
+
if @vi_arg
|
1434
|
+
ed_argument_digit(key)
|
1435
|
+
else
|
1436
|
+
ed_move_to_beg(key)
|
1437
|
+
end
|
1438
|
+
end
|
1593
1439
|
|
1594
1440
|
private def ed_move_to_end(key)
|
1595
1441
|
@byte_pointer = current_line.bytesize
|
@@ -1597,27 +1443,22 @@ class Reline::LineEditor
|
|
1597
1443
|
alias_method :end_of_line, :ed_move_to_end
|
1598
1444
|
|
1599
1445
|
private def generate_searcher(search_key)
|
1600
|
-
search_word = String.new(encoding:
|
1601
|
-
multibyte_buf = String.new(encoding: 'ASCII-8BIT')
|
1446
|
+
search_word = String.new(encoding: encoding)
|
1602
1447
|
hit_pointer = nil
|
1603
1448
|
lambda do |key|
|
1604
1449
|
search_again = false
|
1605
1450
|
case key
|
1606
|
-
when "\C-h"
|
1451
|
+
when "\C-h", "\C-?"
|
1607
1452
|
grapheme_clusters = search_word.grapheme_clusters
|
1608
1453
|
if grapheme_clusters.size > 0
|
1609
1454
|
grapheme_clusters.pop
|
1610
1455
|
search_word = grapheme_clusters.join
|
1611
1456
|
end
|
1612
|
-
when "\C-r"
|
1457
|
+
when "\C-r", "\C-s"
|
1613
1458
|
search_again = true if search_key == key
|
1614
1459
|
search_key = key
|
1615
1460
|
else
|
1616
|
-
|
1617
|
-
if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
|
1618
|
-
search_word << multibyte_buf.dup.force_encoding(@encoding)
|
1619
|
-
multibyte_buf.clear
|
1620
|
-
end
|
1461
|
+
search_word << key
|
1621
1462
|
end
|
1622
1463
|
hit = nil
|
1623
1464
|
if not search_word.empty? and @line_backup_in_history&.include?(search_word)
|
@@ -1630,10 +1471,10 @@ class Reline::LineEditor
|
|
1630
1471
|
end
|
1631
1472
|
if @history_pointer
|
1632
1473
|
case search_key
|
1633
|
-
when "\C-r"
|
1474
|
+
when "\C-r"
|
1634
1475
|
history_pointer_base = 0
|
1635
1476
|
history = Reline::HISTORY[0..(@history_pointer - 1)]
|
1636
|
-
when "\C-s"
|
1477
|
+
when "\C-s"
|
1637
1478
|
history_pointer_base = @history_pointer + 1
|
1638
1479
|
history = Reline::HISTORY[(@history_pointer + 1)..-1]
|
1639
1480
|
end
|
@@ -1643,10 +1484,10 @@ class Reline::LineEditor
|
|
1643
1484
|
end
|
1644
1485
|
elsif @history_pointer
|
1645
1486
|
case search_key
|
1646
|
-
when "\C-r"
|
1487
|
+
when "\C-r"
|
1647
1488
|
history_pointer_base = 0
|
1648
1489
|
history = Reline::HISTORY[0..@history_pointer]
|
1649
|
-
when "\C-s"
|
1490
|
+
when "\C-s"
|
1650
1491
|
history_pointer_base = @history_pointer
|
1651
1492
|
history = Reline::HISTORY[@history_pointer..-1]
|
1652
1493
|
end
|
@@ -1655,11 +1496,11 @@ class Reline::LineEditor
|
|
1655
1496
|
history = Reline::HISTORY
|
1656
1497
|
end
|
1657
1498
|
case search_key
|
1658
|
-
when "\C-r"
|
1499
|
+
when "\C-r"
|
1659
1500
|
hit_index = history.rindex { |item|
|
1660
1501
|
item.include?(search_word)
|
1661
1502
|
}
|
1662
|
-
when "\C-s"
|
1503
|
+
when "\C-s"
|
1663
1504
|
hit_index = history.index { |item|
|
1664
1505
|
item.include?(search_word)
|
1665
1506
|
}
|
@@ -1670,9 +1511,9 @@ class Reline::LineEditor
|
|
1670
1511
|
end
|
1671
1512
|
end
|
1672
1513
|
case search_key
|
1673
|
-
when "\C-r"
|
1514
|
+
when "\C-r"
|
1674
1515
|
prompt_name = 'reverse-i-search'
|
1675
|
-
when "\C-s"
|
1516
|
+
when "\C-s"
|
1676
1517
|
prompt_name = 'i-search'
|
1677
1518
|
end
|
1678
1519
|
prompt_name = "failed #{prompt_name}" unless hit
|
@@ -1681,57 +1522,28 @@ class Reline::LineEditor
|
|
1681
1522
|
end
|
1682
1523
|
|
1683
1524
|
private def incremental_search_history(key)
|
1684
|
-
|
1685
|
-
@line_backup_in_history = whole_buffer
|
1686
|
-
end
|
1525
|
+
backup = @buffer_of_lines.dup, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history
|
1687
1526
|
searcher = generate_searcher(key)
|
1688
1527
|
@searching_prompt = "(reverse-i-search)`': "
|
1689
|
-
termination_keys = ["\C-j"
|
1690
|
-
termination_keys.concat(@config.isearch_terminators
|
1528
|
+
termination_keys = ["\C-j"]
|
1529
|
+
termination_keys.concat(@config.isearch_terminators.chars) if @config.isearch_terminators
|
1691
1530
|
@waiting_proc = ->(k) {
|
1692
|
-
|
1693
|
-
|
1694
|
-
|
1695
|
-
buffer = Reline::HISTORY[@history_pointer]
|
1696
|
-
else
|
1697
|
-
buffer = @line_backup_in_history
|
1698
|
-
end
|
1699
|
-
@buffer_of_lines = buffer.split("\n")
|
1700
|
-
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1701
|
-
@line_index = @buffer_of_lines.size - 1
|
1531
|
+
if k == "\C-g"
|
1532
|
+
# cancel search and restore buffer
|
1533
|
+
@buffer_of_lines, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history = backup
|
1702
1534
|
@searching_prompt = nil
|
1703
1535
|
@waiting_proc = nil
|
1704
|
-
|
1705
|
-
|
1706
|
-
|
1707
|
-
@
|
1708
|
-
@
|
1709
|
-
move_history(
|
1536
|
+
elsif !termination_keys.include?(k) && (k.match?(/[[:print:]]/) || k == "\C-h" || k == "\C-?" || k == "\C-r" || k == "\C-s")
|
1537
|
+
search_word, prompt_name, hit_pointer = searcher.call(k)
|
1538
|
+
Reline.last_incremental_search = search_word
|
1539
|
+
@searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
|
1540
|
+
@searching_prompt += ': ' unless @is_multiline
|
1541
|
+
move_history(hit_pointer, line: :end, cursor: :end) if hit_pointer
|
1542
|
+
else
|
1543
|
+
# terminaton_keys and other keys will terminalte
|
1544
|
+
move_history(@history_pointer, line: :end, cursor: :start)
|
1710
1545
|
@searching_prompt = nil
|
1711
1546
|
@waiting_proc = nil
|
1712
|
-
@byte_pointer = 0
|
1713
|
-
else
|
1714
|
-
chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
|
1715
|
-
if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
|
1716
|
-
search_word, prompt_name, hit_pointer = searcher.call(k)
|
1717
|
-
Reline.last_incremental_search = search_word
|
1718
|
-
@searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
|
1719
|
-
@searching_prompt += ': ' unless @is_multiline
|
1720
|
-
move_history(hit_pointer, line: :end, cursor: :end, save_buffer: false) if hit_pointer
|
1721
|
-
else
|
1722
|
-
if @history_pointer
|
1723
|
-
line = Reline::HISTORY[@history_pointer]
|
1724
|
-
else
|
1725
|
-
line = @line_backup_in_history
|
1726
|
-
end
|
1727
|
-
@line_backup_in_history = whole_buffer
|
1728
|
-
@buffer_of_lines = line.split("\n")
|
1729
|
-
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1730
|
-
@line_index = @buffer_of_lines.size - 1
|
1731
|
-
@searching_prompt = nil
|
1732
|
-
@waiting_proc = nil
|
1733
|
-
@byte_pointer = 0
|
1734
|
-
end
|
1735
1547
|
end
|
1736
1548
|
}
|
1737
1549
|
end
|
@@ -1786,14 +1598,14 @@ class Reline::LineEditor
|
|
1786
1598
|
end
|
1787
1599
|
alias_method :history_search_forward, :ed_search_next_history
|
1788
1600
|
|
1789
|
-
private def move_history(history_pointer, line:, cursor
|
1601
|
+
private def move_history(history_pointer, line:, cursor:)
|
1790
1602
|
history_pointer ||= Reline::HISTORY.size
|
1791
1603
|
return if history_pointer < 0 || history_pointer > Reline::HISTORY.size
|
1792
1604
|
old_history_pointer = @history_pointer || Reline::HISTORY.size
|
1793
1605
|
if old_history_pointer == Reline::HISTORY.size
|
1794
|
-
@line_backup_in_history =
|
1606
|
+
@line_backup_in_history = whole_buffer
|
1795
1607
|
else
|
1796
|
-
Reline::HISTORY[old_history_pointer] = whole_buffer
|
1608
|
+
Reline::HISTORY[old_history_pointer] = whole_buffer
|
1797
1609
|
end
|
1798
1610
|
if history_pointer == Reline::HISTORY.size
|
1799
1611
|
buf = @line_backup_in_history
|
@@ -1803,7 +1615,7 @@ class Reline::LineEditor
|
|
1803
1615
|
@history_pointer = history_pointer
|
1804
1616
|
end
|
1805
1617
|
@buffer_of_lines = buf.split("\n")
|
1806
|
-
@buffer_of_lines = [String.new(encoding:
|
1618
|
+
@buffer_of_lines = [String.new(encoding: encoding)] if @buffer_of_lines.empty?
|
1807
1619
|
@line_index = line == :start ? 0 : line == :end ? @buffer_of_lines.size - 1 : line
|
1808
1620
|
@byte_pointer = cursor == :start ? 0 : cursor == :end ? current_line.bytesize : cursor
|
1809
1621
|
end
|
@@ -1853,17 +1665,10 @@ class Reline::LineEditor
|
|
1853
1665
|
finish
|
1854
1666
|
end
|
1855
1667
|
else
|
1856
|
-
if @line_index ==
|
1857
|
-
if confirm_multiline_termination
|
1858
|
-
finish
|
1859
|
-
else
|
1860
|
-
key_newline(key)
|
1861
|
-
end
|
1862
|
-
else
|
1863
|
-
# should check confirm_multiline_termination to finish?
|
1864
|
-
@line_index = @buffer_of_lines.size - 1
|
1865
|
-
@byte_pointer = current_line.bytesize
|
1668
|
+
if @line_index == @buffer_of_lines.size - 1 && confirm_multiline_termination
|
1866
1669
|
finish
|
1670
|
+
else
|
1671
|
+
key_newline(key)
|
1867
1672
|
end
|
1868
1673
|
end
|
1869
1674
|
else
|
@@ -1871,6 +1676,11 @@ class Reline::LineEditor
|
|
1871
1676
|
end
|
1872
1677
|
end
|
1873
1678
|
|
1679
|
+
private def ed_force_submit(_key)
|
1680
|
+
process_insert(force: true)
|
1681
|
+
finish
|
1682
|
+
end
|
1683
|
+
|
1874
1684
|
private def em_delete_prev_char(key, arg: 1)
|
1875
1685
|
arg.times do
|
1876
1686
|
if @byte_pointer == 0 and @line_index > 0
|
@@ -1937,7 +1747,7 @@ class Reline::LineEditor
|
|
1937
1747
|
alias_method :kill_whole_line, :em_kill_line
|
1938
1748
|
|
1939
1749
|
private def em_delete(key)
|
1940
|
-
if buffer_empty? and key == "\C-d"
|
1750
|
+
if buffer_empty? and key == "\C-d"
|
1941
1751
|
@eof = true
|
1942
1752
|
finish
|
1943
1753
|
elsif @byte_pointer < current_line.bytesize
|
@@ -1955,9 +1765,11 @@ class Reline::LineEditor
|
|
1955
1765
|
if current_line.empty? or @byte_pointer < current_line.bytesize
|
1956
1766
|
em_delete(key)
|
1957
1767
|
elsif !@config.autocompletion # show completed list
|
1958
|
-
|
1768
|
+
pre, target, post, quote = retrieve_completion_block
|
1769
|
+
result = call_completion_proc(pre, target, post, quote)
|
1959
1770
|
if result.is_a?(Array)
|
1960
|
-
|
1771
|
+
candidates = filter_normalize_candidates(target, result)
|
1772
|
+
menu(candidates)
|
1961
1773
|
end
|
1962
1774
|
end
|
1963
1775
|
end
|
@@ -1982,15 +1794,14 @@ class Reline::LineEditor
|
|
1982
1794
|
private def ed_clear_screen(key)
|
1983
1795
|
Reline::IOGate.clear_screen
|
1984
1796
|
@screen_size = Reline::IOGate.get_screen_size
|
1985
|
-
@rendered_screen.lines = []
|
1986
1797
|
@rendered_screen.base_y = 0
|
1987
|
-
|
1798
|
+
clear_rendered_screen_cache
|
1988
1799
|
end
|
1989
1800
|
alias_method :clear_screen, :ed_clear_screen
|
1990
1801
|
|
1991
1802
|
private def em_next_word(key)
|
1992
1803
|
if current_line.bytesize > @byte_pointer
|
1993
|
-
byte_size
|
1804
|
+
byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
1994
1805
|
@byte_pointer += byte_size
|
1995
1806
|
end
|
1996
1807
|
end
|
@@ -1998,7 +1809,7 @@ class Reline::LineEditor
|
|
1998
1809
|
|
1999
1810
|
private def ed_prev_word(key)
|
2000
1811
|
if @byte_pointer > 0
|
2001
|
-
byte_size
|
1812
|
+
byte_size = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
|
2002
1813
|
@byte_pointer -= byte_size
|
2003
1814
|
end
|
2004
1815
|
end
|
@@ -2006,7 +1817,7 @@ class Reline::LineEditor
|
|
2006
1817
|
|
2007
1818
|
private def em_delete_next_word(key)
|
2008
1819
|
if current_line.bytesize > @byte_pointer
|
2009
|
-
byte_size
|
1820
|
+
byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
2010
1821
|
line, word = byteslice!(current_line, @byte_pointer, byte_size)
|
2011
1822
|
set_current_line(line)
|
2012
1823
|
@kill_ring.append(word)
|
@@ -2016,7 +1827,7 @@ class Reline::LineEditor
|
|
2016
1827
|
|
2017
1828
|
private def ed_delete_prev_word(key)
|
2018
1829
|
if @byte_pointer > 0
|
2019
|
-
byte_size
|
1830
|
+
byte_size = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
|
2020
1831
|
line, word = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
|
2021
1832
|
set_current_line(line, @byte_pointer - byte_size)
|
2022
1833
|
@kill_ring.append(word, true)
|
@@ -2056,7 +1867,7 @@ class Reline::LineEditor
|
|
2056
1867
|
|
2057
1868
|
private def em_capitol_case(key)
|
2058
1869
|
if current_line.bytesize > @byte_pointer
|
2059
|
-
byte_size,
|
1870
|
+
byte_size, new_str = Reline::Unicode.em_forward_word_with_capitalization(current_line, @byte_pointer)
|
2060
1871
|
before = current_line.byteslice(0, @byte_pointer)
|
2061
1872
|
after = current_line.byteslice((@byte_pointer + byte_size)..-1)
|
2062
1873
|
set_current_line(before + new_str + after, @byte_pointer + new_str.bytesize)
|
@@ -2066,7 +1877,7 @@ class Reline::LineEditor
|
|
2066
1877
|
|
2067
1878
|
private def em_lower_case(key)
|
2068
1879
|
if current_line.bytesize > @byte_pointer
|
2069
|
-
byte_size
|
1880
|
+
byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
2070
1881
|
part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
|
2071
1882
|
mbchar =~ /[A-Z]/ ? mbchar.downcase : mbchar
|
2072
1883
|
}.join
|
@@ -2079,7 +1890,7 @@ class Reline::LineEditor
|
|
2079
1890
|
|
2080
1891
|
private def em_upper_case(key)
|
2081
1892
|
if current_line.bytesize > @byte_pointer
|
2082
|
-
byte_size
|
1893
|
+
byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
2083
1894
|
part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
|
2084
1895
|
mbchar =~ /[a-z]/ ? mbchar.upcase : mbchar
|
2085
1896
|
}.join
|
@@ -2092,7 +1903,7 @@ class Reline::LineEditor
|
|
2092
1903
|
|
2093
1904
|
private def em_kill_region(key)
|
2094
1905
|
if @byte_pointer > 0
|
2095
|
-
byte_size
|
1906
|
+
byte_size = Reline::Unicode.em_big_backward_word(current_line, @byte_pointer)
|
2096
1907
|
line, deleted = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
|
2097
1908
|
set_current_line(line, @byte_pointer - byte_size)
|
2098
1909
|
@kill_ring.append(deleted, true)
|
@@ -2123,7 +1934,7 @@ class Reline::LineEditor
|
|
2123
1934
|
|
2124
1935
|
private def vi_next_word(key, arg: 1)
|
2125
1936
|
if current_line.bytesize > @byte_pointer
|
2126
|
-
byte_size
|
1937
|
+
byte_size = Reline::Unicode.vi_forward_word(current_line, @byte_pointer, @drop_terminate_spaces)
|
2127
1938
|
@byte_pointer += byte_size
|
2128
1939
|
end
|
2129
1940
|
arg -= 1
|
@@ -2132,7 +1943,7 @@ class Reline::LineEditor
|
|
2132
1943
|
|
2133
1944
|
private def vi_prev_word(key, arg: 1)
|
2134
1945
|
if @byte_pointer > 0
|
2135
|
-
byte_size
|
1946
|
+
byte_size = Reline::Unicode.vi_backward_word(current_line, @byte_pointer)
|
2136
1947
|
@byte_pointer -= byte_size
|
2137
1948
|
end
|
2138
1949
|
arg -= 1
|
@@ -2141,7 +1952,7 @@ class Reline::LineEditor
|
|
2141
1952
|
|
2142
1953
|
private def vi_end_word(key, arg: 1, inclusive: false)
|
2143
1954
|
if current_line.bytesize > @byte_pointer
|
2144
|
-
byte_size
|
1955
|
+
byte_size = Reline::Unicode.vi_forward_end_word(current_line, @byte_pointer)
|
2145
1956
|
@byte_pointer += byte_size
|
2146
1957
|
end
|
2147
1958
|
arg -= 1
|
@@ -2156,7 +1967,7 @@ class Reline::LineEditor
|
|
2156
1967
|
|
2157
1968
|
private def vi_next_big_word(key, arg: 1)
|
2158
1969
|
if current_line.bytesize > @byte_pointer
|
2159
|
-
byte_size
|
1970
|
+
byte_size = Reline::Unicode.vi_big_forward_word(current_line, @byte_pointer)
|
2160
1971
|
@byte_pointer += byte_size
|
2161
1972
|
end
|
2162
1973
|
arg -= 1
|
@@ -2165,7 +1976,7 @@ class Reline::LineEditor
|
|
2165
1976
|
|
2166
1977
|
private def vi_prev_big_word(key, arg: 1)
|
2167
1978
|
if @byte_pointer > 0
|
2168
|
-
byte_size
|
1979
|
+
byte_size = Reline::Unicode.vi_big_backward_word(current_line, @byte_pointer)
|
2169
1980
|
@byte_pointer -= byte_size
|
2170
1981
|
end
|
2171
1982
|
arg -= 1
|
@@ -2174,7 +1985,7 @@ class Reline::LineEditor
|
|
2174
1985
|
|
2175
1986
|
private def vi_end_big_word(key, arg: 1, inclusive: false)
|
2176
1987
|
if current_line.bytesize > @byte_pointer
|
2177
|
-
byte_size
|
1988
|
+
byte_size = Reline::Unicode.vi_big_forward_end_word(current_line, @byte_pointer)
|
2178
1989
|
@byte_pointer += byte_size
|
2179
1990
|
end
|
2180
1991
|
arg -= 1
|
@@ -2259,9 +2070,11 @@ class Reline::LineEditor
|
|
2259
2070
|
line, cut = byteslice!(current_line, @byte_pointer, byte_pointer_diff)
|
2260
2071
|
elsif byte_pointer_diff < 0
|
2261
2072
|
line, cut = byteslice!(current_line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2073
|
+
else
|
2074
|
+
return
|
2262
2075
|
end
|
2263
2076
|
copy_for_vi(cut)
|
2264
|
-
set_current_line(line
|
2077
|
+
set_current_line(line, @byte_pointer + (byte_pointer_diff < 0 ? byte_pointer_diff : 0))
|
2265
2078
|
end
|
2266
2079
|
|
2267
2080
|
private def vi_yank(key, arg: nil)
|
@@ -2280,6 +2093,8 @@ class Reline::LineEditor
|
|
2280
2093
|
cut = current_line.byteslice(@byte_pointer, byte_pointer_diff)
|
2281
2094
|
elsif byte_pointer_diff < 0
|
2282
2095
|
cut = current_line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2096
|
+
else
|
2097
|
+
return
|
2283
2098
|
end
|
2284
2099
|
copy_for_vi(cut)
|
2285
2100
|
end
|
@@ -2325,7 +2140,7 @@ class Reline::LineEditor
|
|
2325
2140
|
}
|
2326
2141
|
system("#{ENV['EDITOR']} #{path}")
|
2327
2142
|
@buffer_of_lines = File.read(path).split("\n")
|
2328
|
-
@buffer_of_lines = [String.new(encoding:
|
2143
|
+
@buffer_of_lines = [String.new(encoding: encoding)] if @buffer_of_lines.empty?
|
2329
2144
|
@line_index = 0
|
2330
2145
|
finish
|
2331
2146
|
end
|
@@ -2350,20 +2165,9 @@ class Reline::LineEditor
|
|
2350
2165
|
end
|
2351
2166
|
|
2352
2167
|
private def ed_argument_digit(key)
|
2353
|
-
|
2354
|
-
|
2355
|
-
|
2356
|
-
unescaped_key = key ^ 0b10000000
|
2357
|
-
unless unescaped_key.chr.to_i.zero?
|
2358
|
-
@vi_arg = unescaped_key.chr.to_i
|
2359
|
-
end
|
2360
|
-
end
|
2361
|
-
else
|
2362
|
-
@vi_arg = key.chr.to_i
|
2363
|
-
end
|
2364
|
-
else
|
2365
|
-
@vi_arg = @vi_arg * 10 + key.chr.to_i
|
2366
|
-
end
|
2168
|
+
# key is expected to be `ESC digit` or `digit`
|
2169
|
+
num = key[/\d/].to_i
|
2170
|
+
@vi_arg = (@vi_arg || 0) * 10 + num
|
2367
2171
|
end
|
2368
2172
|
|
2369
2173
|
private def vi_to_column(key, arg: 0)
|
@@ -2382,7 +2186,7 @@ class Reline::LineEditor
|
|
2382
2186
|
before = current_line.byteslice(0, @byte_pointer)
|
2383
2187
|
remaining_point = @byte_pointer + byte_size
|
2384
2188
|
after = current_line.byteslice(remaining_point, current_line.bytesize - remaining_point)
|
2385
|
-
set_current_line(before + k
|
2189
|
+
set_current_line(before + k + after)
|
2386
2190
|
@waiting_proc = nil
|
2387
2191
|
elsif arg > 1
|
2388
2192
|
byte_size = 0
|
@@ -2392,7 +2196,7 @@ class Reline::LineEditor
|
|
2392
2196
|
before = current_line.byteslice(0, @byte_pointer)
|
2393
2197
|
remaining_point = @byte_pointer + byte_size
|
2394
2198
|
after = current_line.byteslice(remaining_point, current_line.bytesize - remaining_point)
|
2395
|
-
replaced = k
|
2199
|
+
replaced = k * arg
|
2396
2200
|
set_current_line(before + replaced + after, @byte_pointer + replaced.bytesize)
|
2397
2201
|
@waiting_proc = nil
|
2398
2202
|
end
|
@@ -2408,11 +2212,6 @@ class Reline::LineEditor
|
|
2408
2212
|
end
|
2409
2213
|
|
2410
2214
|
private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
|
2411
|
-
if key.instance_of?(String)
|
2412
|
-
inputed_char = key
|
2413
|
-
else
|
2414
|
-
inputed_char = key.chr
|
2415
|
-
end
|
2416
2215
|
prev_total = nil
|
2417
2216
|
total = nil
|
2418
2217
|
found = false
|
@@ -2423,7 +2222,7 @@ class Reline::LineEditor
|
|
2423
2222
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2424
2223
|
total = [mbchar.bytesize, width]
|
2425
2224
|
else
|
2426
|
-
if
|
2225
|
+
if key == mbchar
|
2427
2226
|
arg -= 1
|
2428
2227
|
if arg.zero?
|
2429
2228
|
found = true
|
@@ -2460,11 +2259,6 @@ class Reline::LineEditor
|
|
2460
2259
|
end
|
2461
2260
|
|
2462
2261
|
private def search_prev_char(key, arg, need_next_char = false)
|
2463
|
-
if key.instance_of?(String)
|
2464
|
-
inputed_char = key
|
2465
|
-
else
|
2466
|
-
inputed_char = key.chr
|
2467
|
-
end
|
2468
2262
|
prev_total = nil
|
2469
2263
|
total = nil
|
2470
2264
|
found = false
|
@@ -2475,7 +2269,7 @@ class Reline::LineEditor
|
|
2475
2269
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2476
2270
|
total = [mbchar.bytesize, width]
|
2477
2271
|
else
|
2478
|
-
if
|
2272
|
+
if key == mbchar
|
2479
2273
|
arg -= 1
|
2480
2274
|
if arg.zero?
|
2481
2275
|
found = true
|
@@ -2527,24 +2321,23 @@ class Reline::LineEditor
|
|
2527
2321
|
@config.editing_mode = :vi_insert
|
2528
2322
|
end
|
2529
2323
|
|
2530
|
-
private def
|
2531
|
-
@
|
2324
|
+
private def move_undo_redo(direction)
|
2325
|
+
@restoring = true
|
2326
|
+
return unless (0..@undo_redo_history.size - 1).cover?(@undo_redo_index + direction)
|
2532
2327
|
|
2533
|
-
|
2328
|
+
@undo_redo_index += direction
|
2329
|
+
buffer_of_lines, byte_pointer, line_index = @undo_redo_history[@undo_redo_index]
|
2330
|
+
@buffer_of_lines = buffer_of_lines.dup
|
2331
|
+
@line_index = line_index
|
2332
|
+
@byte_pointer = byte_pointer
|
2333
|
+
end
|
2534
2334
|
|
2535
|
-
|
2536
|
-
|
2537
|
-
set_current_lines(target_lines.dup, target_cursor_x, target_cursor_y)
|
2335
|
+
private def undo(_key)
|
2336
|
+
move_undo_redo(-1)
|
2538
2337
|
end
|
2539
2338
|
|
2540
2339
|
private def redo(_key)
|
2541
|
-
|
2542
|
-
|
2543
|
-
return if @input_lines_position >= @input_lines.size - 1
|
2544
|
-
|
2545
|
-
@input_lines_position += 1
|
2546
|
-
target_lines, target_cursor_x, target_cursor_y = @input_lines[@input_lines_position]
|
2547
|
-
set_current_lines(target_lines.dup, target_cursor_x, target_cursor_y)
|
2340
|
+
move_undo_redo(+1)
|
2548
2341
|
end
|
2549
2342
|
|
2550
2343
|
private def prev_action_state_value(type)
|
@@ -2554,4 +2347,8 @@ class Reline::LineEditor
|
|
2554
2347
|
private def set_next_action_state(type, value)
|
2555
2348
|
@next_action_state = [type, value]
|
2556
2349
|
end
|
2350
|
+
|
2351
|
+
private def re_read_init_file(_key)
|
2352
|
+
@config.reload
|
2353
|
+
end
|
2557
2354
|
end
|