reline 0.1.3 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,7 +17,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
17
17
  # 7 ^G
18
18
  :ed_unassigned,
19
19
  # 8 ^H
20
- :ed_delete_prev_char,
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
- :ed_search_prev_history,
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
- :ed_search_prev_history,
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
- :ed_delete_prev_char,
258
+ :ed_unassigned,
259
259
  # 128 M-^@
260
260
  :ed_unassigned,
261
261
  # 129 M-^A
@@ -37,9 +37,9 @@ class Reline::KeyActor::ViInsert < Reline::KeyActor::Base
37
37
  # 17 ^Q
38
38
  :ed_ignore,
39
39
  # 18 ^R
40
- :ed_search_prev_history,
40
+ :vi_search_prev,
41
41
  # 19 ^S
42
- :ed_search_next_history,
42
+ :vi_search_next,
43
43
  # 20 ^T
44
44
  :ed_insert,
45
45
  # 21 ^U
@@ -42,6 +42,8 @@ class Reline::KeyStroke
42
42
  expand(expand(rhs_bytes) + expand(input.drop(lhs.size)))
43
43
  when Symbol
44
44
  [rhs] + expand(input.drop(lhs.size))
45
+ when Array
46
+ rhs
45
47
  end
46
48
  end
47
49
 
@@ -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, prompt_list)
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(prompt, str, max_width)
215
- lines = [String.new(encoding: @encoding)]
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(prompt, line_to_render.nil? ? '' : line_to_render, @screen_size.last)
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 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.
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
- move_cursor_up(height - 1)
543
+ # Just after rendring, so the cursor is on the last line.
533
544
  if finished?
534
- move_cursor_down(@started_from)
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: true)
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
- @waiting_operator_proc.(cursor_diff, byte_pointer_diff)
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.length != 1
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.(key, arg: @vi_arg)
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&.(key)
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.(key, arg: @vi_arg)
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.(key)
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.(key)
798
+ wrap_method_call(method_symbol, method_obj, key)
759
799
  else
760
- run_for_operators(key, method_symbol) do
761
- method_obj.(key)
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
- if allow_escape_code
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
- width = Reline::Unicode.get_mbchar_width(key)
1112
- if @cursor == @cursor_max
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
- if @cursor == @cursor_max
1127
- @line += key.chr
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 # TODO
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 search_history(key)
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 ed_search_prev_history(key)
1415
- search_history(key)
1455
+ private def vi_search_prev(key)
1456
+ incremental_search_history(key)
1416
1457
  end
1417
- alias_method :reverse_search_history, :ed_search_prev_history
1458
+ alias_method :reverse_search_history, :vi_search_prev
1418
1459
 
1419
- private def ed_search_next_history(key)
1420
- search_history(key)
1460
+ private def vi_search_next(key)
1461
+ incremental_search_history(key)
1421
1462
  end
1422
- alias_method :forward_search_history, :ed_search_next_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 :backward_char, :ed_prev_char
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 = Pathname.new(path).read
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 = false)
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