reline 0.2.0 → 0.2.5

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: 2a20f90b83534916dd8575a842ef2cbe968879cc4b2d1826b5374137f554249a
4
- data.tar.gz: add41e0e8f89fa39b8064c9d45bc0f17ac1b374578ae25872e24c50cb2e0e761
3
+ metadata.gz: 1da0f8ec80f16c720589413fbc14c02ac199dcfc0b0344270a61edbad4aec966
4
+ data.tar.gz: d214e02ad3500323e98d97d57677b7bbc94ea4c09df0b529d5b21ea66374a6a8
5
5
  SHA512:
6
- metadata.gz: ebd4286538633a6bb87dd2007bdd6bf389ef87aa5dd7406288ae8f52cacc1e0d7f87c4d3ae4bbac3fa125004d5f65b5740c6b8d2ec014e130160d7bd22c6ec1e
7
- data.tar.gz: 3a418c2aa202ac4710bb7d90d0017870c066e17ece72c644f90e4d37b9faadb02636150d332c00845c5ca60a421abd1394255f77e95be7626cfce046705b7ed0
6
+ metadata.gz: 0d218a92032ad8b02218e44bda7fe7d7404845f6cc9cceb9aade01d3c49bc33a0396b02857de417ecee59b5d65c0cb787eecec8dea60180ae3b5457df6c89ebd
7
+ data.tar.gz: 12c4b8d00608e1f9f16a1c5e530ae86c7530f39ddb1c0f6100b1d4ae87a6dbd53def15d9f387941062aafdb57360fd499dc5850a5bd60d07776daf60e057acfe
data/README.md CHANGED
@@ -11,3 +11,7 @@ Reline is compatible with the API of Ruby's stdlib 'readline', GNU Readline and
11
11
  ## License
12
12
 
13
13
  The gem is available as open source under the terms of the [Ruby License](https://www.ruby-lang.org/en/about/license.txt).
14
+
15
+ ## Acknowledgments for [rb-readline](https://github.com/ConnorAtherton/rb-readline)
16
+
17
+ In developing Reline, we have used some of the rb-readline implementation, so this library includes [copyright notice, list of conditions and the disclaimer](license_of_rb-readline) under the 3-Clause BSD License. Reline would never have been developed without rb-readline. Thank you for the tremendous accomplishments.
data/lib/reline.rb CHANGED
@@ -243,6 +243,7 @@ module Reline
243
243
  loop do
244
244
  prev_pasting_state = Reline::IOGate.in_pasting?
245
245
  read_io(config.keyseq_timeout) { |inputs|
246
+ line_editor.set_pasting_state(Reline::IOGate.in_pasting?)
246
247
  inputs.each { |c|
247
248
  line_editor.input_key(c)
248
249
  line_editor.rerender
@@ -253,6 +254,7 @@ module Reline
253
254
  end
254
255
  }
255
256
  if prev_pasting_state == true and not Reline::IOGate.in_pasting? and not line_editor.finished?
257
+ line_editor.set_pasting_state(false)
256
258
  prev_pasting_state = false
257
259
  line_editor.rerender_all
258
260
  end
@@ -444,6 +446,10 @@ module Reline
444
446
  }
445
447
  end
446
448
 
449
+ def self.ungetc(c)
450
+ Reline::IOGate.ungetc(c)
451
+ end
452
+
447
453
  def self.line_editor
448
454
  core.line_editor
449
455
  end
data/lib/reline/config.rb CHANGED
@@ -34,8 +34,8 @@ class Reline::Config
34
34
  show-all-if-unmodified
35
35
  visible-stats
36
36
  show-mode-in-prompt
37
- vi-cmd-mode-icon
38
- vi-ins-mode-icon
37
+ vi-cmd-mode-string
38
+ vi-ins-mode-string
39
39
  emacs-mode-string
40
40
  enable-bracketed-paste
41
41
  isearch-terminators
@@ -56,8 +56,8 @@ class Reline::Config
56
56
  @key_actors[:emacs] = Reline::KeyActor::Emacs.new
57
57
  @key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
58
58
  @key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
59
- @vi_cmd_mode_icon = '(cmd)'
60
- @vi_ins_mode_icon = '(ins)'
59
+ @vi_cmd_mode_string = '(cmd)'
60
+ @vi_ins_mode_string = '(ins)'
61
61
  @emacs_mode_string = '@'
62
62
  # https://tiswww.case.edu/php/chet/readline/readline.html#IDX25
63
63
  @history_size = -1 # unlimited
@@ -270,9 +270,9 @@ class Reline::Config
270
270
  @show_mode_in_prompt = false
271
271
  end
272
272
  when 'vi-cmd-mode-string'
273
- @vi_cmd_mode_icon = retrieve_string(value)
273
+ @vi_cmd_mode_string = retrieve_string(value)
274
274
  when 'vi-ins-mode-string'
275
- @vi_ins_mode_icon = retrieve_string(value)
275
+ @vi_ins_mode_string = retrieve_string(value)
276
276
  when 'emacs-mode-string'
277
277
  @emacs_mode_string = retrieve_string(value)
278
278
  when *VARIABLE_NAMES then
@@ -58,34 +58,38 @@ class Reline::LineEditor
58
58
  reset_variables(encoding: encoding)
59
59
  end
60
60
 
61
+ def set_pasting_state(in_pasting)
62
+ @in_pasting = in_pasting
63
+ end
64
+
61
65
  def simplified_rendering?
62
66
  if finished?
63
67
  false
64
68
  elsif @just_cursor_moving and not @rerender_all
65
69
  true
66
70
  else
67
- not @rerender_all and not finished? and Reline::IOGate.in_pasting?
71
+ not @rerender_all and not finished? and @in_pasting
68
72
  end
69
73
  end
70
74
 
71
- private def check_mode_icon
72
- mode_icon = nil
75
+ private def check_mode_string
76
+ mode_string = nil
73
77
  if @config.show_mode_in_prompt
74
78
  if @config.editing_mode_is?(:vi_command)
75
- mode_icon = @config.vi_cmd_mode_icon
79
+ mode_string = @config.vi_cmd_mode_string
76
80
  elsif @config.editing_mode_is?(:vi_insert)
77
- mode_icon = @config.vi_ins_mode_icon
81
+ mode_string = @config.vi_ins_mode_string
78
82
  elsif @config.editing_mode_is?(:emacs)
79
- mode_icon = @config.emacs_mode_string
83
+ mode_string = @config.emacs_mode_string
80
84
  else
81
- mode_icon = '?'
85
+ mode_string = '?'
82
86
  end
83
87
  end
84
- if mode_icon != @prev_mode_icon
88
+ if mode_string != @prev_mode_string
85
89
  @rerender_all = true
86
90
  end
87
- @prev_mode_icon = mode_icon
88
- mode_icon
91
+ @prev_mode_string = mode_string
92
+ mode_string
89
93
  end
90
94
 
91
95
  private def check_multiline_prompt(buffer, prompt)
@@ -99,8 +103,8 @@ class Reline::LineEditor
99
103
  prompt = @prompt
100
104
  end
101
105
  if simplified_rendering?
102
- mode_icon = check_mode_icon
103
- prompt = mode_icon + prompt if mode_icon
106
+ mode_string = check_mode_string
107
+ prompt = mode_string + prompt if mode_string
104
108
  return [prompt, calculate_width(prompt, true), [prompt] * buffer.size]
105
109
  end
106
110
  if @prompt_proc
@@ -112,6 +116,7 @@ class Reline::LineEditor
112
116
  use_cached_prompt_list = true
113
117
  end
114
118
  end
119
+ use_cached_prompt_list = false if @rerender_all
115
120
  if use_cached_prompt_list
116
121
  prompt_list = @cached_prompt_list
117
122
  else
@@ -119,15 +124,22 @@ class Reline::LineEditor
119
124
  @prompt_cache_time = Time.now.to_f
120
125
  end
121
126
  prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
122
- mode_icon = check_mode_icon
123
- prompt_list = prompt_list.map{ |pr| mode_icon + pr } if mode_icon
127
+ prompt_list = [prompt] if prompt_list.empty?
128
+ mode_string = check_mode_string
129
+ prompt_list = prompt_list.map{ |pr| mode_string + pr } if mode_string
124
130
  prompt = prompt_list[@line_index]
125
131
  prompt = prompt_list[0] if prompt.nil?
132
+ prompt = prompt_list.last if prompt.nil?
133
+ if buffer.size > prompt_list.size
134
+ (buffer.size - prompt_list.size).times do
135
+ prompt_list << prompt_list.last
136
+ end
137
+ end
126
138
  prompt_width = calculate_width(prompt, true)
127
139
  [prompt, prompt_width, prompt_list]
128
140
  else
129
- mode_icon = check_mode_icon
130
- prompt = mode_icon + prompt if mode_icon
141
+ mode_string = check_mode_string
142
+ prompt = mode_string + prompt if mode_string
131
143
  prompt_width = calculate_width(prompt, true)
132
144
  [prompt, prompt_width, nil]
133
145
  end
@@ -139,6 +151,13 @@ class Reline::LineEditor
139
151
  @screen_height = @screen_size.first
140
152
  reset_variables(prompt, encoding: encoding)
141
153
  @old_trap = Signal.trap('SIGINT') {
154
+ if @scroll_partial_screen
155
+ move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
156
+ else
157
+ move_cursor_down(@highest_in_all - @line_index - 1)
158
+ end
159
+ Reline::IOGate.move_cursor_column(0)
160
+ scroll_down(1)
142
161
  @old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
143
162
  raise Interrupt
144
163
  }
@@ -218,8 +237,10 @@ class Reline::LineEditor
218
237
  @eof = false
219
238
  @continuous_insertion_buffer = String.new(encoding: @encoding)
220
239
  @scroll_partial_screen = nil
221
- @prev_mode_icon = nil
240
+ @prev_mode_string = nil
222
241
  @drop_terminate_spaces = false
242
+ @in_pasting = false
243
+ @auto_indent_proc = nil
223
244
  reset_line
224
245
  end
225
246
 
@@ -323,8 +344,9 @@ class Reline::LineEditor
323
344
  else
324
345
  end_of_line_cursor = new_cursor_max
325
346
  end
326
- line_to_calc.encode(Encoding::UTF_8).grapheme_clusters.each do |gc|
327
- mbchar_width = Reline::Unicode.get_mbchar_width(gc)
347
+ line_to_calc.grapheme_clusters.each do |gc|
348
+ mbchar = gc.encode(Encoding::UTF_8)
349
+ mbchar_width = Reline::Unicode.get_mbchar_width(mbchar)
328
350
  now = new_cursor + mbchar_width
329
351
  if now > end_of_line_cursor or now > cursor
330
352
  break
@@ -368,10 +390,29 @@ class Reline::LineEditor
368
390
  @cleared = false
369
391
  return
370
392
  end
393
+ if @is_multiline and finished? and @scroll_partial_screen
394
+ # Re-output all code higher than the screen when finished.
395
+ Reline::IOGate.move_cursor_up(@first_line_started_from + @started_from - @scroll_partial_screen)
396
+ Reline::IOGate.move_cursor_column(0)
397
+ @scroll_partial_screen = nil
398
+ prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
399
+ if @previous_line_index
400
+ new_lines = whole_lines(index: @previous_line_index, line: @line)
401
+ else
402
+ new_lines = whole_lines
403
+ end
404
+ modify_lines(new_lines).each_with_index do |line, index|
405
+ @output.write "#{prompt_list ? prompt_list[index] : prompt}#{line}\n"
406
+ Reline::IOGate.erase_after_cursor
407
+ end
408
+ @output.flush
409
+ return
410
+ end
371
411
  new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
372
412
  # FIXME: end of logical line sometimes breaks
413
+ rendered = false
373
414
  if @add_newline_to_end_of_buffer
374
- rerender_added_newline
415
+ rerender_added_newline(prompt, prompt_width)
375
416
  @add_newline_to_end_of_buffer = false
376
417
  else
377
418
  if @just_cursor_moving and not @rerender_all
@@ -389,20 +430,32 @@ class Reline::LineEditor
389
430
  else
390
431
  end
391
432
  end
392
- line = modify_lines(whole_lines)[@line_index]
393
433
  if @is_multiline
394
- prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
395
434
  if finished?
396
435
  # Always rerender on finish because output_modifier_proc may return a different output.
436
+ if @previous_line_index
437
+ new_lines = whole_lines(index: @previous_line_index, line: @line)
438
+ else
439
+ new_lines = whole_lines
440
+ end
441
+ line = modify_lines(new_lines)[@line_index]
442
+ prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
397
443
  render_partial(prompt, prompt_width, line, @first_line_started_from)
444
+ move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
398
445
  scroll_down(1)
399
446
  Reline::IOGate.move_cursor_column(0)
400
447
  Reline::IOGate.erase_after_cursor
401
448
  elsif not rendered
402
- render_partial(prompt, prompt_width, line, @first_line_started_from)
449
+ unless @in_pasting
450
+ line = modify_lines(whole_lines)[@line_index]
451
+ prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
452
+ render_partial(prompt, prompt_width, line, @first_line_started_from)
453
+ end
403
454
  end
404
455
  @buffer_of_lines[@line_index] = @line
456
+ @rest_height = 0 if @scroll_partial_screen
405
457
  else
458
+ line = modify_lines(whole_lines)[@line_index]
406
459
  render_partial(prompt, prompt_width, line, 0)
407
460
  if finished?
408
461
  scroll_down(1)
@@ -445,13 +498,13 @@ class Reline::LineEditor
445
498
  end
446
499
  end
447
500
 
448
- private def rerender_added_newline
501
+ private def rerender_added_newline(prompt, prompt_width)
449
502
  scroll_down(1)
450
- new_lines = whole_lines(index: @previous_line_index, line: @line)
451
- prompt, prompt_width, = check_multiline_prompt(new_lines, prompt)
452
503
  @buffer_of_lines[@previous_line_index] = @line
453
504
  @line = @buffer_of_lines[@line_index]
454
- render_partial(prompt, prompt_width, @line, @first_line_started_from + @started_from + 1, with_control: false)
505
+ unless @in_pasting
506
+ render_partial(prompt, prompt_width, @line, @first_line_started_from + @started_from + 1, with_control: false)
507
+ end
455
508
  @cursor = @cursor_max = calculate_width(@line)
456
509
  @byte_pointer = @line.bytesize
457
510
  @highest_in_all += @highest_in_this
@@ -471,7 +524,7 @@ class Reline::LineEditor
471
524
  calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
472
525
  end
473
526
  first_line_diff = new_first_line_started_from - @first_line_started_from
474
- new_cursor, _, new_started_from, _ = calculate_nearest_cursor(@line, @cursor, @started_from, @byte_pointer, false)
527
+ new_cursor, new_cursor_max, new_started_from, new_byte_pointer = calculate_nearest_cursor(@buffer_of_lines[@line_index], @cursor, @started_from, @byte_pointer, false)
475
528
  new_started_from = calculate_height_by_width(prompt_width + new_cursor) - 1
476
529
  calculate_scroll_partial_screen(@highest_in_all, new_first_line_started_from + new_started_from)
477
530
  @previous_line_index = nil
@@ -485,6 +538,8 @@ class Reline::LineEditor
485
538
  @first_line_started_from = new_first_line_started_from
486
539
  @started_from = new_started_from
487
540
  @cursor = new_cursor
541
+ @cursor_max = new_cursor_max
542
+ @byte_pointer = new_byte_pointer
488
543
  move_cursor_down(first_line_diff + @started_from)
489
544
  Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
490
545
  false
@@ -558,7 +613,13 @@ class Reline::LineEditor
558
613
  new_first_line_started_from = calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
559
614
  end
560
615
  new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1
561
- if back > old_highest_in_all
616
+ calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from)
617
+ if @scroll_partial_screen
618
+ move_cursor_up(@first_line_started_from + @started_from)
619
+ scroll_down(@screen_height - 1)
620
+ move_cursor_up(@screen_height)
621
+ Reline::IOGate.move_cursor_column(0)
622
+ elsif back > old_highest_in_all
562
623
  scroll_down(back - 1)
563
624
  move_cursor_up(back - 1)
564
625
  elsif back < old_highest_in_all
@@ -570,7 +631,6 @@ class Reline::LineEditor
570
631
  end
571
632
  move_cursor_up(old_highest_in_all - 1)
572
633
  end
573
- calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from)
574
634
  render_whole_lines(new_buffer, prompt_list || prompt, prompt_width)
575
635
  if @prompt_proc
576
636
  prompt = prompt_list[@line_index]
@@ -656,8 +716,8 @@ class Reline::LineEditor
656
716
  @highest_in_this = height
657
717
  end
658
718
  move_cursor_up(@started_from)
659
- cursor_up_from_last_line = height - 1 - @started_from
660
719
  @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
720
+ cursor_up_from_last_line = height - 1 - @started_from
661
721
  end
662
722
  if Reline::Unicode::CSI_REGEXP.match?(prompt + line_to_render)
663
723
  @output.write "\e[0m" # clear character decorations
@@ -667,7 +727,7 @@ class Reline::LineEditor
667
727
  if line.nil?
668
728
  if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
669
729
  # reaches the end of line
670
- if Reline::IOGate.win?
730
+ if Reline::IOGate.win? and Reline::IOGate.win_legacy_console?
671
731
  # A newline is automatically inserted if a character is rendered at
672
732
  # eol on command prompt.
673
733
  else
@@ -685,7 +745,7 @@ class Reline::LineEditor
685
745
  next
686
746
  end
687
747
  @output.write line
688
- if Reline::IOGate.win? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
748
+ if Reline::IOGate.win? and Reline::IOGate.win_legacy_console? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
689
749
  # A newline is automatically inserted if a character is rendered at eol on command prompt.
690
750
  @rest_height -= 1 if @rest_height > 0
691
751
  end
@@ -753,6 +813,7 @@ class Reline::LineEditor
753
813
  end
754
814
  move_cursor_up(back)
755
815
  move_cursor_down(@first_line_started_from + @started_from)
816
+ @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
756
817
  Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
757
818
  end
758
819
 
@@ -1080,7 +1141,7 @@ class Reline::LineEditor
1080
1141
  unless completion_occurs
1081
1142
  @completion_state = CompletionState::NORMAL
1082
1143
  end
1083
- if not Reline::IOGate.in_pasting? and @just_cursor_moving.nil?
1144
+ if not @in_pasting and @just_cursor_moving.nil?
1084
1145
  if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
1085
1146
  @just_cursor_moving = true
1086
1147
  elsif @previous_line_index.nil? and @buffer_of_lines[@line_index] == @line and old_line == @line
@@ -1098,8 +1159,25 @@ class Reline::LineEditor
1098
1159
 
1099
1160
  def call_completion_proc
1100
1161
  result = retrieve_completion_block(true)
1101
- slice = result[1]
1102
- result = @completion_proc.(slice) if @completion_proc and slice
1162
+ preposing, target, postposing = result
1163
+ if @completion_proc and target
1164
+ argnum = @completion_proc.parameters.inject(0) { |result, item|
1165
+ case item.first
1166
+ when :req, :opt
1167
+ result + 1
1168
+ when :rest
1169
+ break 3
1170
+ end
1171
+ }
1172
+ case argnum
1173
+ when 1
1174
+ result = @completion_proc.(target)
1175
+ when 2
1176
+ result = @completion_proc.(target, preposing)
1177
+ when 3..Float::INFINITY
1178
+ result = @completion_proc.(target, preposing, postposing)
1179
+ end
1180
+ end
1103
1181
  Reline.core.instance_variable_set(:@completion_quote_character, nil)
1104
1182
  result
1105
1183
  end
@@ -1129,6 +1207,7 @@ class Reline::LineEditor
1129
1207
  new_lines = whole_lines
1130
1208
  end
1131
1209
  new_indent = @auto_indent_proc.(new_lines, @line_index, @byte_pointer, @check_new_auto_indent)
1210
+ new_indent = @cursor_max if new_indent&.> @cursor_max
1132
1211
  if new_indent&.>= 0
1133
1212
  md = new_lines[@line_index].match(/\A */)
1134
1213
  prev_indent = md[0].count(' ')
@@ -1146,8 +1225,16 @@ class Reline::LineEditor
1146
1225
  end
1147
1226
 
1148
1227
  def retrieve_completion_block(set_completion_quote_character = false)
1149
- word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
1150
- quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
1228
+ if Reline.completer_word_break_characters.empty?
1229
+ word_break_regexp = nil
1230
+ else
1231
+ word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
1232
+ end
1233
+ if Reline.completer_quote_characters.empty?
1234
+ quote_characters_regexp = nil
1235
+ else
1236
+ quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
1237
+ end
1151
1238
  before = @line.byteslice(0, @byte_pointer)
1152
1239
  rest = nil
1153
1240
  break_pointer = nil
@@ -1168,14 +1255,14 @@ class Reline::LineEditor
1168
1255
  elsif quote and slice.start_with?(escaped_quote)
1169
1256
  # skip
1170
1257
  i += 2
1171
- elsif slice =~ quote_characters_regexp # find new "
1258
+ elsif quote_characters_regexp and slice =~ quote_characters_regexp # find new "
1172
1259
  rest = $'
1173
1260
  quote = $&
1174
1261
  closing_quote = /(?!\\)#{Regexp.escape(quote)}/
1175
1262
  escaped_quote = /\\#{Regexp.escape(quote)}/
1176
1263
  i += 1
1177
1264
  break_pointer = i - 1
1178
- elsif not quote and slice =~ word_break_regexp
1265
+ elsif word_break_regexp and not quote and slice =~ word_break_regexp
1179
1266
  rest = $'
1180
1267
  i += 1
1181
1268
  before = @line.byteslice(i, @byte_pointer - i)
@@ -1203,6 +1290,19 @@ class Reline::LineEditor
1203
1290
  end
1204
1291
  target = before
1205
1292
  end
1293
+ if @is_multiline
1294
+ if @previous_line_index
1295
+ lines = whole_lines(index: @previous_line_index, line: @line)
1296
+ else
1297
+ lines = whole_lines
1298
+ end
1299
+ if @line_index > 0
1300
+ preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
1301
+ end
1302
+ if (lines.size - 1) > @line_index
1303
+ postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
1304
+ end
1305
+ end
1206
1306
  [preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
1207
1307
  end
1208
1308
 
@@ -1230,10 +1330,32 @@ class Reline::LineEditor
1230
1330
 
1231
1331
  def delete_text(start = nil, length = nil)
1232
1332
  if start.nil? and length.nil?
1233
- @line&.clear
1234
- @byte_pointer = 0
1235
- @cursor = 0
1236
- @cursor_max = 0
1333
+ if @is_multiline
1334
+ if @buffer_of_lines.size == 1
1335
+ @line&.clear
1336
+ @byte_pointer = 0
1337
+ @cursor = 0
1338
+ @cursor_max = 0
1339
+ elsif @line_index == (@buffer_of_lines.size - 1) and @line_index > 0
1340
+ @buffer_of_lines.pop
1341
+ @line_index -= 1
1342
+ @line = @buffer_of_lines[@line_index]
1343
+ @byte_pointer = 0
1344
+ @cursor = 0
1345
+ @cursor_max = calculate_width(@line)
1346
+ elsif @line_index < (@buffer_of_lines.size - 1)
1347
+ @buffer_of_lines.delete_at(@line_index)
1348
+ @line = @buffer_of_lines[@line_index]
1349
+ @byte_pointer = 0
1350
+ @cursor = 0
1351
+ @cursor_max = calculate_width(@line)
1352
+ end
1353
+ else
1354
+ @line&.clear
1355
+ @byte_pointer = 0
1356
+ @cursor = 0
1357
+ @cursor_max = 0
1358
+ end
1237
1359
  elsif not start.nil? and not length.nil?
1238
1360
  if @line
1239
1361
  before = @line.byteslice(0, start)
@@ -1283,7 +1405,11 @@ class Reline::LineEditor
1283
1405
  if @buffer_of_lines.size == 1 and @line.nil?
1284
1406
  nil
1285
1407
  else
1286
- whole_lines.join("\n")
1408
+ if @previous_line_index
1409
+ whole_lines(index: @previous_line_index, line: @line).join("\n")
1410
+ else
1411
+ whole_lines.join("\n")
1412
+ end
1287
1413
  end
1288
1414
  end
1289
1415
 
@@ -1329,14 +1455,14 @@ class Reline::LineEditor
1329
1455
  cursor_line = @line.byteslice(0, @byte_pointer)
1330
1456
  insert_new_line(cursor_line, next_line)
1331
1457
  @cursor = 0
1332
- @check_new_auto_indent = true
1458
+ @check_new_auto_indent = true unless @in_pasting
1333
1459
  end
1334
1460
  end
1335
1461
 
1336
1462
  private def ed_unassigned(key) end # do nothing
1337
1463
 
1338
1464
  private def process_insert(force: false)
1339
- return if @continuous_insertion_buffer.empty? or (Reline::IOGate.in_pasting? and not force)
1465
+ return if @continuous_insertion_buffer.empty? or (@in_pasting and not force)
1340
1466
  width = Reline::Unicode.calculate_width(@continuous_insertion_buffer)
1341
1467
  bytesize = @continuous_insertion_buffer.bytesize
1342
1468
  if @cursor == @cursor_max
@@ -1371,7 +1497,7 @@ class Reline::LineEditor
1371
1497
  str = key.chr
1372
1498
  bytesize = 1
1373
1499
  end
1374
- if Reline::IOGate.in_pasting?
1500
+ if @in_pasting
1375
1501
  @continuous_insertion_buffer << str
1376
1502
  return
1377
1503
  elsif not @continuous_insertion_buffer.empty?
@@ -1722,7 +1848,7 @@ class Reline::LineEditor
1722
1848
  @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
1723
1849
  @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1724
1850
  @line_index = line_no
1725
- @line = @buffer_of_lines.last
1851
+ @line = @buffer_of_lines[@line_index]
1726
1852
  @rerender_all = true
1727
1853
  else
1728
1854
  @line = Reline::HISTORY[@history_pointer]
@@ -1770,7 +1896,7 @@ class Reline::LineEditor
1770
1896
  @line_index = line_no
1771
1897
  end
1772
1898
  @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1773
- @line = @buffer_of_lines.last
1899
+ @line = @buffer_of_lines[@line_index]
1774
1900
  @rerender_all = true
1775
1901
  else
1776
1902
  if @history_pointer.nil? and substr.empty?
@@ -2385,6 +2511,9 @@ class Reline::LineEditor
2385
2511
  width = Reline::Unicode.get_mbchar_width(mbchar)
2386
2512
  @cursor_max -= width
2387
2513
  if @cursor > 0 and @cursor >= @cursor_max
2514
+ byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
2515
+ mbchar = @line.byteslice(@byte_pointer - byte_size, byte_size)
2516
+ width = Reline::Unicode.get_mbchar_width(mbchar)
2388
2517
  @byte_pointer -= byte_size
2389
2518
  @cursor -= width
2390
2519
  end
@@ -2418,11 +2547,23 @@ class Reline::LineEditor
2418
2547
 
2419
2548
  private def vi_histedit(key)
2420
2549
  path = Tempfile.open { |fp|
2421
- fp.write @line
2550
+ if @is_multiline
2551
+ fp.write whole_lines.join("\n")
2552
+ else
2553
+ fp.write @line
2554
+ end
2422
2555
  fp.path
2423
2556
  }
2424
2557
  system("#{ENV['EDITOR']} #{path}")
2425
- @line = File.read(path)
2558
+ if @is_multiline
2559
+ @buffer_of_lines = File.read(path).split("\n")
2560
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
2561
+ @line_index = 0
2562
+ @line = @buffer_of_lines[@line_index]
2563
+ @rerender_all = true
2564
+ else
2565
+ @line = File.read(path)
2566
+ end
2426
2567
  finish
2427
2568
  end
2428
2569