reline 0.1.3 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/lib/reline.rb +27 -11
- data/lib/reline/ansi.rb +48 -7
- data/lib/reline/config.rb +65 -8
- data/lib/reline/general_io.rb +18 -0
- data/lib/reline/history.rb +31 -11
- data/lib/reline/key_actor/emacs.rb +6 -6
- data/lib/reline/key_actor/vi_command.rb +4 -4
- data/lib/reline/key_actor/vi_insert.rb +2 -2
- data/lib/reline/key_stroke.rb +2 -0
- data/lib/reline/line_editor.rb +309 -133
- data/lib/reline/sibori.rb +170 -0
- data/lib/reline/unicode.rb +93 -15
- data/lib/reline/unicode/east_asian_width.rb +1149 -1130
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +26 -3
- metadata +22 -7
@@ -17,7 +17,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
|
|
17
17
|
# 7 ^G
|
18
18
|
:ed_unassigned,
|
19
19
|
# 8 ^H
|
20
|
-
:
|
20
|
+
:ed_unassigned,
|
21
21
|
# 9 ^I
|
22
22
|
:ed_unassigned,
|
23
23
|
# 10 ^J
|
@@ -37,7 +37,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
|
|
37
37
|
# 17 ^Q
|
38
38
|
:ed_ignore,
|
39
39
|
# 18 ^R
|
40
|
-
:
|
40
|
+
:vi_search_prev,
|
41
41
|
# 19 ^S
|
42
42
|
:ed_ignore,
|
43
43
|
# 20 ^T
|
@@ -151,7 +151,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
|
|
151
151
|
# 74 J
|
152
152
|
:vi_join_lines,
|
153
153
|
# 75 K
|
154
|
-
:
|
154
|
+
:vi_search_prev,
|
155
155
|
# 76 L
|
156
156
|
:ed_unassigned,
|
157
157
|
# 77 M
|
@@ -255,7 +255,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
|
|
255
255
|
# 126 ~
|
256
256
|
:vi_change_case,
|
257
257
|
# 127 ^?
|
258
|
-
:
|
258
|
+
:ed_unassigned,
|
259
259
|
# 128 M-^@
|
260
260
|
:ed_unassigned,
|
261
261
|
# 129 M-^A
|
data/lib/reline/key_stroke.rb
CHANGED
data/lib/reline/line_editor.rb
CHANGED
@@ -2,7 +2,6 @@ require 'reline/kill_ring'
|
|
2
2
|
require 'reline/unicode'
|
3
3
|
|
4
4
|
require 'tempfile'
|
5
|
-
require 'pathname'
|
6
5
|
|
7
6
|
class Reline::LineEditor
|
8
7
|
# TODO: undo
|
@@ -51,18 +50,20 @@ class Reline::LineEditor
|
|
51
50
|
CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
|
52
51
|
MenuInfo = Struct.new('MenuInfo', :target, :list)
|
53
52
|
|
54
|
-
CSI_REGEXP = /\e\[[\d;]*[ABCDEFGHJKSTfminsuhl]/
|
55
|
-
OSC_REGEXP = /\e\]\d+(?:;[^;]+)*\a/
|
56
|
-
NON_PRINTING_START = "\1"
|
57
|
-
NON_PRINTING_END = "\2"
|
58
|
-
WIDTH_SCANNER = /\G(?:#{NON_PRINTING_START}|#{NON_PRINTING_END}|#{CSI_REGEXP}|#{OSC_REGEXP}|\X)/
|
59
|
-
|
60
53
|
def initialize(config, encoding)
|
61
54
|
@config = config
|
62
55
|
@completion_append_character = ''
|
63
56
|
reset_variables(encoding: encoding)
|
64
57
|
end
|
65
58
|
|
59
|
+
def simplified_rendering?
|
60
|
+
if finished?
|
61
|
+
false
|
62
|
+
else
|
63
|
+
not @rerender_all and not finished? and Reline::IOGate.in_pasting?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
66
67
|
private def check_multiline_prompt(buffer, prompt)
|
67
68
|
if @vi_arg
|
68
69
|
prompt = "(arg: #{@vi_arg}) "
|
@@ -73,14 +74,39 @@ class Reline::LineEditor
|
|
73
74
|
else
|
74
75
|
prompt = @prompt
|
75
76
|
end
|
77
|
+
return [prompt, calculate_width(prompt, true), [prompt] * buffer.size] if simplified_rendering?
|
76
78
|
if @prompt_proc
|
77
79
|
prompt_list = @prompt_proc.(buffer)
|
78
80
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
81
|
+
if @config.show_mode_in_prompt
|
82
|
+
if @config.editing_mode_is?(:vi_command)
|
83
|
+
mode_icon = @config.vi_cmd_mode_icon
|
84
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
85
|
+
mode_icon = @config.vi_ins_mode_icon
|
86
|
+
elsif @config.editing_mode_is?(:emacs)
|
87
|
+
mode_icon = @config.emacs_mode_string
|
88
|
+
else
|
89
|
+
mode_icon = '?'
|
90
|
+
end
|
91
|
+
prompt_list.map!{ |pr| mode_icon + pr }
|
92
|
+
end
|
79
93
|
prompt = prompt_list[@line_index]
|
80
94
|
prompt_width = calculate_width(prompt, true)
|
81
95
|
[prompt, prompt_width, prompt_list]
|
82
96
|
else
|
83
97
|
prompt_width = calculate_width(prompt, true)
|
98
|
+
if @config.show_mode_in_prompt
|
99
|
+
if @config.editing_mode_is?(:vi_command)
|
100
|
+
mode_icon = @config.vi_cmd_mode_icon
|
101
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
102
|
+
mode_icon = @config.vi_ins_mode_icon
|
103
|
+
elsif @config.editing_mode_is?(:emacs)
|
104
|
+
mode_icon = @config.emacs_mode_string
|
105
|
+
else
|
106
|
+
mode_icon = '?'
|
107
|
+
end
|
108
|
+
prompt = mode_icon + prompt
|
109
|
+
end
|
84
110
|
[prompt, prompt_width, nil]
|
85
111
|
end
|
86
112
|
end
|
@@ -116,7 +142,7 @@ class Reline::LineEditor
|
|
116
142
|
if @line_index.zero?
|
117
143
|
0
|
118
144
|
else
|
119
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
145
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
120
146
|
end
|
121
147
|
if @prompt_proc
|
122
148
|
prompt = prompt_list[@line_index]
|
@@ -161,6 +187,7 @@ class Reline::LineEditor
|
|
161
187
|
@searching_prompt = nil
|
162
188
|
@first_char = true
|
163
189
|
@eof = false
|
190
|
+
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
164
191
|
reset_line
|
165
192
|
end
|
166
193
|
|
@@ -190,10 +217,10 @@ class Reline::LineEditor
|
|
190
217
|
@is_multiline = false
|
191
218
|
end
|
192
219
|
|
193
|
-
private def calculate_height_by_lines(lines,
|
220
|
+
private def calculate_height_by_lines(lines, prompt)
|
194
221
|
result = 0
|
222
|
+
prompt_list = prompt.is_a?(Array) ? prompt : nil
|
195
223
|
lines.each_with_index { |line, i|
|
196
|
-
prompt = ''
|
197
224
|
prompt = prompt_list[i] if prompt_list and prompt_list[i]
|
198
225
|
result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
|
199
226
|
}
|
@@ -211,40 +238,8 @@ class Reline::LineEditor
|
|
211
238
|
width.div(@screen_size.last) + 1
|
212
239
|
end
|
213
240
|
|
214
|
-
private def split_by_width(
|
215
|
-
|
216
|
-
height = 1
|
217
|
-
width = 0
|
218
|
-
rest = "#{prompt}#{str}".encode(Encoding::UTF_8)
|
219
|
-
in_zero_width = false
|
220
|
-
rest.scan(WIDTH_SCANNER) do |gc|
|
221
|
-
case gc
|
222
|
-
when NON_PRINTING_START
|
223
|
-
in_zero_width = true
|
224
|
-
when NON_PRINTING_END
|
225
|
-
in_zero_width = false
|
226
|
-
when CSI_REGEXP, OSC_REGEXP
|
227
|
-
lines.last << gc
|
228
|
-
else
|
229
|
-
unless in_zero_width
|
230
|
-
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
|
231
|
-
if (width += mbchar_width) > max_width
|
232
|
-
width = mbchar_width
|
233
|
-
lines << nil
|
234
|
-
lines << String.new(encoding: @encoding)
|
235
|
-
height += 1
|
236
|
-
end
|
237
|
-
end
|
238
|
-
lines.last << gc
|
239
|
-
end
|
240
|
-
end
|
241
|
-
# The cursor moves to next line in first
|
242
|
-
if width == max_width
|
243
|
-
lines << nil
|
244
|
-
lines << String.new(encoding: @encoding)
|
245
|
-
height += 1
|
246
|
-
end
|
247
|
-
[lines, height]
|
241
|
+
private def split_by_width(str, max_width)
|
242
|
+
Reline::Unicode.split_by_width(str, max_width, @encoding)
|
248
243
|
end
|
249
244
|
|
250
245
|
private def scroll_down(val)
|
@@ -312,6 +307,11 @@ class Reline::LineEditor
|
|
312
307
|
@byte_pointer = new_byte_pointer
|
313
308
|
end
|
314
309
|
|
310
|
+
def rerender_all
|
311
|
+
@rerender_all = true
|
312
|
+
rerender
|
313
|
+
end
|
314
|
+
|
315
315
|
def rerender
|
316
316
|
return if @line.nil?
|
317
317
|
if @menu_info
|
@@ -358,7 +358,7 @@ class Reline::LineEditor
|
|
358
358
|
new_lines = whole_lines
|
359
359
|
end
|
360
360
|
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
361
|
-
all_height = calculate_height_by_lines(new_lines, prompt_list)
|
361
|
+
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
362
362
|
diff = all_height - @highest_in_all
|
363
363
|
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
364
364
|
if diff > 0
|
@@ -398,7 +398,7 @@ class Reline::LineEditor
|
|
398
398
|
if @line_index.zero?
|
399
399
|
0
|
400
400
|
else
|
401
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
401
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
402
402
|
end
|
403
403
|
if @prompt_proc
|
404
404
|
prompt = prompt_list[@line_index]
|
@@ -457,7 +457,7 @@ class Reline::LineEditor
|
|
457
457
|
if @line_index.zero?
|
458
458
|
0
|
459
459
|
else
|
460
|
-
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list)
|
460
|
+
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
461
461
|
end
|
462
462
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
463
463
|
move_cursor_down(@first_line_started_from + @started_from)
|
@@ -488,7 +488,7 @@ class Reline::LineEditor
|
|
488
488
|
end
|
489
489
|
|
490
490
|
private def render_partial(prompt, prompt_width, line_to_render, with_control = true)
|
491
|
-
visual_lines, height = split_by_width(
|
491
|
+
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
|
492
492
|
if with_control
|
493
493
|
if height > @highest_in_this
|
494
494
|
diff = height - @highest_in_this
|
@@ -507,8 +507,18 @@ class Reline::LineEditor
|
|
507
507
|
Reline::IOGate.move_cursor_column(0)
|
508
508
|
visual_lines.each_with_index do |line, index|
|
509
509
|
if line.nil?
|
510
|
-
if
|
511
|
-
#
|
510
|
+
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
511
|
+
# reaches the end of line
|
512
|
+
if Reline::IOGate.win?
|
513
|
+
# A newline is automatically inserted if a character is rendered at
|
514
|
+
# eol on command prompt.
|
515
|
+
else
|
516
|
+
# When the cursor is at the end of the line and erases characters
|
517
|
+
# after the cursor, some terminals delete the character at the
|
518
|
+
# cursor position.
|
519
|
+
move_cursor_down(1)
|
520
|
+
Reline::IOGate.move_cursor_column(0)
|
521
|
+
end
|
512
522
|
else
|
513
523
|
Reline::IOGate.erase_after_cursor
|
514
524
|
move_cursor_down(1)
|
@@ -528,22 +538,25 @@ class Reline::LineEditor
|
|
528
538
|
end
|
529
539
|
end
|
530
540
|
Reline::IOGate.erase_after_cursor
|
541
|
+
Reline::IOGate.move_cursor_column(0)
|
531
542
|
if with_control
|
532
|
-
|
543
|
+
# Just after rendring, so the cursor is on the last line.
|
533
544
|
if finished?
|
534
|
-
|
545
|
+
Reline::IOGate.move_cursor_column(0)
|
546
|
+
else
|
547
|
+
# Moves up from bottom of lines to the cursor position.
|
548
|
+
move_cursor_up(height - 1 - @started_from)
|
549
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
535
550
|
end
|
536
|
-
move_cursor_down(@started_from)
|
537
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
538
551
|
end
|
539
552
|
height
|
540
553
|
end
|
541
554
|
|
542
555
|
private def modify_lines(before)
|
543
|
-
return before if before.nil? || before.empty?
|
556
|
+
return before if before.nil? || before.empty? || simplified_rendering?
|
544
557
|
|
545
558
|
if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?)
|
546
|
-
after.lines(chomp
|
559
|
+
after.lines("\n").map { |l| l.chomp('') }
|
547
560
|
else
|
548
561
|
before
|
549
562
|
end
|
@@ -568,7 +581,7 @@ class Reline::LineEditor
|
|
568
581
|
else
|
569
582
|
i&.start_with?(target)
|
570
583
|
end
|
571
|
-
}
|
584
|
+
}.uniq
|
572
585
|
if is_menu
|
573
586
|
menu(target, list)
|
574
587
|
return nil
|
@@ -685,7 +698,7 @@ class Reline::LineEditor
|
|
685
698
|
if @waiting_operator_proc
|
686
699
|
if VI_MOTIONS.include?(method_symbol)
|
687
700
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
688
|
-
block.()
|
701
|
+
block.(true)
|
689
702
|
unless @waiting_proc
|
690
703
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
691
704
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
@@ -693,27 +706,54 @@ class Reline::LineEditor
|
|
693
706
|
else
|
694
707
|
old_waiting_proc = @waiting_proc
|
695
708
|
old_waiting_operator_proc = @waiting_operator_proc
|
709
|
+
current_waiting_operator_proc = @waiting_operator_proc
|
696
710
|
@waiting_proc = proc { |k|
|
697
711
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
698
712
|
old_waiting_proc.(k)
|
699
713
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
700
714
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
701
|
-
|
715
|
+
current_waiting_operator_proc.(cursor_diff, byte_pointer_diff)
|
702
716
|
@waiting_operator_proc = old_waiting_operator_proc
|
703
717
|
}
|
704
718
|
end
|
705
719
|
else
|
706
720
|
# Ignores operator when not motion is given.
|
707
|
-
block.()
|
721
|
+
block.(false)
|
708
722
|
end
|
709
723
|
@waiting_operator_proc = nil
|
710
724
|
else
|
711
|
-
block.()
|
725
|
+
block.(false)
|
712
726
|
end
|
713
727
|
end
|
714
728
|
|
715
729
|
private def argumentable?(method_obj)
|
716
|
-
method_obj and method_obj.parameters.
|
730
|
+
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :arg }
|
731
|
+
end
|
732
|
+
|
733
|
+
private def inclusive?(method_obj)
|
734
|
+
# If a motion method with the keyword argument "inclusive" follows the
|
735
|
+
# operator, it must contain the character at the cursor position.
|
736
|
+
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :inclusive }
|
737
|
+
end
|
738
|
+
|
739
|
+
def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
|
740
|
+
if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
|
741
|
+
not_insertion = method_symbol != :ed_insert
|
742
|
+
process_insert(force: not_insertion)
|
743
|
+
end
|
744
|
+
if @vi_arg and argumentable?(method_obj)
|
745
|
+
if with_operator and inclusive?(method_obj)
|
746
|
+
method_obj.(key, arg: @vi_arg, inclusive: true)
|
747
|
+
else
|
748
|
+
method_obj.(key, arg: @vi_arg)
|
749
|
+
end
|
750
|
+
else
|
751
|
+
if with_operator and inclusive?(method_obj)
|
752
|
+
method_obj.(key, inclusive: true)
|
753
|
+
else
|
754
|
+
method_obj.(key)
|
755
|
+
end
|
756
|
+
end
|
717
757
|
end
|
718
758
|
|
719
759
|
private def process_key(key, method_symbol)
|
@@ -724,11 +764,11 @@ class Reline::LineEditor
|
|
724
764
|
end
|
725
765
|
if method_symbol and key.is_a?(Symbol)
|
726
766
|
if @vi_arg and argumentable?(method_obj)
|
727
|
-
run_for_operators(key, method_symbol) do
|
728
|
-
method_obj
|
767
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
768
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
729
769
|
end
|
730
770
|
else
|
731
|
-
method_obj
|
771
|
+
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
732
772
|
end
|
733
773
|
@kill_ring.process
|
734
774
|
@vi_arg = nil
|
@@ -737,15 +777,15 @@ class Reline::LineEditor
|
|
737
777
|
ed_argument_digit(key)
|
738
778
|
else
|
739
779
|
if argumentable?(method_obj)
|
740
|
-
run_for_operators(key, method_symbol) do
|
741
|
-
method_obj
|
780
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
781
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
742
782
|
end
|
743
783
|
elsif @waiting_proc
|
744
784
|
@waiting_proc.(key)
|
745
785
|
elsif method_obj
|
746
|
-
method_obj
|
786
|
+
wrap_method_call(method_symbol, method_obj, key)
|
747
787
|
else
|
748
|
-
ed_insert(key)
|
788
|
+
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
749
789
|
end
|
750
790
|
@kill_ring.process
|
751
791
|
@vi_arg = nil
|
@@ -755,15 +795,15 @@ class Reline::LineEditor
|
|
755
795
|
@kill_ring.process
|
756
796
|
elsif method_obj
|
757
797
|
if method_symbol == :ed_argument_digit
|
758
|
-
method_obj
|
798
|
+
wrap_method_call(method_symbol, method_obj, key)
|
759
799
|
else
|
760
|
-
run_for_operators(key, method_symbol) do
|
761
|
-
method_obj
|
800
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
801
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
762
802
|
end
|
763
803
|
end
|
764
804
|
@kill_ring.process
|
765
805
|
else
|
766
|
-
ed_insert(key)
|
806
|
+
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
767
807
|
end
|
768
808
|
end
|
769
809
|
|
@@ -820,6 +860,7 @@ class Reline::LineEditor
|
|
820
860
|
result = call_completion_proc
|
821
861
|
if result.is_a?(Array)
|
822
862
|
completion_occurs = true
|
863
|
+
process_insert
|
823
864
|
complete(result)
|
824
865
|
end
|
825
866
|
end
|
@@ -828,6 +869,7 @@ class Reline::LineEditor
|
|
828
869
|
result = call_completion_proc
|
829
870
|
if result.is_a?(Array)
|
830
871
|
completion_occurs = true
|
872
|
+
process_insert
|
831
873
|
move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
|
832
874
|
end
|
833
875
|
end
|
@@ -839,7 +881,7 @@ class Reline::LineEditor
|
|
839
881
|
unless completion_occurs
|
840
882
|
@completion_state = CompletionState::NORMAL
|
841
883
|
end
|
842
|
-
if @is_multiline and @auto_indent_proc
|
884
|
+
if @is_multiline and @auto_indent_proc and not simplified_rendering?
|
843
885
|
process_auto_indent
|
844
886
|
end
|
845
887
|
end
|
@@ -1041,6 +1083,7 @@ class Reline::LineEditor
|
|
1041
1083
|
|
1042
1084
|
def finish
|
1043
1085
|
@finished = true
|
1086
|
+
@rerender_all = true
|
1044
1087
|
@config.reset
|
1045
1088
|
end
|
1046
1089
|
|
@@ -1058,29 +1101,7 @@ class Reline::LineEditor
|
|
1058
1101
|
end
|
1059
1102
|
|
1060
1103
|
private def calculate_width(str, allow_escape_code = false)
|
1061
|
-
|
1062
|
-
width = 0
|
1063
|
-
rest = str.encode(Encoding::UTF_8)
|
1064
|
-
in_zero_width = false
|
1065
|
-
rest.scan(WIDTH_SCANNER) do |gc|
|
1066
|
-
case gc
|
1067
|
-
when NON_PRINTING_START
|
1068
|
-
in_zero_width = true
|
1069
|
-
when NON_PRINTING_END
|
1070
|
-
in_zero_width = false
|
1071
|
-
when CSI_REGEXP, OSC_REGEXP
|
1072
|
-
else
|
1073
|
-
unless in_zero_width
|
1074
|
-
width += Reline::Unicode.get_mbchar_width(gc)
|
1075
|
-
end
|
1076
|
-
end
|
1077
|
-
end
|
1078
|
-
width
|
1079
|
-
else
|
1080
|
-
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
|
1081
|
-
w + Reline::Unicode.get_mbchar_width(gc)
|
1082
|
-
}
|
1083
|
-
end
|
1104
|
+
Reline::Unicode.calculate_width(str, allow_escape_code)
|
1084
1105
|
end
|
1085
1106
|
|
1086
1107
|
private def key_delete(key)
|
@@ -1101,38 +1122,57 @@ class Reline::LineEditor
|
|
1101
1122
|
|
1102
1123
|
private def ed_unassigned(key) end # do nothing
|
1103
1124
|
|
1125
|
+
private def process_insert(force: false)
|
1126
|
+
return if @continuous_insertion_buffer.empty? or (Reline::IOGate.in_pasting? and not force)
|
1127
|
+
width = Reline::Unicode.calculate_width(@continuous_insertion_buffer)
|
1128
|
+
bytesize = @continuous_insertion_buffer.bytesize
|
1129
|
+
if @cursor == @cursor_max
|
1130
|
+
@line += @continuous_insertion_buffer
|
1131
|
+
else
|
1132
|
+
@line = byteinsert(@line, @byte_pointer, @continuous_insertion_buffer)
|
1133
|
+
end
|
1134
|
+
@byte_pointer += bytesize
|
1135
|
+
@cursor += width
|
1136
|
+
@cursor_max += width
|
1137
|
+
@continuous_insertion_buffer.clear
|
1138
|
+
end
|
1139
|
+
|
1104
1140
|
private def ed_insert(key)
|
1141
|
+
str = nil
|
1142
|
+
width = nil
|
1143
|
+
bytesize = nil
|
1105
1144
|
if key.instance_of?(String)
|
1106
1145
|
begin
|
1107
1146
|
key.encode(Encoding::UTF_8)
|
1108
1147
|
rescue Encoding::UndefinedConversionError
|
1109
1148
|
return
|
1110
1149
|
end
|
1111
|
-
|
1112
|
-
|
1113
|
-
@line += key
|
1114
|
-
else
|
1115
|
-
@line = byteinsert(@line, @byte_pointer, key)
|
1116
|
-
end
|
1117
|
-
@byte_pointer += key.bytesize
|
1118
|
-
@cursor += width
|
1119
|
-
@cursor_max += width
|
1150
|
+
str = key
|
1151
|
+
bytesize = key.bytesize
|
1120
1152
|
else
|
1121
1153
|
begin
|
1122
1154
|
key.chr.encode(Encoding::UTF_8)
|
1123
1155
|
rescue Encoding::UndefinedConversionError
|
1124
1156
|
return
|
1125
1157
|
end
|
1126
|
-
|
1127
|
-
|
1128
|
-
else
|
1129
|
-
@line = byteinsert(@line, @byte_pointer, key.chr)
|
1130
|
-
end
|
1131
|
-
width = Reline::Unicode.get_mbchar_width(key.chr)
|
1132
|
-
@byte_pointer += 1
|
1133
|
-
@cursor += width
|
1134
|
-
@cursor_max += width
|
1158
|
+
str = key.chr
|
1159
|
+
bytesize = 1
|
1135
1160
|
end
|
1161
|
+
if Reline::IOGate.in_pasting?
|
1162
|
+
@continuous_insertion_buffer << str
|
1163
|
+
return
|
1164
|
+
elsif not @continuous_insertion_buffer.empty?
|
1165
|
+
process_insert
|
1166
|
+
end
|
1167
|
+
width = Reline::Unicode.get_mbchar_width(str)
|
1168
|
+
if @cursor == @cursor_max
|
1169
|
+
@line += str
|
1170
|
+
else
|
1171
|
+
@line = byteinsert(@line, @byte_pointer, str)
|
1172
|
+
end
|
1173
|
+
@byte_pointer += bytesize
|
1174
|
+
@cursor += width
|
1175
|
+
@cursor_max += width
|
1136
1176
|
end
|
1137
1177
|
alias_method :ed_digit, :ed_insert
|
1138
1178
|
alias_method :self_insert, :ed_insert
|
@@ -1189,6 +1229,7 @@ class Reline::LineEditor
|
|
1189
1229
|
arg -= 1
|
1190
1230
|
ed_prev_char(key, arg: arg) if arg > 0
|
1191
1231
|
end
|
1232
|
+
alias_method :backward_char, :ed_prev_char
|
1192
1233
|
|
1193
1234
|
private def vi_first_print(key)
|
1194
1235
|
@byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line)
|
@@ -1258,7 +1299,7 @@ class Reline::LineEditor
|
|
1258
1299
|
if search_word.empty? and Reline.last_incremental_search
|
1259
1300
|
search_word = Reline.last_incremental_search
|
1260
1301
|
end
|
1261
|
-
if @history_pointer
|
1302
|
+
if @history_pointer
|
1262
1303
|
case prev_search_key
|
1263
1304
|
when "\C-r".ord
|
1264
1305
|
history_pointer_base = 0
|
@@ -1330,7 +1371,7 @@ class Reline::LineEditor
|
|
1330
1371
|
end
|
1331
1372
|
end
|
1332
1373
|
|
1333
|
-
private def
|
1374
|
+
private def incremental_search_history(key)
|
1334
1375
|
unless @history_pointer
|
1335
1376
|
if @is_multiline
|
1336
1377
|
@line_backup_in_history = whole_buffer
|
@@ -1411,15 +1452,114 @@ class Reline::LineEditor
|
|
1411
1452
|
}
|
1412
1453
|
end
|
1413
1454
|
|
1414
|
-
private def
|
1415
|
-
|
1455
|
+
private def vi_search_prev(key)
|
1456
|
+
incremental_search_history(key)
|
1416
1457
|
end
|
1417
|
-
alias_method :reverse_search_history, :
|
1458
|
+
alias_method :reverse_search_history, :vi_search_prev
|
1418
1459
|
|
1419
|
-
private def
|
1420
|
-
|
1460
|
+
private def vi_search_next(key)
|
1461
|
+
incremental_search_history(key)
|
1421
1462
|
end
|
1422
|
-
alias_method :forward_search_history, :
|
1463
|
+
alias_method :forward_search_history, :vi_search_next
|
1464
|
+
|
1465
|
+
private def ed_search_prev_history(key, arg: 1)
|
1466
|
+
history = nil
|
1467
|
+
h_pointer = nil
|
1468
|
+
line_no = nil
|
1469
|
+
substr = @line.slice(0, @byte_pointer)
|
1470
|
+
if @history_pointer.nil?
|
1471
|
+
return if not @line.empty? and substr.empty?
|
1472
|
+
history = Reline::HISTORY
|
1473
|
+
elsif @history_pointer.zero?
|
1474
|
+
history = nil
|
1475
|
+
h_pointer = nil
|
1476
|
+
else
|
1477
|
+
history = Reline::HISTORY.slice(0, @history_pointer)
|
1478
|
+
end
|
1479
|
+
return if history.nil?
|
1480
|
+
if @is_multiline
|
1481
|
+
h_pointer = history.rindex { |h|
|
1482
|
+
h.split("\n").each_with_index { |l, i|
|
1483
|
+
if l.start_with?(substr)
|
1484
|
+
line_no = i
|
1485
|
+
break
|
1486
|
+
end
|
1487
|
+
}
|
1488
|
+
not line_no.nil?
|
1489
|
+
}
|
1490
|
+
else
|
1491
|
+
h_pointer = history.rindex { |l|
|
1492
|
+
l.start_with?(substr)
|
1493
|
+
}
|
1494
|
+
end
|
1495
|
+
return if h_pointer.nil?
|
1496
|
+
@history_pointer = h_pointer
|
1497
|
+
if @is_multiline
|
1498
|
+
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1499
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1500
|
+
@line_index = line_no
|
1501
|
+
@line = @buffer_of_lines.last
|
1502
|
+
@rerender_all = true
|
1503
|
+
else
|
1504
|
+
@line = Reline::HISTORY[@history_pointer]
|
1505
|
+
end
|
1506
|
+
@cursor_max = calculate_width(@line)
|
1507
|
+
arg -= 1
|
1508
|
+
ed_search_prev_history(key, arg: arg) if arg > 0
|
1509
|
+
end
|
1510
|
+
alias_method :history_search_backward, :ed_search_prev_history
|
1511
|
+
|
1512
|
+
private def ed_search_next_history(key, arg: 1)
|
1513
|
+
substr = @line.slice(0, @byte_pointer)
|
1514
|
+
if @history_pointer.nil?
|
1515
|
+
return
|
1516
|
+
elsif @history_pointer == (Reline::HISTORY.size - 1) and not substr.empty?
|
1517
|
+
return
|
1518
|
+
end
|
1519
|
+
history = Reline::HISTORY.slice((@history_pointer + 1)..-1)
|
1520
|
+
h_pointer = nil
|
1521
|
+
line_no = nil
|
1522
|
+
if @is_multiline
|
1523
|
+
h_pointer = history.index { |h|
|
1524
|
+
h.split("\n").each_with_index { |l, i|
|
1525
|
+
if l.start_with?(substr)
|
1526
|
+
line_no = i
|
1527
|
+
break
|
1528
|
+
end
|
1529
|
+
}
|
1530
|
+
not line_no.nil?
|
1531
|
+
}
|
1532
|
+
else
|
1533
|
+
h_pointer = history.index { |l|
|
1534
|
+
l.start_with?(substr)
|
1535
|
+
}
|
1536
|
+
end
|
1537
|
+
h_pointer += @history_pointer + 1 if h_pointer and @history_pointer
|
1538
|
+
return if h_pointer.nil? and not substr.empty?
|
1539
|
+
@history_pointer = h_pointer
|
1540
|
+
if @is_multiline
|
1541
|
+
if @history_pointer.nil? and substr.empty?
|
1542
|
+
@buffer_of_lines = []
|
1543
|
+
@line_index = 0
|
1544
|
+
else
|
1545
|
+
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1546
|
+
@line_index = line_no
|
1547
|
+
end
|
1548
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1549
|
+
@line = @buffer_of_lines.last
|
1550
|
+
@rerender_all = true
|
1551
|
+
else
|
1552
|
+
if @history_pointer.nil? and substr.empty?
|
1553
|
+
@line = ''
|
1554
|
+
else
|
1555
|
+
@line = Reline::HISTORY[@history_pointer]
|
1556
|
+
end
|
1557
|
+
end
|
1558
|
+
@cursor_max = calculate_width(@line)
|
1559
|
+
arg -= 1
|
1560
|
+
ed_search_next_history(key, arg: arg) if arg > 0
|
1561
|
+
end
|
1562
|
+
alias_method :history_search_forward, :ed_search_next_history
|
1423
1563
|
|
1424
1564
|
private def ed_prev_history(key, arg: 1)
|
1425
1565
|
if @is_multiline and @line_index > 0
|
@@ -1519,6 +1659,7 @@ class Reline::LineEditor
|
|
1519
1659
|
end
|
1520
1660
|
|
1521
1661
|
private def ed_newline(key)
|
1662
|
+
process_insert(force: true)
|
1522
1663
|
if @is_multiline
|
1523
1664
|
if @config.editing_mode_is?(:vi_command)
|
1524
1665
|
if @line_index < (@buffer_of_lines.size - 1)
|
@@ -1816,7 +1957,7 @@ class Reline::LineEditor
|
|
1816
1957
|
ed_prev_char(key)
|
1817
1958
|
@config.editing_mode = :vi_command
|
1818
1959
|
end
|
1819
|
-
alias_method :
|
1960
|
+
alias_method :vi_movement_mode, :vi_command_mode
|
1820
1961
|
|
1821
1962
|
private def vi_next_word(key, arg: 1)
|
1822
1963
|
if @line.bytesize > @byte_pointer
|
@@ -1838,13 +1979,22 @@ class Reline::LineEditor
|
|
1838
1979
|
vi_prev_word(key, arg: arg) if arg > 0
|
1839
1980
|
end
|
1840
1981
|
|
1841
|
-
private def vi_end_word(key, arg: 1)
|
1982
|
+
private def vi_end_word(key, arg: 1, inclusive: false)
|
1842
1983
|
if @line.bytesize > @byte_pointer
|
1843
1984
|
byte_size, width = Reline::Unicode.vi_forward_end_word(@line, @byte_pointer)
|
1844
1985
|
@byte_pointer += byte_size
|
1845
1986
|
@cursor += width
|
1846
1987
|
end
|
1847
1988
|
arg -= 1
|
1989
|
+
if inclusive and arg.zero?
|
1990
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
1991
|
+
if byte_size > 0
|
1992
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
1993
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
1994
|
+
@byte_pointer += byte_size
|
1995
|
+
@cursor += width
|
1996
|
+
end
|
1997
|
+
end
|
1848
1998
|
vi_end_word(key, arg: arg) if arg > 0
|
1849
1999
|
end
|
1850
2000
|
|
@@ -1868,13 +2018,22 @@ class Reline::LineEditor
|
|
1868
2018
|
vi_prev_big_word(key, arg: arg) if arg > 0
|
1869
2019
|
end
|
1870
2020
|
|
1871
|
-
private def vi_end_big_word(key, arg: 1)
|
2021
|
+
private def vi_end_big_word(key, arg: 1, inclusive: false)
|
1872
2022
|
if @line.bytesize > @byte_pointer
|
1873
2023
|
byte_size, width = Reline::Unicode.vi_big_forward_end_word(@line, @byte_pointer)
|
1874
2024
|
@byte_pointer += byte_size
|
1875
2025
|
@cursor += width
|
1876
2026
|
end
|
1877
2027
|
arg -= 1
|
2028
|
+
if inclusive and arg.zero?
|
2029
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2030
|
+
if byte_size > 0
|
2031
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2032
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2033
|
+
@byte_pointer += byte_size
|
2034
|
+
@cursor += width
|
2035
|
+
end
|
2036
|
+
end
|
1878
2037
|
vi_end_big_word(key, arg: arg) if arg > 0
|
1879
2038
|
end
|
1880
2039
|
|
@@ -1959,6 +2118,14 @@ class Reline::LineEditor
|
|
1959
2118
|
end
|
1960
2119
|
|
1961
2120
|
private def vi_yank(key)
|
2121
|
+
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2122
|
+
if byte_pointer_diff > 0
|
2123
|
+
cut = @line.byteslice(@byte_pointer, byte_pointer_diff)
|
2124
|
+
elsif byte_pointer_diff < 0
|
2125
|
+
cut = @line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2126
|
+
end
|
2127
|
+
copy_for_vi(cut)
|
2128
|
+
}
|
1962
2129
|
end
|
1963
2130
|
|
1964
2131
|
private def vi_list_or_eof(key)
|
@@ -2022,7 +2189,7 @@ class Reline::LineEditor
|
|
2022
2189
|
fp.path
|
2023
2190
|
}
|
2024
2191
|
system("#{ENV['EDITOR']} #{path}")
|
2025
|
-
@line =
|
2192
|
+
@line = File.read(path)
|
2026
2193
|
finish
|
2027
2194
|
end
|
2028
2195
|
|
@@ -2103,15 +2270,15 @@ class Reline::LineEditor
|
|
2103
2270
|
}
|
2104
2271
|
end
|
2105
2272
|
|
2106
|
-
private def vi_next_char(key, arg: 1)
|
2107
|
-
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) }
|
2273
|
+
private def vi_next_char(key, arg: 1, inclusive: false)
|
2274
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, inclusive: inclusive) }
|
2108
2275
|
end
|
2109
2276
|
|
2110
|
-
private def vi_to_next_char(key, arg: 1)
|
2111
|
-
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, true) }
|
2277
|
+
private def vi_to_next_char(key, arg: 1, inclusive: false)
|
2278
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, need_prev_char: true, inclusive: inclusive) }
|
2112
2279
|
end
|
2113
2280
|
|
2114
|
-
private def search_next_char(key, arg, need_prev_char
|
2281
|
+
private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
|
2115
2282
|
if key.instance_of?(String)
|
2116
2283
|
inputed_char = key
|
2117
2284
|
else
|
@@ -2148,6 +2315,15 @@ class Reline::LineEditor
|
|
2148
2315
|
@byte_pointer += byte_size
|
2149
2316
|
@cursor += width
|
2150
2317
|
end
|
2318
|
+
if inclusive
|
2319
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2320
|
+
if byte_size > 0
|
2321
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2322
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2323
|
+
@byte_pointer += byte_size
|
2324
|
+
@cursor += width
|
2325
|
+
end
|
2326
|
+
end
|
2151
2327
|
@waiting_proc = nil
|
2152
2328
|
end
|
2153
2329
|
|
@@ -2219,10 +2395,10 @@ class Reline::LineEditor
|
|
2219
2395
|
alias_method :set_mark, :em_set_mark
|
2220
2396
|
|
2221
2397
|
private def em_exchange_mark(key)
|
2398
|
+
return unless @mark_pointer
|
2222
2399
|
new_pointer = [@byte_pointer, @line_index]
|
2223
2400
|
@previous_line_index = @line_index
|
2224
2401
|
@byte_pointer, @line_index = @mark_pointer
|
2225
|
-
@byte_pointer, @line_index = @mark_pointer
|
2226
2402
|
@cursor = calculate_width(@line.byteslice(0, @byte_pointer))
|
2227
2403
|
@cursor_max = calculate_width(@line)
|
2228
2404
|
@mark_pointer = new_pointer
|