reline 0.1.7 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bba0b20f31c4777e7cdce2647964cc5837a1646a000019e0b376da8327b0babe
4
- data.tar.gz: 887ac3397cb9f1fcfaf60c61fc90ec31c397fc01516f343fff34bf813b2d8f7f
3
+ metadata.gz: 89f055454b502262f43495a2d8de12ba033259287a4c09e7c93afeb0462c9c33
4
+ data.tar.gz: 6c6f081e14d699faed9ac7ba08001dac978318535f58458fd2c777db56b57b50
5
5
  SHA512:
6
- metadata.gz: 5361850fc7402585b7bf9590299e5e218480a4ecac129b677bd69ae7bbcdde98d48b15d6f098f54fa7b50c5135b8d483085b77d0323c4032f29756b9b77626ea
7
- data.tar.gz: b9d33fe3736de66b49338b70dbde2efd49bae38146c4bd3330a5cce97cf230715cc0db27b1efce4e6cb6e005d14df6030cc74427b00e7ef9bcccf46f7b171b01
6
+ metadata.gz: 6bd17793312ff9f2996ce08ebada091005c7efd9ff1d0ef007ea982ee5e74ee03a83fb0bb7c975c6da747db712a3837d14c108fd7de087094f1db37e051876aa
7
+ data.tar.gz: 148dd6767b1aae9a295d334445957b64e6f6a46b8941a10517e5d4567a29eac369fe3843c6c8c1bb5fd66d1165c82988316b030eb05080517bb7105bc5174281
@@ -1,6 +1,10 @@
1
1
  require 'timeout'
2
2
 
3
3
  class Reline::GeneralIO
4
+ def self.reset
5
+ @@pasting = false
6
+ end
7
+
4
8
  def self.encoding
5
9
  RUBY_PLATFORM =~ /mswin|mingw/ ? Encoding::UTF_8 : Encoding::default_external
6
10
  end
@@ -67,8 +71,18 @@ class Reline::GeneralIO
67
71
  def self.set_winch_handler(&handler)
68
72
  end
69
73
 
74
+ @@pasting = false
75
+
70
76
  def self.in_pasting?
71
- false
77
+ @@pasting
78
+ end
79
+
80
+ def self.start_pasting
81
+ @@pasting = true
82
+ end
83
+
84
+ def self.finish_pasting
85
+ @@pasting = false
72
86
  end
73
87
 
74
88
  def self.prep
@@ -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
@@ -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
@@ -698,7 +698,7 @@ class Reline::LineEditor
698
698
  if @waiting_operator_proc
699
699
  if VI_MOTIONS.include?(method_symbol)
700
700
  old_cursor, old_byte_pointer = @cursor, @byte_pointer
701
- block.()
701
+ block.(true)
702
702
  unless @waiting_proc
703
703
  cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
704
704
  @cursor, @byte_pointer = old_cursor, old_byte_pointer
@@ -706,38 +706,53 @@ class Reline::LineEditor
706
706
  else
707
707
  old_waiting_proc = @waiting_proc
708
708
  old_waiting_operator_proc = @waiting_operator_proc
709
+ current_waiting_operator_proc = @waiting_operator_proc
709
710
  @waiting_proc = proc { |k|
710
711
  old_cursor, old_byte_pointer = @cursor, @byte_pointer
711
712
  old_waiting_proc.(k)
712
713
  cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
713
714
  @cursor, @byte_pointer = old_cursor, old_byte_pointer
714
- @waiting_operator_proc.(cursor_diff, byte_pointer_diff)
715
+ current_waiting_operator_proc.(cursor_diff, byte_pointer_diff)
715
716
  @waiting_operator_proc = old_waiting_operator_proc
716
717
  }
717
718
  end
718
719
  else
719
720
  # Ignores operator when not motion is given.
720
- block.()
721
+ block.(false)
721
722
  end
722
723
  @waiting_operator_proc = nil
723
724
  else
724
- block.()
725
+ block.(false)
725
726
  end
726
727
  end
727
728
 
728
729
  private def argumentable?(method_obj)
729
- method_obj and method_obj.parameters.length != 1
730
+ method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :arg }
730
731
  end
731
732
 
732
- def wrap_method_call(method_symbol, method_obj, key)
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)
733
740
  if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
734
741
  not_insertion = method_symbol != :ed_insert
735
742
  process_insert(force: not_insertion)
736
743
  end
737
- if @vi_arg
738
- method_obj.(key, arg: @vi_arg)
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
739
750
  else
740
- method_obj.(key)
751
+ if with_operator and inclusive?(method_obj)
752
+ method_obj.(key, inclusive: true)
753
+ else
754
+ method_obj.(key)
755
+ end
741
756
  end
742
757
  end
743
758
 
@@ -749,8 +764,8 @@ class Reline::LineEditor
749
764
  end
750
765
  if method_symbol and key.is_a?(Symbol)
751
766
  if @vi_arg and argumentable?(method_obj)
752
- run_for_operators(key, method_symbol) do
753
- wrap_method_call(method_symbol, method_obj, key)
767
+ run_for_operators(key, method_symbol) do |with_operator|
768
+ wrap_method_call(method_symbol, method_obj, key, with_operator)
754
769
  end
755
770
  else
756
771
  wrap_method_call(method_symbol, method_obj, key) if method_obj
@@ -762,15 +777,15 @@ class Reline::LineEditor
762
777
  ed_argument_digit(key)
763
778
  else
764
779
  if argumentable?(method_obj)
765
- run_for_operators(key, method_symbol) do
766
- wrap_method_call(method_symbol, method_obj, key)
780
+ run_for_operators(key, method_symbol) do |with_operator|
781
+ wrap_method_call(method_symbol, method_obj, key, with_operator)
767
782
  end
768
783
  elsif @waiting_proc
769
784
  @waiting_proc.(key)
770
785
  elsif method_obj
771
786
  wrap_method_call(method_symbol, method_obj, key)
772
787
  else
773
- ed_insert(key)
788
+ ed_insert(key) unless @config.editing_mode_is?(:vi_command)
774
789
  end
775
790
  @kill_ring.process
776
791
  @vi_arg = nil
@@ -782,13 +797,13 @@ class Reline::LineEditor
782
797
  if method_symbol == :ed_argument_digit
783
798
  wrap_method_call(method_symbol, method_obj, key)
784
799
  else
785
- run_for_operators(key, method_symbol) do
786
- wrap_method_call(method_symbol, method_obj, key)
800
+ run_for_operators(key, method_symbol) do |with_operator|
801
+ wrap_method_call(method_symbol, method_obj, key, with_operator)
787
802
  end
788
803
  end
789
804
  @kill_ring.process
790
805
  else
791
- ed_insert(key)
806
+ ed_insert(key) unless @config.editing_mode_is?(:vi_command)
792
807
  end
793
808
  end
794
809
 
@@ -1146,6 +1161,8 @@ class Reline::LineEditor
1146
1161
  if Reline::IOGate.in_pasting?
1147
1162
  @continuous_insertion_buffer << str
1148
1163
  return
1164
+ elsif not @continuous_insertion_buffer.empty?
1165
+ process_insert
1149
1166
  end
1150
1167
  width = Reline::Unicode.get_mbchar_width(str)
1151
1168
  if @cursor == @cursor_max
@@ -1212,6 +1229,7 @@ class Reline::LineEditor
1212
1229
  arg -= 1
1213
1230
  ed_prev_char(key, arg: arg) if arg > 0
1214
1231
  end
1232
+ alias_method :backward_char, :ed_prev_char
1215
1233
 
1216
1234
  private def vi_first_print(key)
1217
1235
  @byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line)
@@ -1939,7 +1957,7 @@ class Reline::LineEditor
1939
1957
  ed_prev_char(key)
1940
1958
  @config.editing_mode = :vi_command
1941
1959
  end
1942
- alias_method :backward_char, :ed_prev_char
1960
+ alias_method :vi_movement_mode, :vi_command_mode
1943
1961
 
1944
1962
  private def vi_next_word(key, arg: 1)
1945
1963
  if @line.bytesize > @byte_pointer
@@ -1961,13 +1979,22 @@ class Reline::LineEditor
1961
1979
  vi_prev_word(key, arg: arg) if arg > 0
1962
1980
  end
1963
1981
 
1964
- private def vi_end_word(key, arg: 1)
1982
+ private def vi_end_word(key, arg: 1, inclusive: false)
1965
1983
  if @line.bytesize > @byte_pointer
1966
1984
  byte_size, width = Reline::Unicode.vi_forward_end_word(@line, @byte_pointer)
1967
1985
  @byte_pointer += byte_size
1968
1986
  @cursor += width
1969
1987
  end
1970
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
1971
1998
  vi_end_word(key, arg: arg) if arg > 0
1972
1999
  end
1973
2000
 
@@ -1991,13 +2018,22 @@ class Reline::LineEditor
1991
2018
  vi_prev_big_word(key, arg: arg) if arg > 0
1992
2019
  end
1993
2020
 
1994
- private def vi_end_big_word(key, arg: 1)
2021
+ private def vi_end_big_word(key, arg: 1, inclusive: false)
1995
2022
  if @line.bytesize > @byte_pointer
1996
2023
  byte_size, width = Reline::Unicode.vi_big_forward_end_word(@line, @byte_pointer)
1997
2024
  @byte_pointer += byte_size
1998
2025
  @cursor += width
1999
2026
  end
2000
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
2001
2037
  vi_end_big_word(key, arg: arg) if arg > 0
2002
2038
  end
2003
2039
 
@@ -2082,6 +2118,14 @@ class Reline::LineEditor
2082
2118
  end
2083
2119
 
2084
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
+ }
2085
2129
  end
2086
2130
 
2087
2131
  private def vi_list_or_eof(key)
@@ -2226,15 +2270,15 @@ class Reline::LineEditor
2226
2270
  }
2227
2271
  end
2228
2272
 
2229
- private def vi_next_char(key, arg: 1)
2230
- @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) }
2231
2275
  end
2232
2276
 
2233
- private def vi_to_next_char(key, arg: 1)
2234
- @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) }
2235
2279
  end
2236
2280
 
2237
- 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)
2238
2282
  if key.instance_of?(String)
2239
2283
  inputed_char = key
2240
2284
  else
@@ -2271,6 +2315,15 @@ class Reline::LineEditor
2271
2315
  @byte_pointer += byte_size
2272
2316
  @cursor += width
2273
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
2274
2327
  @waiting_proc = nil
2275
2328
  end
2276
2329
 
@@ -2342,6 +2395,7 @@ class Reline::LineEditor
2342
2395
  alias_method :set_mark, :em_set_mark
2343
2396
 
2344
2397
  private def em_exchange_mark(key)
2398
+ return unless @mark_pointer
2345
2399
  new_pointer = [@byte_pointer, @line_index]
2346
2400
  @previous_line_index = @line_index
2347
2401
  @byte_pointer, @line_index = @mark_pointer
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.1.7'
2
+ VERSION = '0.1.8'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-01 00:00:00.000000000 Z
11
+ date: 2020-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -103,8 +103,6 @@ files:
103
103
  - lib/reline/key_stroke.rb
104
104
  - lib/reline/kill_ring.rb
105
105
  - lib/reline/line_editor.rb
106
- - lib/reline/line_editor.rb.orig
107
- - lib/reline/line_editor.rb.rej
108
106
  - lib/reline/sibori.rb
109
107
  - lib/reline/unicode.rb
110
108
  - lib/reline/unicode/east_asian_width.rb
@@ -1,2384 +0,0 @@
1
- require 'reline/kill_ring'
2
- require 'reline/unicode'
3
-
4
- require 'tempfile'
5
-
6
- class Reline::LineEditor
7
- # TODO: undo
8
- attr_reader :line
9
- attr_reader :byte_pointer
10
- attr_accessor :confirm_multiline_termination_proc
11
- attr_accessor :completion_proc
12
- attr_accessor :completion_append_character
13
- attr_accessor :output_modifier_proc
14
- attr_accessor :prompt_proc
15
- attr_accessor :auto_indent_proc
16
- attr_accessor :pre_input_hook
17
- attr_accessor :dig_perfect_match_proc
18
- attr_writer :output
19
-
20
- VI_MOTIONS = %i{
21
- ed_prev_char
22
- ed_next_char
23
- vi_zero
24
- ed_move_to_beg
25
- ed_move_to_end
26
- vi_to_column
27
- vi_next_char
28
- vi_prev_char
29
- vi_next_word
30
- vi_prev_word
31
- vi_to_next_char
32
- vi_to_prev_char
33
- vi_end_word
34
- vi_next_big_word
35
- vi_prev_big_word
36
- vi_end_big_word
37
- vi_repeat_next_char
38
- vi_repeat_prev_char
39
- }
40
-
41
- module CompletionState
42
- NORMAL = :normal
43
- COMPLETION = :completion
44
- MENU = :menu
45
- JOURNEY = :journey
46
- MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
47
- PERFECT_MATCH = :perfect_match
48
- end
49
-
50
- CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
51
- MenuInfo = Struct.new('MenuInfo', :target, :list)
52
-
53
- def initialize(config, encoding)
54
- @config = config
55
- @completion_append_character = ''
56
- reset_variables(encoding: encoding)
57
- end
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
-
67
- private def check_multiline_prompt(buffer, prompt)
68
- if @vi_arg
69
- prompt = "(arg: #{@vi_arg}) "
70
- @rerender_all = true
71
- elsif @searching_prompt
72
- prompt = @searching_prompt
73
- @rerender_all = true
74
- else
75
- prompt = @prompt
76
- end
77
- return [prompt, calculate_width(prompt, true), [prompt] * buffer.size] if simplified_rendering?
78
- if @prompt_proc
79
- prompt_list = @prompt_proc.(buffer)
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
93
- prompt = prompt_list[@line_index]
94
- prompt_width = calculate_width(prompt, true)
95
- [prompt, prompt_width, prompt_list]
96
- else
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
110
- [prompt, prompt_width, nil]
111
- end
112
- end
113
-
114
- def reset(prompt = '', encoding:)
115
- @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
116
- @screen_size = Reline::IOGate.get_screen_size
117
- reset_variables(prompt, encoding: encoding)
118
- @old_trap = Signal.trap('SIGINT') {
119
- @old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
120
- raise Interrupt
121
- }
122
- Reline::IOGate.set_winch_handler do
123
- @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
124
- old_screen_size = @screen_size
125
- @screen_size = Reline::IOGate.get_screen_size
126
- if old_screen_size.last < @screen_size.last # columns increase
127
- @rerender_all = true
128
- rerender
129
- else
130
- back = 0
131
- new_buffer = whole_lines
132
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
133
- new_buffer.each_with_index do |line, index|
134
- prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
135
- width = prompt_width + calculate_width(line)
136
- height = calculate_height_by_width(width)
137
- back += height
138
- end
139
- @highest_in_all = back
140
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
141
- @first_line_started_from =
142
- if @line_index.zero?
143
- 0
144
- else
145
- calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
146
- end
147
- if @prompt_proc
148
- prompt = prompt_list[@line_index]
149
- prompt_width = calculate_width(prompt, true)
150
- end
151
- calculate_nearest_cursor
152
- @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
153
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
154
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
155
- @rerender_all = true
156
- end
157
- end
158
- end
159
-
160
- def finalize
161
- Signal.trap('SIGINT', @old_trap)
162
- end
163
-
164
- def eof?
165
- @eof
166
- end
167
-
168
- def reset_variables(prompt = '', encoding:)
169
- @prompt = prompt
170
- @mark_pointer = nil
171
- @encoding = encoding
172
- @is_multiline = false
173
- @finished = false
174
- @cleared = false
175
- @rerender_all = false
176
- @history_pointer = nil
177
- @kill_ring = Reline::KillRing.new
178
- @vi_clipboard = ''
179
- @vi_arg = nil
180
- @waiting_proc = nil
181
- @waiting_operator_proc = nil
182
- @completion_journey_data = nil
183
- @completion_state = CompletionState::NORMAL
184
- @perfect_matched = nil
185
- @menu_info = nil
186
- @first_prompt = true
187
- @searching_prompt = nil
188
- @first_char = true
189
- @eof = false
190
- @continuous_insertion_buffer = String.new(encoding: @encoding)
191
- reset_line
192
- end
193
-
194
- def reset_line
195
- @cursor = 0
196
- @cursor_max = 0
197
- @byte_pointer = 0
198
- @buffer_of_lines = [String.new(encoding: @encoding)]
199
- @line_index = 0
200
- @previous_line_index = nil
201
- @line = @buffer_of_lines[0]
202
- @first_line_started_from = 0
203
- @move_up = 0
204
- @started_from = 0
205
- @highest_in_this = 1
206
- @highest_in_all = 1
207
- @line_backup_in_history = nil
208
- @multibyte_buffer = String.new(encoding: 'ASCII-8BIT')
209
- @check_new_auto_indent = false
210
- end
211
-
212
- def multiline_on
213
- @is_multiline = true
214
- end
215
-
216
- def multiline_off
217
- @is_multiline = false
218
- end
219
-
220
- private def calculate_height_by_lines(lines, prompt)
221
- result = 0
222
- prompt_list = prompt.is_a?(Array) ? prompt : nil
223
- lines.each_with_index { |line, i|
224
- prompt = prompt_list[i] if prompt_list and prompt_list[i]
225
- result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
226
- }
227
- result
228
- end
229
-
230
- private def insert_new_line(cursor_line, next_line)
231
- @line = cursor_line
232
- @buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
233
- @previous_line_index = @line_index
234
- @line_index += 1
235
- end
236
-
237
- private def calculate_height_by_width(width)
238
- width.div(@screen_size.last) + 1
239
- end
240
-
241
- private def split_by_width(str, max_width)
242
- Reline::Unicode.split_by_width(str, max_width, @encoding)
243
- end
244
-
245
- private def scroll_down(val)
246
- if val <= @rest_height
247
- Reline::IOGate.move_cursor_down(val)
248
- @rest_height -= val
249
- else
250
- Reline::IOGate.move_cursor_down(@rest_height)
251
- Reline::IOGate.scroll_down(val - @rest_height)
252
- @rest_height = 0
253
- end
254
- end
255
-
256
- private def move_cursor_up(val)
257
- if val > 0
258
- Reline::IOGate.move_cursor_up(val)
259
- @rest_height += val
260
- elsif val < 0
261
- move_cursor_down(-val)
262
- end
263
- end
264
-
265
- private def move_cursor_down(val)
266
- if val > 0
267
- Reline::IOGate.move_cursor_down(val)
268
- @rest_height -= val
269
- @rest_height = 0 if @rest_height < 0
270
- elsif val < 0
271
- move_cursor_up(-val)
272
- end
273
- end
274
-
275
- private def calculate_nearest_cursor
276
- @cursor_max = calculate_width(line)
277
- new_cursor = 0
278
- new_byte_pointer = 0
279
- height = 1
280
- max_width = @screen_size.last
281
- if @config.editing_mode_is?(:vi_command)
282
- last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @line.bytesize)
283
- if last_byte_size > 0
284
- last_mbchar = @line.byteslice(@line.bytesize - last_byte_size, last_byte_size)
285
- last_width = Reline::Unicode.get_mbchar_width(last_mbchar)
286
- cursor_max = @cursor_max - last_width
287
- else
288
- cursor_max = @cursor_max
289
- end
290
- else
291
- cursor_max = @cursor_max
292
- end
293
- @line.encode(Encoding::UTF_8).grapheme_clusters.each do |gc|
294
- mbchar_width = Reline::Unicode.get_mbchar_width(gc)
295
- now = new_cursor + mbchar_width
296
- if now > cursor_max or now > @cursor
297
- break
298
- end
299
- new_cursor += mbchar_width
300
- if new_cursor > max_width * height
301
- height += 1
302
- end
303
- new_byte_pointer += gc.bytesize
304
- end
305
- @started_from = height - 1
306
- @cursor = new_cursor
307
- @byte_pointer = new_byte_pointer
308
- end
309
-
310
- def rerender_all
311
- @rerender_all = true
312
- rerender
313
- end
314
-
315
- def rerender
316
- return if @line.nil?
317
- if @menu_info
318
- scroll_down(@highest_in_all - @first_line_started_from)
319
- @rerender_all = true
320
- @menu_info.list.sort!.each do |item|
321
- Reline::IOGate.move_cursor_column(0)
322
- @output.write item
323
- @output.flush
324
- scroll_down(1)
325
- end
326
- scroll_down(@highest_in_all - 1)
327
- move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
328
- @menu_info = nil
329
- end
330
- prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
331
- if @cleared
332
- Reline::IOGate.clear_screen
333
- @cleared = false
334
- back = 0
335
- modify_lines(whole_lines).each_with_index do |line, index|
336
- if @prompt_proc
337
- pr = prompt_list[index]
338
- height = render_partial(pr, calculate_width(pr), line, false)
339
- else
340
- height = render_partial(prompt, prompt_width, line, false)
341
- end
342
- if index < (@buffer_of_lines.size - 1)
343
- move_cursor_down(height)
344
- back += height
345
- end
346
- end
347
- move_cursor_up(back)
348
- move_cursor_down(@first_line_started_from + @started_from)
349
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
350
- return
351
- end
352
- new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
353
- # FIXME: end of logical line sometimes breaks
354
- if @previous_line_index or new_highest_in_this != @highest_in_this
355
- if @previous_line_index
356
- new_lines = whole_lines(index: @previous_line_index, line: @line)
357
- else
358
- new_lines = whole_lines
359
- end
360
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
361
- all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
362
- diff = all_height - @highest_in_all
363
- move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
364
- if diff > 0
365
- scroll_down(diff)
366
- move_cursor_up(all_height - 1)
367
- elsif diff < 0
368
- (-diff).times do
369
- Reline::IOGate.move_cursor_column(0)
370
- Reline::IOGate.erase_after_cursor
371
- move_cursor_up(1)
372
- end
373
- move_cursor_up(all_height - 1)
374
- else
375
- move_cursor_up(all_height - 1)
376
- end
377
- @highest_in_all = all_height
378
- back = 0
379
- modify_lines(new_lines).each_with_index do |line, index|
380
- if @prompt_proc
381
- prompt = prompt_list[index]
382
- prompt_width = calculate_width(prompt, true)
383
- end
384
- height = render_partial(prompt, prompt_width, line, false)
385
- if index < (new_lines.size - 1)
386
- scroll_down(1)
387
- back += height
388
- else
389
- back += height - 1
390
- end
391
- end
392
- move_cursor_up(back)
393
- if @previous_line_index
394
- @buffer_of_lines[@previous_line_index] = @line
395
- @line = @buffer_of_lines[@line_index]
396
- end
397
- @first_line_started_from =
398
- if @line_index.zero?
399
- 0
400
- else
401
- calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
402
- end
403
- if @prompt_proc
404
- prompt = prompt_list[@line_index]
405
- prompt_width = calculate_width(prompt, true)
406
- end
407
- move_cursor_down(@first_line_started_from)
408
- calculate_nearest_cursor
409
- @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
410
- move_cursor_down(@started_from)
411
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
412
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
413
- @previous_line_index = nil
414
- rendered = true
415
- elsif @rerender_all
416
- move_cursor_up(@first_line_started_from + @started_from)
417
- Reline::IOGate.move_cursor_column(0)
418
- back = 0
419
- new_buffer = whole_lines
420
- prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
421
- new_buffer.each_with_index do |line, index|
422
- prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
423
- width = prompt_width + calculate_width(line)
424
- height = calculate_height_by_width(width)
425
- back += height
426
- end
427
- if back > @highest_in_all
428
- scroll_down(back - 1)
429
- move_cursor_up(back - 1)
430
- elsif back < @highest_in_all
431
- scroll_down(back)
432
- Reline::IOGate.erase_after_cursor
433
- (@highest_in_all - back - 1).times do
434
- scroll_down(1)
435
- Reline::IOGate.erase_after_cursor
436
- end
437
- move_cursor_up(@highest_in_all - 1)
438
- end
439
- modify_lines(new_buffer).each_with_index do |line, index|
440
- if @prompt_proc
441
- prompt = prompt_list[index]
442
- prompt_width = calculate_width(prompt, true)
443
- end
444
- render_partial(prompt, prompt_width, line, false)
445
- if index < (new_buffer.size - 1)
446
- move_cursor_down(1)
447
- end
448
- end
449
- move_cursor_up(back - 1)
450
- if @prompt_proc
451
- prompt = prompt_list[@line_index]
452
- prompt_width = calculate_width(prompt, true)
453
- end
454
- @highest_in_all = back
455
- @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
456
- @first_line_started_from =
457
- if @line_index.zero?
458
- 0
459
- else
460
- calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
461
- end
462
- @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
463
- move_cursor_down(@first_line_started_from + @started_from)
464
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
465
- @rerender_all = false
466
- rendered = true
467
- end
468
- line = modify_lines(whole_lines)[@line_index]
469
- if @is_multiline
470
- prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
471
- if finished?
472
- # Always rerender on finish because output_modifier_proc may return a different output.
473
- render_partial(prompt, prompt_width, line)
474
- scroll_down(1)
475
- Reline::IOGate.move_cursor_column(0)
476
- Reline::IOGate.erase_after_cursor
477
- elsif not rendered
478
- render_partial(prompt, prompt_width, line)
479
- end
480
- else
481
- render_partial(prompt, prompt_width, line)
482
- if finished?
483
- scroll_down(1)
484
- Reline::IOGate.move_cursor_column(0)
485
- Reline::IOGate.erase_after_cursor
486
- end
487
- end
488
- end
489
-
490
- private def render_partial(prompt, prompt_width, line_to_render, with_control = true)
491
- visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
492
- if with_control
493
- if height > @highest_in_this
494
- diff = height - @highest_in_this
495
- scroll_down(diff)
496
- @highest_in_all += diff
497
- @highest_in_this = height
498
- move_cursor_up(diff)
499
- elsif height < @highest_in_this
500
- diff = @highest_in_this - height
501
- @highest_in_all -= diff
502
- @highest_in_this = height
503
- end
504
- move_cursor_up(@started_from)
505
- @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
506
- end
507
- Reline::IOGate.move_cursor_column(0)
508
- visual_lines.each_with_index do |line, index|
509
- if line.nil?
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
522
- else
523
- Reline::IOGate.erase_after_cursor
524
- move_cursor_down(1)
525
- Reline::IOGate.move_cursor_column(0)
526
- end
527
- next
528
- end
529
- @output.write line
530
- if Reline::IOGate.win? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
531
- # A newline is automatically inserted if a character is rendered at eol on command prompt.
532
- @rest_height -= 1 if @rest_height > 0
533
- end
534
- @output.flush
535
- if @first_prompt
536
- @first_prompt = false
537
- @pre_input_hook&.call
538
- end
539
- end
540
- Reline::IOGate.erase_after_cursor
541
- Reline::IOGate.move_cursor_column(0)
542
- if with_control
543
- # Just after rendring, so the cursor is on the last line.
544
- if finished?
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)
550
- end
551
- end
552
- height
553
- end
554
-
555
- private def modify_lines(before)
556
- return before if before.nil? || before.empty? || simplified_rendering?
557
-
558
- if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?)
559
- after.lines("\n").map { |l| l.chomp('') }
560
- else
561
- before
562
- end
563
- end
564
-
565
- def editing_mode
566
- @config.editing_mode
567
- end
568
-
569
- private def menu(target, list)
570
- @menu_info = MenuInfo.new(target, list)
571
- end
572
-
573
- private def complete_internal_proc(list, is_menu)
574
- preposing, target, postposing = retrieve_completion_block
575
- list = list.select { |i|
576
- if i and not Encoding.compatible?(target.encoding, i.encoding)
577
- raise Encoding::CompatibilityError, "#{target.encoding.name} is not compatible with #{i.encoding.name}"
578
- end
579
- if @config.completion_ignore_case
580
- i&.downcase&.start_with?(target.downcase)
581
- else
582
- i&.start_with?(target)
583
- end
584
- }.uniq
585
- if is_menu
586
- menu(target, list)
587
- return nil
588
- end
589
- completed = list.inject { |memo, item|
590
- begin
591
- memo_mbchars = memo.unicode_normalize.grapheme_clusters
592
- item_mbchars = item.unicode_normalize.grapheme_clusters
593
- rescue Encoding::CompatibilityError
594
- memo_mbchars = memo.grapheme_clusters
595
- item_mbchars = item.grapheme_clusters
596
- end
597
- size = [memo_mbchars.size, item_mbchars.size].min
598
- result = ''
599
- size.times do |i|
600
- if @config.completion_ignore_case
601
- if memo_mbchars[i].casecmp?(item_mbchars[i])
602
- result << memo_mbchars[i]
603
- else
604
- break
605
- end
606
- else
607
- if memo_mbchars[i] == item_mbchars[i]
608
- result << memo_mbchars[i]
609
- else
610
- break
611
- end
612
- end
613
- end
614
- result
615
- }
616
- [target, preposing, completed, postposing]
617
- end
618
-
619
- private def complete(list, just_show_list = false)
620
- case @completion_state
621
- when CompletionState::NORMAL, CompletionState::JOURNEY
622
- @completion_state = CompletionState::COMPLETION
623
- when CompletionState::PERFECT_MATCH
624
- @dig_perfect_match_proc&.(@perfect_matched)
625
- end
626
- if just_show_list
627
- is_menu = true
628
- elsif @completion_state == CompletionState::MENU
629
- is_menu = true
630
- elsif @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
631
- is_menu = true
632
- else
633
- is_menu = false
634
- end
635
- result = complete_internal_proc(list, is_menu)
636
- if @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
637
- @completion_state = CompletionState::PERFECT_MATCH
638
- end
639
- return if result.nil?
640
- target, preposing, completed, postposing = result
641
- return if completed.nil?
642
- if target <= completed and (@completion_state == CompletionState::COMPLETION)
643
- if list.include?(completed)
644
- if list.one?
645
- @completion_state = CompletionState::PERFECT_MATCH
646
- else
647
- @completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
648
- end
649
- @perfect_matched = completed
650
- else
651
- @completion_state = CompletionState::MENU
652
- end
653
- if not just_show_list and target < completed
654
- @line = preposing + completed + completion_append_character.to_s + postposing
655
- line_to_pointer = preposing + completed + completion_append_character.to_s
656
- @cursor_max = calculate_width(@line)
657
- @cursor = calculate_width(line_to_pointer)
658
- @byte_pointer = line_to_pointer.bytesize
659
- end
660
- end
661
- end
662
-
663
- private def move_completed_list(list, direction)
664
- case @completion_state
665
- when CompletionState::NORMAL, CompletionState::COMPLETION,
666
- CompletionState::MENU, CompletionState::MENU_WITH_PERFECT_MATCH
667
- @completion_state = CompletionState::JOURNEY
668
- result = retrieve_completion_block
669
- return if result.nil?
670
- preposing, target, postposing = result
671
- @completion_journey_data = CompletionJourneyData.new(
672
- preposing, postposing,
673
- [target] + list.select{ |item| item.start_with?(target) }, 0)
674
- @completion_state = CompletionState::JOURNEY
675
- else
676
- case direction
677
- when :up
678
- @completion_journey_data.pointer -= 1
679
- if @completion_journey_data.pointer < 0
680
- @completion_journey_data.pointer = @completion_journey_data.list.size - 1
681
- end
682
- when :down
683
- @completion_journey_data.pointer += 1
684
- if @completion_journey_data.pointer >= @completion_journey_data.list.size
685
- @completion_journey_data.pointer = 0
686
- end
687
- end
688
- completed = @completion_journey_data.list[@completion_journey_data.pointer]
689
- @line = @completion_journey_data.preposing + completed + @completion_journey_data.postposing
690
- line_to_pointer = @completion_journey_data.preposing + completed
691
- @cursor_max = calculate_width(@line)
692
- @cursor = calculate_width(line_to_pointer)
693
- @byte_pointer = line_to_pointer.bytesize
694
- end
695
- end
696
-
697
- private def run_for_operators(key, method_symbol, &block)
698
- if @waiting_operator_proc
699
- if VI_MOTIONS.include?(method_symbol)
700
- old_cursor, old_byte_pointer = @cursor, @byte_pointer
701
- block.()
702
- unless @waiting_proc
703
- cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
704
- @cursor, @byte_pointer = old_cursor, old_byte_pointer
705
- @waiting_operator_proc.(cursor_diff, byte_pointer_diff)
706
- else
707
- old_waiting_proc = @waiting_proc
708
- old_waiting_operator_proc = @waiting_operator_proc
709
- @waiting_proc = proc { |k|
710
- old_cursor, old_byte_pointer = @cursor, @byte_pointer
711
- old_waiting_proc.(k)
712
- cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
713
- @cursor, @byte_pointer = old_cursor, old_byte_pointer
714
- @waiting_operator_proc.(cursor_diff, byte_pointer_diff)
715
- @waiting_operator_proc = old_waiting_operator_proc
716
- }
717
- end
718
- else
719
- # Ignores operator when not motion is given.
720
- block.()
721
- end
722
- @waiting_operator_proc = nil
723
- else
724
- block.()
725
- end
726
- end
727
-
728
- private def argumentable?(method_obj)
729
- method_obj and method_obj.parameters.length != 1
730
- end
731
-
732
- def wrap_method_call(method_symbol, method_obj, key)
733
- if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
734
- #$stderr.puts 'aoooooooooooooooooooo'
735
- process_insert
736
- elsif method_symbol != :ed_insert
737
- #$stderr.puts 'not :ed_insert'
738
- process_insert
739
- end
740
- if @vi_arg
741
- method_obj.(key, arg: @vi_arg)
742
- else
743
- method_obj.(key)
744
- end
745
- end
746
-
747
- # TODO done
748
- # method_symbol が :ed_inesrt であるかどうかを判定して、違ったら process_insert を走らせるという wrapper を method_obj.() の全てに遠す。
749
- # ed_insert は in_pasting? である限りバッファに蓄積する。
750
- private def process_key(key, method_symbol)
751
- if method_symbol and respond_to?(method_symbol, true)
752
- method_obj = method(method_symbol)
753
- else
754
- method_obj = nil
755
- end
756
- if method_symbol and key.is_a?(Symbol)
757
- if @vi_arg and argumentable?(method_obj)
758
- run_for_operators(key, method_symbol) do
759
- wrap_method_call(method_symbol, method_obj, key)
760
- end
761
- else
762
- wrap_method_call(method_symbol, method_obj, key) if method_obj
763
- end
764
- @kill_ring.process
765
- @vi_arg = nil
766
- elsif @vi_arg
767
- if key.chr =~ /[0-9]/
768
- ed_argument_digit(key)
769
- else
770
- if argumentable?(method_obj)
771
- run_for_operators(key, method_symbol) do
772
- wrap_method_call(method_symbol, method_obj, key)
773
- end
774
- elsif @waiting_proc
775
- @waiting_proc.(key)
776
- elsif method_obj
777
- wrap_method_call(method_symbol, method_obj, key)
778
- else
779
- ed_insert(key)
780
- end
781
- @kill_ring.process
782
- @vi_arg = nil
783
- end
784
- elsif @waiting_proc
785
- @waiting_proc.(key)
786
- @kill_ring.process
787
- elsif method_obj
788
- if method_symbol == :ed_argument_digit
789
- wrap_method_call(method_symbol, method_obj, key)
790
- else
791
- run_for_operators(key, method_symbol) do
792
- wrap_method_call(method_symbol, method_obj, key)
793
- end
794
- end
795
- @kill_ring.process
796
- else
797
- ed_insert(key)
798
- end
799
- end
800
-
801
- private def normal_char(key)
802
- method_symbol = method_obj = nil
803
- if key.combined_char.is_a?(Symbol)
804
- process_key(key.combined_char, key.combined_char)
805
- return
806
- end
807
- @multibyte_buffer << key.combined_char
808
- if @multibyte_buffer.size > 1
809
- if @multibyte_buffer.dup.force_encoding(@encoding).valid_encoding?
810
- process_key(@multibyte_buffer.dup.force_encoding(@encoding), nil)
811
- @multibyte_buffer.clear
812
- else
813
- # invalid
814
- return
815
- end
816
- else # single byte
817
- return if key.char >= 128 # maybe, first byte of multi byte
818
- method_symbol = @config.editing_mode.get_method(key.combined_char)
819
- if key.with_meta and method_symbol == :ed_unassigned
820
- # split ESC + key
821
- method_symbol = @config.editing_mode.get_method("\e".ord)
822
- process_key("\e".ord, method_symbol)
823
- method_symbol = @config.editing_mode.get_method(key.char)
824
- process_key(key.char, method_symbol)
825
- else
826
- process_key(key.combined_char, method_symbol)
827
- end
828
- @multibyte_buffer.clear
829
- end
830
- if @config.editing_mode_is?(:vi_command) and @cursor > 0 and @cursor == @cursor_max
831
- byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
832
- @byte_pointer -= byte_size
833
- mbchar = @line.byteslice(@byte_pointer, byte_size)
834
- width = Reline::Unicode.get_mbchar_width(mbchar)
835
- @cursor -= width
836
- end
837
- end
838
-
839
- def input_key(key)
840
- # TODO done
841
- # :vi_insert か :emacs で、
842
- # @waiting_proc も @waiting_operator_proc も設定されていない、
843
- # ということでなければ process_insert を走らせてまとめて処理。
844
- # finish 時も?
845
- if key.char.nil?
846
- if @first_char
847
- @line = nil
848
- end
849
- finish
850
- return
851
- end
852
- @first_char = false
853
- completion_occurs = false
854
- if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
855
- unless @config.disable_completion
856
- result = call_completion_proc
857
- if result.is_a?(Array)
858
- completion_occurs = true
859
- # TODO done
860
- # ここでも process_insert
861
- process_insert
862
- complete(result)
863
- end
864
- end
865
- elsif not @config.disable_completion and @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
866
- unless @config.disable_completion
867
- result = call_completion_proc
868
- if result.is_a?(Array)
869
- completion_occurs = true
870
- # TODO done
871
- # ここでも process_insert
872
- process_insert
873
- move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
874
- end
875
- end
876
- elsif Symbol === key.char and respond_to?(key.char, true)
877
- process_key(key.char, key.char)
878
- else
879
- normal_char(key)
880
- end
881
- unless completion_occurs
882
- @completion_state = CompletionState::NORMAL
883
- end
884
- if @is_multiline and @auto_indent_proc and not simplified_rendering?
885
- process_auto_indent
886
- end
887
- end
888
-
889
- def call_completion_proc
890
- result = retrieve_completion_block(true)
891
- slice = result[1]
892
- result = @completion_proc.(slice) if @completion_proc and slice
893
- Reline.core.instance_variable_set(:@completion_quote_character, nil)
894
- result
895
- end
896
-
897
- private def process_auto_indent
898
- return if not @check_new_auto_indent and @previous_line_index # move cursor up or down
899
- if @check_new_auto_indent and @previous_line_index and @previous_line_index > 0 and @line_index > @previous_line_index
900
- # Fix indent of a line when a newline is inserted to the next
901
- new_lines = whole_lines(index: @previous_line_index, line: @line)
902
- new_indent = @auto_indent_proc.(new_lines[0..-3].push(''), @line_index - 1, 0, true)
903
- md = @line.match(/\A */)
904
- prev_indent = md[0].count(' ')
905
- @line = ' ' * new_indent + @line.lstrip
906
-
907
- new_indent = nil
908
- result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, (new_lines[-2].size + 1), false)
909
- if result
910
- new_indent = result
911
- end
912
- if new_indent&.>= 0
913
- @line = ' ' * new_indent + @line.lstrip
914
- end
915
- end
916
- if @previous_line_index
917
- new_lines = whole_lines(index: @previous_line_index, line: @line)
918
- else
919
- new_lines = whole_lines
920
- end
921
- new_indent = @auto_indent_proc.(new_lines, @line_index, @byte_pointer, @check_new_auto_indent)
922
- if new_indent&.>= 0
923
- md = new_lines[@line_index].match(/\A */)
924
- prev_indent = md[0].count(' ')
925
- if @check_new_auto_indent
926
- @buffer_of_lines[@line_index] = ' ' * new_indent + @buffer_of_lines[@line_index].lstrip
927
- @cursor = new_indent
928
- @byte_pointer = new_indent
929
- else
930
- @line = ' ' * new_indent + @line.lstrip
931
- @cursor += new_indent - prev_indent
932
- @byte_pointer += new_indent - prev_indent
933
- end
934
- end
935
- @check_new_auto_indent = false
936
- end
937
-
938
- def retrieve_completion_block(set_completion_quote_character = false)
939
- word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
940
- quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
941
- before = @line.byteslice(0, @byte_pointer)
942
- rest = nil
943
- break_pointer = nil
944
- quote = nil
945
- closing_quote = nil
946
- escaped_quote = nil
947
- i = 0
948
- while i < @byte_pointer do
949
- slice = @line.byteslice(i, @byte_pointer - i)
950
- unless slice.valid_encoding?
951
- i += 1
952
- next
953
- end
954
- if quote and slice.start_with?(closing_quote)
955
- quote = nil
956
- i += 1
957
- rest = nil
958
- elsif quote and slice.start_with?(escaped_quote)
959
- # skip
960
- i += 2
961
- elsif slice =~ quote_characters_regexp # find new "
962
- rest = $'
963
- quote = $&
964
- closing_quote = /(?!\\)#{Regexp.escape(quote)}/
965
- escaped_quote = /\\#{Regexp.escape(quote)}/
966
- i += 1
967
- break_pointer = i - 1
968
- elsif not quote and slice =~ word_break_regexp
969
- rest = $'
970
- i += 1
971
- before = @line.byteslice(i, @byte_pointer - i)
972
- break_pointer = i
973
- else
974
- i += 1
975
- end
976
- end
977
- postposing = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
978
- if rest
979
- preposing = @line.byteslice(0, break_pointer)
980
- target = rest
981
- if set_completion_quote_character and quote
982
- Reline.core.instance_variable_set(:@completion_quote_character, quote)
983
- if postposing !~ /(?!\\)#{Regexp.escape(quote)}/ # closing quote
984
- insert_text(quote)
985
- end
986
- end
987
- else
988
- preposing = ''
989
- if break_pointer
990
- preposing = @line.byteslice(0, break_pointer)
991
- else
992
- preposing = ''
993
- end
994
- target = before
995
- end
996
- [preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
997
- end
998
-
999
- def confirm_multiline_termination
1000
- temp_buffer = @buffer_of_lines.dup
1001
- if @previous_line_index and @line_index == (@buffer_of_lines.size - 1)
1002
- #$stderr.puts 'A'
1003
- temp_buffer[@previous_line_index] = @line
1004
- else
1005
- #$stderr.puts 'B'
1006
- temp_buffer[@line_index] = @line
1007
- end
1008
- #$stderr.puts ?0 * 100
1009
- #$stderr.puts ?1 * 100
1010
- #$stderr.puts @line.inspect
1011
- #$stderr.puts ?2 * 100
1012
- #$stderr.puts temp_buffer.join("\n")
1013
- #$stderr.puts ?3 * 100
1014
- @confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
1015
- end
1016
-
1017
- def insert_text(text)
1018
- width = calculate_width(text)
1019
- if @cursor == @cursor_max
1020
- @line += text
1021
- else
1022
- @line = byteinsert(@line, @byte_pointer, text)
1023
- end
1024
- @byte_pointer += text.bytesize
1025
- @cursor += width
1026
- @cursor_max += width
1027
- end
1028
-
1029
- def delete_text(start = nil, length = nil)
1030
- if start.nil? and length.nil?
1031
- @line&.clear
1032
- @byte_pointer = 0
1033
- @cursor = 0
1034
- @cursor_max = 0
1035
- elsif not start.nil? and not length.nil?
1036
- if @line
1037
- before = @line.byteslice(0, start)
1038
- after = @line.byteslice(start + length, @line.bytesize)
1039
- @line = before + after
1040
- @byte_pointer = @line.bytesize if @byte_pointer > @line.bytesize
1041
- str = @line.byteslice(0, @byte_pointer)
1042
- @cursor = calculate_width(str)
1043
- @cursor_max = calculate_width(@line)
1044
- end
1045
- elsif start.is_a?(Range)
1046
- range = start
1047
- first = range.first
1048
- last = range.last
1049
- last = @line.bytesize - 1 if last > @line.bytesize
1050
- last += @line.bytesize if last < 0
1051
- first += @line.bytesize if first < 0
1052
- range = range.exclude_end? ? first...last : first..last
1053
- @line = @line.bytes.reject.with_index{ |c, i| range.include?(i) }.map{ |c| c.chr(Encoding::ASCII_8BIT) }.join.force_encoding(@encoding)
1054
- @byte_pointer = @line.bytesize if @byte_pointer > @line.bytesize
1055
- str = @line.byteslice(0, @byte_pointer)
1056
- @cursor = calculate_width(str)
1057
- @cursor_max = calculate_width(@line)
1058
- else
1059
- @line = @line.byteslice(0, start)
1060
- @byte_pointer = @line.bytesize if @byte_pointer > @line.bytesize
1061
- str = @line.byteslice(0, @byte_pointer)
1062
- @cursor = calculate_width(str)
1063
- @cursor_max = calculate_width(@line)
1064
- end
1065
- end
1066
-
1067
- def byte_pointer=(val)
1068
- @byte_pointer = val
1069
- str = @line.byteslice(0, @byte_pointer)
1070
- @cursor = calculate_width(str)
1071
- @cursor_max = calculate_width(@line)
1072
- end
1073
-
1074
- def whole_lines(index: @line_index, line: @line)
1075
- temp_lines = @buffer_of_lines.dup
1076
- temp_lines[index] = line
1077
- temp_lines
1078
- end
1079
-
1080
- def whole_buffer
1081
- if @buffer_of_lines.size == 1 and @line.nil?
1082
- nil
1083
- else
1084
- whole_lines.join("\n")
1085
- end
1086
- end
1087
-
1088
- def finished?
1089
- @finished
1090
- end
1091
-
1092
- def finish
1093
- @finished = true
1094
- process_insert
1095
- @rerender_all = true
1096
- @config.reset
1097
- end
1098
-
1099
- private def byteslice!(str, byte_pointer, size)
1100
- new_str = str.byteslice(0, byte_pointer)
1101
- new_str << str.byteslice(byte_pointer + size, str.bytesize)
1102
- [new_str, str.byteslice(byte_pointer, size)]
1103
- end
1104
-
1105
- private def byteinsert(str, byte_pointer, other)
1106
- new_str = str.byteslice(0, byte_pointer)
1107
- new_str << other
1108
- new_str << str.byteslice(byte_pointer, str.bytesize)
1109
- new_str
1110
- end
1111
-
1112
- private def calculate_width(str, allow_escape_code = false)
1113
- Reline::Unicode.calculate_width(str, allow_escape_code)
1114
- end
1115
-
1116
- private def key_delete(key)
1117
- if @config.editing_mode_is?(:vi_insert, :emacs)
1118
- ed_delete_next_char(key)
1119
- end
1120
- end
1121
-
1122
- private def key_newline(key)
1123
- if @is_multiline
1124
- next_line = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
1125
- cursor_line = @line.byteslice(0, @byte_pointer)
1126
- insert_new_line(cursor_line, next_line)
1127
- @cursor = 0
1128
- @check_new_auto_indent = true
1129
- end
1130
- end
1131
-
1132
- private def ed_unassigned(key) end # do nothing
1133
-
1134
- private def process_insert(force: false)
1135
- return if @continuous_insertion_buffer.empty? or (Reline::IOGate.in_pasting? and not force)
1136
- $stderr.puts 'process!!!!!!!'
1137
- width = Reline::Unicode.calculate_width(@continuous_insertion_buffer)
1138
- bytesize = @continuous_insertion_buffer.bytesize
1139
- if @cursor == @cursor_max
1140
- @line += @continuous_insertion_buffer
1141
- else
1142
- @line = byteinsert(@line, @byte_pointer, @continuous_insertion_buffer)
1143
- end
1144
- #$stderr.puts "#### #{@continuous_insertion_buffer.inspect} => #{@line.inspect}"
1145
- #$stderr.puts "width #{width.inspect} bytesize #{bytesize.inspect}"
1146
- @byte_pointer += bytesize
1147
- @cursor += width
1148
- @cursor_max += width
1149
- @continuous_insertion_buffer.clear
1150
- @rerender_all = true
1151
- end
1152
-
1153
- private def ed_insert(key)
1154
- str = nil
1155
- width = nil
1156
- bytesize = nil
1157
- if key.instance_of?(String)
1158
- begin
1159
- key.encode(Encoding::UTF_8)
1160
- rescue Encoding::UndefinedConversionError
1161
- return
1162
- end
1163
- str = key
1164
- bytesize = key.bytesize
1165
- else
1166
- begin
1167
- key.chr.encode(Encoding::UTF_8)
1168
- rescue Encoding::UndefinedConversionError
1169
- return
1170
- end
1171
- str = key.chr
1172
- bytesize = 1
1173
- end
1174
- if Reline::IOGate.in_pasting?
1175
- $stderr.puts 'pasting!!!!!!'
1176
- @continuous_insertion_buffer << str
1177
- return
1178
- end
1179
- width = Reline::Unicode.get_mbchar_width(str)
1180
- if @cursor == @cursor_max
1181
- @line += str
1182
- else
1183
- @line = byteinsert(@line, @byte_pointer, str)
1184
- end
1185
- @byte_pointer += bytesize
1186
- @cursor += width
1187
- @cursor_max += width
1188
- end
1189
- alias_method :ed_digit, :ed_insert
1190
- alias_method :self_insert, :ed_insert
1191
-
1192
- private def ed_quoted_insert(str, arg: 1)
1193
- @waiting_proc = proc { |key|
1194
- arg.times do
1195
- if key == "\C-j".ord or key == "\C-m".ord
1196
- key_newline(key)
1197
- else
1198
- ed_insert(key)
1199
- end
1200
- end
1201
- @waiting_proc = nil
1202
- }
1203
- end
1204
- alias_method :quoted_insert, :ed_quoted_insert
1205
-
1206
- private def ed_next_char(key, arg: 1)
1207
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
1208
- if (@byte_pointer < @line.bytesize)
1209
- mbchar = @line.byteslice(@byte_pointer, byte_size)
1210
- width = Reline::Unicode.get_mbchar_width(mbchar)
1211
- @cursor += width if width
1212
- @byte_pointer += byte_size
1213
- elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer == @line.bytesize and @line_index < @buffer_of_lines.size - 1
1214
- next_line = @buffer_of_lines[@line_index + 1]
1215
- @cursor = 0
1216
- @byte_pointer = 0
1217
- @cursor_max = calculate_width(next_line)
1218
- @previous_line_index = @line_index
1219
- @line_index += 1
1220
- end
1221
- arg -= 1
1222
- ed_next_char(key, arg: arg) if arg > 0
1223
- end
1224
- alias_method :forward_char, :ed_next_char
1225
-
1226
- private def ed_prev_char(key, arg: 1)
1227
- if @cursor > 0
1228
- byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
1229
- @byte_pointer -= byte_size
1230
- mbchar = @line.byteslice(@byte_pointer, byte_size)
1231
- width = Reline::Unicode.get_mbchar_width(mbchar)
1232
- @cursor -= width
1233
- elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer == 0 and @line_index > 0
1234
- prev_line = @buffer_of_lines[@line_index - 1]
1235
- @cursor = calculate_width(prev_line)
1236
- @byte_pointer = prev_line.bytesize
1237
- @cursor_max = calculate_width(prev_line)
1238
- @previous_line_index = @line_index
1239
- @line_index -= 1
1240
- end
1241
- arg -= 1
1242
- ed_prev_char(key, arg: arg) if arg > 0
1243
- end
1244
-
1245
- private def vi_first_print(key)
1246
- @byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line)
1247
- end
1248
-
1249
- private def ed_move_to_beg(key)
1250
- @byte_pointer = @cursor = 0
1251
- end
1252
- alias_method :beginning_of_line, :ed_move_to_beg
1253
-
1254
- private def ed_move_to_end(key)
1255
- @byte_pointer = 0
1256
- @cursor = 0
1257
- byte_size = 0
1258
- while @byte_pointer < @line.bytesize
1259
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
1260
- if byte_size > 0
1261
- mbchar = @line.byteslice(@byte_pointer, byte_size)
1262
- @cursor += Reline::Unicode.get_mbchar_width(mbchar)
1263
- end
1264
- @byte_pointer += byte_size
1265
- end
1266
- end
1267
- alias_method :end_of_line, :ed_move_to_end
1268
-
1269
- private def generate_searcher
1270
- Fiber.new do |first_key|
1271
- prev_search_key = first_key
1272
- search_word = String.new(encoding: @encoding)
1273
- multibyte_buf = String.new(encoding: 'ASCII-8BIT')
1274
- last_hit = nil
1275
- case first_key
1276
- when "\C-r".ord
1277
- prompt_name = 'reverse-i-search'
1278
- when "\C-s".ord
1279
- prompt_name = 'i-search'
1280
- end
1281
- loop do
1282
- key = Fiber.yield(search_word)
1283
- search_again = false
1284
- case key
1285
- when -1 # determined
1286
- Reline.last_incremental_search = search_word
1287
- break
1288
- when "\C-h".ord, "\C-?".ord
1289
- grapheme_clusters = search_word.grapheme_clusters
1290
- if grapheme_clusters.size > 0
1291
- grapheme_clusters.pop
1292
- search_word = grapheme_clusters.join
1293
- end
1294
- when "\C-r".ord, "\C-s".ord
1295
- search_again = true if prev_search_key == key
1296
- prev_search_key = key
1297
- else
1298
- multibyte_buf << key
1299
- if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
1300
- search_word << multibyte_buf.dup.force_encoding(@encoding)
1301
- multibyte_buf.clear
1302
- end
1303
- end
1304
- hit = nil
1305
- if not search_word.empty? and @line_backup_in_history&.include?(search_word)
1306
- @history_pointer = nil
1307
- hit = @line_backup_in_history
1308
- else
1309
- if search_again
1310
- if search_word.empty? and Reline.last_incremental_search
1311
- search_word = Reline.last_incremental_search
1312
- end
1313
- if @history_pointer
1314
- case prev_search_key
1315
- when "\C-r".ord
1316
- history_pointer_base = 0
1317
- history = Reline::HISTORY[0..(@history_pointer - 1)]
1318
- when "\C-s".ord
1319
- history_pointer_base = @history_pointer + 1
1320
- history = Reline::HISTORY[(@history_pointer + 1)..-1]
1321
- end
1322
- else
1323
- history_pointer_base = 0
1324
- history = Reline::HISTORY
1325
- end
1326
- elsif @history_pointer
1327
- case prev_search_key
1328
- when "\C-r".ord
1329
- history_pointer_base = 0
1330
- history = Reline::HISTORY[0..@history_pointer]
1331
- when "\C-s".ord
1332
- history_pointer_base = @history_pointer
1333
- history = Reline::HISTORY[@history_pointer..-1]
1334
- end
1335
- else
1336
- history_pointer_base = 0
1337
- history = Reline::HISTORY
1338
- end
1339
- case prev_search_key
1340
- when "\C-r".ord
1341
- hit_index = history.rindex { |item|
1342
- item.include?(search_word)
1343
- }
1344
- when "\C-s".ord
1345
- hit_index = history.index { |item|
1346
- item.include?(search_word)
1347
- }
1348
- end
1349
- if hit_index
1350
- @history_pointer = history_pointer_base + hit_index
1351
- hit = Reline::HISTORY[@history_pointer]
1352
- end
1353
- end
1354
- case prev_search_key
1355
- when "\C-r".ord
1356
- prompt_name = 'reverse-i-search'
1357
- when "\C-s".ord
1358
- prompt_name = 'i-search'
1359
- end
1360
- if hit
1361
- if @is_multiline
1362
- @buffer_of_lines = hit.split("\n")
1363
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1364
- @line_index = @buffer_of_lines.size - 1
1365
- @line = @buffer_of_lines.last
1366
- @rerender_all = true
1367
- @searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
1368
- else
1369
- @line = hit
1370
- @searching_prompt = "(%s)`%s': %s" % [prompt_name, search_word, hit]
1371
- end
1372
- last_hit = hit
1373
- else
1374
- if @is_multiline
1375
- @rerender_all = true
1376
- @searching_prompt = "(failed %s)`%s'" % [prompt_name, search_word]
1377
- else
1378
- @searching_prompt = "(failed %s)`%s': %s" % [prompt_name, search_word, last_hit]
1379
- end
1380
- end
1381
- end
1382
- end
1383
- end
1384
-
1385
- private def incremental_search_history(key)
1386
- unless @history_pointer
1387
- if @is_multiline
1388
- @line_backup_in_history = whole_buffer
1389
- else
1390
- @line_backup_in_history = @line
1391
- end
1392
- end
1393
- searcher = generate_searcher
1394
- searcher.resume(key)
1395
- @searching_prompt = "(reverse-i-search)`': "
1396
- @waiting_proc = ->(k) {
1397
- case k
1398
- when "\C-j".ord
1399
- if @history_pointer
1400
- buffer = Reline::HISTORY[@history_pointer]
1401
- else
1402
- buffer = @line_backup_in_history
1403
- end
1404
- if @is_multiline
1405
- @buffer_of_lines = buffer.split("\n")
1406
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1407
- @line_index = @buffer_of_lines.size - 1
1408
- @line = @buffer_of_lines.last
1409
- @rerender_all = true
1410
- else
1411
- @line = buffer
1412
- end
1413
- @searching_prompt = nil
1414
- @waiting_proc = nil
1415
- @cursor_max = calculate_width(@line)
1416
- @cursor = @byte_pointer = 0
1417
- searcher.resume(-1)
1418
- when "\C-g".ord
1419
- if @is_multiline
1420
- @buffer_of_lines = @line_backup_in_history.split("\n")
1421
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1422
- @line_index = @buffer_of_lines.size - 1
1423
- @line = @buffer_of_lines.last
1424
- @rerender_all = true
1425
- else
1426
- @line = @line_backup_in_history
1427
- end
1428
- @history_pointer = nil
1429
- @searching_prompt = nil
1430
- @waiting_proc = nil
1431
- @line_backup_in_history = nil
1432
- @cursor_max = calculate_width(@line)
1433
- @cursor = @byte_pointer = 0
1434
- @rerender_all = true
1435
- else
1436
- chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
1437
- if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
1438
- searcher.resume(k)
1439
- else
1440
- if @history_pointer
1441
- line = Reline::HISTORY[@history_pointer]
1442
- else
1443
- line = @line_backup_in_history
1444
- end
1445
- if @is_multiline
1446
- @line_backup_in_history = whole_buffer
1447
- @buffer_of_lines = line.split("\n")
1448
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1449
- @line_index = @buffer_of_lines.size - 1
1450
- @line = @buffer_of_lines.last
1451
- @rerender_all = true
1452
- else
1453
- @line_backup_in_history = @line
1454
- @line = line
1455
- end
1456
- @searching_prompt = nil
1457
- @waiting_proc = nil
1458
- @cursor_max = calculate_width(@line)
1459
- @cursor = @byte_pointer = 0
1460
- searcher.resume(-1)
1461
- end
1462
- end
1463
- }
1464
- end
1465
-
1466
- private def vi_search_prev(key)
1467
- incremental_search_history(key)
1468
- end
1469
- alias_method :reverse_search_history, :vi_search_prev
1470
-
1471
- private def vi_search_next(key)
1472
- incremental_search_history(key)
1473
- end
1474
- alias_method :forward_search_history, :vi_search_next
1475
-
1476
- private def ed_search_prev_history(key, arg: 1)
1477
- history = nil
1478
- h_pointer = nil
1479
- line_no = nil
1480
- substr = @line.slice(0, @byte_pointer)
1481
- if @history_pointer.nil?
1482
- return if not @line.empty? and substr.empty?
1483
- history = Reline::HISTORY
1484
- elsif @history_pointer.zero?
1485
- history = nil
1486
- h_pointer = nil
1487
- else
1488
- history = Reline::HISTORY.slice(0, @history_pointer)
1489
- end
1490
- return if history.nil?
1491
- if @is_multiline
1492
- h_pointer = history.rindex { |h|
1493
- h.split("\n").each_with_index { |l, i|
1494
- if l.start_with?(substr)
1495
- line_no = i
1496
- break
1497
- end
1498
- }
1499
- not line_no.nil?
1500
- }
1501
- else
1502
- h_pointer = history.rindex { |l|
1503
- l.start_with?(substr)
1504
- }
1505
- end
1506
- return if h_pointer.nil?
1507
- @history_pointer = h_pointer
1508
- if @is_multiline
1509
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
1510
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1511
- @line_index = line_no
1512
- @line = @buffer_of_lines.last
1513
- @rerender_all = true
1514
- else
1515
- @line = Reline::HISTORY[@history_pointer]
1516
- end
1517
- @cursor_max = calculate_width(@line)
1518
- arg -= 1
1519
- ed_search_prev_history(key, arg: arg) if arg > 0
1520
- end
1521
- alias_method :history_search_backward, :ed_search_prev_history
1522
-
1523
- private def ed_search_next_history(key, arg: 1)
1524
- substr = @line.slice(0, @byte_pointer)
1525
- if @history_pointer.nil?
1526
- return
1527
- elsif @history_pointer == (Reline::HISTORY.size - 1) and not substr.empty?
1528
- return
1529
- end
1530
- history = Reline::HISTORY.slice((@history_pointer + 1)..-1)
1531
- h_pointer = nil
1532
- line_no = nil
1533
- if @is_multiline
1534
- h_pointer = history.index { |h|
1535
- h.split("\n").each_with_index { |l, i|
1536
- if l.start_with?(substr)
1537
- line_no = i
1538
- break
1539
- end
1540
- }
1541
- not line_no.nil?
1542
- }
1543
- else
1544
- h_pointer = history.index { |l|
1545
- l.start_with?(substr)
1546
- }
1547
- end
1548
- h_pointer += @history_pointer + 1 if h_pointer and @history_pointer
1549
- return if h_pointer.nil? and not substr.empty?
1550
- @history_pointer = h_pointer
1551
- if @is_multiline
1552
- if @history_pointer.nil? and substr.empty?
1553
- @buffer_of_lines = []
1554
- @line_index = 0
1555
- else
1556
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
1557
- @line_index = line_no
1558
- end
1559
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1560
- @line = @buffer_of_lines.last
1561
- @rerender_all = true
1562
- else
1563
- if @history_pointer.nil? and substr.empty?
1564
- @line = ''
1565
- else
1566
- @line = Reline::HISTORY[@history_pointer]
1567
- end
1568
- end
1569
- @cursor_max = calculate_width(@line)
1570
- arg -= 1
1571
- ed_search_next_history(key, arg: arg) if arg > 0
1572
- end
1573
- alias_method :history_search_forward, :ed_search_next_history
1574
-
1575
- private def ed_prev_history(key, arg: 1)
1576
- if @is_multiline and @line_index > 0
1577
- @previous_line_index = @line_index
1578
- @line_index -= 1
1579
- return
1580
- end
1581
- if Reline::HISTORY.empty?
1582
- return
1583
- end
1584
- if @history_pointer.nil?
1585
- @history_pointer = Reline::HISTORY.size - 1
1586
- if @is_multiline
1587
- @line_backup_in_history = whole_buffer
1588
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
1589
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1590
- @line_index = @buffer_of_lines.size - 1
1591
- @line = @buffer_of_lines.last
1592
- @rerender_all = true
1593
- else
1594
- @line_backup_in_history = @line
1595
- @line = Reline::HISTORY[@history_pointer]
1596
- end
1597
- elsif @history_pointer.zero?
1598
- return
1599
- else
1600
- if @is_multiline
1601
- Reline::HISTORY[@history_pointer] = whole_buffer
1602
- @history_pointer -= 1
1603
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
1604
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1605
- @line_index = @buffer_of_lines.size - 1
1606
- @line = @buffer_of_lines.last
1607
- @rerender_all = true
1608
- else
1609
- Reline::HISTORY[@history_pointer] = @line
1610
- @history_pointer -= 1
1611
- @line = Reline::HISTORY[@history_pointer]
1612
- end
1613
- end
1614
- if @config.editing_mode_is?(:emacs, :vi_insert)
1615
- @cursor_max = @cursor = calculate_width(@line)
1616
- @byte_pointer = @line.bytesize
1617
- elsif @config.editing_mode_is?(:vi_command)
1618
- @byte_pointer = @cursor = 0
1619
- @cursor_max = calculate_width(@line)
1620
- end
1621
- arg -= 1
1622
- ed_prev_history(key, arg: arg) if arg > 0
1623
- end
1624
-
1625
- private def ed_next_history(key, arg: 1)
1626
- if @is_multiline and @line_index < (@buffer_of_lines.size - 1)
1627
- @previous_line_index = @line_index
1628
- @line_index += 1
1629
- return
1630
- end
1631
- if @history_pointer.nil?
1632
- return
1633
- elsif @history_pointer == (Reline::HISTORY.size - 1)
1634
- if @is_multiline
1635
- @history_pointer = nil
1636
- @buffer_of_lines = @line_backup_in_history.split("\n")
1637
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1638
- @line_index = 0
1639
- @line = @buffer_of_lines.first
1640
- @rerender_all = true
1641
- else
1642
- @history_pointer = nil
1643
- @line = @line_backup_in_history
1644
- end
1645
- else
1646
- if @is_multiline
1647
- Reline::HISTORY[@history_pointer] = whole_buffer
1648
- @history_pointer += 1
1649
- @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
1650
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1651
- @line_index = 0
1652
- @line = @buffer_of_lines.first
1653
- @rerender_all = true
1654
- else
1655
- Reline::HISTORY[@history_pointer] = @line
1656
- @history_pointer += 1
1657
- @line = Reline::HISTORY[@history_pointer]
1658
- end
1659
- end
1660
- @line = '' unless @line
1661
- if @config.editing_mode_is?(:emacs, :vi_insert)
1662
- @cursor_max = @cursor = calculate_width(@line)
1663
- @byte_pointer = @line.bytesize
1664
- elsif @config.editing_mode_is?(:vi_command)
1665
- @byte_pointer = @cursor = 0
1666
- @cursor_max = calculate_width(@line)
1667
- end
1668
- arg -= 1
1669
- ed_next_history(key, arg: arg) if arg > 0
1670
- end
1671
-
1672
- private def ed_newline(key)
1673
- process_insert(force: true)
1674
- if @is_multiline
1675
- if @config.editing_mode_is?(:vi_command)
1676
- if @line_index < (@buffer_of_lines.size - 1)
1677
- ed_next_history(key) # means cursor down
1678
- else
1679
- # should check confirm_multiline_termination to finish?
1680
- finish
1681
- end
1682
- else
1683
- if @line_index == (@buffer_of_lines.size - 1)
1684
- #$stderr.puts ?* * 100
1685
- if confirm_multiline_termination
1686
- #$stderr.puts 'finish'
1687
- finish
1688
- else
1689
- key_newline(key)
1690
- end
1691
- else
1692
- # should check confirm_multiline_termination to finish?
1693
- @previous_line_index = @line_index
1694
- @line_index = @buffer_of_lines.size - 1
1695
- finish
1696
- end
1697
- end
1698
- else
1699
- if @history_pointer
1700
- Reline::HISTORY[@history_pointer] = @line
1701
- @history_pointer = nil
1702
- end
1703
- finish
1704
- end
1705
- end
1706
-
1707
- private def em_delete_prev_char(key)
1708
- if @is_multiline and @cursor == 0 and @line_index > 0
1709
- @buffer_of_lines[@line_index] = @line
1710
- @cursor = calculate_width(@buffer_of_lines[@line_index - 1])
1711
- @byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
1712
- @buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
1713
- @line_index -= 1
1714
- @line = @buffer_of_lines[@line_index]
1715
- @cursor_max = calculate_width(@line)
1716
- @rerender_all = true
1717
- elsif @cursor > 0
1718
- byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
1719
- @byte_pointer -= byte_size
1720
- @line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
1721
- width = Reline::Unicode.get_mbchar_width(mbchar)
1722
- @cursor -= width
1723
- @cursor_max -= width
1724
- end
1725
- end
1726
- alias_method :backward_delete_char, :em_delete_prev_char
1727
-
1728
- private def ed_kill_line(key)
1729
- if @line.bytesize > @byte_pointer
1730
- @line, deleted = byteslice!(@line, @byte_pointer, @line.bytesize - @byte_pointer)
1731
- @byte_pointer = @line.bytesize
1732
- @cursor = @cursor_max = calculate_width(@line)
1733
- @kill_ring.append(deleted)
1734
- elsif @is_multiline and @byte_pointer == @line.bytesize and @buffer_of_lines.size > @line_index + 1
1735
- @cursor = calculate_width(@line)
1736
- @byte_pointer = @line.bytesize
1737
- @line += @buffer_of_lines.delete_at(@line_index + 1)
1738
- @cursor_max = calculate_width(@line)
1739
- @buffer_of_lines[@line_index] = @line
1740
- @rerender_all = true
1741
- @rest_height += 1
1742
- end
1743
- end
1744
-
1745
- private def em_kill_line(key)
1746
- if @byte_pointer > 0
1747
- @line, deleted = byteslice!(@line, 0, @byte_pointer)
1748
- @byte_pointer = 0
1749
- @kill_ring.append(deleted, true)
1750
- @cursor_max = calculate_width(@line)
1751
- @cursor = 0
1752
- end
1753
- end
1754
-
1755
- private def em_delete(key)
1756
- if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
1757
- @line = nil
1758
- if @buffer_of_lines.size > 1
1759
- scroll_down(@highest_in_all - @first_line_started_from)
1760
- end
1761
- Reline::IOGate.move_cursor_column(0)
1762
- @eof = true
1763
- finish
1764
- elsif @byte_pointer < @line.bytesize
1765
- splitted_last = @line.byteslice(@byte_pointer, @line.bytesize)
1766
- mbchar = splitted_last.grapheme_clusters.first
1767
- width = Reline::Unicode.get_mbchar_width(mbchar)
1768
- @cursor_max -= width
1769
- @line, = byteslice!(@line, @byte_pointer, mbchar.bytesize)
1770
- elsif @is_multiline and @byte_pointer == @line.bytesize and @buffer_of_lines.size > @line_index + 1
1771
- @cursor = calculate_width(@line)
1772
- @byte_pointer = @line.bytesize
1773
- @line += @buffer_of_lines.delete_at(@line_index + 1)
1774
- @cursor_max = calculate_width(@line)
1775
- @buffer_of_lines[@line_index] = @line
1776
- @rerender_all = true
1777
- @rest_height += 1
1778
- end
1779
- end
1780
- alias_method :delete_char, :em_delete
1781
-
1782
- private def em_delete_or_list(key)
1783
- if @line.empty? or @byte_pointer < @line.bytesize
1784
- em_delete(key)
1785
- else # show completed list
1786
- result = call_completion_proc
1787
- if result.is_a?(Array)
1788
- complete(result, true)
1789
- end
1790
- end
1791
- end
1792
- alias_method :delete_char_or_list, :em_delete_or_list
1793
-
1794
- private def em_yank(key)
1795
- yanked = @kill_ring.yank
1796
- if yanked
1797
- @line = byteinsert(@line, @byte_pointer, yanked)
1798
- yanked_width = calculate_width(yanked)
1799
- @cursor += yanked_width
1800
- @cursor_max += yanked_width
1801
- @byte_pointer += yanked.bytesize
1802
- end
1803
- end
1804
-
1805
- private def em_yank_pop(key)
1806
- yanked, prev_yank = @kill_ring.yank_pop
1807
- if yanked
1808
- prev_yank_width = calculate_width(prev_yank)
1809
- @cursor -= prev_yank_width
1810
- @cursor_max -= prev_yank_width
1811
- @byte_pointer -= prev_yank.bytesize
1812
- @line, = byteslice!(@line, @byte_pointer, prev_yank.bytesize)
1813
- @line = byteinsert(@line, @byte_pointer, yanked)
1814
- yanked_width = calculate_width(yanked)
1815
- @cursor += yanked_width
1816
- @cursor_max += yanked_width
1817
- @byte_pointer += yanked.bytesize
1818
- end
1819
- end
1820
-
1821
- private def ed_clear_screen(key)
1822
- @cleared = true
1823
- end
1824
- alias_method :clear_screen, :ed_clear_screen
1825
-
1826
- private def em_next_word(key)
1827
- if @line.bytesize > @byte_pointer
1828
- byte_size, width = Reline::Unicode.em_forward_word(@line, @byte_pointer)
1829
- @byte_pointer += byte_size
1830
- @cursor += width
1831
- end
1832
- end
1833
- alias_method :forward_word, :em_next_word
1834
-
1835
- private def ed_prev_word(key)
1836
- if @byte_pointer > 0
1837
- byte_size, width = Reline::Unicode.em_backward_word(@line, @byte_pointer)
1838
- @byte_pointer -= byte_size
1839
- @cursor -= width
1840
- end
1841
- end
1842
- alias_method :backward_word, :ed_prev_word
1843
-
1844
- private def em_delete_next_word(key)
1845
- if @line.bytesize > @byte_pointer
1846
- byte_size, width = Reline::Unicode.em_forward_word(@line, @byte_pointer)
1847
- @line, word = byteslice!(@line, @byte_pointer, byte_size)
1848
- @kill_ring.append(word)
1849
- @cursor_max -= width
1850
- end
1851
- end
1852
-
1853
- private def ed_delete_prev_word(key)
1854
- if @byte_pointer > 0
1855
- byte_size, width = Reline::Unicode.em_backward_word(@line, @byte_pointer)
1856
- @line, word = byteslice!(@line, @byte_pointer - byte_size, byte_size)
1857
- @kill_ring.append(word, true)
1858
- @byte_pointer -= byte_size
1859
- @cursor -= width
1860
- @cursor_max -= width
1861
- end
1862
- end
1863
-
1864
- private def ed_transpose_chars(key)
1865
- if @byte_pointer > 0
1866
- if @cursor_max > @cursor
1867
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
1868
- mbchar = @line.byteslice(@byte_pointer, byte_size)
1869
- width = Reline::Unicode.get_mbchar_width(mbchar)
1870
- @cursor += width
1871
- @byte_pointer += byte_size
1872
- end
1873
- back1_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
1874
- if (@byte_pointer - back1_byte_size) > 0
1875
- back2_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer - back1_byte_size)
1876
- back2_pointer = @byte_pointer - back1_byte_size - back2_byte_size
1877
- @line, back2_mbchar = byteslice!(@line, back2_pointer, back2_byte_size)
1878
- @line = byteinsert(@line, @byte_pointer - back2_byte_size, back2_mbchar)
1879
- end
1880
- end
1881
- end
1882
- alias_method :transpose_chars, :ed_transpose_chars
1883
-
1884
- private def ed_transpose_words(key)
1885
- left_word_start, middle_start, right_word_start, after_start = Reline::Unicode.ed_transpose_words(@line, @byte_pointer)
1886
- before = @line.byteslice(0, left_word_start)
1887
- left_word = @line.byteslice(left_word_start, middle_start - left_word_start)
1888
- middle = @line.byteslice(middle_start, right_word_start - middle_start)
1889
- right_word = @line.byteslice(right_word_start, after_start - right_word_start)
1890
- after = @line.byteslice(after_start, @line.bytesize - after_start)
1891
- return if left_word.empty? or right_word.empty?
1892
- @line = before + right_word + middle + left_word + after
1893
- from_head_to_left_word = before + right_word + middle + left_word
1894
- @byte_pointer = from_head_to_left_word.bytesize
1895
- @cursor = calculate_width(from_head_to_left_word)
1896
- end
1897
- alias_method :transpose_words, :ed_transpose_words
1898
-
1899
- private def em_capitol_case(key)
1900
- if @line.bytesize > @byte_pointer
1901
- byte_size, _, new_str = Reline::Unicode.em_forward_word_with_capitalization(@line, @byte_pointer)
1902
- before = @line.byteslice(0, @byte_pointer)
1903
- after = @line.byteslice((@byte_pointer + byte_size)..-1)
1904
- @line = before + new_str + after
1905
- @byte_pointer += new_str.bytesize
1906
- @cursor += calculate_width(new_str)
1907
- end
1908
- end
1909
- alias_method :capitalize_word, :em_capitol_case
1910
-
1911
- private def em_lower_case(key)
1912
- if @line.bytesize > @byte_pointer
1913
- byte_size, = Reline::Unicode.em_forward_word(@line, @byte_pointer)
1914
- part = @line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
1915
- mbchar =~ /[A-Z]/ ? mbchar.downcase : mbchar
1916
- }.join
1917
- rest = @line.byteslice((@byte_pointer + byte_size)..-1)
1918
- @line = @line.byteslice(0, @byte_pointer) + part
1919
- @byte_pointer = @line.bytesize
1920
- @cursor = calculate_width(@line)
1921
- @cursor_max = @cursor + calculate_width(rest)
1922
- @line += rest
1923
- end
1924
- end
1925
- alias_method :downcase_word, :em_lower_case
1926
-
1927
- private def em_upper_case(key)
1928
- if @line.bytesize > @byte_pointer
1929
- byte_size, = Reline::Unicode.em_forward_word(@line, @byte_pointer)
1930
- part = @line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
1931
- mbchar =~ /[a-z]/ ? mbchar.upcase : mbchar
1932
- }.join
1933
- rest = @line.byteslice((@byte_pointer + byte_size)..-1)
1934
- @line = @line.byteslice(0, @byte_pointer) + part
1935
- @byte_pointer = @line.bytesize
1936
- @cursor = calculate_width(@line)
1937
- @cursor_max = @cursor + calculate_width(rest)
1938
- @line += rest
1939
- end
1940
- end
1941
- alias_method :upcase_word, :em_upper_case
1942
-
1943
- private def em_kill_region(key)
1944
- if @byte_pointer > 0
1945
- byte_size, width = Reline::Unicode.em_big_backward_word(@line, @byte_pointer)
1946
- @line, deleted = byteslice!(@line, @byte_pointer - byte_size, byte_size)
1947
- @byte_pointer -= byte_size
1948
- @cursor -= width
1949
- @cursor_max -= width
1950
- @kill_ring.append(deleted)
1951
- end
1952
- end
1953
-
1954
- private def copy_for_vi(text)
1955
- if @config.editing_mode_is?(:vi_insert) or @config.editing_mode_is?(:vi_command)
1956
- @vi_clipboard = text
1957
- end
1958
- end
1959
-
1960
- private def vi_insert(key)
1961
- @config.editing_mode = :vi_insert
1962
- end
1963
-
1964
- private def vi_add(key)
1965
- @config.editing_mode = :vi_insert
1966
- ed_next_char(key)
1967
- end
1968
-
1969
- private def vi_command_mode(key)
1970
- ed_prev_char(key)
1971
- @config.editing_mode = :vi_command
1972
- end
1973
- alias_method :backward_char, :ed_prev_char
1974
-
1975
- private def vi_next_word(key, arg: 1)
1976
- if @line.bytesize > @byte_pointer
1977
- byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer)
1978
- @byte_pointer += byte_size
1979
- @cursor += width
1980
- end
1981
- arg -= 1
1982
- vi_next_word(key, arg: arg) if arg > 0
1983
- end
1984
-
1985
- private def vi_prev_word(key, arg: 1)
1986
- if @byte_pointer > 0
1987
- byte_size, width = Reline::Unicode.vi_backward_word(@line, @byte_pointer)
1988
- @byte_pointer -= byte_size
1989
- @cursor -= width
1990
- end
1991
- arg -= 1
1992
- vi_prev_word(key, arg: arg) if arg > 0
1993
- end
1994
-
1995
- private def vi_end_word(key, arg: 1)
1996
- if @line.bytesize > @byte_pointer
1997
- byte_size, width = Reline::Unicode.vi_forward_end_word(@line, @byte_pointer)
1998
- @byte_pointer += byte_size
1999
- @cursor += width
2000
- end
2001
- arg -= 1
2002
- vi_end_word(key, arg: arg) if arg > 0
2003
- end
2004
-
2005
- private def vi_next_big_word(key, arg: 1)
2006
- if @line.bytesize > @byte_pointer
2007
- byte_size, width = Reline::Unicode.vi_big_forward_word(@line, @byte_pointer)
2008
- @byte_pointer += byte_size
2009
- @cursor += width
2010
- end
2011
- arg -= 1
2012
- vi_next_big_word(key, arg: arg) if arg > 0
2013
- end
2014
-
2015
- private def vi_prev_big_word(key, arg: 1)
2016
- if @byte_pointer > 0
2017
- byte_size, width = Reline::Unicode.vi_big_backward_word(@line, @byte_pointer)
2018
- @byte_pointer -= byte_size
2019
- @cursor -= width
2020
- end
2021
- arg -= 1
2022
- vi_prev_big_word(key, arg: arg) if arg > 0
2023
- end
2024
-
2025
- private def vi_end_big_word(key, arg: 1)
2026
- if @line.bytesize > @byte_pointer
2027
- byte_size, width = Reline::Unicode.vi_big_forward_end_word(@line, @byte_pointer)
2028
- @byte_pointer += byte_size
2029
- @cursor += width
2030
- end
2031
- arg -= 1
2032
- vi_end_big_word(key, arg: arg) if arg > 0
2033
- end
2034
-
2035
- private def vi_delete_prev_char(key)
2036
- if @is_multiline and @cursor == 0 and @line_index > 0
2037
- @buffer_of_lines[@line_index] = @line
2038
- @cursor = calculate_width(@buffer_of_lines[@line_index - 1])
2039
- @byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
2040
- @buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
2041
- @line_index -= 1
2042
- @line = @buffer_of_lines[@line_index]
2043
- @cursor_max = calculate_width(@line)
2044
- @rerender_all = true
2045
- elsif @cursor > 0
2046
- byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
2047
- @byte_pointer -= byte_size
2048
- @line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
2049
- width = Reline::Unicode.get_mbchar_width(mbchar)
2050
- @cursor -= width
2051
- @cursor_max -= width
2052
- end
2053
- end
2054
-
2055
- private def vi_insert_at_bol(key)
2056
- ed_move_to_beg(key)
2057
- @config.editing_mode = :vi_insert
2058
- end
2059
-
2060
- private def vi_add_at_eol(key)
2061
- ed_move_to_end(key)
2062
- @config.editing_mode = :vi_insert
2063
- end
2064
-
2065
- private def ed_delete_prev_char(key, arg: 1)
2066
- deleted = ''
2067
- arg.times do
2068
- if @cursor > 0
2069
- byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
2070
- @byte_pointer -= byte_size
2071
- @line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
2072
- deleted.prepend(mbchar)
2073
- width = Reline::Unicode.get_mbchar_width(mbchar)
2074
- @cursor -= width
2075
- @cursor_max -= width
2076
- end
2077
- end
2078
- copy_for_vi(deleted)
2079
- end
2080
-
2081
- private def vi_zero(key)
2082
- @byte_pointer = 0
2083
- @cursor = 0
2084
- end
2085
-
2086
- private def vi_change_meta(key)
2087
- @waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
2088
- if byte_pointer_diff > 0
2089
- @line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
2090
- elsif byte_pointer_diff < 0
2091
- @line, cut = byteslice!(@line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2092
- end
2093
- copy_for_vi(cut)
2094
- @cursor += cursor_diff if cursor_diff < 0
2095
- @cursor_max -= cursor_diff.abs
2096
- @byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
2097
- @config.editing_mode = :vi_insert
2098
- }
2099
- end
2100
-
2101
- private def vi_delete_meta(key)
2102
- @waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
2103
- if byte_pointer_diff > 0
2104
- @line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
2105
- elsif byte_pointer_diff < 0
2106
- @line, cut = byteslice!(@line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2107
- end
2108
- copy_for_vi(cut)
2109
- @cursor += cursor_diff if cursor_diff < 0
2110
- @cursor_max -= cursor_diff.abs
2111
- @byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
2112
- }
2113
- end
2114
-
2115
- private def vi_yank(key)
2116
- end
2117
-
2118
- private def vi_list_or_eof(key)
2119
- if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
2120
- @line = nil
2121
- if @buffer_of_lines.size > 1
2122
- scroll_down(@highest_in_all - @first_line_started_from)
2123
- end
2124
- Reline::IOGate.move_cursor_column(0)
2125
- @eof = true
2126
- finish
2127
- else
2128
- ed_newline(key)
2129
- end
2130
- end
2131
- alias_method :vi_end_of_transmission, :vi_list_or_eof
2132
- alias_method :vi_eof_maybe, :vi_list_or_eof
2133
-
2134
- private def ed_delete_next_char(key, arg: 1)
2135
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
2136
- unless @line.empty? || byte_size == 0
2137
- @line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
2138
- copy_for_vi(mbchar)
2139
- width = Reline::Unicode.get_mbchar_width(mbchar)
2140
- @cursor_max -= width
2141
- if @cursor > 0 and @cursor >= @cursor_max
2142
- @byte_pointer -= byte_size
2143
- @cursor -= width
2144
- end
2145
- end
2146
- arg -= 1
2147
- ed_delete_next_char(key, arg: arg) if arg > 0
2148
- end
2149
-
2150
- private def vi_to_history_line(key)
2151
- if Reline::HISTORY.empty?
2152
- return
2153
- end
2154
- if @history_pointer.nil?
2155
- @history_pointer = 0
2156
- @line_backup_in_history = @line
2157
- @line = Reline::HISTORY[@history_pointer]
2158
- @cursor_max = calculate_width(@line)
2159
- @cursor = 0
2160
- @byte_pointer = 0
2161
- elsif @history_pointer.zero?
2162
- return
2163
- else
2164
- Reline::HISTORY[@history_pointer] = @line
2165
- @history_pointer = 0
2166
- @line = Reline::HISTORY[@history_pointer]
2167
- @cursor_max = calculate_width(@line)
2168
- @cursor = 0
2169
- @byte_pointer = 0
2170
- end
2171
- end
2172
-
2173
- private def vi_histedit(key)
2174
- path = Tempfile.open { |fp|
2175
- fp.write @line
2176
- fp.path
2177
- }
2178
- system("#{ENV['EDITOR']} #{path}")
2179
- @line = File.read(path)
2180
- finish
2181
- end
2182
-
2183
- private def vi_paste_prev(key, arg: 1)
2184
- if @vi_clipboard.size > 0
2185
- @line = byteinsert(@line, @byte_pointer, @vi_clipboard)
2186
- @cursor_max += calculate_width(@vi_clipboard)
2187
- cursor_point = @vi_clipboard.grapheme_clusters[0..-2].join
2188
- @cursor += calculate_width(cursor_point)
2189
- @byte_pointer += cursor_point.bytesize
2190
- end
2191
- arg -= 1
2192
- vi_paste_prev(key, arg: arg) if arg > 0
2193
- end
2194
-
2195
- private def vi_paste_next(key, arg: 1)
2196
- if @vi_clipboard.size > 0
2197
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
2198
- @line = byteinsert(@line, @byte_pointer + byte_size, @vi_clipboard)
2199
- @cursor_max += calculate_width(@vi_clipboard)
2200
- @cursor += calculate_width(@vi_clipboard)
2201
- @byte_pointer += @vi_clipboard.bytesize
2202
- end
2203
- arg -= 1
2204
- vi_paste_next(key, arg: arg) if arg > 0
2205
- end
2206
-
2207
- private def ed_argument_digit(key)
2208
- if @vi_arg.nil?
2209
- unless key.chr.to_i.zero?
2210
- @vi_arg = key.chr.to_i
2211
- end
2212
- else
2213
- @vi_arg = @vi_arg * 10 + key.chr.to_i
2214
- end
2215
- end
2216
-
2217
- private def vi_to_column(key, arg: 0)
2218
- @byte_pointer, @cursor = @line.grapheme_clusters.inject([0, 0]) { |total, gc|
2219
- # total has [byte_size, cursor]
2220
- mbchar_width = Reline::Unicode.get_mbchar_width(gc)
2221
- if (total.last + mbchar_width) >= arg
2222
- break total
2223
- elsif (total.last + mbchar_width) >= @cursor_max
2224
- break total
2225
- else
2226
- total = [total.first + gc.bytesize, total.last + mbchar_width]
2227
- total
2228
- end
2229
- }
2230
- end
2231
-
2232
- private def vi_replace_char(key, arg: 1)
2233
- @waiting_proc = ->(k) {
2234
- if arg == 1
2235
- byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
2236
- before = @line.byteslice(0, @byte_pointer)
2237
- remaining_point = @byte_pointer + byte_size
2238
- after = @line.byteslice(remaining_point, @line.size - remaining_point)
2239
- @line = before + k.chr + after
2240
- @cursor_max = calculate_width(@line)
2241
- @waiting_proc = nil
2242
- elsif arg > 1
2243
- byte_size = 0
2244
- arg.times do
2245
- byte_size += Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer + byte_size)
2246
- end
2247
- before = @line.byteslice(0, @byte_pointer)
2248
- remaining_point = @byte_pointer + byte_size
2249
- after = @line.byteslice(remaining_point, @line.size - remaining_point)
2250
- replaced = k.chr * arg
2251
- @line = before + replaced + after
2252
- @byte_pointer += replaced.bytesize
2253
- @cursor += calculate_width(replaced)
2254
- @cursor_max = calculate_width(@line)
2255
- @waiting_proc = nil
2256
- end
2257
- }
2258
- end
2259
-
2260
- private def vi_next_char(key, arg: 1)
2261
- @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) }
2262
- end
2263
-
2264
- private def vi_to_next_char(key, arg: 1)
2265
- @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, true) }
2266
- end
2267
-
2268
- private def search_next_char(key, arg, need_prev_char = false)
2269
- if key.instance_of?(String)
2270
- inputed_char = key
2271
- else
2272
- inputed_char = key.chr
2273
- end
2274
- prev_total = nil
2275
- total = nil
2276
- found = false
2277
- @line.byteslice(@byte_pointer..-1).grapheme_clusters.each do |mbchar|
2278
- # total has [byte_size, cursor]
2279
- unless total
2280
- # skip cursor point
2281
- width = Reline::Unicode.get_mbchar_width(mbchar)
2282
- total = [mbchar.bytesize, width]
2283
- else
2284
- if inputed_char == mbchar
2285
- arg -= 1
2286
- if arg.zero?
2287
- found = true
2288
- break
2289
- end
2290
- end
2291
- width = Reline::Unicode.get_mbchar_width(mbchar)
2292
- prev_total = total
2293
- total = [total.first + mbchar.bytesize, total.last + width]
2294
- end
2295
- end
2296
- if not need_prev_char and found and total
2297
- byte_size, width = total
2298
- @byte_pointer += byte_size
2299
- @cursor += width
2300
- elsif need_prev_char and found and prev_total
2301
- byte_size, width = prev_total
2302
- @byte_pointer += byte_size
2303
- @cursor += width
2304
- end
2305
- @waiting_proc = nil
2306
- end
2307
-
2308
- private def vi_prev_char(key, arg: 1)
2309
- @waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg) }
2310
- end
2311
-
2312
- private def vi_to_prev_char(key, arg: 1)
2313
- @waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg, true) }
2314
- end
2315
-
2316
- private def search_prev_char(key, arg, need_next_char = false)
2317
- if key.instance_of?(String)
2318
- inputed_char = key
2319
- else
2320
- inputed_char = key.chr
2321
- end
2322
- prev_total = nil
2323
- total = nil
2324
- found = false
2325
- @line.byteslice(0..@byte_pointer).grapheme_clusters.reverse_each do |mbchar|
2326
- # total has [byte_size, cursor]
2327
- unless total
2328
- # skip cursor point
2329
- width = Reline::Unicode.get_mbchar_width(mbchar)
2330
- total = [mbchar.bytesize, width]
2331
- else
2332
- if inputed_char == mbchar
2333
- arg -= 1
2334
- if arg.zero?
2335
- found = true
2336
- break
2337
- end
2338
- end
2339
- width = Reline::Unicode.get_mbchar_width(mbchar)
2340
- prev_total = total
2341
- total = [total.first + mbchar.bytesize, total.last + width]
2342
- end
2343
- end
2344
- if not need_next_char and found and total
2345
- byte_size, width = total
2346
- @byte_pointer -= byte_size
2347
- @cursor -= width
2348
- elsif need_next_char and found and prev_total
2349
- byte_size, width = prev_total
2350
- @byte_pointer -= byte_size
2351
- @cursor -= width
2352
- end
2353
- @waiting_proc = nil
2354
- end
2355
-
2356
- private def vi_join_lines(key, arg: 1)
2357
- if @is_multiline and @buffer_of_lines.size > @line_index + 1
2358
- @cursor = calculate_width(@line)
2359
- @byte_pointer = @line.bytesize
2360
- @line += ' ' + @buffer_of_lines.delete_at(@line_index + 1).lstrip
2361
- @cursor_max = calculate_width(@line)
2362
- @buffer_of_lines[@line_index] = @line
2363
- @rerender_all = true
2364
- @rest_height += 1
2365
- end
2366
- arg -= 1
2367
- vi_join_lines(key, arg: arg) if arg > 0
2368
- end
2369
-
2370
- private def em_set_mark(key)
2371
- @mark_pointer = [@byte_pointer, @line_index]
2372
- end
2373
- alias_method :set_mark, :em_set_mark
2374
-
2375
- private def em_exchange_mark(key)
2376
- new_pointer = [@byte_pointer, @line_index]
2377
- @previous_line_index = @line_index
2378
- @byte_pointer, @line_index = @mark_pointer
2379
- @cursor = calculate_width(@line.byteslice(0, @byte_pointer))
2380
- @cursor_max = calculate_width(@line)
2381
- @mark_pointer = new_pointer
2382
- end
2383
- alias_method :exchange_point_and_mark, :em_exchange_mark
2384
- end