reline 0.5.10 → 0.6.0
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 +22 -26
- data/lib/reline/history.rb +3 -3
- data/lib/reline/io/ansi.rb +64 -111
- data/lib/reline/io/dumb.rb +16 -2
- data/lib/reline/io/windows.rb +77 -60
- 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 +238 -404
- data/lib/reline/unicode.rb +140 -396
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +18 -18
- metadata +6 -4
- 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 = '▄'
|
@@ -219,10 +221,9 @@ class Reline::LineEditor
|
|
219
221
|
@eof
|
220
222
|
end
|
221
223
|
|
222
|
-
def reset_variables(prompt = ''
|
224
|
+
def reset_variables(prompt = '')
|
223
225
|
@prompt = prompt.gsub("\n", "\\n")
|
224
226
|
@mark_pointer = nil
|
225
|
-
@encoding = encoding
|
226
227
|
@is_multiline = false
|
227
228
|
@finished = false
|
228
229
|
@history_pointer = nil
|
@@ -239,7 +240,7 @@ class Reline::LineEditor
|
|
239
240
|
@searching_prompt = nil
|
240
241
|
@just_cursor_moving = false
|
241
242
|
@eof = false
|
242
|
-
@continuous_insertion_buffer = String.new(encoding:
|
243
|
+
@continuous_insertion_buffer = String.new(encoding: encoding)
|
243
244
|
@scroll_partial_screen = 0
|
244
245
|
@drop_terminate_spaces = false
|
245
246
|
@in_pasting = false
|
@@ -251,7 +252,7 @@ class Reline::LineEditor
|
|
251
252
|
@rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
|
252
253
|
@input_lines = [[[""], 0, 0]]
|
253
254
|
@input_lines_position = 0
|
254
|
-
@
|
255
|
+
@restoring = false
|
255
256
|
@prev_action_state = NullActionState
|
256
257
|
@next_action_state = NullActionState
|
257
258
|
reset_line
|
@@ -259,11 +260,10 @@ class Reline::LineEditor
|
|
259
260
|
|
260
261
|
def reset_line
|
261
262
|
@byte_pointer = 0
|
262
|
-
@buffer_of_lines = [String.new(encoding:
|
263
|
+
@buffer_of_lines = [String.new(encoding: encoding)]
|
263
264
|
@line_index = 0
|
264
265
|
@cache.clear
|
265
266
|
@line_backup_in_history = nil
|
266
|
-
@multibyte_buffer = String.new(encoding: 'ASCII-8BIT')
|
267
267
|
end
|
268
268
|
|
269
269
|
def multiline_on
|
@@ -275,7 +275,7 @@ class Reline::LineEditor
|
|
275
275
|
end
|
276
276
|
|
277
277
|
private def insert_new_line(cursor_line, next_line)
|
278
|
-
@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))
|
279
279
|
@buffer_of_lines[@line_index] = cursor_line
|
280
280
|
@line_index += 1
|
281
281
|
@byte_pointer = 0
|
@@ -297,8 +297,8 @@ class Reline::LineEditor
|
|
297
297
|
end
|
298
298
|
end
|
299
299
|
|
300
|
-
private def
|
301
|
-
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)
|
302
302
|
end
|
303
303
|
|
304
304
|
def current_byte_pointer_cursor
|
@@ -388,8 +388,8 @@ class Reline::LineEditor
|
|
388
388
|
if (cached = cached_wraps[[prompt, line]])
|
389
389
|
next cached
|
390
390
|
end
|
391
|
-
*wrapped_prompts, code_line_prompt =
|
392
|
-
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))
|
393
393
|
wrapped_prompts.map { |p| [p, ''] } + [[code_line_prompt, wrapped_lines.first]] + wrapped_lines.drop(1).map { |c| ['', c] }
|
394
394
|
end
|
395
395
|
end
|
@@ -413,7 +413,7 @@ class Reline::LineEditor
|
|
413
413
|
# do nothing
|
414
414
|
elsif level == :blank
|
415
415
|
Reline::IOGate.move_cursor_column base_x
|
416
|
-
|
416
|
+
Reline::IOGate.write "#{Reline::IOGate.reset_color_sequence}#{' ' * width}"
|
417
417
|
else
|
418
418
|
x, w, content = new_items[level]
|
419
419
|
cover_begin = base_x != 0 && new_levels[base_x - 1] == level
|
@@ -423,7 +423,7 @@ class Reline::LineEditor
|
|
423
423
|
content, pos = Reline::Unicode.take_mbchar_range(content, base_x - x, width, cover_begin: cover_begin, cover_end: cover_end, padding: true)
|
424
424
|
end
|
425
425
|
Reline::IOGate.move_cursor_column x + pos
|
426
|
-
|
426
|
+
Reline::IOGate.write "#{Reline::IOGate.reset_color_sequence}#{content}#{Reline::IOGate.reset_color_sequence}"
|
427
427
|
end
|
428
428
|
base_x += width
|
429
429
|
end
|
@@ -436,8 +436,8 @@ class Reline::LineEditor
|
|
436
436
|
# Calculate cursor position in word wrapped content.
|
437
437
|
def wrapped_cursor_position
|
438
438
|
prompt_width = calculate_width(prompt_list[@line_index], true)
|
439
|
-
line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
|
440
|
-
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)
|
441
441
|
wrapped_cursor_y = wrapped_prompt_and_input_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
|
442
442
|
wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
|
443
443
|
[wrapped_cursor_x, wrapped_cursor_y]
|
@@ -459,18 +459,23 @@ class Reline::LineEditor
|
|
459
459
|
end
|
460
460
|
|
461
461
|
def render_finished
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
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
|
467
470
|
end
|
468
|
-
@output.puts lines.map { |l| "#{l}\r\n" }.join
|
469
471
|
end
|
470
472
|
|
471
473
|
def print_nomultiline_prompt
|
474
|
+
Reline::IOGate.disable_auto_linewrap(true) if Reline::IOGate.win?
|
472
475
|
# Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence.
|
473
|
-
|
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?
|
474
479
|
end
|
475
480
|
|
476
481
|
def render
|
@@ -499,13 +504,16 @@ class Reline::LineEditor
|
|
499
504
|
end
|
500
505
|
end
|
501
506
|
|
502
|
-
|
507
|
+
Reline::IOGate.buffered_output do
|
508
|
+
render_differential new_lines, wrapped_cursor_x, wrapped_cursor_y - screen_scroll_top
|
509
|
+
end
|
503
510
|
end
|
504
511
|
|
505
512
|
# Reflects lines to be rendered and new cursor position to the screen
|
506
513
|
# by calculating the difference from the previous render.
|
507
514
|
|
508
515
|
private def render_differential(new_lines, new_cursor_x, new_cursor_y)
|
516
|
+
Reline::IOGate.disable_auto_linewrap(true) if Reline::IOGate.win?
|
509
517
|
rendered_lines = @rendered_screen.lines
|
510
518
|
cursor_y = @rendered_screen.cursor_y
|
511
519
|
if new_lines != rendered_lines
|
@@ -536,6 +544,8 @@ class Reline::LineEditor
|
|
536
544
|
Reline::IOGate.move_cursor_column new_cursor_x
|
537
545
|
Reline::IOGate.move_cursor_down new_cursor_y - cursor_y
|
538
546
|
@rendered_screen.cursor_y = new_cursor_y
|
547
|
+
ensure
|
548
|
+
Reline::IOGate.disable_auto_linewrap(false) if Reline::IOGate.win?
|
539
549
|
end
|
540
550
|
|
541
551
|
private def clear_rendered_screen_cache
|
@@ -570,8 +580,9 @@ class Reline::LineEditor
|
|
570
580
|
@context
|
571
581
|
end
|
572
582
|
|
573
|
-
def retrieve_completion_block(
|
574
|
-
@line_editor.retrieve_completion_block
|
583
|
+
def retrieve_completion_block(_unused = false)
|
584
|
+
preposing, target, postposing, _quote = @line_editor.retrieve_completion_block
|
585
|
+
[preposing, target, postposing]
|
575
586
|
end
|
576
587
|
|
577
588
|
def call_completion_proc_with_checking_args(pre, target, post)
|
@@ -791,98 +802,73 @@ class Reline::LineEditor
|
|
791
802
|
@config.editing_mode
|
792
803
|
end
|
793
804
|
|
794
|
-
private def menu(
|
805
|
+
private def menu(list)
|
795
806
|
@menu_info = MenuInfo.new(list)
|
796
807
|
end
|
797
808
|
|
798
|
-
private def
|
799
|
-
|
800
|
-
list
|
801
|
-
|
802
|
-
|
809
|
+
private def filter_normalize_candidates(target, list)
|
810
|
+
target = target.downcase if @config.completion_ignore_case
|
811
|
+
list.select do |item|
|
812
|
+
next unless item
|
813
|
+
unless Encoding.compatible?(target.encoding, item.encoding)
|
814
|
+
# Workaround for Readline test
|
815
|
+
if defined?(::Readline) && ::Readline == ::Reline
|
816
|
+
raise Encoding::CompatibilityError, "incompatible character encodings: #{target.encoding} and #{item.encoding}"
|
817
|
+
end
|
803
818
|
end
|
819
|
+
|
804
820
|
if @config.completion_ignore_case
|
805
|
-
|
821
|
+
item.downcase.start_with?(target)
|
806
822
|
else
|
807
|
-
|
808
|
-
end
|
809
|
-
}.uniq
|
810
|
-
if is_menu
|
811
|
-
menu(target, list)
|
812
|
-
return nil
|
813
|
-
end
|
814
|
-
completed = list.inject { |memo, item|
|
815
|
-
begin
|
816
|
-
memo_mbchars = memo.unicode_normalize.grapheme_clusters
|
817
|
-
item_mbchars = item.unicode_normalize.grapheme_clusters
|
818
|
-
rescue Encoding::CompatibilityError
|
819
|
-
memo_mbchars = memo.grapheme_clusters
|
820
|
-
item_mbchars = item.grapheme_clusters
|
821
|
-
end
|
822
|
-
size = [memo_mbchars.size, item_mbchars.size].min
|
823
|
-
result = +''
|
824
|
-
size.times do |i|
|
825
|
-
if @config.completion_ignore_case
|
826
|
-
if memo_mbchars[i].casecmp?(item_mbchars[i])
|
827
|
-
result << memo_mbchars[i]
|
828
|
-
else
|
829
|
-
break
|
830
|
-
end
|
831
|
-
else
|
832
|
-
if memo_mbchars[i] == item_mbchars[i]
|
833
|
-
result << memo_mbchars[i]
|
834
|
-
else
|
835
|
-
break
|
836
|
-
end
|
837
|
-
end
|
823
|
+
item.start_with?(target)
|
838
824
|
end
|
839
|
-
|
840
|
-
|
841
|
-
|
825
|
+
end.map do |item|
|
826
|
+
item.unicode_normalize
|
827
|
+
rescue Encoding::CompatibilityError
|
828
|
+
item
|
829
|
+
end.uniq
|
842
830
|
end
|
843
831
|
|
844
|
-
private def perform_completion(
|
832
|
+
private def perform_completion(preposing, target, postposing, quote, list)
|
833
|
+
candidates = filter_normalize_candidates(target, list)
|
834
|
+
|
845
835
|
case @completion_state
|
846
|
-
when CompletionState::NORMAL
|
847
|
-
@completion_state = CompletionState::COMPLETION
|
848
836
|
when CompletionState::PERFECT_MATCH
|
849
|
-
@dig_perfect_match_proc
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
is_menu = false
|
859
|
-
end
|
860
|
-
result = complete_internal_proc(list, is_menu)
|
861
|
-
if @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
|
837
|
+
if @dig_perfect_match_proc
|
838
|
+
@dig_perfect_match_proc.call(@perfect_matched)
|
839
|
+
return
|
840
|
+
end
|
841
|
+
when CompletionState::MENU
|
842
|
+
menu(candidates)
|
843
|
+
return
|
844
|
+
when CompletionState::MENU_WITH_PERFECT_MATCH
|
845
|
+
menu(candidates)
|
862
846
|
@completion_state = CompletionState::PERFECT_MATCH
|
847
|
+
return
|
863
848
|
end
|
864
|
-
|
865
|
-
|
866
|
-
return if completed.
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
@
|
849
|
+
|
850
|
+
completed = Reline::Unicode.common_prefix(candidates, ignore_case: @config.completion_ignore_case)
|
851
|
+
return if completed.empty?
|
852
|
+
|
853
|
+
append_character = ''
|
854
|
+
if candidates.include?(completed)
|
855
|
+
if candidates.one?
|
856
|
+
append_character = quote || completion_append_character.to_s
|
857
|
+
@completion_state = CompletionState::PERFECT_MATCH
|
858
|
+
elsif @config.show_all_if_ambiguous
|
859
|
+
menu(candidates)
|
860
|
+
@completion_state = CompletionState::PERFECT_MATCH
|
876
861
|
else
|
877
|
-
@completion_state = CompletionState::
|
878
|
-
perform_completion(list, true) if @config.show_all_if_ambiguous
|
879
|
-
end
|
880
|
-
if not just_show_list and target < completed
|
881
|
-
@buffer_of_lines[@line_index] = (preposing + completed + completion_append_character.to_s + postposing).split("\n")[@line_index] || String.new(encoding: @encoding)
|
882
|
-
line_to_pointer = (preposing + completed + completion_append_character.to_s).split("\n")[@line_index] || String.new(encoding: @encoding)
|
883
|
-
@byte_pointer = line_to_pointer.bytesize
|
862
|
+
@completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
|
884
863
|
end
|
864
|
+
@perfect_matched = completed
|
865
|
+
else
|
866
|
+
@completion_state = CompletionState::MENU
|
867
|
+
menu(candidates) if @config.show_all_if_ambiguous
|
885
868
|
end
|
869
|
+
@buffer_of_lines[@line_index] = (preposing + completed + append_character + postposing).split("\n")[@line_index] || String.new(encoding: encoding)
|
870
|
+
line_to_pointer = (preposing + completed + append_character).split("\n")[@line_index] || String.new(encoding: encoding)
|
871
|
+
@byte_pointer = line_to_pointer.bytesize
|
886
872
|
end
|
887
873
|
|
888
874
|
def dialog_proc_scope_completion_journey_data
|
@@ -911,8 +897,8 @@ class Reline::LineEditor
|
|
911
897
|
end
|
912
898
|
|
913
899
|
private def retrieve_completion_journey_state
|
914
|
-
preposing, target, postposing = retrieve_completion_block
|
915
|
-
list = call_completion_proc
|
900
|
+
preposing, target, postposing, quote = retrieve_completion_block
|
901
|
+
list = call_completion_proc(preposing, target, postposing, quote)
|
916
902
|
return unless list.is_a?(Array)
|
917
903
|
|
918
904
|
candidates = list.select{ |item| item.start_with?(target) }
|
@@ -987,10 +973,18 @@ class Reline::LineEditor
|
|
987
973
|
@drop_terminate_spaces = false
|
988
974
|
end
|
989
975
|
|
976
|
+
ARGUMENT_DIGIT_METHODS = %i[ed_digit vi_zero ed_argument_digit]
|
977
|
+
VI_WAITING_ACCEPT_METHODS = %i[vi_change_meta vi_delete_meta vi_yank ed_insert ed_argument_digit]
|
978
|
+
|
990
979
|
private def process_key(key, method_symbol)
|
991
|
-
if
|
992
|
-
cleanup_waiting
|
993
|
-
|
980
|
+
if @waiting_proc
|
981
|
+
cleanup_waiting unless key.size == 1
|
982
|
+
end
|
983
|
+
if @vi_waiting_operator
|
984
|
+
cleanup_waiting unless VI_WAITING_ACCEPT_METHODS.include?(method_symbol) || VI_MOTIONS.include?(method_symbol)
|
985
|
+
end
|
986
|
+
|
987
|
+
if @waiting_proc
|
994
988
|
old_byte_pointer = @byte_pointer
|
995
989
|
@waiting_proc.call(key)
|
996
990
|
if @vi_waiting_operator
|
@@ -1004,23 +998,14 @@ class Reline::LineEditor
|
|
1004
998
|
return
|
1005
999
|
end
|
1006
1000
|
|
1001
|
+
# Reject multibyte input (converted to ed_insert) in vi_command mode
|
1002
|
+
return if method_symbol == :ed_insert && @config.editing_mode_is?(:vi_command)
|
1003
|
+
|
1007
1004
|
if method_symbol and respond_to?(method_symbol, true)
|
1008
1005
|
method_obj = method(method_symbol)
|
1009
1006
|
end
|
1010
|
-
if
|
1011
|
-
if
|
1012
|
-
run_for_operators(key, method_symbol) do |with_operator|
|
1013
|
-
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
1014
|
-
end
|
1015
|
-
else
|
1016
|
-
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
1017
|
-
end
|
1018
|
-
@kill_ring.process
|
1019
|
-
if @vi_arg
|
1020
|
-
@vi_arg = nil
|
1021
|
-
end
|
1022
|
-
elsif @vi_arg
|
1023
|
-
if key.chr =~ /[0-9]/
|
1007
|
+
if @vi_arg
|
1008
|
+
if ARGUMENT_DIGIT_METHODS.include?(method_symbol)
|
1024
1009
|
ed_argument_digit(key)
|
1025
1010
|
else
|
1026
1011
|
if argumentable?(method_obj)
|
@@ -1029,13 +1014,9 @@ class Reline::LineEditor
|
|
1029
1014
|
end
|
1030
1015
|
elsif method_obj
|
1031
1016
|
wrap_method_call(method_symbol, method_obj, key)
|
1032
|
-
else
|
1033
|
-
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
1034
1017
|
end
|
1035
1018
|
@kill_ring.process
|
1036
|
-
|
1037
|
-
@vi_arg = nil
|
1038
|
-
end
|
1019
|
+
@vi_arg = nil
|
1039
1020
|
end
|
1040
1021
|
elsif method_obj
|
1041
1022
|
if method_symbol == :ed_argument_digit
|
@@ -1046,30 +1027,6 @@ class Reline::LineEditor
|
|
1046
1027
|
end
|
1047
1028
|
end
|
1048
1029
|
@kill_ring.process
|
1049
|
-
else
|
1050
|
-
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
1051
|
-
end
|
1052
|
-
end
|
1053
|
-
|
1054
|
-
private def normal_char(key)
|
1055
|
-
@multibyte_buffer << key.combined_char
|
1056
|
-
if @multibyte_buffer.size > 1
|
1057
|
-
if @multibyte_buffer.dup.force_encoding(@encoding).valid_encoding?
|
1058
|
-
process_key(@multibyte_buffer.dup.force_encoding(@encoding), nil)
|
1059
|
-
@multibyte_buffer.clear
|
1060
|
-
else
|
1061
|
-
# invalid
|
1062
|
-
return
|
1063
|
-
end
|
1064
|
-
else # single byte
|
1065
|
-
return if key.char >= 128 # maybe, first byte of multi byte
|
1066
|
-
method_symbol = @config.editing_mode.get_method(key.combined_char)
|
1067
|
-
process_key(key.combined_char, method_symbol)
|
1068
|
-
@multibyte_buffer.clear
|
1069
|
-
end
|
1070
|
-
if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
|
1071
|
-
byte_size = Reline::Unicode.get_prev_mbchar_size(@buffer_of_lines[@line_index], @byte_pointer)
|
1072
|
-
@byte_pointer -= byte_size
|
1073
1030
|
end
|
1074
1031
|
end
|
1075
1032
|
|
@@ -1086,23 +1043,23 @@ class Reline::LineEditor
|
|
1086
1043
|
def input_key(key)
|
1087
1044
|
save_old_buffer
|
1088
1045
|
@config.reset_oneshot_key_bindings
|
1089
|
-
@dialogs.each do |dialog|
|
1090
|
-
if key.char.instance_of?(Symbol) and key.char == dialog.name
|
1091
|
-
return
|
1092
|
-
end
|
1093
|
-
end
|
1094
1046
|
if key.char.nil?
|
1095
1047
|
process_insert(force: true)
|
1096
1048
|
@eof = buffer_empty?
|
1097
1049
|
finish
|
1098
1050
|
return
|
1099
1051
|
end
|
1052
|
+
@dialogs.each do |dialog|
|
1053
|
+
if key.method_symbol == dialog.name
|
1054
|
+
return
|
1055
|
+
end
|
1056
|
+
end
|
1100
1057
|
@completion_occurs = false
|
1101
1058
|
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1059
|
+
process_key(key.char, key.method_symbol)
|
1060
|
+
if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
|
1061
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(@buffer_of_lines[@line_index], @byte_pointer)
|
1062
|
+
@byte_pointer -= byte_size
|
1106
1063
|
end
|
1107
1064
|
|
1108
1065
|
@prev_action_state, @next_action_state = @next_action_state, NullActionState
|
@@ -1112,8 +1069,8 @@ class Reline::LineEditor
|
|
1112
1069
|
@completion_journey_state = nil
|
1113
1070
|
end
|
1114
1071
|
|
1115
|
-
push_input_lines unless @
|
1116
|
-
@
|
1072
|
+
push_input_lines unless @restoring
|
1073
|
+
@restoring = false
|
1117
1074
|
|
1118
1075
|
if @in_pasting
|
1119
1076
|
clear_dialogs
|
@@ -1162,9 +1119,8 @@ class Reline::LineEditor
|
|
1162
1119
|
end
|
1163
1120
|
end
|
1164
1121
|
|
1165
|
-
def call_completion_proc
|
1166
|
-
|
1167
|
-
pre, target, post = result
|
1122
|
+
def call_completion_proc(pre, target, post, quote)
|
1123
|
+
Reline.core.instance_variable_set(:@completion_quote_character, quote)
|
1168
1124
|
result = call_completion_proc_with_checking_args(pre, target, post)
|
1169
1125
|
Reline.core.instance_variable_set(:@completion_quote_character, nil)
|
1170
1126
|
result
|
@@ -1228,84 +1184,32 @@ class Reline::LineEditor
|
|
1228
1184
|
process_auto_indent
|
1229
1185
|
end
|
1230
1186
|
|
1231
|
-
def
|
1232
|
-
|
1233
|
-
|
1234
|
-
@line_index = line_index
|
1235
|
-
if byte_pointer
|
1236
|
-
@byte_pointer = byte_pointer
|
1237
|
-
else
|
1238
|
-
calculate_nearest_cursor(cursor)
|
1239
|
-
end
|
1240
|
-
process_auto_indent
|
1241
|
-
end
|
1242
|
-
|
1243
|
-
def retrieve_completion_block(set_completion_quote_character = false)
|
1244
|
-
if Reline.completer_word_break_characters.empty?
|
1245
|
-
word_break_regexp = nil
|
1246
|
-
else
|
1247
|
-
word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
|
1248
|
-
end
|
1249
|
-
if Reline.completer_quote_characters.empty?
|
1250
|
-
quote_characters_regexp = nil
|
1251
|
-
else
|
1252
|
-
quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
|
1253
|
-
end
|
1254
|
-
before = current_line.byteslice(0, @byte_pointer)
|
1255
|
-
rest = nil
|
1256
|
-
break_pointer = nil
|
1187
|
+
def retrieve_completion_block
|
1188
|
+
quote_characters = Reline.completer_quote_characters
|
1189
|
+
before = current_line.byteslice(0, @byte_pointer).grapheme_clusters
|
1257
1190
|
quote = nil
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
elsif quote and slice.start_with?(escaped_quote)
|
1272
|
-
# skip
|
1273
|
-
i += 2
|
1274
|
-
elsif quote_characters_regexp and slice =~ quote_characters_regexp # find new "
|
1275
|
-
rest = $'
|
1276
|
-
quote = $&
|
1277
|
-
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
|
1278
|
-
escaped_quote = /\\#{Regexp.escape(quote)}/
|
1279
|
-
i += 1
|
1280
|
-
break_pointer = i - 1
|
1281
|
-
elsif word_break_regexp and not quote and slice =~ word_break_regexp
|
1282
|
-
rest = $'
|
1283
|
-
i += 1
|
1284
|
-
before = current_line.byteslice(i, @byte_pointer - i)
|
1285
|
-
break_pointer = i
|
1286
|
-
else
|
1287
|
-
i += 1
|
1288
|
-
end
|
1289
|
-
end
|
1290
|
-
postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
|
1291
|
-
if rest
|
1292
|
-
preposing = current_line.byteslice(0, break_pointer)
|
1293
|
-
target = rest
|
1294
|
-
if set_completion_quote_character and quote
|
1295
|
-
Reline.core.instance_variable_set(:@completion_quote_character, quote)
|
1296
|
-
if postposing !~ /(?!\\)#{Regexp.escape(quote)}/ # closing quote
|
1297
|
-
insert_text(quote)
|
1191
|
+
# Calcualte closing quote when cursor is at the end of the line
|
1192
|
+
if current_line.bytesize == @byte_pointer && !quote_characters.empty?
|
1193
|
+
escaped = false
|
1194
|
+
before.each do |c|
|
1195
|
+
if escaped
|
1196
|
+
escaped = false
|
1197
|
+
next
|
1198
|
+
elsif c == '\\'
|
1199
|
+
escaped = true
|
1200
|
+
elsif quote
|
1201
|
+
quote = nil if c == quote
|
1202
|
+
elsif quote_characters.include?(c)
|
1203
|
+
quote = c
|
1298
1204
|
end
|
1299
1205
|
end
|
1300
|
-
else
|
1301
|
-
preposing = ''
|
1302
|
-
if break_pointer
|
1303
|
-
preposing = current_line.byteslice(0, break_pointer)
|
1304
|
-
else
|
1305
|
-
preposing = ''
|
1306
|
-
end
|
1307
|
-
target = before
|
1308
1206
|
end
|
1207
|
+
|
1208
|
+
word_break_characters = quote_characters + Reline.completer_word_break_characters
|
1209
|
+
break_index = before.rindex { |c| word_break_characters.include?(c) || quote_characters.include?(c) } || -1
|
1210
|
+
preposing = before.take(break_index + 1).join
|
1211
|
+
target = before.drop(break_index + 1).join
|
1212
|
+
postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
|
1309
1213
|
lines = whole_lines
|
1310
1214
|
if @line_index > 0
|
1311
1215
|
preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
|
@@ -1313,7 +1217,7 @@ class Reline::LineEditor
|
|
1313
1217
|
if (lines.size - 1) > @line_index
|
1314
1218
|
postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
|
1315
1219
|
end
|
1316
|
-
[preposing.encode(
|
1220
|
+
[preposing.encode(encoding), target.encode(encoding), postposing.encode(encoding), quote&.encode(encoding)]
|
1317
1221
|
end
|
1318
1222
|
|
1319
1223
|
def confirm_multiline_termination
|
@@ -1322,15 +1226,13 @@ class Reline::LineEditor
|
|
1322
1226
|
end
|
1323
1227
|
|
1324
1228
|
def insert_multiline_text(text)
|
1325
|
-
save_old_buffer
|
1326
1229
|
pre = @buffer_of_lines[@line_index].byteslice(0, @byte_pointer)
|
1327
1230
|
post = @buffer_of_lines[@line_index].byteslice(@byte_pointer..)
|
1328
|
-
lines = (pre + text.gsub(/\r\n?/, "\n") + post).split("\n", -1)
|
1231
|
+
lines = (pre + Reline::Unicode.safe_encode(text, encoding).gsub(/\r\n?/, "\n") + post).split("\n", -1)
|
1329
1232
|
lines << '' if lines.empty?
|
1330
1233
|
@buffer_of_lines[@line_index, 1] = lines
|
1331
1234
|
@line_index += lines.size - 1
|
1332
1235
|
@byte_pointer = @buffer_of_lines[@line_index].bytesize - post.bytesize
|
1333
|
-
push_input_lines
|
1334
1236
|
end
|
1335
1237
|
|
1336
1238
|
def insert_text(text)
|
@@ -1370,7 +1272,7 @@ class Reline::LineEditor
|
|
1370
1272
|
last += current_line.bytesize if last < 0
|
1371
1273
|
first += current_line.bytesize if first < 0
|
1372
1274
|
range = range.exclude_end? ? first...last : first..last
|
1373
|
-
line = current_line.bytes.reject.with_index{ |c, i| range.include?(i) }.map{ |c| c.chr(Encoding::ASCII_8BIT) }.join.force_encoding(
|
1275
|
+
line = current_line.bytes.reject.with_index{ |c, i| range.include?(i) }.map{ |c| c.chr(Encoding::ASCII_8BIT) }.join.force_encoding(encoding)
|
1374
1276
|
set_current_line(line)
|
1375
1277
|
else
|
1376
1278
|
set_current_line(current_line.byteslice(0, start))
|
@@ -1444,10 +1346,11 @@ class Reline::LineEditor
|
|
1444
1346
|
@completion_occurs = move_completed_list(:down)
|
1445
1347
|
else
|
1446
1348
|
@completion_journey_state = nil
|
1447
|
-
|
1349
|
+
pre, target, post, quote = retrieve_completion_block
|
1350
|
+
result = call_completion_proc(pre, target, post, quote)
|
1448
1351
|
if result.is_a?(Array)
|
1449
1352
|
@completion_occurs = true
|
1450
|
-
perform_completion(
|
1353
|
+
perform_completion(pre, target, post, quote, result)
|
1451
1354
|
end
|
1452
1355
|
end
|
1453
1356
|
end
|
@@ -1495,21 +1398,11 @@ class Reline::LineEditor
|
|
1495
1398
|
# digit or if the existing argument is already greater than a
|
1496
1399
|
# million.
|
1497
1400
|
# GNU Readline:: +self-insert+ (a, b, A, 1, !, …) Insert yourself.
|
1498
|
-
private def ed_insert(
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
return
|
1504
|
-
end
|
1505
|
-
str = key
|
1506
|
-
else
|
1507
|
-
begin
|
1508
|
-
key.chr.encode(Encoding::UTF_8)
|
1509
|
-
rescue Encoding::UndefinedConversionError
|
1510
|
-
return
|
1511
|
-
end
|
1512
|
-
str = key.chr
|
1401
|
+
private def ed_insert(str)
|
1402
|
+
begin
|
1403
|
+
str.encode(Encoding::UTF_8)
|
1404
|
+
rescue Encoding::UndefinedConversionError
|
1405
|
+
return
|
1513
1406
|
end
|
1514
1407
|
if @in_pasting
|
1515
1408
|
@continuous_insertion_buffer << str
|
@@ -1523,21 +1416,16 @@ class Reline::LineEditor
|
|
1523
1416
|
alias_method :ed_digit, :ed_insert
|
1524
1417
|
alias_method :self_insert, :ed_insert
|
1525
1418
|
|
1526
|
-
private def
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
else
|
1534
|
-
ed_insert(key)
|
1535
|
-
end
|
1419
|
+
private def insert_raw_char(str, arg: 1)
|
1420
|
+
arg.times do
|
1421
|
+
if str == "\C-j" or str == "\C-m"
|
1422
|
+
key_newline(str)
|
1423
|
+
elsif str != "\0"
|
1424
|
+
# Ignore NUL.
|
1425
|
+
ed_insert(str)
|
1536
1426
|
end
|
1537
|
-
|
1538
|
-
}
|
1427
|
+
end
|
1539
1428
|
end
|
1540
|
-
alias_method :quoted_insert, :ed_quoted_insert
|
1541
1429
|
|
1542
1430
|
private def ed_next_char(key, arg: 1)
|
1543
1431
|
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
@@ -1566,7 +1454,7 @@ class Reline::LineEditor
|
|
1566
1454
|
alias_method :backward_char, :ed_prev_char
|
1567
1455
|
|
1568
1456
|
private def vi_first_print(key)
|
1569
|
-
@byte_pointer
|
1457
|
+
@byte_pointer = Reline::Unicode.vi_first_print(current_line)
|
1570
1458
|
end
|
1571
1459
|
|
1572
1460
|
private def ed_move_to_beg(key)
|
@@ -1581,27 +1469,22 @@ class Reline::LineEditor
|
|
1581
1469
|
alias_method :end_of_line, :ed_move_to_end
|
1582
1470
|
|
1583
1471
|
private def generate_searcher(search_key)
|
1584
|
-
search_word = String.new(encoding:
|
1585
|
-
multibyte_buf = String.new(encoding: 'ASCII-8BIT')
|
1472
|
+
search_word = String.new(encoding: encoding)
|
1586
1473
|
hit_pointer = nil
|
1587
1474
|
lambda do |key|
|
1588
1475
|
search_again = false
|
1589
1476
|
case key
|
1590
|
-
when "\C-h"
|
1477
|
+
when "\C-h", "\C-?"
|
1591
1478
|
grapheme_clusters = search_word.grapheme_clusters
|
1592
1479
|
if grapheme_clusters.size > 0
|
1593
1480
|
grapheme_clusters.pop
|
1594
1481
|
search_word = grapheme_clusters.join
|
1595
1482
|
end
|
1596
|
-
when "\C-r"
|
1483
|
+
when "\C-r", "\C-s"
|
1597
1484
|
search_again = true if search_key == key
|
1598
1485
|
search_key = key
|
1599
1486
|
else
|
1600
|
-
|
1601
|
-
if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
|
1602
|
-
search_word << multibyte_buf.dup.force_encoding(@encoding)
|
1603
|
-
multibyte_buf.clear
|
1604
|
-
end
|
1487
|
+
search_word << key
|
1605
1488
|
end
|
1606
1489
|
hit = nil
|
1607
1490
|
if not search_word.empty? and @line_backup_in_history&.include?(search_word)
|
@@ -1614,10 +1497,10 @@ class Reline::LineEditor
|
|
1614
1497
|
end
|
1615
1498
|
if @history_pointer
|
1616
1499
|
case search_key
|
1617
|
-
when "\C-r"
|
1500
|
+
when "\C-r"
|
1618
1501
|
history_pointer_base = 0
|
1619
1502
|
history = Reline::HISTORY[0..(@history_pointer - 1)]
|
1620
|
-
when "\C-s"
|
1503
|
+
when "\C-s"
|
1621
1504
|
history_pointer_base = @history_pointer + 1
|
1622
1505
|
history = Reline::HISTORY[(@history_pointer + 1)..-1]
|
1623
1506
|
end
|
@@ -1627,10 +1510,10 @@ class Reline::LineEditor
|
|
1627
1510
|
end
|
1628
1511
|
elsif @history_pointer
|
1629
1512
|
case search_key
|
1630
|
-
when "\C-r"
|
1513
|
+
when "\C-r"
|
1631
1514
|
history_pointer_base = 0
|
1632
1515
|
history = Reline::HISTORY[0..@history_pointer]
|
1633
|
-
when "\C-s"
|
1516
|
+
when "\C-s"
|
1634
1517
|
history_pointer_base = @history_pointer
|
1635
1518
|
history = Reline::HISTORY[@history_pointer..-1]
|
1636
1519
|
end
|
@@ -1639,11 +1522,11 @@ class Reline::LineEditor
|
|
1639
1522
|
history = Reline::HISTORY
|
1640
1523
|
end
|
1641
1524
|
case search_key
|
1642
|
-
when "\C-r"
|
1525
|
+
when "\C-r"
|
1643
1526
|
hit_index = history.rindex { |item|
|
1644
1527
|
item.include?(search_word)
|
1645
1528
|
}
|
1646
|
-
when "\C-s"
|
1529
|
+
when "\C-s"
|
1647
1530
|
hit_index = history.index { |item|
|
1648
1531
|
item.include?(search_word)
|
1649
1532
|
}
|
@@ -1654,9 +1537,9 @@ class Reline::LineEditor
|
|
1654
1537
|
end
|
1655
1538
|
end
|
1656
1539
|
case search_key
|
1657
|
-
when "\C-r"
|
1540
|
+
when "\C-r"
|
1658
1541
|
prompt_name = 'reverse-i-search'
|
1659
|
-
when "\C-s"
|
1542
|
+
when "\C-s"
|
1660
1543
|
prompt_name = 'i-search'
|
1661
1544
|
end
|
1662
1545
|
prompt_name = "failed #{prompt_name}" unless hit
|
@@ -1665,57 +1548,28 @@ class Reline::LineEditor
|
|
1665
1548
|
end
|
1666
1549
|
|
1667
1550
|
private def incremental_search_history(key)
|
1668
|
-
|
1669
|
-
@line_backup_in_history = whole_buffer
|
1670
|
-
end
|
1551
|
+
backup = @buffer_of_lines.dup, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history
|
1671
1552
|
searcher = generate_searcher(key)
|
1672
1553
|
@searching_prompt = "(reverse-i-search)`': "
|
1673
|
-
termination_keys = ["\C-j"
|
1674
|
-
termination_keys.concat(@config.isearch_terminators
|
1554
|
+
termination_keys = ["\C-j"]
|
1555
|
+
termination_keys.concat(@config.isearch_terminators.chars) if @config.isearch_terminators
|
1675
1556
|
@waiting_proc = ->(k) {
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
buffer = Reline::HISTORY[@history_pointer]
|
1680
|
-
else
|
1681
|
-
buffer = @line_backup_in_history
|
1682
|
-
end
|
1683
|
-
@buffer_of_lines = buffer.split("\n")
|
1684
|
-
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1685
|
-
@line_index = @buffer_of_lines.size - 1
|
1557
|
+
if k == "\C-g"
|
1558
|
+
# cancel search and restore buffer
|
1559
|
+
@buffer_of_lines, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history = backup
|
1686
1560
|
@searching_prompt = nil
|
1687
1561
|
@waiting_proc = nil
|
1688
|
-
|
1689
|
-
|
1690
|
-
|
1691
|
-
@
|
1692
|
-
@
|
1693
|
-
move_history(
|
1562
|
+
elsif !termination_keys.include?(k) && (k.match?(/[[:print:]]/) || k == "\C-h" || k == "\C-?" || k == "\C-r" || k == "\C-s")
|
1563
|
+
search_word, prompt_name, hit_pointer = searcher.call(k)
|
1564
|
+
Reline.last_incremental_search = search_word
|
1565
|
+
@searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
|
1566
|
+
@searching_prompt += ': ' unless @is_multiline
|
1567
|
+
move_history(hit_pointer, line: :end, cursor: :end) if hit_pointer
|
1568
|
+
else
|
1569
|
+
# terminaton_keys and other keys will terminalte
|
1570
|
+
move_history(@history_pointer, line: :end, cursor: :start)
|
1694
1571
|
@searching_prompt = nil
|
1695
1572
|
@waiting_proc = nil
|
1696
|
-
@byte_pointer = 0
|
1697
|
-
else
|
1698
|
-
chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
|
1699
|
-
if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
|
1700
|
-
search_word, prompt_name, hit_pointer = searcher.call(k)
|
1701
|
-
Reline.last_incremental_search = search_word
|
1702
|
-
@searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
|
1703
|
-
@searching_prompt += ': ' unless @is_multiline
|
1704
|
-
move_history(hit_pointer, line: :end, cursor: :end, save_buffer: false) if hit_pointer
|
1705
|
-
else
|
1706
|
-
if @history_pointer
|
1707
|
-
line = Reline::HISTORY[@history_pointer]
|
1708
|
-
else
|
1709
|
-
line = @line_backup_in_history
|
1710
|
-
end
|
1711
|
-
@line_backup_in_history = whole_buffer
|
1712
|
-
@buffer_of_lines = line.split("\n")
|
1713
|
-
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1714
|
-
@line_index = @buffer_of_lines.size - 1
|
1715
|
-
@searching_prompt = nil
|
1716
|
-
@waiting_proc = nil
|
1717
|
-
@byte_pointer = 0
|
1718
|
-
end
|
1719
1573
|
end
|
1720
1574
|
}
|
1721
1575
|
end
|
@@ -1770,14 +1624,14 @@ class Reline::LineEditor
|
|
1770
1624
|
end
|
1771
1625
|
alias_method :history_search_forward, :ed_search_next_history
|
1772
1626
|
|
1773
|
-
private def move_history(history_pointer, line:, cursor
|
1627
|
+
private def move_history(history_pointer, line:, cursor:)
|
1774
1628
|
history_pointer ||= Reline::HISTORY.size
|
1775
1629
|
return if history_pointer < 0 || history_pointer > Reline::HISTORY.size
|
1776
1630
|
old_history_pointer = @history_pointer || Reline::HISTORY.size
|
1777
1631
|
if old_history_pointer == Reline::HISTORY.size
|
1778
|
-
@line_backup_in_history =
|
1632
|
+
@line_backup_in_history = whole_buffer
|
1779
1633
|
else
|
1780
|
-
Reline::HISTORY[old_history_pointer] = whole_buffer
|
1634
|
+
Reline::HISTORY[old_history_pointer] = whole_buffer
|
1781
1635
|
end
|
1782
1636
|
if history_pointer == Reline::HISTORY.size
|
1783
1637
|
buf = @line_backup_in_history
|
@@ -1787,7 +1641,7 @@ class Reline::LineEditor
|
|
1787
1641
|
@history_pointer = history_pointer
|
1788
1642
|
end
|
1789
1643
|
@buffer_of_lines = buf.split("\n")
|
1790
|
-
@buffer_of_lines = [String.new(encoding:
|
1644
|
+
@buffer_of_lines = [String.new(encoding: encoding)] if @buffer_of_lines.empty?
|
1791
1645
|
@line_index = line == :start ? 0 : line == :end ? @buffer_of_lines.size - 1 : line
|
1792
1646
|
@byte_pointer = cursor == :start ? 0 : cursor == :end ? current_line.bytesize : cursor
|
1793
1647
|
end
|
@@ -1921,7 +1775,7 @@ class Reline::LineEditor
|
|
1921
1775
|
alias_method :kill_whole_line, :em_kill_line
|
1922
1776
|
|
1923
1777
|
private def em_delete(key)
|
1924
|
-
if buffer_empty? and key == "\C-d"
|
1778
|
+
if buffer_empty? and key == "\C-d"
|
1925
1779
|
@eof = true
|
1926
1780
|
finish
|
1927
1781
|
elsif @byte_pointer < current_line.bytesize
|
@@ -1939,9 +1793,11 @@ class Reline::LineEditor
|
|
1939
1793
|
if current_line.empty? or @byte_pointer < current_line.bytesize
|
1940
1794
|
em_delete(key)
|
1941
1795
|
elsif !@config.autocompletion # show completed list
|
1942
|
-
|
1796
|
+
pre, target, post, quote = retrieve_completion_block
|
1797
|
+
result = call_completion_proc(pre, target, post, quote)
|
1943
1798
|
if result.is_a?(Array)
|
1944
|
-
|
1799
|
+
candidates = filter_normalize_candidates(target, result)
|
1800
|
+
menu(candidates)
|
1945
1801
|
end
|
1946
1802
|
end
|
1947
1803
|
end
|
@@ -1973,7 +1829,7 @@ class Reline::LineEditor
|
|
1973
1829
|
|
1974
1830
|
private def em_next_word(key)
|
1975
1831
|
if current_line.bytesize > @byte_pointer
|
1976
|
-
byte_size
|
1832
|
+
byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
1977
1833
|
@byte_pointer += byte_size
|
1978
1834
|
end
|
1979
1835
|
end
|
@@ -1981,7 +1837,7 @@ class Reline::LineEditor
|
|
1981
1837
|
|
1982
1838
|
private def ed_prev_word(key)
|
1983
1839
|
if @byte_pointer > 0
|
1984
|
-
byte_size
|
1840
|
+
byte_size = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
|
1985
1841
|
@byte_pointer -= byte_size
|
1986
1842
|
end
|
1987
1843
|
end
|
@@ -1989,7 +1845,7 @@ class Reline::LineEditor
|
|
1989
1845
|
|
1990
1846
|
private def em_delete_next_word(key)
|
1991
1847
|
if current_line.bytesize > @byte_pointer
|
1992
|
-
byte_size
|
1848
|
+
byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
1993
1849
|
line, word = byteslice!(current_line, @byte_pointer, byte_size)
|
1994
1850
|
set_current_line(line)
|
1995
1851
|
@kill_ring.append(word)
|
@@ -1999,7 +1855,7 @@ class Reline::LineEditor
|
|
1999
1855
|
|
2000
1856
|
private def ed_delete_prev_word(key)
|
2001
1857
|
if @byte_pointer > 0
|
2002
|
-
byte_size
|
1858
|
+
byte_size = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
|
2003
1859
|
line, word = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
|
2004
1860
|
set_current_line(line, @byte_pointer - byte_size)
|
2005
1861
|
@kill_ring.append(word, true)
|
@@ -2039,7 +1895,7 @@ class Reline::LineEditor
|
|
2039
1895
|
|
2040
1896
|
private def em_capitol_case(key)
|
2041
1897
|
if current_line.bytesize > @byte_pointer
|
2042
|
-
byte_size,
|
1898
|
+
byte_size, new_str = Reline::Unicode.em_forward_word_with_capitalization(current_line, @byte_pointer)
|
2043
1899
|
before = current_line.byteslice(0, @byte_pointer)
|
2044
1900
|
after = current_line.byteslice((@byte_pointer + byte_size)..-1)
|
2045
1901
|
set_current_line(before + new_str + after, @byte_pointer + new_str.bytesize)
|
@@ -2049,7 +1905,7 @@ class Reline::LineEditor
|
|
2049
1905
|
|
2050
1906
|
private def em_lower_case(key)
|
2051
1907
|
if current_line.bytesize > @byte_pointer
|
2052
|
-
byte_size
|
1908
|
+
byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
2053
1909
|
part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
|
2054
1910
|
mbchar =~ /[A-Z]/ ? mbchar.downcase : mbchar
|
2055
1911
|
}.join
|
@@ -2062,7 +1918,7 @@ class Reline::LineEditor
|
|
2062
1918
|
|
2063
1919
|
private def em_upper_case(key)
|
2064
1920
|
if current_line.bytesize > @byte_pointer
|
2065
|
-
byte_size
|
1921
|
+
byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
2066
1922
|
part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
|
2067
1923
|
mbchar =~ /[a-z]/ ? mbchar.upcase : mbchar
|
2068
1924
|
}.join
|
@@ -2075,7 +1931,7 @@ class Reline::LineEditor
|
|
2075
1931
|
|
2076
1932
|
private def em_kill_region(key)
|
2077
1933
|
if @byte_pointer > 0
|
2078
|
-
byte_size
|
1934
|
+
byte_size = Reline::Unicode.em_big_backward_word(current_line, @byte_pointer)
|
2079
1935
|
line, deleted = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
|
2080
1936
|
set_current_line(line, @byte_pointer - byte_size)
|
2081
1937
|
@kill_ring.append(deleted, true)
|
@@ -2106,7 +1962,7 @@ class Reline::LineEditor
|
|
2106
1962
|
|
2107
1963
|
private def vi_next_word(key, arg: 1)
|
2108
1964
|
if current_line.bytesize > @byte_pointer
|
2109
|
-
byte_size
|
1965
|
+
byte_size = Reline::Unicode.vi_forward_word(current_line, @byte_pointer, @drop_terminate_spaces)
|
2110
1966
|
@byte_pointer += byte_size
|
2111
1967
|
end
|
2112
1968
|
arg -= 1
|
@@ -2115,7 +1971,7 @@ class Reline::LineEditor
|
|
2115
1971
|
|
2116
1972
|
private def vi_prev_word(key, arg: 1)
|
2117
1973
|
if @byte_pointer > 0
|
2118
|
-
byte_size
|
1974
|
+
byte_size = Reline::Unicode.vi_backward_word(current_line, @byte_pointer)
|
2119
1975
|
@byte_pointer -= byte_size
|
2120
1976
|
end
|
2121
1977
|
arg -= 1
|
@@ -2124,7 +1980,7 @@ class Reline::LineEditor
|
|
2124
1980
|
|
2125
1981
|
private def vi_end_word(key, arg: 1, inclusive: false)
|
2126
1982
|
if current_line.bytesize > @byte_pointer
|
2127
|
-
byte_size
|
1983
|
+
byte_size = Reline::Unicode.vi_forward_end_word(current_line, @byte_pointer)
|
2128
1984
|
@byte_pointer += byte_size
|
2129
1985
|
end
|
2130
1986
|
arg -= 1
|
@@ -2139,7 +1995,7 @@ class Reline::LineEditor
|
|
2139
1995
|
|
2140
1996
|
private def vi_next_big_word(key, arg: 1)
|
2141
1997
|
if current_line.bytesize > @byte_pointer
|
2142
|
-
byte_size
|
1998
|
+
byte_size = Reline::Unicode.vi_big_forward_word(current_line, @byte_pointer)
|
2143
1999
|
@byte_pointer += byte_size
|
2144
2000
|
end
|
2145
2001
|
arg -= 1
|
@@ -2148,7 +2004,7 @@ class Reline::LineEditor
|
|
2148
2004
|
|
2149
2005
|
private def vi_prev_big_word(key, arg: 1)
|
2150
2006
|
if @byte_pointer > 0
|
2151
|
-
byte_size
|
2007
|
+
byte_size = Reline::Unicode.vi_big_backward_word(current_line, @byte_pointer)
|
2152
2008
|
@byte_pointer -= byte_size
|
2153
2009
|
end
|
2154
2010
|
arg -= 1
|
@@ -2157,7 +2013,7 @@ class Reline::LineEditor
|
|
2157
2013
|
|
2158
2014
|
private def vi_end_big_word(key, arg: 1, inclusive: false)
|
2159
2015
|
if current_line.bytesize > @byte_pointer
|
2160
|
-
byte_size
|
2016
|
+
byte_size = Reline::Unicode.vi_big_forward_end_word(current_line, @byte_pointer)
|
2161
2017
|
@byte_pointer += byte_size
|
2162
2018
|
end
|
2163
2019
|
arg -= 1
|
@@ -2312,7 +2168,7 @@ class Reline::LineEditor
|
|
2312
2168
|
}
|
2313
2169
|
system("#{ENV['EDITOR']} #{path}")
|
2314
2170
|
@buffer_of_lines = File.read(path).split("\n")
|
2315
|
-
@buffer_of_lines = [String.new(encoding:
|
2171
|
+
@buffer_of_lines = [String.new(encoding: encoding)] if @buffer_of_lines.empty?
|
2316
2172
|
@line_index = 0
|
2317
2173
|
finish
|
2318
2174
|
end
|
@@ -2337,20 +2193,9 @@ class Reline::LineEditor
|
|
2337
2193
|
end
|
2338
2194
|
|
2339
2195
|
private def ed_argument_digit(key)
|
2340
|
-
|
2341
|
-
|
2342
|
-
|
2343
|
-
unescaped_key = key ^ 0b10000000
|
2344
|
-
unless unescaped_key.chr.to_i.zero?
|
2345
|
-
@vi_arg = unescaped_key.chr.to_i
|
2346
|
-
end
|
2347
|
-
end
|
2348
|
-
else
|
2349
|
-
@vi_arg = key.chr.to_i
|
2350
|
-
end
|
2351
|
-
else
|
2352
|
-
@vi_arg = @vi_arg * 10 + key.chr.to_i
|
2353
|
-
end
|
2196
|
+
# key is expected to be `ESC digit` or `digit`
|
2197
|
+
num = key[/\d/].to_i
|
2198
|
+
@vi_arg = (@vi_arg || 0) * 10 + num
|
2354
2199
|
end
|
2355
2200
|
|
2356
2201
|
private def vi_to_column(key, arg: 0)
|
@@ -2369,7 +2214,7 @@ class Reline::LineEditor
|
|
2369
2214
|
before = current_line.byteslice(0, @byte_pointer)
|
2370
2215
|
remaining_point = @byte_pointer + byte_size
|
2371
2216
|
after = current_line.byteslice(remaining_point, current_line.bytesize - remaining_point)
|
2372
|
-
set_current_line(before + k
|
2217
|
+
set_current_line(before + k + after)
|
2373
2218
|
@waiting_proc = nil
|
2374
2219
|
elsif arg > 1
|
2375
2220
|
byte_size = 0
|
@@ -2379,7 +2224,7 @@ class Reline::LineEditor
|
|
2379
2224
|
before = current_line.byteslice(0, @byte_pointer)
|
2380
2225
|
remaining_point = @byte_pointer + byte_size
|
2381
2226
|
after = current_line.byteslice(remaining_point, current_line.bytesize - remaining_point)
|
2382
|
-
replaced = k
|
2227
|
+
replaced = k * arg
|
2383
2228
|
set_current_line(before + replaced + after, @byte_pointer + replaced.bytesize)
|
2384
2229
|
@waiting_proc = nil
|
2385
2230
|
end
|
@@ -2395,11 +2240,6 @@ class Reline::LineEditor
|
|
2395
2240
|
end
|
2396
2241
|
|
2397
2242
|
private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
|
2398
|
-
if key.instance_of?(String)
|
2399
|
-
inputed_char = key
|
2400
|
-
else
|
2401
|
-
inputed_char = key.chr
|
2402
|
-
end
|
2403
2243
|
prev_total = nil
|
2404
2244
|
total = nil
|
2405
2245
|
found = false
|
@@ -2410,7 +2250,7 @@ class Reline::LineEditor
|
|
2410
2250
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2411
2251
|
total = [mbchar.bytesize, width]
|
2412
2252
|
else
|
2413
|
-
if
|
2253
|
+
if key == mbchar
|
2414
2254
|
arg -= 1
|
2415
2255
|
if arg.zero?
|
2416
2256
|
found = true
|
@@ -2447,11 +2287,6 @@ class Reline::LineEditor
|
|
2447
2287
|
end
|
2448
2288
|
|
2449
2289
|
private def search_prev_char(key, arg, need_next_char = false)
|
2450
|
-
if key.instance_of?(String)
|
2451
|
-
inputed_char = key
|
2452
|
-
else
|
2453
|
-
inputed_char = key.chr
|
2454
|
-
end
|
2455
2290
|
prev_total = nil
|
2456
2291
|
total = nil
|
2457
2292
|
found = false
|
@@ -2462,7 +2297,7 @@ class Reline::LineEditor
|
|
2462
2297
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2463
2298
|
total = [mbchar.bytesize, width]
|
2464
2299
|
else
|
2465
|
-
if
|
2300
|
+
if key == mbchar
|
2466
2301
|
arg -= 1
|
2467
2302
|
if arg.zero?
|
2468
2303
|
found = true
|
@@ -2514,24 +2349,23 @@ class Reline::LineEditor
|
|
2514
2349
|
@config.editing_mode = :vi_insert
|
2515
2350
|
end
|
2516
2351
|
|
2517
|
-
private def
|
2518
|
-
@
|
2352
|
+
private def move_undo_redo(direction)
|
2353
|
+
@restoring = true
|
2354
|
+
return unless (0..@input_lines.size - 1).cover?(@input_lines_position + direction)
|
2519
2355
|
|
2520
|
-
|
2356
|
+
@input_lines_position += direction
|
2357
|
+
buffer_of_lines, byte_pointer, line_index = @input_lines[@input_lines_position]
|
2358
|
+
@buffer_of_lines = buffer_of_lines.dup
|
2359
|
+
@line_index = line_index
|
2360
|
+
@byte_pointer = byte_pointer
|
2361
|
+
end
|
2521
2362
|
|
2522
|
-
|
2523
|
-
|
2524
|
-
set_current_lines(target_lines.dup, target_cursor_x, target_cursor_y)
|
2363
|
+
private def undo(_key)
|
2364
|
+
move_undo_redo(-1)
|
2525
2365
|
end
|
2526
2366
|
|
2527
2367
|
private def redo(_key)
|
2528
|
-
|
2529
|
-
|
2530
|
-
return if @input_lines_position >= @input_lines.size - 1
|
2531
|
-
|
2532
|
-
@input_lines_position += 1
|
2533
|
-
target_lines, target_cursor_x, target_cursor_y = @input_lines[@input_lines_position]
|
2534
|
-
set_current_lines(target_lines.dup, target_cursor_x, target_cursor_y)
|
2368
|
+
move_undo_redo(+1)
|
2535
2369
|
end
|
2536
2370
|
|
2537
2371
|
private def prev_action_state_value(type)
|