reline 0.0.6 → 0.1.3
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.rb +41 -20
- data/lib/reline/ansi.rb +59 -26
- data/lib/reline/config.rb +6 -4
- data/lib/reline/general_io.rb +8 -0
- data/lib/reline/history.rb +4 -4
- data/lib/reline/key_actor/emacs.rb +2 -2
- data/lib/reline/key_actor/vi_command.rb +1 -1
- data/lib/reline/key_actor/vi_insert.rb +1 -1
- data/lib/reline/line_editor.rb +339 -89
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +41 -4
- metadata +18 -4
data/lib/reline/line_editor.rb
CHANGED
@@ -10,6 +10,7 @@ class Reline::LineEditor
|
|
10
10
|
attr_reader :byte_pointer
|
11
11
|
attr_accessor :confirm_multiline_termination_proc
|
12
12
|
attr_accessor :completion_proc
|
13
|
+
attr_accessor :completion_append_character
|
13
14
|
attr_accessor :output_modifier_proc
|
14
15
|
attr_accessor :prompt_proc
|
15
16
|
attr_accessor :auto_indent_proc
|
@@ -43,6 +44,7 @@ class Reline::LineEditor
|
|
43
44
|
COMPLETION = :completion
|
44
45
|
MENU = :menu
|
45
46
|
JOURNEY = :journey
|
47
|
+
MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
|
46
48
|
PERFECT_MATCH = :perfect_match
|
47
49
|
end
|
48
50
|
|
@@ -55,9 +57,10 @@ class Reline::LineEditor
|
|
55
57
|
NON_PRINTING_END = "\2"
|
56
58
|
WIDTH_SCANNER = /\G(?:#{NON_PRINTING_START}|#{NON_PRINTING_END}|#{CSI_REGEXP}|#{OSC_REGEXP}|\X)/
|
57
59
|
|
58
|
-
def initialize(config)
|
60
|
+
def initialize(config, encoding)
|
59
61
|
@config = config
|
60
|
-
|
62
|
+
@completion_append_character = ''
|
63
|
+
reset_variables(encoding: encoding)
|
61
64
|
end
|
62
65
|
|
63
66
|
private def check_multiline_prompt(buffer, prompt)
|
@@ -82,10 +85,10 @@ class Reline::LineEditor
|
|
82
85
|
end
|
83
86
|
end
|
84
87
|
|
85
|
-
def reset(prompt = '', encoding
|
88
|
+
def reset(prompt = '', encoding:)
|
86
89
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
87
90
|
@screen_size = Reline::IOGate.get_screen_size
|
88
|
-
reset_variables(prompt, encoding)
|
91
|
+
reset_variables(prompt, encoding: encoding)
|
89
92
|
@old_trap = Signal.trap('SIGINT') {
|
90
93
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
91
94
|
raise Interrupt
|
@@ -136,7 +139,7 @@ class Reline::LineEditor
|
|
136
139
|
@eof
|
137
140
|
end
|
138
141
|
|
139
|
-
def reset_variables(prompt = '', encoding
|
142
|
+
def reset_variables(prompt = '', encoding:)
|
140
143
|
@prompt = prompt
|
141
144
|
@mark_pointer = nil
|
142
145
|
@encoding = encoding
|
@@ -192,7 +195,7 @@ class Reline::LineEditor
|
|
192
195
|
lines.each_with_index { |line, i|
|
193
196
|
prompt = ''
|
194
197
|
prompt = prompt_list[i] if prompt_list and prompt_list[i]
|
195
|
-
result += calculate_height_by_width(calculate_width(prompt + line))
|
198
|
+
result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
|
196
199
|
}
|
197
200
|
result
|
198
201
|
end
|
@@ -314,9 +317,9 @@ class Reline::LineEditor
|
|
314
317
|
if @menu_info
|
315
318
|
scroll_down(@highest_in_all - @first_line_started_from)
|
316
319
|
@rerender_all = true
|
317
|
-
@menu_info.list.each do |item|
|
320
|
+
@menu_info.list.sort!.each do |item|
|
318
321
|
Reline::IOGate.move_cursor_column(0)
|
319
|
-
@output.
|
322
|
+
@output.write item
|
320
323
|
@output.flush
|
321
324
|
scroll_down(1)
|
322
325
|
end
|
@@ -504,12 +507,20 @@ class Reline::LineEditor
|
|
504
507
|
Reline::IOGate.move_cursor_column(0)
|
505
508
|
visual_lines.each_with_index do |line, index|
|
506
509
|
if line.nil?
|
507
|
-
Reline::IOGate.
|
508
|
-
|
509
|
-
|
510
|
+
if Reline::IOGate.win? and calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
511
|
+
# A newline is automatically inserted if a character is rendered at eol on command prompt.
|
512
|
+
else
|
513
|
+
Reline::IOGate.erase_after_cursor
|
514
|
+
move_cursor_down(1)
|
515
|
+
Reline::IOGate.move_cursor_column(0)
|
516
|
+
end
|
510
517
|
next
|
511
518
|
end
|
512
|
-
@output.
|
519
|
+
@output.write line
|
520
|
+
if Reline::IOGate.win? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
|
521
|
+
# A newline is automatically inserted if a character is rendered at eol on command prompt.
|
522
|
+
@rest_height -= 1 if @rest_height > 0
|
523
|
+
end
|
513
524
|
@output.flush
|
514
525
|
if @first_prompt
|
515
526
|
@first_prompt = false
|
@@ -549,10 +560,14 @@ class Reline::LineEditor
|
|
549
560
|
private def complete_internal_proc(list, is_menu)
|
550
561
|
preposing, target, postposing = retrieve_completion_block
|
551
562
|
list = list.select { |i|
|
552
|
-
if i and
|
553
|
-
raise Encoding::CompatibilityError
|
563
|
+
if i and not Encoding.compatible?(target.encoding, i.encoding)
|
564
|
+
raise Encoding::CompatibilityError, "#{target.encoding.name} is not compatible with #{i.encoding.name}"
|
565
|
+
end
|
566
|
+
if @config.completion_ignore_case
|
567
|
+
i&.downcase&.start_with?(target.downcase)
|
568
|
+
else
|
569
|
+
i&.start_with?(target)
|
554
570
|
end
|
555
|
-
i&.start_with?(target)
|
556
571
|
}
|
557
572
|
if is_menu
|
558
573
|
menu(target, list)
|
@@ -569,10 +584,18 @@ class Reline::LineEditor
|
|
569
584
|
size = [memo_mbchars.size, item_mbchars.size].min
|
570
585
|
result = ''
|
571
586
|
size.times do |i|
|
572
|
-
if
|
573
|
-
|
587
|
+
if @config.completion_ignore_case
|
588
|
+
if memo_mbchars[i].casecmp?(item_mbchars[i])
|
589
|
+
result << memo_mbchars[i]
|
590
|
+
else
|
591
|
+
break
|
592
|
+
end
|
574
593
|
else
|
575
|
-
|
594
|
+
if memo_mbchars[i] == item_mbchars[i]
|
595
|
+
result << memo_mbchars[i]
|
596
|
+
else
|
597
|
+
break
|
598
|
+
end
|
576
599
|
end
|
577
600
|
end
|
578
601
|
result
|
@@ -580,27 +603,43 @@ class Reline::LineEditor
|
|
580
603
|
[target, preposing, completed, postposing]
|
581
604
|
end
|
582
605
|
|
583
|
-
private def complete(list)
|
606
|
+
private def complete(list, just_show_list = false)
|
584
607
|
case @completion_state
|
585
608
|
when CompletionState::NORMAL, CompletionState::JOURNEY
|
586
609
|
@completion_state = CompletionState::COMPLETION
|
587
610
|
when CompletionState::PERFECT_MATCH
|
588
611
|
@dig_perfect_match_proc&.(@perfect_matched)
|
589
612
|
end
|
590
|
-
|
613
|
+
if just_show_list
|
614
|
+
is_menu = true
|
615
|
+
elsif @completion_state == CompletionState::MENU
|
616
|
+
is_menu = true
|
617
|
+
elsif @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
|
618
|
+
is_menu = true
|
619
|
+
else
|
620
|
+
is_menu = false
|
621
|
+
end
|
591
622
|
result = complete_internal_proc(list, is_menu)
|
623
|
+
if @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
|
624
|
+
@completion_state = CompletionState::PERFECT_MATCH
|
625
|
+
end
|
592
626
|
return if result.nil?
|
593
627
|
target, preposing, completed, postposing = result
|
594
628
|
return if completed.nil?
|
595
|
-
if target <= completed and (@completion_state == CompletionState::COMPLETION
|
596
|
-
@completion_state = CompletionState::MENU
|
629
|
+
if target <= completed and (@completion_state == CompletionState::COMPLETION)
|
597
630
|
if list.include?(completed)
|
598
|
-
|
631
|
+
if list.one?
|
632
|
+
@completion_state = CompletionState::PERFECT_MATCH
|
633
|
+
else
|
634
|
+
@completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
|
635
|
+
end
|
599
636
|
@perfect_matched = completed
|
637
|
+
else
|
638
|
+
@completion_state = CompletionState::MENU
|
600
639
|
end
|
601
|
-
if target < completed
|
602
|
-
@line = preposing + completed + postposing
|
603
|
-
line_to_pointer = preposing + completed
|
640
|
+
if not just_show_list and target < completed
|
641
|
+
@line = preposing + completed + completion_append_character.to_s + postposing
|
642
|
+
line_to_pointer = preposing + completed + completion_append_character.to_s
|
604
643
|
@cursor_max = calculate_width(@line)
|
605
644
|
@cursor = calculate_width(line_to_pointer)
|
606
645
|
@byte_pointer = line_to_pointer.bytesize
|
@@ -610,7 +649,8 @@ class Reline::LineEditor
|
|
610
649
|
|
611
650
|
private def move_completed_list(list, direction)
|
612
651
|
case @completion_state
|
613
|
-
when CompletionState::NORMAL, CompletionState::COMPLETION,
|
652
|
+
when CompletionState::NORMAL, CompletionState::COMPLETION,
|
653
|
+
CompletionState::MENU, CompletionState::MENU_WITH_PERFECT_MATCH
|
614
654
|
@completion_state = CompletionState::JOURNEY
|
615
655
|
result = retrieve_completion_block
|
616
656
|
return if result.nil?
|
@@ -776,20 +816,20 @@ class Reline::LineEditor
|
|
776
816
|
@first_char = false
|
777
817
|
completion_occurs = false
|
778
818
|
if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
819
|
+
unless @config.disable_completion
|
820
|
+
result = call_completion_proc
|
821
|
+
if result.is_a?(Array)
|
822
|
+
completion_occurs = true
|
823
|
+
complete(result)
|
824
|
+
end
|
785
825
|
end
|
786
|
-
elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
826
|
+
elsif not @config.disable_completion and @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
|
827
|
+
unless @config.disable_completion
|
828
|
+
result = call_completion_proc
|
829
|
+
if result.is_a?(Array)
|
830
|
+
completion_occurs = true
|
831
|
+
move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
|
832
|
+
end
|
793
833
|
end
|
794
834
|
elsif Symbol === key.char and respond_to?(key.char, true)
|
795
835
|
process_key(key.char, key.char)
|
@@ -804,6 +844,14 @@ class Reline::LineEditor
|
|
804
844
|
end
|
805
845
|
end
|
806
846
|
|
847
|
+
def call_completion_proc
|
848
|
+
result = retrieve_completion_block(true)
|
849
|
+
slice = result[1]
|
850
|
+
result = @completion_proc.(slice) if @completion_proc and slice
|
851
|
+
Reline.core.instance_variable_set(:@completion_quote_character, nil)
|
852
|
+
result
|
853
|
+
end
|
854
|
+
|
807
855
|
private def process_auto_indent
|
808
856
|
return if not @check_new_auto_indent and @previous_line_index # move cursor up or down
|
809
857
|
if @check_new_auto_indent and @previous_line_index and @previous_line_index > 0 and @line_index > @previous_line_index
|
@@ -815,12 +863,9 @@ class Reline::LineEditor
|
|
815
863
|
@line = ' ' * new_indent + @line.lstrip
|
816
864
|
|
817
865
|
new_indent = nil
|
818
|
-
(new_lines[-2].size + 1)
|
819
|
-
|
820
|
-
|
821
|
-
new_indent = result
|
822
|
-
break
|
823
|
-
end
|
866
|
+
result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, (new_lines[-2].size + 1), false)
|
867
|
+
if result
|
868
|
+
new_indent = result
|
824
869
|
end
|
825
870
|
if new_indent&.>= 0
|
826
871
|
@line = ' ' * new_indent + @line.lstrip
|
@@ -848,7 +893,7 @@ class Reline::LineEditor
|
|
848
893
|
@check_new_auto_indent = false
|
849
894
|
end
|
850
895
|
|
851
|
-
def retrieve_completion_block
|
896
|
+
def retrieve_completion_block(set_completion_quote_character = false)
|
852
897
|
word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
|
853
898
|
quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
|
854
899
|
before = @line.byteslice(0, @byte_pointer)
|
@@ -867,31 +912,46 @@ class Reline::LineEditor
|
|
867
912
|
if quote and slice.start_with?(closing_quote)
|
868
913
|
quote = nil
|
869
914
|
i += 1
|
915
|
+
rest = nil
|
870
916
|
elsif quote and slice.start_with?(escaped_quote)
|
871
917
|
# skip
|
872
918
|
i += 2
|
873
919
|
elsif slice =~ quote_characters_regexp # find new "
|
920
|
+
rest = $'
|
874
921
|
quote = $&
|
875
922
|
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
|
876
923
|
escaped_quote = /\\#{Regexp.escape(quote)}/
|
877
924
|
i += 1
|
925
|
+
break_pointer = i - 1
|
878
926
|
elsif not quote and slice =~ word_break_regexp
|
879
927
|
rest = $'
|
880
928
|
i += 1
|
929
|
+
before = @line.byteslice(i, @byte_pointer - i)
|
881
930
|
break_pointer = i
|
882
931
|
else
|
883
932
|
i += 1
|
884
933
|
end
|
885
934
|
end
|
935
|
+
postposing = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
|
886
936
|
if rest
|
887
937
|
preposing = @line.byteslice(0, break_pointer)
|
888
938
|
target = rest
|
939
|
+
if set_completion_quote_character and quote
|
940
|
+
Reline.core.instance_variable_set(:@completion_quote_character, quote)
|
941
|
+
if postposing !~ /(?!\\)#{Regexp.escape(quote)}/ # closing quote
|
942
|
+
insert_text(quote)
|
943
|
+
end
|
944
|
+
end
|
889
945
|
else
|
890
946
|
preposing = ''
|
947
|
+
if break_pointer
|
948
|
+
preposing = @line.byteslice(0, break_pointer)
|
949
|
+
else
|
950
|
+
preposing = ''
|
951
|
+
end
|
891
952
|
target = before
|
892
953
|
end
|
893
|
-
|
894
|
-
[preposing, target, postposing]
|
954
|
+
[preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
|
895
955
|
end
|
896
956
|
|
897
957
|
def confirm_multiline_termination
|
@@ -1043,6 +1103,11 @@ class Reline::LineEditor
|
|
1043
1103
|
|
1044
1104
|
private def ed_insert(key)
|
1045
1105
|
if key.instance_of?(String)
|
1106
|
+
begin
|
1107
|
+
key.encode(Encoding::UTF_8)
|
1108
|
+
rescue Encoding::UndefinedConversionError
|
1109
|
+
return
|
1110
|
+
end
|
1046
1111
|
width = Reline::Unicode.get_mbchar_width(key)
|
1047
1112
|
if @cursor == @cursor_max
|
1048
1113
|
@line += key
|
@@ -1053,6 +1118,11 @@ class Reline::LineEditor
|
|
1053
1118
|
@cursor += width
|
1054
1119
|
@cursor_max += width
|
1055
1120
|
else
|
1121
|
+
begin
|
1122
|
+
key.chr.encode(Encoding::UTF_8)
|
1123
|
+
rescue Encoding::UndefinedConversionError
|
1124
|
+
return
|
1125
|
+
end
|
1056
1126
|
if @cursor == @cursor_max
|
1057
1127
|
@line += key.chr
|
1058
1128
|
else
|
@@ -1144,25 +1214,34 @@ class Reline::LineEditor
|
|
1144
1214
|
end
|
1145
1215
|
alias_method :end_of_line, :ed_move_to_end
|
1146
1216
|
|
1147
|
-
private def
|
1148
|
-
|
1149
|
-
|
1150
|
-
else
|
1151
|
-
@line_backup_in_history = @line
|
1152
|
-
end
|
1153
|
-
searcher = Fiber.new do
|
1217
|
+
private def generate_searcher
|
1218
|
+
Fiber.new do |first_key|
|
1219
|
+
prev_search_key = first_key
|
1154
1220
|
search_word = String.new(encoding: @encoding)
|
1155
1221
|
multibyte_buf = String.new(encoding: 'ASCII-8BIT')
|
1156
1222
|
last_hit = nil
|
1223
|
+
case first_key
|
1224
|
+
when "\C-r".ord
|
1225
|
+
prompt_name = 'reverse-i-search'
|
1226
|
+
when "\C-s".ord
|
1227
|
+
prompt_name = 'i-search'
|
1228
|
+
end
|
1157
1229
|
loop do
|
1158
1230
|
key = Fiber.yield(search_word)
|
1231
|
+
search_again = false
|
1159
1232
|
case key
|
1160
|
-
when
|
1233
|
+
when -1 # determined
|
1234
|
+
Reline.last_incremental_search = search_word
|
1235
|
+
break
|
1236
|
+
when "\C-h".ord, "\C-?".ord
|
1161
1237
|
grapheme_clusters = search_word.grapheme_clusters
|
1162
1238
|
if grapheme_clusters.size > 0
|
1163
1239
|
grapheme_clusters.pop
|
1164
1240
|
search_word = grapheme_clusters.join
|
1165
1241
|
end
|
1242
|
+
when "\C-r".ord, "\C-s".ord
|
1243
|
+
search_again = true if prev_search_key == key
|
1244
|
+
prev_search_key = key
|
1166
1245
|
else
|
1167
1246
|
multibyte_buf << key
|
1168
1247
|
if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
|
@@ -1171,18 +1250,61 @@ class Reline::LineEditor
|
|
1171
1250
|
end
|
1172
1251
|
end
|
1173
1252
|
hit = nil
|
1174
|
-
if @line_backup_in_history
|
1253
|
+
if not search_word.empty? and @line_backup_in_history&.include?(search_word)
|
1175
1254
|
@history_pointer = nil
|
1176
1255
|
hit = @line_backup_in_history
|
1177
1256
|
else
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1257
|
+
if search_again
|
1258
|
+
if search_word.empty? and Reline.last_incremental_search
|
1259
|
+
search_word = Reline.last_incremental_search
|
1260
|
+
end
|
1261
|
+
if @history_pointer # TODO
|
1262
|
+
case prev_search_key
|
1263
|
+
when "\C-r".ord
|
1264
|
+
history_pointer_base = 0
|
1265
|
+
history = Reline::HISTORY[0..(@history_pointer - 1)]
|
1266
|
+
when "\C-s".ord
|
1267
|
+
history_pointer_base = @history_pointer + 1
|
1268
|
+
history = Reline::HISTORY[(@history_pointer + 1)..-1]
|
1269
|
+
end
|
1270
|
+
else
|
1271
|
+
history_pointer_base = 0
|
1272
|
+
history = Reline::HISTORY
|
1273
|
+
end
|
1274
|
+
elsif @history_pointer
|
1275
|
+
case prev_search_key
|
1276
|
+
when "\C-r".ord
|
1277
|
+
history_pointer_base = 0
|
1278
|
+
history = Reline::HISTORY[0..@history_pointer]
|
1279
|
+
when "\C-s".ord
|
1280
|
+
history_pointer_base = @history_pointer
|
1281
|
+
history = Reline::HISTORY[@history_pointer..-1]
|
1282
|
+
end
|
1283
|
+
else
|
1284
|
+
history_pointer_base = 0
|
1285
|
+
history = Reline::HISTORY
|
1286
|
+
end
|
1287
|
+
case prev_search_key
|
1288
|
+
when "\C-r".ord
|
1289
|
+
hit_index = history.rindex { |item|
|
1290
|
+
item.include?(search_word)
|
1291
|
+
}
|
1292
|
+
when "\C-s".ord
|
1293
|
+
hit_index = history.index { |item|
|
1294
|
+
item.include?(search_word)
|
1295
|
+
}
|
1296
|
+
end
|
1181
1297
|
if hit_index
|
1182
|
-
@history_pointer = hit_index
|
1298
|
+
@history_pointer = history_pointer_base + hit_index
|
1183
1299
|
hit = Reline::HISTORY[@history_pointer]
|
1184
1300
|
end
|
1185
1301
|
end
|
1302
|
+
case prev_search_key
|
1303
|
+
when "\C-r".ord
|
1304
|
+
prompt_name = 'reverse-i-search'
|
1305
|
+
when "\C-s".ord
|
1306
|
+
prompt_name = 'i-search'
|
1307
|
+
end
|
1186
1308
|
if hit
|
1187
1309
|
if @is_multiline
|
1188
1310
|
@buffer_of_lines = hit.split("\n")
|
@@ -1190,47 +1312,77 @@ class Reline::LineEditor
|
|
1190
1312
|
@line_index = @buffer_of_lines.size - 1
|
1191
1313
|
@line = @buffer_of_lines.last
|
1192
1314
|
@rerender_all = true
|
1193
|
-
@searching_prompt = "(
|
1315
|
+
@searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
|
1194
1316
|
else
|
1195
1317
|
@line = hit
|
1196
|
-
@searching_prompt = "(
|
1318
|
+
@searching_prompt = "(%s)`%s': %s" % [prompt_name, search_word, hit]
|
1197
1319
|
end
|
1198
1320
|
last_hit = hit
|
1199
1321
|
else
|
1200
1322
|
if @is_multiline
|
1201
1323
|
@rerender_all = true
|
1202
|
-
@searching_prompt = "(failed
|
1324
|
+
@searching_prompt = "(failed %s)`%s'" % [prompt_name, search_word]
|
1203
1325
|
else
|
1204
|
-
@searching_prompt = "(failed
|
1326
|
+
@searching_prompt = "(failed %s)`%s': %s" % [prompt_name, search_word, last_hit]
|
1205
1327
|
end
|
1206
1328
|
end
|
1207
1329
|
end
|
1208
1330
|
end
|
1209
|
-
|
1331
|
+
end
|
1332
|
+
|
1333
|
+
private def search_history(key)
|
1334
|
+
unless @history_pointer
|
1335
|
+
if @is_multiline
|
1336
|
+
@line_backup_in_history = whole_buffer
|
1337
|
+
else
|
1338
|
+
@line_backup_in_history = @line
|
1339
|
+
end
|
1340
|
+
end
|
1341
|
+
searcher = generate_searcher
|
1342
|
+
searcher.resume(key)
|
1210
1343
|
@searching_prompt = "(reverse-i-search)`': "
|
1211
1344
|
@waiting_proc = ->(k) {
|
1212
1345
|
case k
|
1213
|
-
when "\C-j".ord
|
1346
|
+
when "\C-j".ord
|
1214
1347
|
if @history_pointer
|
1215
|
-
|
1348
|
+
buffer = Reline::HISTORY[@history_pointer]
|
1216
1349
|
else
|
1217
|
-
|
1350
|
+
buffer = @line_backup_in_history
|
1351
|
+
end
|
1352
|
+
if @is_multiline
|
1353
|
+
@buffer_of_lines = buffer.split("\n")
|
1354
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1355
|
+
@line_index = @buffer_of_lines.size - 1
|
1356
|
+
@line = @buffer_of_lines.last
|
1357
|
+
@rerender_all = true
|
1358
|
+
else
|
1359
|
+
@line = buffer
|
1218
1360
|
end
|
1219
1361
|
@searching_prompt = nil
|
1220
1362
|
@waiting_proc = nil
|
1221
1363
|
@cursor_max = calculate_width(@line)
|
1222
1364
|
@cursor = @byte_pointer = 0
|
1365
|
+
searcher.resume(-1)
|
1223
1366
|
when "\C-g".ord
|
1224
|
-
|
1367
|
+
if @is_multiline
|
1368
|
+
@buffer_of_lines = @line_backup_in_history.split("\n")
|
1369
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1370
|
+
@line_index = @buffer_of_lines.size - 1
|
1371
|
+
@line = @buffer_of_lines.last
|
1372
|
+
@rerender_all = true
|
1373
|
+
else
|
1374
|
+
@line = @line_backup_in_history
|
1375
|
+
end
|
1225
1376
|
@history_pointer = nil
|
1226
1377
|
@searching_prompt = nil
|
1227
1378
|
@waiting_proc = nil
|
1228
1379
|
@line_backup_in_history = nil
|
1229
1380
|
@cursor_max = calculate_width(@line)
|
1230
1381
|
@cursor = @byte_pointer = 0
|
1382
|
+
@rerender_all = true
|
1231
1383
|
else
|
1232
1384
|
chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
|
1233
|
-
if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k ==
|
1385
|
+
if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
|
1234
1386
|
searcher.resume(k)
|
1235
1387
|
else
|
1236
1388
|
if @history_pointer
|
@@ -1253,13 +1405,21 @@ class Reline::LineEditor
|
|
1253
1405
|
@waiting_proc = nil
|
1254
1406
|
@cursor_max = calculate_width(@line)
|
1255
1407
|
@cursor = @byte_pointer = 0
|
1408
|
+
searcher.resume(-1)
|
1256
1409
|
end
|
1257
1410
|
end
|
1258
1411
|
}
|
1259
1412
|
end
|
1260
1413
|
|
1414
|
+
private def ed_search_prev_history(key)
|
1415
|
+
search_history(key)
|
1416
|
+
end
|
1417
|
+
alias_method :reverse_search_history, :ed_search_prev_history
|
1418
|
+
|
1261
1419
|
private def ed_search_next_history(key)
|
1420
|
+
search_history(key)
|
1262
1421
|
end
|
1422
|
+
alias_method :forward_search_history, :ed_search_next_history
|
1263
1423
|
|
1264
1424
|
private def ed_prev_history(key, arg: 1)
|
1265
1425
|
if @is_multiline and @line_index > 0
|
@@ -1417,6 +1577,14 @@ class Reline::LineEditor
|
|
1417
1577
|
@byte_pointer = @line.bytesize
|
1418
1578
|
@cursor = @cursor_max = calculate_width(@line)
|
1419
1579
|
@kill_ring.append(deleted)
|
1580
|
+
elsif @is_multiline and @byte_pointer == @line.bytesize and @buffer_of_lines.size > @line_index + 1
|
1581
|
+
@cursor = calculate_width(@line)
|
1582
|
+
@byte_pointer = @line.bytesize
|
1583
|
+
@line += @buffer_of_lines.delete_at(@line_index + 1)
|
1584
|
+
@cursor_max = calculate_width(@line)
|
1585
|
+
@buffer_of_lines[@line_index] = @line
|
1586
|
+
@rerender_all = true
|
1587
|
+
@rest_height += 1
|
1420
1588
|
end
|
1421
1589
|
end
|
1422
1590
|
|
@@ -1430,7 +1598,7 @@ class Reline::LineEditor
|
|
1430
1598
|
end
|
1431
1599
|
end
|
1432
1600
|
|
1433
|
-
private def
|
1601
|
+
private def em_delete(key)
|
1434
1602
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
1435
1603
|
@line = nil
|
1436
1604
|
if @buffer_of_lines.size > 1
|
@@ -1455,7 +1623,19 @@ class Reline::LineEditor
|
|
1455
1623
|
@rest_height += 1
|
1456
1624
|
end
|
1457
1625
|
end
|
1458
|
-
alias_method :delete_char, :
|
1626
|
+
alias_method :delete_char, :em_delete
|
1627
|
+
|
1628
|
+
private def em_delete_or_list(key)
|
1629
|
+
if @line.empty? or @byte_pointer < @line.bytesize
|
1630
|
+
em_delete(key)
|
1631
|
+
else # show completed list
|
1632
|
+
result = call_completion_proc
|
1633
|
+
if result.is_a?(Array)
|
1634
|
+
complete(result, true)
|
1635
|
+
end
|
1636
|
+
end
|
1637
|
+
end
|
1638
|
+
alias_method :delete_char_or_list, :em_delete_or_list
|
1459
1639
|
|
1460
1640
|
private def em_yank(key)
|
1461
1641
|
yanked = @kill_ring.yank
|
@@ -1718,6 +1898,16 @@ class Reline::LineEditor
|
|
1718
1898
|
end
|
1719
1899
|
end
|
1720
1900
|
|
1901
|
+
private def vi_insert_at_bol(key)
|
1902
|
+
ed_move_to_beg(key)
|
1903
|
+
@config.editing_mode = :vi_insert
|
1904
|
+
end
|
1905
|
+
|
1906
|
+
private def vi_add_at_eol(key)
|
1907
|
+
ed_move_to_end(key)
|
1908
|
+
@config.editing_mode = :vi_insert
|
1909
|
+
end
|
1910
|
+
|
1721
1911
|
private def ed_delete_prev_char(key, arg: 1)
|
1722
1912
|
deleted = ''
|
1723
1913
|
arg.times do
|
@@ -1740,6 +1930,18 @@ class Reline::LineEditor
|
|
1740
1930
|
end
|
1741
1931
|
|
1742
1932
|
private def vi_change_meta(key)
|
1933
|
+
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
1934
|
+
if byte_pointer_diff > 0
|
1935
|
+
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
1936
|
+
elsif byte_pointer_diff < 0
|
1937
|
+
@line, cut = byteslice!(@line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
1938
|
+
end
|
1939
|
+
copy_for_vi(cut)
|
1940
|
+
@cursor += cursor_diff if cursor_diff < 0
|
1941
|
+
@cursor_max -= cursor_diff.abs
|
1942
|
+
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
1943
|
+
@config.editing_mode = :vi_insert
|
1944
|
+
}
|
1743
1945
|
end
|
1744
1946
|
|
1745
1947
|
private def vi_delete_meta(key)
|
@@ -1759,18 +1961,6 @@ class Reline::LineEditor
|
|
1759
1961
|
private def vi_yank(key)
|
1760
1962
|
end
|
1761
1963
|
|
1762
|
-
private def vi_end_of_transmission(key)
|
1763
|
-
if @line.empty?
|
1764
|
-
@line = nil
|
1765
|
-
if @buffer_of_lines.size > 1
|
1766
|
-
scroll_down(@highest_in_all - @first_line_started_from)
|
1767
|
-
end
|
1768
|
-
Reline::IOGate.move_cursor_column(0)
|
1769
|
-
@eof = true
|
1770
|
-
finish
|
1771
|
-
end
|
1772
|
-
end
|
1773
|
-
|
1774
1964
|
private def vi_list_or_eof(key)
|
1775
1965
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
1776
1966
|
@line = nil
|
@@ -1781,9 +1971,11 @@ class Reline::LineEditor
|
|
1781
1971
|
@eof = true
|
1782
1972
|
finish
|
1783
1973
|
else
|
1784
|
-
|
1974
|
+
ed_newline(key)
|
1785
1975
|
end
|
1786
1976
|
end
|
1977
|
+
alias_method :vi_end_of_transmission, :vi_list_or_eof
|
1978
|
+
alias_method :vi_eof_maybe, :vi_list_or_eof
|
1787
1979
|
|
1788
1980
|
private def ed_delete_next_char(key, arg: 1)
|
1789
1981
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
@@ -1915,12 +2107,17 @@ class Reline::LineEditor
|
|
1915
2107
|
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) }
|
1916
2108
|
end
|
1917
2109
|
|
1918
|
-
private def
|
2110
|
+
private def vi_to_next_char(key, arg: 1)
|
2111
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, true) }
|
2112
|
+
end
|
2113
|
+
|
2114
|
+
private def search_next_char(key, arg, need_prev_char = false)
|
1919
2115
|
if key.instance_of?(String)
|
1920
2116
|
inputed_char = key
|
1921
2117
|
else
|
1922
2118
|
inputed_char = key.chr
|
1923
2119
|
end
|
2120
|
+
prev_total = nil
|
1924
2121
|
total = nil
|
1925
2122
|
found = false
|
1926
2123
|
@line.byteslice(@byte_pointer..-1).grapheme_clusters.each do |mbchar|
|
@@ -1938,13 +2135,66 @@ class Reline::LineEditor
|
|
1938
2135
|
end
|
1939
2136
|
end
|
1940
2137
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2138
|
+
prev_total = total
|
1941
2139
|
total = [total.first + mbchar.bytesize, total.last + width]
|
1942
2140
|
end
|
1943
2141
|
end
|
1944
|
-
if found and total
|
2142
|
+
if not need_prev_char and found and total
|
1945
2143
|
byte_size, width = total
|
1946
2144
|
@byte_pointer += byte_size
|
1947
2145
|
@cursor += width
|
2146
|
+
elsif need_prev_char and found and prev_total
|
2147
|
+
byte_size, width = prev_total
|
2148
|
+
@byte_pointer += byte_size
|
2149
|
+
@cursor += width
|
2150
|
+
end
|
2151
|
+
@waiting_proc = nil
|
2152
|
+
end
|
2153
|
+
|
2154
|
+
private def vi_prev_char(key, arg: 1)
|
2155
|
+
@waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg) }
|
2156
|
+
end
|
2157
|
+
|
2158
|
+
private def vi_to_prev_char(key, arg: 1)
|
2159
|
+
@waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg, true) }
|
2160
|
+
end
|
2161
|
+
|
2162
|
+
private def search_prev_char(key, arg, need_next_char = false)
|
2163
|
+
if key.instance_of?(String)
|
2164
|
+
inputed_char = key
|
2165
|
+
else
|
2166
|
+
inputed_char = key.chr
|
2167
|
+
end
|
2168
|
+
prev_total = nil
|
2169
|
+
total = nil
|
2170
|
+
found = false
|
2171
|
+
@line.byteslice(0..@byte_pointer).grapheme_clusters.reverse_each do |mbchar|
|
2172
|
+
# total has [byte_size, cursor]
|
2173
|
+
unless total
|
2174
|
+
# skip cursor point
|
2175
|
+
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2176
|
+
total = [mbchar.bytesize, width]
|
2177
|
+
else
|
2178
|
+
if inputed_char == mbchar
|
2179
|
+
arg -= 1
|
2180
|
+
if arg.zero?
|
2181
|
+
found = true
|
2182
|
+
break
|
2183
|
+
end
|
2184
|
+
end
|
2185
|
+
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2186
|
+
prev_total = total
|
2187
|
+
total = [total.first + mbchar.bytesize, total.last + width]
|
2188
|
+
end
|
2189
|
+
end
|
2190
|
+
if not need_next_char and found and total
|
2191
|
+
byte_size, width = total
|
2192
|
+
@byte_pointer -= byte_size
|
2193
|
+
@cursor -= width
|
2194
|
+
elsif need_next_char and found and prev_total
|
2195
|
+
byte_size, width = prev_total
|
2196
|
+
@byte_pointer -= byte_size
|
2197
|
+
@cursor -= width
|
1948
2198
|
end
|
1949
2199
|
@waiting_proc = nil
|
1950
2200
|
end
|