reline 0.1.4 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41e0f73e51def110f060454c69c3a3a29d3b0cedce2c432cc594df4f6cccf012
4
- data.tar.gz: c47d69d20e28f8940c86eedba5349892ecf5e5745e83a8c701eab327e326c6ba
3
+ metadata.gz: 981888e54748ace72084309bf4d0b832503970956188b3865629da1c577b5edb
4
+ data.tar.gz: 11815b8b07d66ef247ab83e37d6c8884fdc4745e681b298237e2e32b631c4cbb
5
5
  SHA512:
6
- metadata.gz: b494460955ab34ccf2e4c0443e76598c692d2846162be5081ecc8234181b47771721e5969fa76acd54bcce7f6d2c3dbc7e6469b23897c144c6233db5c08c915b
7
- data.tar.gz: 7c0f5bd0caf7f79113b479e9785aa2260536356ff7196fa1d034fe669f60fca7078771575d8002edf18a5c256105e22d550d9b4e0382071e2c240ef0a34b0287
6
+ metadata.gz: 8b9df7fa6a4e7196773314b8e5287021fbdfcdd81372740c0c32d60c03cc4aed8fda02f5b14e337c428c160b1ff5a2c4144f5a5866af8f57e50fcb25b51b9099
7
+ data.tar.gz: 50920eaeb2ef94d831c4eb4d798e41ccf9c7a826f237b1fc3343969e6af2ea05332cb38e4638c2fa439a29231a3789b0f4d0ee731d3106aaa6f6d08b12cbcb39
@@ -7,6 +7,7 @@ require 'reline/key_actor'
7
7
  require 'reline/key_stroke'
8
8
  require 'reline/line_editor'
9
9
  require 'reline/history'
10
+ require 'rbconfig'
10
11
 
11
12
  module Reline
12
13
  FILENAME_COMPLETION_PROC = nil
@@ -98,22 +99,22 @@ module Reline
98
99
  end
99
100
 
100
101
  def completion_proc=(p)
101
- raise ArgumentError unless p.respond_to?(:call)
102
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
102
103
  @completion_proc = p
103
104
  end
104
105
 
105
106
  def output_modifier_proc=(p)
106
- raise ArgumentError unless p.respond_to?(:call)
107
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
107
108
  @output_modifier_proc = p
108
109
  end
109
110
 
110
111
  def prompt_proc=(p)
111
- raise ArgumentError unless p.respond_to?(:call)
112
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
112
113
  @prompt_proc = p
113
114
  end
114
115
 
115
116
  def auto_indent_proc=(p)
116
- raise ArgumentError unless p.respond_to?(:call)
117
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
117
118
  @auto_indent_proc = p
118
119
  end
119
120
 
@@ -122,7 +123,7 @@ module Reline
122
123
  end
123
124
 
124
125
  def dig_perfect_match_proc=(p)
125
- raise ArgumentError unless p.respond_to?(:call)
126
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
126
127
  @dig_perfect_match_proc = p
127
128
  end
128
129
 
@@ -222,7 +223,6 @@ module Reline
222
223
  line_editor.auto_indent_proc = auto_indent_proc
223
224
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
224
225
  line_editor.pre_input_hook = pre_input_hook
225
- line_editor.rerender
226
226
 
227
227
  unless config.test_mode
228
228
  config.read
@@ -232,17 +232,27 @@ module Reline
232
232
  end
233
233
  end
234
234
 
235
+ line_editor.rerender
236
+
235
237
  begin
238
+ prev_pasting_state = false
236
239
  loop do
240
+ prev_pasting_state = Reline::IOGate.in_pasting?
237
241
  read_io(config.keyseq_timeout) { |inputs|
238
242
  inputs.each { |c|
239
243
  line_editor.input_key(c)
240
244
  line_editor.rerender
241
245
  }
242
246
  }
247
+ if prev_pasting_state == true and not Reline::IOGate.in_pasting? and not line_editor.finished?
248
+ prev_pasting_state = false
249
+ line_editor.rerender_all
250
+ end
243
251
  break if line_editor.finished?
244
252
  end
245
253
  Reline::IOGate.move_cursor_column(0)
254
+ rescue Errno::EIO
255
+ # Maybe the I/O has been closed.
246
256
  rescue StandardError => e
247
257
  line_editor.finalize
248
258
  Reline::IOGate.deprep(otio)
@@ -28,12 +28,22 @@ class Reline::ANSI
28
28
  [27, 71, 67] => :ed_next_char, # →
29
29
  [27, 71, 68] => :ed_prev_char, # ←
30
30
 
31
+ # urxvt / exoterm
32
+ [27, 91, 55, 126] => :ed_move_to_beg, # Home
33
+ [27, 91, 56, 126] => :ed_move_to_end, # End
34
+
31
35
  # GNOME
32
36
  [27, 79, 72] => :ed_move_to_beg, # Home
33
37
  [27, 79, 70] => :ed_move_to_end, # End
34
38
  # Del is 0x08
35
39
  # Arrow keys are the same of KDE
36
40
 
41
+ # iTerm2
42
+ [27, 27, 91, 67] => :em_next_word, # Option+→
43
+ [27, 27, 91, 68] => :ed_prev_word, # Option+←
44
+ [195, 166] => :em_next_word, # Option+f
45
+ [195, 162] => :ed_prev_word, # Option+b
46
+
37
47
  # others
38
48
  [27, 32] => :em_set_mark, # M-<space>
39
49
  [24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows
@@ -61,8 +71,29 @@ class Reline::ANSI
61
71
  unless @@buf.empty?
62
72
  return @@buf.shift
63
73
  end
64
- c = @@input.raw(intr: true, &:getbyte)
74
+ until c = @@input.raw(intr: true, &:getbyte)
75
+ sleep 0.1
76
+ end
65
77
  (c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
78
+ rescue Errno::EIO
79
+ # Maybe the I/O has been closed.
80
+ nil
81
+ end
82
+
83
+ def self.in_pasting?
84
+ not Reline::IOGate.empty_buffer?
85
+ end
86
+
87
+ def self.empty_buffer?
88
+ unless @@buf.empty?
89
+ return false
90
+ end
91
+ rs, = IO.select([@@input], [], [], 0.00001)
92
+ if rs and rs[0]
93
+ false
94
+ else
95
+ true
96
+ end
66
97
  end
67
98
 
68
99
  def self.ungetc(c)
@@ -105,10 +136,13 @@ class Reline::ANSI
105
136
  @@input.raw do |stdin|
106
137
  @@output << "\e[6n"
107
138
  @@output.flush
108
- while (c = stdin.getc) != 'R'
109
- res << c if c
139
+ loop do
140
+ c = stdin.getc
141
+ next if c.nil?
142
+ res << c
143
+ m = res.match(/\e\[(?<row>\d+);(?<column>\d+)R/)
144
+ break if m
110
145
  end
111
- m = res.match(/\e\[(?<row>\d+);(?<column>\d+)/)
112
146
  (m.pre_match + m.post_match).chars.reverse_each do |ch|
113
147
  stdin.ungetc ch
114
148
  end
@@ -116,9 +150,16 @@ class Reline::ANSI
116
150
  column = m[:column].to_i - 1
117
151
  row = m[:row].to_i - 1
118
152
  rescue Errno::ENOTTY
119
- buf = @@output.pread(@@output.pos, 0)
120
- row = buf.count("\n")
121
- column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
153
+ begin
154
+ buf = @@output.pread(@@output.pos, 0)
155
+ row = buf.count("\n")
156
+ column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
157
+ rescue Errno::ESPIPE
158
+ # Just returns column 1 for ambiguous width because this I/O is not
159
+ # tty and can't seek.
160
+ row = 0
161
+ column = 1
162
+ end
122
163
  end
123
164
  Reline::CursorPos.new(column, row)
124
165
  end
@@ -1,5 +1,3 @@
1
- require 'pathname'
2
-
3
1
  class Reline::Config
4
2
  attr_reader :test_mode
5
3
 
@@ -35,6 +33,10 @@ class Reline::Config
35
33
  show-all-if-ambiguous
36
34
  show-all-if-unmodified
37
35
  visible-stats
36
+ show-mode-in-prompt
37
+ vi-cmd-mode-icon
38
+ vi-ins-mode-icon
39
+ emacs-mode-string
38
40
  }
39
41
  VARIABLE_NAME_SYMBOLS = VARIABLE_NAMES.map { |v| :"#{v.tr(?-, ?_)}" }
40
42
  VARIABLE_NAME_SYMBOLS.each do |v|
@@ -52,6 +54,10 @@ class Reline::Config
52
54
  @key_actors[:emacs] = Reline::KeyActor::Emacs.new
53
55
  @key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
54
56
  @key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
57
+ @vi_cmd_mode_icon = '(cmd)'
58
+ @vi_ins_mode_icon = '(ins)'
59
+ @emacs_mode_string = '@'
60
+ # https://tiswww.case.edu/php/chet/readline/readline.html#IDX25
55
61
  @history_size = -1 # unlimited
56
62
  @keyseq_timeout = 500
57
63
  @test_mode = false
@@ -159,7 +165,7 @@ class Reline::Config
159
165
 
160
166
  case line
161
167
  when /^set +([^ ]+) +([^ ]+)/i
162
- var, value = $1.downcase, $2.downcase
168
+ var, value = $1.downcase, $2
163
169
  bind_variable(var, value)
164
170
  next
165
171
  when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
@@ -209,7 +215,11 @@ class Reline::Config
209
215
  def bind_variable(name, value)
210
216
  case name
211
217
  when 'history-size'
212
- @history_size = value.to_i
218
+ begin
219
+ @history_size = Integer(value)
220
+ rescue ArgumentError
221
+ @history_size = 500
222
+ end
213
223
  when 'bell-style'
214
224
  @bell_style =
215
225
  case value
@@ -248,12 +258,35 @@ class Reline::Config
248
258
  end
249
259
  when 'keyseq-timeout'
250
260
  @keyseq_timeout = value.to_i
261
+ when 'show-mode-in-prompt'
262
+ case value
263
+ when 'off'
264
+ @show_mode_in_prompt = false
265
+ when 'on'
266
+ @show_mode_in_prompt = true
267
+ else
268
+ @show_mode_in_prompt = false
269
+ end
270
+ when 'vi-cmd-mode-string'
271
+ @vi_cmd_mode_icon = retrieve_string(value)
272
+ when 'vi-ins-mode-string'
273
+ @vi_ins_mode_icon = retrieve_string(value)
274
+ when 'emacs-mode-string'
275
+ @emacs_mode_string = retrieve_string(value)
251
276
  when *VARIABLE_NAMES then
252
277
  variable_name = :"@#{name.tr(?-, ?_)}"
253
278
  instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
254
279
  end
255
280
  end
256
281
 
282
+ def retrieve_string(str)
283
+ if str =~ /\A"(.*)"\z/
284
+ parse_keyseq($1).map(&:chr).join
285
+ else
286
+ parse_keyseq(str).map(&:chr).join
287
+ end
288
+ end
289
+
257
290
  def bind_key(key, func_name)
258
291
  if key =~ /\A"(.*)"\z/
259
292
  keyseq = parse_keyseq($1)
@@ -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,6 +71,20 @@ class Reline::GeneralIO
67
71
  def self.set_winch_handler(&handler)
68
72
  end
69
73
 
74
+ @@pasting = false
75
+
76
+ def self.in_pasting?
77
+ @@pasting
78
+ end
79
+
80
+ def self.start_pasting
81
+ @@pasting = true
82
+ end
83
+
84
+ def self.finish_pasting
85
+ @@pasting = false
86
+ end
87
+
70
88
  def self.prep
71
89
  end
72
90
 
@@ -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
@@ -42,6 +42,8 @@ class Reline::KeyStroke
42
42
  expand(expand(rhs_bytes) + expand(input.drop(lhs.size)))
43
43
  when Symbol
44
44
  [rhs] + expand(input.drop(lhs.size))
45
+ when Array
46
+ rhs
45
47
  end
46
48
  end
47
49
 
@@ -2,7 +2,6 @@ require 'reline/kill_ring'
2
2
  require 'reline/unicode'
3
3
 
4
4
  require 'tempfile'
5
- require 'pathname'
6
5
 
7
6
  class Reline::LineEditor
8
7
  # TODO: undo
@@ -51,18 +50,20 @@ class Reline::LineEditor
51
50
  CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
52
51
  MenuInfo = Struct.new('MenuInfo', :target, :list)
53
52
 
54
- CSI_REGEXP = /\e\[[\d;]*[ABCDEFGHJKSTfminsuhl]/
55
- OSC_REGEXP = /\e\]\d+(?:;[^;]+)*\a/
56
- NON_PRINTING_START = "\1"
57
- NON_PRINTING_END = "\2"
58
- WIDTH_SCANNER = /\G(?:#{NON_PRINTING_START}|#{NON_PRINTING_END}|#{CSI_REGEXP}|#{OSC_REGEXP}|\X)/
59
-
60
53
  def initialize(config, encoding)
61
54
  @config = config
62
55
  @completion_append_character = ''
63
56
  reset_variables(encoding: encoding)
64
57
  end
65
58
 
59
+ def simplified_rendering?
60
+ if finished?
61
+ false
62
+ else
63
+ not @rerender_all and not finished? and Reline::IOGate.in_pasting?
64
+ end
65
+ end
66
+
66
67
  private def check_multiline_prompt(buffer, prompt)
67
68
  if @vi_arg
68
69
  prompt = "(arg: #{@vi_arg}) "
@@ -73,14 +74,39 @@ class Reline::LineEditor
73
74
  else
74
75
  prompt = @prompt
75
76
  end
77
+ return [prompt, calculate_width(prompt, true), [prompt] * buffer.size] if simplified_rendering?
76
78
  if @prompt_proc
77
79
  prompt_list = @prompt_proc.(buffer)
78
80
  prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
81
+ if @config.show_mode_in_prompt
82
+ if @config.editing_mode_is?(:vi_command)
83
+ mode_icon = @config.vi_cmd_mode_icon
84
+ elsif @config.editing_mode_is?(:vi_insert)
85
+ mode_icon = @config.vi_ins_mode_icon
86
+ elsif @config.editing_mode_is?(:emacs)
87
+ mode_icon = @config.emacs_mode_string
88
+ else
89
+ mode_icon = '?'
90
+ end
91
+ prompt_list.map!{ |pr| mode_icon + pr }
92
+ end
79
93
  prompt = prompt_list[@line_index]
80
94
  prompt_width = calculate_width(prompt, true)
81
95
  [prompt, prompt_width, prompt_list]
82
96
  else
83
97
  prompt_width = calculate_width(prompt, true)
98
+ if @config.show_mode_in_prompt
99
+ if @config.editing_mode_is?(:vi_command)
100
+ mode_icon = @config.vi_cmd_mode_icon
101
+ elsif @config.editing_mode_is?(:vi_insert)
102
+ mode_icon = @config.vi_ins_mode_icon
103
+ elsif @config.editing_mode_is?(:emacs)
104
+ mode_icon = @config.emacs_mode_string
105
+ else
106
+ mode_icon = '?'
107
+ end
108
+ prompt = mode_icon + prompt
109
+ end
84
110
  [prompt, prompt_width, nil]
85
111
  end
86
112
  end
@@ -116,7 +142,7 @@ class Reline::LineEditor
116
142
  if @line_index.zero?
117
143
  0
118
144
  else
119
- calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
145
+ calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
120
146
  end
121
147
  if @prompt_proc
122
148
  prompt = prompt_list[@line_index]
@@ -153,6 +179,7 @@ class Reline::LineEditor
153
179
  @vi_arg = nil
154
180
  @waiting_proc = nil
155
181
  @waiting_operator_proc = nil
182
+ @waiting_operator_vi_arg = nil
156
183
  @completion_journey_data = nil
157
184
  @completion_state = CompletionState::NORMAL
158
185
  @perfect_matched = nil
@@ -160,7 +187,9 @@ class Reline::LineEditor
160
187
  @first_prompt = true
161
188
  @searching_prompt = nil
162
189
  @first_char = true
190
+ @add_newline_to_end_of_buffer = false
163
191
  @eof = false
192
+ @continuous_insertion_buffer = String.new(encoding: @encoding)
164
193
  reset_line
165
194
  end
166
195
 
@@ -190,10 +219,10 @@ class Reline::LineEditor
190
219
  @is_multiline = false
191
220
  end
192
221
 
193
- private def calculate_height_by_lines(lines, prompt_list)
222
+ private def calculate_height_by_lines(lines, prompt)
194
223
  result = 0
224
+ prompt_list = prompt.is_a?(Array) ? prompt : nil
195
225
  lines.each_with_index { |line, i|
196
- prompt = ''
197
226
  prompt = prompt_list[i] if prompt_list and prompt_list[i]
198
227
  result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
199
228
  }
@@ -211,40 +240,8 @@ class Reline::LineEditor
211
240
  width.div(@screen_size.last) + 1
212
241
  end
213
242
 
214
- private def split_by_width(prompt, str, max_width)
215
- lines = [String.new(encoding: @encoding)]
216
- height = 1
217
- width = 0
218
- rest = "#{prompt}#{str}".encode(Encoding::UTF_8)
219
- in_zero_width = false
220
- rest.scan(WIDTH_SCANNER) do |gc|
221
- case gc
222
- when NON_PRINTING_START
223
- in_zero_width = true
224
- when NON_PRINTING_END
225
- in_zero_width = false
226
- when CSI_REGEXP, OSC_REGEXP
227
- lines.last << gc
228
- else
229
- unless in_zero_width
230
- mbchar_width = Reline::Unicode.get_mbchar_width(gc)
231
- if (width += mbchar_width) > max_width
232
- width = mbchar_width
233
- lines << nil
234
- lines << String.new(encoding: @encoding)
235
- height += 1
236
- end
237
- end
238
- lines.last << gc
239
- end
240
- end
241
- # The cursor moves to next line in first
242
- if width == max_width
243
- lines << nil
244
- lines << String.new(encoding: @encoding)
245
- height += 1
246
- end
247
- [lines, height]
243
+ private def split_by_width(str, max_width)
244
+ Reline::Unicode.split_by_width(str, max_width, @encoding)
248
245
  end
249
246
 
250
247
  private def scroll_down(val)
@@ -312,6 +309,11 @@ class Reline::LineEditor
312
309
  @byte_pointer = new_byte_pointer
313
310
  end
314
311
 
312
+ def rerender_all
313
+ @rerender_all = true
314
+ rerender
315
+ end
316
+
315
317
  def rerender
316
318
  return if @line.nil?
317
319
  if @menu_info
@@ -351,14 +353,29 @@ class Reline::LineEditor
351
353
  end
352
354
  new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
353
355
  # FIXME: end of logical line sometimes breaks
354
- if @previous_line_index or new_highest_in_this != @highest_in_this
356
+ if @add_newline_to_end_of_buffer
357
+ scroll_down(1)
358
+ new_lines = whole_lines(index: @previous_line_index, line: @line)
359
+ prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
360
+ @buffer_of_lines[@previous_line_index] = @line
361
+ @line = @buffer_of_lines[@line_index]
362
+ render_partial(prompt, prompt_width, @line, false)
363
+ @cursor = @cursor_max = calculate_width(@line)
364
+ @byte_pointer = @line.bytesize
365
+ @highest_in_all += @highest_in_this
366
+ @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
367
+ @first_line_started_from += @started_from + 1
368
+ @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
369
+ @previous_line_index = nil
370
+ @add_newline_to_end_of_buffer = false
371
+ elsif @previous_line_index or new_highest_in_this != @highest_in_this
355
372
  if @previous_line_index
356
373
  new_lines = whole_lines(index: @previous_line_index, line: @line)
357
374
  else
358
375
  new_lines = whole_lines
359
376
  end
360
377
  prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
361
- all_height = calculate_height_by_lines(new_lines, prompt_list)
378
+ all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
362
379
  diff = all_height - @highest_in_all
363
380
  move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
364
381
  if diff > 0
@@ -398,7 +415,7 @@ class Reline::LineEditor
398
415
  if @line_index.zero?
399
416
  0
400
417
  else
401
- calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
418
+ calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
402
419
  end
403
420
  if @prompt_proc
404
421
  prompt = prompt_list[@line_index]
@@ -457,7 +474,7 @@ class Reline::LineEditor
457
474
  if @line_index.zero?
458
475
  0
459
476
  else
460
- calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list)
477
+ calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
461
478
  end
462
479
  @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
463
480
  move_cursor_down(@first_line_started_from + @started_from)
@@ -488,7 +505,7 @@ class Reline::LineEditor
488
505
  end
489
506
 
490
507
  private def render_partial(prompt, prompt_width, line_to_render, with_control = true)
491
- visual_lines, height = split_by_width(prompt, line_to_render.nil? ? '' : line_to_render, @screen_size.last)
508
+ visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
492
509
  if with_control
493
510
  if height > @highest_in_this
494
511
  diff = height - @highest_in_this
@@ -507,8 +524,18 @@ class Reline::LineEditor
507
524
  Reline::IOGate.move_cursor_column(0)
508
525
  visual_lines.each_with_index do |line, index|
509
526
  if line.nil?
510
- if Reline::IOGate.win? and calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
511
- # A newline is automatically inserted if a character is rendered at eol on command prompt.
527
+ if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
528
+ # reaches the end of line
529
+ if Reline::IOGate.win?
530
+ # A newline is automatically inserted if a character is rendered at
531
+ # eol on command prompt.
532
+ else
533
+ # When the cursor is at the end of the line and erases characters
534
+ # after the cursor, some terminals delete the character at the
535
+ # cursor position.
536
+ move_cursor_down(1)
537
+ Reline::IOGate.move_cursor_column(0)
538
+ end
512
539
  else
513
540
  Reline::IOGate.erase_after_cursor
514
541
  move_cursor_down(1)
@@ -528,22 +555,25 @@ class Reline::LineEditor
528
555
  end
529
556
  end
530
557
  Reline::IOGate.erase_after_cursor
558
+ Reline::IOGate.move_cursor_column(0)
531
559
  if with_control
532
- move_cursor_up(height - 1)
560
+ # Just after rendring, so the cursor is on the last line.
533
561
  if finished?
534
- move_cursor_down(@started_from)
562
+ Reline::IOGate.move_cursor_column(0)
563
+ else
564
+ # Moves up from bottom of lines to the cursor position.
565
+ move_cursor_up(height - 1 - @started_from)
566
+ Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
535
567
  end
536
- move_cursor_down(@started_from)
537
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
538
568
  end
539
569
  height
540
570
  end
541
571
 
542
572
  private def modify_lines(before)
543
- return before if before.nil? || before.empty?
573
+ return before if before.nil? || before.empty? || simplified_rendering?
544
574
 
545
575
  if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?)
546
- after.lines("\n", chomp: true)
576
+ after.lines("\n").map { |l| l.chomp('') }
547
577
  else
548
578
  before
549
579
  end
@@ -568,7 +598,7 @@ class Reline::LineEditor
568
598
  else
569
599
  i&.start_with?(target)
570
600
  end
571
- }
601
+ }.uniq
572
602
  if is_menu
573
603
  menu(target, list)
574
604
  return nil
@@ -685,7 +715,8 @@ class Reline::LineEditor
685
715
  if @waiting_operator_proc
686
716
  if VI_MOTIONS.include?(method_symbol)
687
717
  old_cursor, old_byte_pointer = @cursor, @byte_pointer
688
- block.()
718
+ @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg > 1
719
+ block.(true)
689
720
  unless @waiting_proc
690
721
  cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
691
722
  @cursor, @byte_pointer = old_cursor, old_byte_pointer
@@ -693,27 +724,56 @@ class Reline::LineEditor
693
724
  else
694
725
  old_waiting_proc = @waiting_proc
695
726
  old_waiting_operator_proc = @waiting_operator_proc
727
+ current_waiting_operator_proc = @waiting_operator_proc
696
728
  @waiting_proc = proc { |k|
697
729
  old_cursor, old_byte_pointer = @cursor, @byte_pointer
698
730
  old_waiting_proc.(k)
699
731
  cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
700
732
  @cursor, @byte_pointer = old_cursor, old_byte_pointer
701
- @waiting_operator_proc.(cursor_diff, byte_pointer_diff)
733
+ current_waiting_operator_proc.(cursor_diff, byte_pointer_diff)
702
734
  @waiting_operator_proc = old_waiting_operator_proc
703
735
  }
704
736
  end
705
737
  else
706
738
  # Ignores operator when not motion is given.
707
- block.()
739
+ block.(false)
708
740
  end
709
741
  @waiting_operator_proc = nil
742
+ @waiting_operator_vi_arg = nil
743
+ @vi_arg = nil
710
744
  else
711
- block.()
745
+ block.(false)
712
746
  end
713
747
  end
714
748
 
715
749
  private def argumentable?(method_obj)
716
- method_obj and method_obj.parameters.length != 1
750
+ method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :arg }
751
+ end
752
+
753
+ private def inclusive?(method_obj)
754
+ # If a motion method with the keyword argument "inclusive" follows the
755
+ # operator, it must contain the character at the cursor position.
756
+ method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :inclusive }
757
+ end
758
+
759
+ def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
760
+ if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
761
+ not_insertion = method_symbol != :ed_insert
762
+ process_insert(force: not_insertion)
763
+ end
764
+ if @vi_arg and argumentable?(method_obj)
765
+ if with_operator and inclusive?(method_obj)
766
+ method_obj.(key, arg: @vi_arg, inclusive: true)
767
+ else
768
+ method_obj.(key, arg: @vi_arg)
769
+ end
770
+ else
771
+ if with_operator and inclusive?(method_obj)
772
+ method_obj.(key, inclusive: true)
773
+ else
774
+ method_obj.(key)
775
+ end
776
+ end
717
777
  end
718
778
 
719
779
  private def process_key(key, method_symbol)
@@ -724,11 +784,11 @@ class Reline::LineEditor
724
784
  end
725
785
  if method_symbol and key.is_a?(Symbol)
726
786
  if @vi_arg and argumentable?(method_obj)
727
- run_for_operators(key, method_symbol) do
728
- method_obj.(key, arg: @vi_arg)
787
+ run_for_operators(key, method_symbol) do |with_operator|
788
+ wrap_method_call(method_symbol, method_obj, key, with_operator)
729
789
  end
730
790
  else
731
- method_obj&.(key)
791
+ wrap_method_call(method_symbol, method_obj, key) if method_obj
732
792
  end
733
793
  @kill_ring.process
734
794
  @vi_arg = nil
@@ -737,15 +797,15 @@ class Reline::LineEditor
737
797
  ed_argument_digit(key)
738
798
  else
739
799
  if argumentable?(method_obj)
740
- run_for_operators(key, method_symbol) do
741
- method_obj.(key, arg: @vi_arg)
800
+ run_for_operators(key, method_symbol) do |with_operator|
801
+ wrap_method_call(method_symbol, method_obj, key, with_operator)
742
802
  end
743
803
  elsif @waiting_proc
744
804
  @waiting_proc.(key)
745
805
  elsif method_obj
746
- method_obj.(key)
806
+ wrap_method_call(method_symbol, method_obj, key)
747
807
  else
748
- ed_insert(key)
808
+ ed_insert(key) unless @config.editing_mode_is?(:vi_command)
749
809
  end
750
810
  @kill_ring.process
751
811
  @vi_arg = nil
@@ -755,15 +815,15 @@ class Reline::LineEditor
755
815
  @kill_ring.process
756
816
  elsif method_obj
757
817
  if method_symbol == :ed_argument_digit
758
- method_obj.(key)
818
+ wrap_method_call(method_symbol, method_obj, key)
759
819
  else
760
- run_for_operators(key, method_symbol) do
761
- method_obj.(key)
820
+ run_for_operators(key, method_symbol) do |with_operator|
821
+ wrap_method_call(method_symbol, method_obj, key, with_operator)
762
822
  end
763
823
  end
764
824
  @kill_ring.process
765
825
  else
766
- ed_insert(key)
826
+ ed_insert(key) unless @config.editing_mode_is?(:vi_command)
767
827
  end
768
828
  end
769
829
 
@@ -820,6 +880,7 @@ class Reline::LineEditor
820
880
  result = call_completion_proc
821
881
  if result.is_a?(Array)
822
882
  completion_occurs = true
883
+ process_insert
823
884
  complete(result)
824
885
  end
825
886
  end
@@ -828,6 +889,7 @@ class Reline::LineEditor
828
889
  result = call_completion_proc
829
890
  if result.is_a?(Array)
830
891
  completion_occurs = true
892
+ process_insert
831
893
  move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
832
894
  end
833
895
  end
@@ -839,7 +901,7 @@ class Reline::LineEditor
839
901
  unless completion_occurs
840
902
  @completion_state = CompletionState::NORMAL
841
903
  end
842
- if @is_multiline and @auto_indent_proc
904
+ if @is_multiline and @auto_indent_proc and not simplified_rendering?
843
905
  process_auto_indent
844
906
  end
845
907
  end
@@ -1041,6 +1103,7 @@ class Reline::LineEditor
1041
1103
 
1042
1104
  def finish
1043
1105
  @finished = true
1106
+ @rerender_all = true
1044
1107
  @config.reset
1045
1108
  end
1046
1109
 
@@ -1058,29 +1121,7 @@ class Reline::LineEditor
1058
1121
  end
1059
1122
 
1060
1123
  private def calculate_width(str, allow_escape_code = false)
1061
- if allow_escape_code
1062
- width = 0
1063
- rest = str.encode(Encoding::UTF_8)
1064
- in_zero_width = false
1065
- rest.scan(WIDTH_SCANNER) do |gc|
1066
- case gc
1067
- when NON_PRINTING_START
1068
- in_zero_width = true
1069
- when NON_PRINTING_END
1070
- in_zero_width = false
1071
- when CSI_REGEXP, OSC_REGEXP
1072
- else
1073
- unless in_zero_width
1074
- width += Reline::Unicode.get_mbchar_width(gc)
1075
- end
1076
- end
1077
- end
1078
- width
1079
- else
1080
- str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
1081
- w + Reline::Unicode.get_mbchar_width(gc)
1082
- }
1083
- end
1124
+ Reline::Unicode.calculate_width(str, allow_escape_code)
1084
1125
  end
1085
1126
 
1086
1127
  private def key_delete(key)
@@ -1091,6 +1132,9 @@ class Reline::LineEditor
1091
1132
 
1092
1133
  private def key_newline(key)
1093
1134
  if @is_multiline
1135
+ if (@buffer_of_lines.size - 1) == @line_index and @line.bytesize == @byte_pointer
1136
+ @add_newline_to_end_of_buffer = true
1137
+ end
1094
1138
  next_line = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
1095
1139
  cursor_line = @line.byteslice(0, @byte_pointer)
1096
1140
  insert_new_line(cursor_line, next_line)
@@ -1101,38 +1145,57 @@ class Reline::LineEditor
1101
1145
 
1102
1146
  private def ed_unassigned(key) end # do nothing
1103
1147
 
1148
+ private def process_insert(force: false)
1149
+ return if @continuous_insertion_buffer.empty? or (Reline::IOGate.in_pasting? and not force)
1150
+ width = Reline::Unicode.calculate_width(@continuous_insertion_buffer)
1151
+ bytesize = @continuous_insertion_buffer.bytesize
1152
+ if @cursor == @cursor_max
1153
+ @line += @continuous_insertion_buffer
1154
+ else
1155
+ @line = byteinsert(@line, @byte_pointer, @continuous_insertion_buffer)
1156
+ end
1157
+ @byte_pointer += bytesize
1158
+ @cursor += width
1159
+ @cursor_max += width
1160
+ @continuous_insertion_buffer.clear
1161
+ end
1162
+
1104
1163
  private def ed_insert(key)
1164
+ str = nil
1165
+ width = nil
1166
+ bytesize = nil
1105
1167
  if key.instance_of?(String)
1106
1168
  begin
1107
1169
  key.encode(Encoding::UTF_8)
1108
1170
  rescue Encoding::UndefinedConversionError
1109
1171
  return
1110
1172
  end
1111
- width = Reline::Unicode.get_mbchar_width(key)
1112
- if @cursor == @cursor_max
1113
- @line += key
1114
- else
1115
- @line = byteinsert(@line, @byte_pointer, key)
1116
- end
1117
- @byte_pointer += key.bytesize
1118
- @cursor += width
1119
- @cursor_max += width
1173
+ str = key
1174
+ bytesize = key.bytesize
1120
1175
  else
1121
1176
  begin
1122
1177
  key.chr.encode(Encoding::UTF_8)
1123
1178
  rescue Encoding::UndefinedConversionError
1124
1179
  return
1125
1180
  end
1126
- if @cursor == @cursor_max
1127
- @line += key.chr
1128
- else
1129
- @line = byteinsert(@line, @byte_pointer, key.chr)
1130
- end
1131
- width = Reline::Unicode.get_mbchar_width(key.chr)
1132
- @byte_pointer += 1
1133
- @cursor += width
1134
- @cursor_max += width
1181
+ str = key.chr
1182
+ bytesize = 1
1183
+ end
1184
+ if Reline::IOGate.in_pasting?
1185
+ @continuous_insertion_buffer << str
1186
+ return
1187
+ elsif not @continuous_insertion_buffer.empty?
1188
+ process_insert
1135
1189
  end
1190
+ width = Reline::Unicode.get_mbchar_width(str)
1191
+ if @cursor == @cursor_max
1192
+ @line += str
1193
+ else
1194
+ @line = byteinsert(@line, @byte_pointer, str)
1195
+ end
1196
+ @byte_pointer += bytesize
1197
+ @cursor += width
1198
+ @cursor_max += width
1136
1199
  end
1137
1200
  alias_method :ed_digit, :ed_insert
1138
1201
  alias_method :self_insert, :ed_insert
@@ -1189,6 +1252,7 @@ class Reline::LineEditor
1189
1252
  arg -= 1
1190
1253
  ed_prev_char(key, arg: arg) if arg > 0
1191
1254
  end
1255
+ alias_method :backward_char, :ed_prev_char
1192
1256
 
1193
1257
  private def vi_first_print(key)
1194
1258
  @byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line)
@@ -1258,7 +1322,7 @@ class Reline::LineEditor
1258
1322
  if search_word.empty? and Reline.last_incremental_search
1259
1323
  search_word = Reline.last_incremental_search
1260
1324
  end
1261
- if @history_pointer # TODO
1325
+ if @history_pointer
1262
1326
  case prev_search_key
1263
1327
  when "\C-r".ord
1264
1328
  history_pointer_base = 0
@@ -1618,6 +1682,7 @@ class Reline::LineEditor
1618
1682
  end
1619
1683
 
1620
1684
  private def ed_newline(key)
1685
+ process_insert(force: true)
1621
1686
  if @is_multiline
1622
1687
  if @config.editing_mode_is?(:vi_command)
1623
1688
  if @line_index < (@buffer_of_lines.size - 1)
@@ -1915,7 +1980,7 @@ class Reline::LineEditor
1915
1980
  ed_prev_char(key)
1916
1981
  @config.editing_mode = :vi_command
1917
1982
  end
1918
- alias_method :backward_char, :ed_prev_char
1983
+ alias_method :vi_movement_mode, :vi_command_mode
1919
1984
 
1920
1985
  private def vi_next_word(key, arg: 1)
1921
1986
  if @line.bytesize > @byte_pointer
@@ -1937,13 +2002,22 @@ class Reline::LineEditor
1937
2002
  vi_prev_word(key, arg: arg) if arg > 0
1938
2003
  end
1939
2004
 
1940
- private def vi_end_word(key, arg: 1)
2005
+ private def vi_end_word(key, arg: 1, inclusive: false)
1941
2006
  if @line.bytesize > @byte_pointer
1942
2007
  byte_size, width = Reline::Unicode.vi_forward_end_word(@line, @byte_pointer)
1943
2008
  @byte_pointer += byte_size
1944
2009
  @cursor += width
1945
2010
  end
1946
2011
  arg -= 1
2012
+ if inclusive and arg.zero?
2013
+ byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
2014
+ if byte_size > 0
2015
+ c = @line.byteslice(@byte_pointer, byte_size)
2016
+ width = Reline::Unicode.get_mbchar_width(c)
2017
+ @byte_pointer += byte_size
2018
+ @cursor += width
2019
+ end
2020
+ end
1947
2021
  vi_end_word(key, arg: arg) if arg > 0
1948
2022
  end
1949
2023
 
@@ -1967,13 +2041,22 @@ class Reline::LineEditor
1967
2041
  vi_prev_big_word(key, arg: arg) if arg > 0
1968
2042
  end
1969
2043
 
1970
- private def vi_end_big_word(key, arg: 1)
2044
+ private def vi_end_big_word(key, arg: 1, inclusive: false)
1971
2045
  if @line.bytesize > @byte_pointer
1972
2046
  byte_size, width = Reline::Unicode.vi_big_forward_end_word(@line, @byte_pointer)
1973
2047
  @byte_pointer += byte_size
1974
2048
  @cursor += width
1975
2049
  end
1976
2050
  arg -= 1
2051
+ if inclusive and arg.zero?
2052
+ byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
2053
+ if byte_size > 0
2054
+ c = @line.byteslice(@byte_pointer, byte_size)
2055
+ width = Reline::Unicode.get_mbchar_width(c)
2056
+ @byte_pointer += byte_size
2057
+ @cursor += width
2058
+ end
2059
+ end
1977
2060
  vi_end_big_word(key, arg: arg) if arg > 0
1978
2061
  end
1979
2062
 
@@ -2028,7 +2111,7 @@ class Reline::LineEditor
2028
2111
  @cursor = 0
2029
2112
  end
2030
2113
 
2031
- private def vi_change_meta(key)
2114
+ private def vi_change_meta(key, arg: 1)
2032
2115
  @waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
2033
2116
  if byte_pointer_diff > 0
2034
2117
  @line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
@@ -2041,9 +2124,10 @@ class Reline::LineEditor
2041
2124
  @byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
2042
2125
  @config.editing_mode = :vi_insert
2043
2126
  }
2127
+ @waiting_operator_vi_arg = arg
2044
2128
  end
2045
2129
 
2046
- private def vi_delete_meta(key)
2130
+ private def vi_delete_meta(key, arg: 1)
2047
2131
  @waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
2048
2132
  if byte_pointer_diff > 0
2049
2133
  @line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
@@ -2055,9 +2139,19 @@ class Reline::LineEditor
2055
2139
  @cursor_max -= cursor_diff.abs
2056
2140
  @byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
2057
2141
  }
2142
+ @waiting_operator_vi_arg = arg
2058
2143
  end
2059
2144
 
2060
- private def vi_yank(key)
2145
+ private def vi_yank(key, arg: 1)
2146
+ @waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
2147
+ if byte_pointer_diff > 0
2148
+ cut = @line.byteslice(@byte_pointer, byte_pointer_diff)
2149
+ elsif byte_pointer_diff < 0
2150
+ cut = @line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
2151
+ end
2152
+ copy_for_vi(cut)
2153
+ }
2154
+ @waiting_operator_vi_arg = arg
2061
2155
  end
2062
2156
 
2063
2157
  private def vi_list_or_eof(key)
@@ -2121,7 +2215,7 @@ class Reline::LineEditor
2121
2215
  fp.path
2122
2216
  }
2123
2217
  system("#{ENV['EDITOR']} #{path}")
2124
- @line = Pathname.new(path).read
2218
+ @line = File.read(path)
2125
2219
  finish
2126
2220
  end
2127
2221
 
@@ -2202,15 +2296,15 @@ class Reline::LineEditor
2202
2296
  }
2203
2297
  end
2204
2298
 
2205
- private def vi_next_char(key, arg: 1)
2206
- @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) }
2299
+ private def vi_next_char(key, arg: 1, inclusive: false)
2300
+ @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, inclusive: inclusive) }
2207
2301
  end
2208
2302
 
2209
- private def vi_to_next_char(key, arg: 1)
2210
- @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, true) }
2303
+ private def vi_to_next_char(key, arg: 1, inclusive: false)
2304
+ @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, need_prev_char: true, inclusive: inclusive) }
2211
2305
  end
2212
2306
 
2213
- private def search_next_char(key, arg, need_prev_char = false)
2307
+ private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
2214
2308
  if key.instance_of?(String)
2215
2309
  inputed_char = key
2216
2310
  else
@@ -2247,6 +2341,15 @@ class Reline::LineEditor
2247
2341
  @byte_pointer += byte_size
2248
2342
  @cursor += width
2249
2343
  end
2344
+ if inclusive
2345
+ byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
2346
+ if byte_size > 0
2347
+ c = @line.byteslice(@byte_pointer, byte_size)
2348
+ width = Reline::Unicode.get_mbchar_width(c)
2349
+ @byte_pointer += byte_size
2350
+ @cursor += width
2351
+ end
2352
+ end
2250
2353
  @waiting_proc = nil
2251
2354
  end
2252
2355
 
@@ -2318,10 +2421,10 @@ class Reline::LineEditor
2318
2421
  alias_method :set_mark, :em_set_mark
2319
2422
 
2320
2423
  private def em_exchange_mark(key)
2424
+ return unless @mark_pointer
2321
2425
  new_pointer = [@byte_pointer, @line_index]
2322
2426
  @previous_line_index = @line_index
2323
2427
  @byte_pointer, @line_index = @mark_pointer
2324
- @byte_pointer, @line_index = @mark_pointer
2325
2428
  @cursor = calculate_width(@line.byteslice(0, @byte_pointer))
2326
2429
  @cursor_max = calculate_width(@line)
2327
2430
  @mark_pointer = new_pointer