reline 0.5.0.pre.1 → 0.5.1

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: a6fdc2426aceadd0680462674da1132c7042924e9fda18daecd68b82e459a867
4
- data.tar.gz: 5fc586dfacfd1cc5524320b0e3190982098fbdaf541a6ff56f9c153aa828560c
3
+ metadata.gz: 47d262bd9b8ab9b3fb314863af068783c217d53177f947372eb979df2ceaf3cf
4
+ data.tar.gz: 8112b4441c7946a697adf3d16ec79531fc744fac132ea3cca89ef16b9b6518d3
5
5
  SHA512:
6
- metadata.gz: 052abb950ef133821f1c3bf43f4727e8ef6afe7210fb9a9f56df65ad1a16a96d54cfc519ecbab94c8b5502d8c6976fdfc7b5e87effb4f7b9e15a800ffaefa92f
7
- data.tar.gz: 617b9876da860ac3b08fd659e9ef8e7cda3afc189e496b34cd116d9850bee9ed176e68f129b4ceff6443f3f3992460ef22ddad9d16c2a6dbdf07a3ee020a25e0
6
+ metadata.gz: 22570f23bfa74602ef627bf977982304b0e5849cbc52abb33f321a32b3860348eabb3efd88bfcfa6b46032433a012813a0fa9aa70422438d0f0694a724ca3055
7
+ data.tar.gz: 6a30e3c69c1a64149a5cd0517e3c46c6dfb9d724532ded4ab4475c6b595bcf5cdf6b6ffd1b9d32156b8a8a447885bb918d35f78be90934a2e2513f87fdf40bb0
data/lib/reline/ansi.rb CHANGED
@@ -3,6 +3,8 @@ require 'io/wait'
3
3
  require_relative 'terminfo'
4
4
 
5
5
  class Reline::ANSI
6
+ RESET_COLOR = "\e[0m"
7
+
6
8
  CAPNAME_KEY_BINDINGS = {
7
9
  'khome' => :ed_move_to_beg,
8
10
  'kend' => :ed_move_to_end,
@@ -149,7 +151,11 @@ class Reline::ANSI
149
151
  end
150
152
 
151
153
  def self.with_raw_input
152
- @@input.raw { yield }
154
+ if @@input.tty?
155
+ @@input.raw(intr: true) { yield }
156
+ else
157
+ yield
158
+ end
153
159
  end
154
160
 
155
161
  @@buf = []
@@ -157,11 +163,13 @@ class Reline::ANSI
157
163
  unless @@buf.empty?
158
164
  return @@buf.shift
159
165
  end
160
- until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte }
161
- timeout_second -= 0.1
166
+ until @@input.wait_readable(0.01)
167
+ timeout_second -= 0.01
162
168
  return nil if timeout_second <= 0
163
- Reline.core.line_editor.resize
169
+
170
+ Reline.core.line_editor.handle_signal
164
171
  end
172
+ c = @@input.getbyte
165
173
  (c == 0x16 && @@input.raw(min: 0, time: 0, &:getbyte)) || c
166
174
  rescue Errno::EIO
167
175
  # Maybe the I/O has been closed.
@@ -307,7 +315,7 @@ class Reline::ANSI
307
315
  end
308
316
 
309
317
  def self.hide_cursor
310
- if Reline::Terminfo.enabled?
318
+ if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
311
319
  begin
312
320
  @@output.write Reline::Terminfo.tigetstr('civis')
313
321
  rescue Reline::Terminfo::TerminfoError
@@ -319,7 +327,7 @@ class Reline::ANSI
319
327
  end
320
328
 
321
329
  def self.show_cursor
322
- if Reline::Terminfo.enabled?
330
+ if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
323
331
  begin
324
332
  @@output.write Reline::Terminfo.tigetstr('cnorm')
325
333
  rescue Reline::Terminfo::TerminfoError
data/lib/reline/face.rb CHANGED
@@ -186,9 +186,9 @@ class Reline::Face
186
186
  conf.define :scrollbar, style: :reset
187
187
  end
188
188
  config(:completion_dialog) do |conf|
189
- conf.define :default, foreground: :white, background: :cyan
190
- conf.define :enhanced, foreground: :white, background: :magenta
191
- conf.define :scrollbar, foreground: :white, background: :cyan
189
+ conf.define :default, foreground: :bright_white, background: :gray
190
+ conf.define :enhanced, foreground: :black, background: :white
191
+ conf.define :scrollbar, foreground: :white, background: :gray
192
192
  end
193
193
  end
194
194
 
@@ -1,6 +1,8 @@
1
1
  require 'io/wait'
2
2
 
3
3
  class Reline::GeneralIO
4
+ RESET_COLOR = '' # Do not send color reset sequence
5
+
4
6
  def self.reset(encoding: nil)
5
7
  @@pasting = false
6
8
  if encoding
@@ -44,6 +46,7 @@ class Reline::GeneralIO
44
46
  end
45
47
  c = nil
46
48
  loop do
49
+ Reline.core.line_editor.handle_signal
47
50
  result = @@input.wait_readable(0.1)
48
51
  next if result.nil?
49
52
  c = @@input.read(1)
@@ -100,14 +103,6 @@ class Reline::GeneralIO
100
103
  @@pasting
101
104
  end
102
105
 
103
- def self.start_pasting
104
- @@pasting = true
105
- end
106
-
107
- def self.finish_pasting
108
- @@pasting = false
109
- end
110
-
111
106
  def self.prep
112
107
  end
113
108
 
@@ -62,7 +62,7 @@ class Reline::History < Array
62
62
  private def check_index(index)
63
63
  index += size if index < 0
64
64
  if index < -2147483648 or 2147483647 < index
65
- raise RangeError.new("integer #{index} too big to convert to `int'")
65
+ raise RangeError.new("integer #{index} too big to convert to 'int'")
66
66
  end
67
67
  # If history_size is negative, history size is unlimited.
68
68
  if @config.history_size.positive?
@@ -14,7 +14,7 @@ class Reline::KillRing
14
14
  end
15
15
 
16
16
  def ==(other)
17
- object_id == other.object_id
17
+ equal?(other)
18
18
  end
19
19
  end
20
20
 
@@ -68,7 +68,7 @@ class Reline::KillRing
68
68
  def append(string, before_p = false)
69
69
  case @state
70
70
  when State::FRESH, State::YANK
71
- @ring << RingPoint.new(string)
71
+ @ring << RingPoint.new(+string)
72
72
  @state = State::CONTINUED
73
73
  when State::CONTINUED, State::PROCESSED
74
74
  if before_p
@@ -41,21 +41,42 @@ class Reline::LineEditor
41
41
  NORMAL = :normal
42
42
  COMPLETION = :completion
43
43
  MENU = :menu
44
- JOURNEY = :journey
45
44
  MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
46
45
  PERFECT_MATCH = :perfect_match
47
46
  end
48
47
 
49
- CompletionJourneyData = Struct.new(:preposing, :postposing, :list, :pointer)
50
- MenuInfo = Struct.new(:target, :list)
48
+ RenderedScreen = Struct.new(:base_y, :lines, :cursor_y, keyword_init: true)
49
+
50
+ CompletionJourneyState = Struct.new(:line_index, :pre, :target, :post, :list, :pointer)
51
+
52
+ class MenuInfo
53
+ attr_reader :list
54
+
55
+ def initialize(list)
56
+ @list = list
57
+ end
58
+
59
+ def lines(screen_width)
60
+ return [] if @list.empty?
61
+
62
+ list = @list.sort
63
+ sizes = list.map { |item| Reline::Unicode.calculate_width(item) }
64
+ item_width = sizes.max + 2
65
+ num_cols = [screen_width / item_width, 1].max
66
+ num_rows = list.size.fdiv(num_cols).ceil
67
+ list_with_padding = list.zip(sizes).map { |item, size| item + ' ' * (item_width - size) }
68
+ aligned = (list_with_padding + [nil] * (num_rows * num_cols - list_with_padding.size)).each_slice(num_rows).to_a.transpose
69
+ aligned.map do |row|
70
+ row.join.rstrip
71
+ end
72
+ end
73
+ end
51
74
 
52
- PROMPT_LIST_CACHE_TIMEOUT = 0.5
53
75
  MINIMUM_SCROLLBAR_HEIGHT = 1
54
76
 
55
77
  def initialize(config, encoding)
56
78
  @config = config
57
79
  @completion_append_character = ''
58
- @cursor_base_y = 0
59
80
  @screen_size = Reline::IOGate.get_screen_size
60
81
  reset_variables(encoding: encoding)
61
82
  end
@@ -65,6 +86,9 @@ class Reline::LineEditor
65
86
  end
66
87
 
67
88
  def set_pasting_state(in_pasting)
89
+ # While pasting, text to be inserted is stored to @continuous_insertion_buffer.
90
+ # After pasting, this buffer should be force inserted.
91
+ process_insert(force: true) if @in_pasting && !in_pasting
68
92
  @in_pasting = in_pasting
69
93
  end
70
94
 
@@ -82,7 +106,7 @@ class Reline::LineEditor
82
106
  end
83
107
  end
84
108
 
85
- private def check_multiline_prompt(buffer)
109
+ private def check_multiline_prompt(buffer, mode_string)
86
110
  if @vi_arg
87
111
  prompt = "(arg: #{@vi_arg}) "
88
112
  elsif @searching_prompt
@@ -94,7 +118,6 @@ class Reline::LineEditor
94
118
  prompt_list = @prompt_proc.(buffer).map { |pr| pr.gsub("\n", "\\n") }
95
119
  prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
96
120
  prompt_list = [prompt] if prompt_list.empty?
97
- mode_string = check_mode_string
98
121
  prompt_list = prompt_list.map{ |pr| mode_string + pr } if mode_string
99
122
  prompt = prompt_list[@line_index]
100
123
  prompt = prompt_list[0] if prompt.nil?
@@ -106,19 +129,15 @@ class Reline::LineEditor
106
129
  end
107
130
  prompt_list
108
131
  else
109
- mode_string = check_mode_string
110
132
  prompt = mode_string + prompt if mode_string
111
133
  [prompt] * buffer.size
112
134
  end
113
135
  end
114
136
 
115
137
  def reset(prompt = '', encoding:)
116
- @cursor_base_y = Reline::IOGate.cursor_pos.y
117
138
  @screen_size = Reline::IOGate.get_screen_size
118
139
  reset_variables(prompt, encoding: encoding)
119
- Reline::IOGate.set_winch_handler do
120
- @resized = true
121
- end
140
+ @rendered_screen.base_y = Reline::IOGate.cursor_pos.y
122
141
  if ENV.key?('RELINE_ALT_SCROLLBAR')
123
142
  @full_block = '::'
124
143
  @upper_half_block = "''"
@@ -142,41 +161,53 @@ class Reline::LineEditor
142
161
  end
143
162
  end
144
163
 
145
- def resize
164
+ def handle_signal
165
+ handle_interrupted
166
+ handle_resized
167
+ end
168
+
169
+ private def handle_resized
146
170
  return unless @resized
147
171
 
148
- Reline::IOGate.hide_cursor
149
172
  @screen_size = Reline::IOGate.get_screen_size
150
173
  @resized = false
151
174
  scroll_into_view
152
- Reline::IOGate.move_cursor_up @cursor_y
153
- @cursor_base_y = Reline::IOGate.cursor_pos.y
154
- @cursor_y = 0
155
- @rendered_screen_cache = nil
175
+ Reline::IOGate.move_cursor_up @rendered_screen.cursor_y
176
+ @rendered_screen.base_y = Reline::IOGate.cursor_pos.y
177
+ @rendered_screen.lines = []
178
+ @rendered_screen.cursor_y = 0
156
179
  render_differential
157
- Reline::IOGate.show_cursor
180
+ end
181
+
182
+ private def handle_interrupted
183
+ return unless @interrupted
184
+
185
+ @interrupted = false
186
+ clear_dialogs
187
+ scrolldown = render_differential
188
+ Reline::IOGate.scroll_down scrolldown
189
+ Reline::IOGate.move_cursor_column 0
190
+ @rendered_screen.lines = []
191
+ @rendered_screen.cursor_y = 0
192
+ case @old_trap
193
+ when 'DEFAULT', 'SYSTEM_DEFAULT'
194
+ raise Interrupt
195
+ when 'IGNORE'
196
+ # Do nothing
197
+ when 'EXIT'
198
+ exit
199
+ else
200
+ @old_trap.call if @old_trap.respond_to?(:call)
201
+ end
158
202
  end
159
203
 
160
204
  def set_signal_handlers
161
- @old_trap = Signal.trap('INT') {
162
- Reline::IOGate.hide_cursor
163
- clear_dialogs
164
- scrolldown = render_differential
165
- Reline::IOGate.scroll_down scrolldown
166
- Reline::IOGate.move_cursor_column 0
167
- @rendered_screen_cache = nil
168
- Reline::IOGate.show_cursor
169
- case @old_trap
170
- when 'DEFAULT', 'SYSTEM_DEFAULT'
171
- raise Interrupt
172
- when 'IGNORE'
173
- # Do nothing
174
- when 'EXIT'
175
- exit
176
- else
177
- @old_trap.call if @old_trap.respond_to?(:call)
178
- end
179
- }
205
+ Reline::IOGate.set_winch_handler do
206
+ @resized = true
207
+ end
208
+ @old_trap = Signal.trap('INT') do
209
+ @interrupted = true
210
+ end
180
211
  end
181
212
 
182
213
  def finalize
@@ -193,7 +224,6 @@ class Reline::LineEditor
193
224
  @encoding = encoding
194
225
  @is_multiline = false
195
226
  @finished = false
196
- @cleared = false
197
227
  @history_pointer = nil
198
228
  @kill_ring ||= Reline::KillRing.new
199
229
  @vi_clipboard = ''
@@ -201,13 +231,13 @@ class Reline::LineEditor
201
231
  @waiting_proc = nil
202
232
  @waiting_operator_proc = nil
203
233
  @waiting_operator_vi_arg = nil
204
- @completion_journey_data = nil
234
+ @completion_journey_state = nil
205
235
  @completion_state = CompletionState::NORMAL
206
236
  @perfect_matched = nil
207
237
  @menu_info = nil
208
- @first_prompt = true
209
238
  @searching_prompt = nil
210
239
  @first_char = true
240
+ @just_cursor_moving = false
211
241
  @eof = false
212
242
  @continuous_insertion_buffer = String.new(encoding: @encoding)
213
243
  @scroll_partial_screen = 0
@@ -215,10 +245,10 @@ class Reline::LineEditor
215
245
  @in_pasting = false
216
246
  @auto_indent_proc = nil
217
247
  @dialogs = []
248
+ @interrupted = false
218
249
  @resized = false
219
- @cursor_y = 0
220
250
  @cache = {}
221
- @rendered_screen_cache = nil
251
+ @rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
222
252
  reset_line
223
253
  end
224
254
 
@@ -266,20 +296,6 @@ class Reline::LineEditor
266
296
  Reline::Unicode.split_by_width(str, max_width, @encoding)
267
297
  end
268
298
 
269
- private def scroll_down(val)
270
- if @cursor_base_y + @cursor_y + val < screen_height
271
- Reline::IOGate.move_cursor_down(val)
272
- @cursor_y += val
273
- else
274
- move = screen_height - @cursor_base_y - @cursor_y - 1
275
- scroll = val - move
276
- Reline::IOGate.scroll_down(move)
277
- Reline::IOGate.scroll_down(scroll)
278
- @cursor_y += move
279
- @cursor_base_y = [@cursor_base_y - scroll, 0].max
280
- end
281
- end
282
-
283
299
  def current_byte_pointer_cursor
284
300
  calculate_width(current_line.byteslice(0, @byte_pointer))
285
301
  end
@@ -334,8 +350,8 @@ class Reline::LineEditor
334
350
  end
335
351
 
336
352
  def prompt_list
337
- with_cache(__method__, whole_lines, @vi_arg, @searching_prompt) do |lines|
338
- check_multiline_prompt(lines)
353
+ with_cache(__method__, whole_lines, check_mode_string, @vi_arg, @searching_prompt) do |lines, mode_string|
354
+ check_multiline_prompt(lines, mode_string)
339
355
  end
340
356
  end
341
357
 
@@ -387,12 +403,12 @@ class Reline::LineEditor
387
403
  # do nothing
388
404
  elsif level == :blank
389
405
  Reline::IOGate.move_cursor_column base_x
390
- @output.write "\e[0m#{' ' * width}"
406
+ @output.write "#{Reline::IOGate::RESET_COLOR}#{' ' * width}"
391
407
  else
392
408
  x, w, content = new_items[level]
393
409
  content = Reline::Unicode.take_range(content, base_x - x, width) unless x == base_x && w == width
394
410
  Reline::IOGate.move_cursor_column base_x
395
- @output.write "\e[0m#{content}\e[0m"
411
+ @output.write "#{Reline::IOGate::RESET_COLOR}#{content}#{Reline::IOGate::RESET_COLOR}"
396
412
  end
397
413
  base_x += width
398
414
  end
@@ -402,12 +418,14 @@ class Reline::LineEditor
402
418
  end
403
419
  end
404
420
 
405
- def editor_cursor_position
406
- line = ' ' * calculate_width(prompt_list[@line_index], true) + whole_lines[@line_index].byteslice(0, @byte_pointer)
407
- wrapped_line_before_cursor = split_by_width(line, screen_width).first.compact
408
- editor_cursor_y = wrapped_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
409
- editor_cursor_x = calculate_width(wrapped_line_before_cursor.last)
410
- [editor_cursor_x, editor_cursor_y]
421
+ # Calculate cursor position in word wrapped content.
422
+ def wrapped_cursor_position
423
+ prompt_width = calculate_width(prompt_list[@line_index], true)
424
+ line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
425
+ wrapped_line_before_cursor = split_by_width(' ' * prompt_width + line_before_cursor, screen_width).first.compact
426
+ wrapped_cursor_y = wrapped_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
427
+ wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
428
+ [wrapped_cursor_x, wrapped_cursor_y]
411
429
  end
412
430
 
413
431
  def clear_dialogs
@@ -418,19 +436,23 @@ class Reline::LineEditor
418
436
  end
419
437
 
420
438
  def update_dialogs(key = nil)
421
- @dialog_initialzed = true
422
- editor_cursor_x, editor_cursor_y = editor_cursor_position
439
+ wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
423
440
  @dialogs.each do |dialog|
424
441
  dialog.trap_key = nil
425
- update_each_dialog(dialog, editor_cursor_x, editor_cursor_y - screen_scroll_top, key)
442
+ update_each_dialog(dialog, wrapped_cursor_x, wrapped_cursor_y - screen_scroll_top, key)
426
443
  end
427
444
  end
428
445
 
446
+ def render_finished
447
+ clear_rendered_lines
448
+ render_full_content
449
+ end
450
+
429
451
  def clear_rendered_lines
430
- Reline::IOGate.move_cursor_up @cursor_y
452
+ Reline::IOGate.move_cursor_up @rendered_screen.cursor_y
431
453
  Reline::IOGate.move_cursor_column 0
432
454
 
433
- num_lines = @rendered_screen_cache&.size
455
+ num_lines = @rendered_screen.lines.size
434
456
  return unless num_lines && num_lines >= 1
435
457
 
436
458
  Reline::IOGate.move_cursor_down num_lines - 1
@@ -439,7 +461,8 @@ class Reline::LineEditor
439
461
  Reline::IOGate.move_cursor_up 1
440
462
  end
441
463
  Reline::IOGate.erase_after_cursor
442
- @rendered_screen_cache = nil
464
+ @rendered_screen.lines = []
465
+ @rendered_screen.cursor_y = 0
443
466
  end
444
467
 
445
468
  def render_full_content
@@ -455,23 +478,20 @@ class Reline::LineEditor
455
478
  return unless prompt && !@is_multiline
456
479
 
457
480
  # Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence.
458
- @rendered_screen_cache = [[[0, Reline::Unicode.calculate_width(prompt, true), prompt]]]
481
+ @rendered_screen.lines = [[[0, Reline::Unicode.calculate_width(prompt, true), prompt]]]
482
+ @rendered_screen.cursor_y = 0
459
483
  @output.write prompt
460
484
  end
461
485
 
462
486
  def render_differential
463
- unless @dialog_initialzed
464
- update_dialogs
465
- end
466
-
467
- editor_cursor_x, editor_cursor_y = editor_cursor_position
487
+ wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
468
488
 
469
- rendered_lines = @rendered_screen_cache || []
489
+ rendered_lines = @rendered_screen.lines
470
490
  new_lines = wrapped_lines.flatten[screen_scroll_top, screen_height].map do |l|
471
491
  [[0, Reline::Unicode.calculate_width(l, true), l]]
472
492
  end
473
493
  if @menu_info
474
- @menu_info.list.sort!.each do |item|
494
+ @menu_info.lines(screen_width).each do |item|
475
495
  new_lines << [[0, Reline::Unicode.calculate_width(item), item]]
476
496
  end
477
497
  @menu_info = nil # TODO: do not change state here
@@ -480,7 +500,7 @@ class Reline::LineEditor
480
500
  @dialogs.each_with_index do |dialog, index|
481
501
  next unless dialog.contents
482
502
 
483
- x_range, y_range = dialog_range dialog, editor_cursor_y - screen_scroll_top
503
+ x_range, y_range = dialog_range dialog, wrapped_cursor_y - screen_scroll_top
484
504
  y_range.each do |row|
485
505
  next if row < 0 || row >= screen_height
486
506
  dialog_rows = new_lines[row] ||= []
@@ -488,77 +508,58 @@ class Reline::LineEditor
488
508
  end
489
509
  end
490
510
 
491
- num_lines = [[new_lines.size, rendered_lines.size].max, screen_height].min
492
- scroll_down(num_lines - 1 - @cursor_y) if (num_lines - 1 - @cursor_y) > 0
493
- @cursor_y = num_lines - 1
494
- num_lines.times do |i|
495
- rendered_line = rendered_lines[i] || []
496
- line_to_render = new_lines[i] || []
497
- next if rendered_line == line_to_render
498
-
499
- Reline::IOGate.move_cursor_down i - @cursor_y
500
- @cursor_y = i
501
- unless rendered_lines[i]
502
- Reline::IOGate.move_cursor_column 0
503
- Reline::IOGate.erase_after_cursor
511
+ cursor_y = @rendered_screen.cursor_y
512
+ if new_lines != rendered_lines
513
+ # Hide cursor while rendering to avoid cursor flickering.
514
+ Reline::IOGate.hide_cursor
515
+ num_lines = [[new_lines.size, rendered_lines.size].max, screen_height].min
516
+ if @rendered_screen.base_y + num_lines > screen_height
517
+ Reline::IOGate.scroll_down(num_lines - cursor_y - 1)
518
+ @rendered_screen.base_y = screen_height - num_lines
519
+ cursor_y = num_lines - 1
520
+ end
521
+ num_lines.times do |i|
522
+ rendered_line = rendered_lines[i] || []
523
+ line_to_render = new_lines[i] || []
524
+ next if rendered_line == line_to_render
525
+
526
+ Reline::IOGate.move_cursor_down i - cursor_y
527
+ cursor_y = i
528
+ unless rendered_lines[i]
529
+ Reline::IOGate.move_cursor_column 0
530
+ Reline::IOGate.erase_after_cursor
531
+ end
532
+ render_line_differential(rendered_line, line_to_render)
504
533
  end
505
- render_line_differential(rendered_line, line_to_render)
534
+ @rendered_screen.lines = new_lines
535
+ Reline::IOGate.show_cursor
506
536
  end
507
- @rendered_screen_cache = new_lines
508
- y = editor_cursor_y - screen_scroll_top
509
- Reline::IOGate.move_cursor_column editor_cursor_x
510
- Reline::IOGate.move_cursor_down y - @cursor_y
511
- @cursor_y = y
512
- new_lines.size - @cursor_y
537
+ y = wrapped_cursor_y - screen_scroll_top
538
+ Reline::IOGate.move_cursor_column wrapped_cursor_x
539
+ Reline::IOGate.move_cursor_down y - cursor_y
540
+ @rendered_screen.cursor_y = y
541
+ new_lines.size - y
513
542
  end
514
543
 
515
544
  def current_row
516
- wrapped_lines.flatten[editor_cursor_y]
517
- end
518
-
519
- def upper_space_height
520
- @cursor_y - 1
545
+ wrapped_lines.flatten[wrapped_cursor_y]
521
546
  end
522
547
 
523
- def rest_height
524
- screen_height - @cursor_y - @cursor_base_y - 1
548
+ def upper_space_height(wrapped_cursor_y)
549
+ wrapped_cursor_y - screen_scroll_top
525
550
  end
526
551
 
527
- def rerender_all
528
- Reline::IOGate.hide_cursor
529
- process_insert(force: true)
530
- handle_cleared
531
- render_differential unless @in_pasting
532
- Reline::IOGate.show_cursor
533
- end
534
-
535
- def handle_cleared
536
- return unless @cleared
537
-
538
- @cleared = false
539
- @rendered_screen_cache = nil
540
- Reline::IOGate.clear_screen
541
- @screen_size = Reline::IOGate.get_screen_size
542
- @cursor_base_y = 0
543
- @cursor_y = 0
544
- scroll_into_view
545
- render_differential
552
+ def rest_height(wrapped_cursor_y)
553
+ screen_height - wrapped_cursor_y + screen_scroll_top - @rendered_screen.base_y - 1
546
554
  end
547
555
 
548
556
  def rerender
549
- Reline::IOGate.hide_cursor
550
- finished = finished?
551
- handle_cleared
552
- if finished
553
- clear_rendered_lines
554
- render_full_content
555
- elsif !@in_pasting
556
- render_differential
557
- end
558
- Reline::IOGate.show_cursor
557
+ render_differential unless @in_pasting
559
558
  end
560
559
 
561
560
  class DialogProcScope
561
+ CompletionJourneyData = Struct.new(:preposing, :postposing, :list, :pointer)
562
+
562
563
  def initialize(line_editor, config, proc_to_exec, context)
563
564
  @line_editor = line_editor
564
565
  @config = config
@@ -617,12 +618,12 @@ class Reline::LineEditor
617
618
  end
618
619
 
619
620
  def preferred_dialog_height
620
- _editor_cursor_x, editor_cursor_y = @line_editor.editor_cursor_position
621
- [editor_cursor_y - @line_editor.screen_scroll_top, @line_editor.rest_height, (screen_height + 6) / 5].max
621
+ _wrapped_cursor_x, wrapped_cursor_y = @line_editor.wrapped_cursor_position
622
+ [@line_editor.upper_space_height(wrapped_cursor_y), @line_editor.rest_height(wrapped_cursor_y), (screen_height + 6) / 5].max
622
623
  end
623
624
 
624
625
  def completion_journey_data
625
- @line_editor.instance_variable_get(:@completion_journey_data)
626
+ @line_editor.dialog_proc_scope_completion_journey_data
626
627
  end
627
628
 
628
629
  def config
@@ -747,16 +748,15 @@ class Reline::LineEditor
747
748
  else
748
749
  scrollbar_pos = nil
749
750
  end
750
- upper_space = upper_space_height
751
751
  dialog.column = dialog_render_info.pos.x
752
752
  dialog.width += @block_elem_width if scrollbar_pos
753
753
  diff = (dialog.column + dialog.width) - screen_width
754
754
  if diff > 0
755
755
  dialog.column -= diff
756
756
  end
757
- if (rest_height - dialog_render_info.pos.y) >= height
757
+ if rest_height(screen_scroll_top + cursor_row) - dialog_render_info.pos.y >= height
758
758
  dialog.vertical_offset = dialog_render_info.pos.y + 1
759
- elsif upper_space >= height
759
+ elsif cursor_row >= height
760
760
  dialog.vertical_offset = dialog_render_info.pos.y - height
761
761
  else
762
762
  dialog.vertical_offset = dialog_render_info.pos.y + 1
@@ -794,7 +794,7 @@ class Reline::LineEditor
794
794
  if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: complete)
795
795
  after.lines("\n").map { |l| l.chomp('') }
796
796
  else
797
- before
797
+ before.map { |l| Reline::Unicode.escape_for_print(l) }
798
798
  end
799
799
  end
800
800
 
@@ -802,8 +802,8 @@ class Reline::LineEditor
802
802
  @config.editing_mode
803
803
  end
804
804
 
805
- private def menu(target, list)
806
- @menu_info = MenuInfo.new(target, list)
805
+ private def menu(_target, list)
806
+ @menu_info = MenuInfo.new(list)
807
807
  end
808
808
 
809
809
  private def complete_internal_proc(list, is_menu)
@@ -831,7 +831,7 @@ class Reline::LineEditor
831
831
  item_mbchars = item.grapheme_clusters
832
832
  end
833
833
  size = [memo_mbchars.size, item_mbchars.size].min
834
- result = ''
834
+ result = +''
835
835
  size.times do |i|
836
836
  if @config.completion_ignore_case
837
837
  if memo_mbchars[i].casecmp?(item_mbchars[i])
@@ -852,9 +852,9 @@ class Reline::LineEditor
852
852
  [target, preposing, completed, postposing]
853
853
  end
854
854
 
855
- private def complete(list, just_show_list = false)
855
+ private def complete(list, just_show_list)
856
856
  case @completion_state
857
- when CompletionState::NORMAL, CompletionState::JOURNEY
857
+ when CompletionState::NORMAL
858
858
  @completion_state = CompletionState::COMPLETION
859
859
  when CompletionState::PERFECT_MATCH
860
860
  @dig_perfect_match_proc&.(@perfect_matched)
@@ -888,52 +888,50 @@ class Reline::LineEditor
888
888
  end
889
889
  if not just_show_list and target < completed
890
890
  @buffer_of_lines[@line_index] = (preposing + completed + completion_append_character.to_s + postposing).split("\n")[@line_index] || String.new(encoding: @encoding)
891
- line_to_pointer = (preposing + completed + completion_append_character.to_s).split("\n").last || String.new(encoding: @encoding)
891
+ line_to_pointer = (preposing + completed + completion_append_character.to_s).split("\n")[@line_index] || String.new(encoding: @encoding)
892
892
  @byte_pointer = line_to_pointer.bytesize
893
893
  end
894
894
  end
895
895
  end
896
896
 
897
- private def move_completed_list(list, direction)
898
- case @completion_state
899
- when CompletionState::NORMAL, CompletionState::COMPLETION,
900
- CompletionState::MENU, CompletionState::MENU_WITH_PERFECT_MATCH
901
- @completion_state = CompletionState::JOURNEY
902
- result = retrieve_completion_block
903
- return if result.nil?
904
- preposing, target, postposing = result
905
- @completion_journey_data = CompletionJourneyData.new(
906
- preposing, postposing,
907
- [target] + list.select{ |item| item.start_with?(target) }, 0)
908
- if @completion_journey_data.list.size == 1
909
- @completion_journey_data.pointer = 0
910
- else
911
- case direction
912
- when :up
913
- @completion_journey_data.pointer = @completion_journey_data.list.size - 1
914
- when :down
915
- @completion_journey_data.pointer = 1
916
- end
917
- end
918
- @completion_state = CompletionState::JOURNEY
919
- else
920
- case direction
921
- when :up
922
- @completion_journey_data.pointer -= 1
923
- if @completion_journey_data.pointer < 0
924
- @completion_journey_data.pointer = @completion_journey_data.list.size - 1
925
- end
926
- when :down
927
- @completion_journey_data.pointer += 1
928
- if @completion_journey_data.pointer >= @completion_journey_data.list.size
929
- @completion_journey_data.pointer = 0
930
- end
931
- end
897
+ def dialog_proc_scope_completion_journey_data
898
+ return nil unless @completion_journey_state
899
+ line_index = @completion_journey_state.line_index
900
+ pre_lines = @buffer_of_lines[0...line_index].map { |line| line + "\n" }
901
+ post_lines = @buffer_of_lines[(line_index + 1)..-1].map { |line| line + "\n" }
902
+ DialogProcScope::CompletionJourneyData.new(
903
+ pre_lines.join + @completion_journey_state.pre,
904
+ @completion_journey_state.post + post_lines.join,
905
+ @completion_journey_state.list,
906
+ @completion_journey_state.pointer
907
+ )
908
+ end
909
+
910
+ private def move_completed_list(direction)
911
+ @completion_journey_state ||= retrieve_completion_journey_state
912
+ return false unless @completion_journey_state
913
+
914
+ if (delta = { up: -1, down: +1 }[direction])
915
+ @completion_journey_state.pointer = (@completion_journey_state.pointer + delta) % @completion_journey_state.list.size
932
916
  end
933
- completed = @completion_journey_data.list[@completion_journey_data.pointer]
934
- line_to_pointer = (@completion_journey_data.preposing + completed).split("\n")[@line_index] || String.new(encoding: @encoding)
935
- new_line = line_to_pointer + (@completion_journey_data.postposing.split("\n").first || '')
936
- set_current_line(new_line, line_to_pointer.bytesize)
917
+ completed = @completion_journey_state.list[@completion_journey_state.pointer]
918
+ set_current_line(@completion_journey_state.pre + completed + @completion_journey_state.post, @completion_journey_state.pre.bytesize + completed.bytesize)
919
+ true
920
+ end
921
+
922
+ private def retrieve_completion_journey_state
923
+ preposing, target, postposing = retrieve_completion_block
924
+ list = call_completion_proc
925
+ return unless list.is_a?(Array)
926
+
927
+ candidates = list.select{ |item| item.start_with?(target) }
928
+ return if candidates.empty?
929
+
930
+ pre = preposing.split("\n", -1).last || ''
931
+ post = postposing.split("\n", -1).first || ''
932
+ CompletionJourneyState.new(
933
+ @line_index, pre, target, post, [target] + candidates, 0
934
+ )
937
935
  end
938
936
 
939
937
  private def run_for_operators(key, method_symbol, &block)
@@ -1122,59 +1120,65 @@ class Reline::LineEditor
1122
1120
  @first_char = false
1123
1121
  completion_occurs = false
1124
1122
  if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
1125
- unless @config.disable_completion
1126
- result = call_completion_proc
1127
- if result.is_a?(Array)
1128
- completion_occurs = true
1129
- process_insert
1130
- if @config.autocompletion
1131
- move_completed_list(result, :down)
1132
- else
1133
- complete(result)
1123
+ if !@config.disable_completion
1124
+ process_insert(force: true)
1125
+ if @config.autocompletion
1126
+ @completion_state = CompletionState::NORMAL
1127
+ completion_occurs = move_completed_list(:down)
1128
+ else
1129
+ @completion_journey_state = nil
1130
+ result = call_completion_proc
1131
+ if result.is_a?(Array)
1132
+ completion_occurs = true
1133
+ complete(result, false)
1134
1134
  end
1135
1135
  end
1136
1136
  end
1137
1137
  elsif @config.editing_mode_is?(:emacs, :vi_insert) and key.char == :completion_journey_up
1138
1138
  if not @config.disable_completion and @config.autocompletion
1139
- result = call_completion_proc
1140
- if result.is_a?(Array)
1141
- completion_occurs = true
1142
- process_insert
1143
- move_completed_list(result, :up)
1144
- end
1139
+ process_insert(force: true)
1140
+ @completion_state = CompletionState::NORMAL
1141
+ completion_occurs = move_completed_list(:up)
1145
1142
  end
1146
- elsif not @config.disable_completion and @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
1147
- unless @config.disable_completion
1148
- result = call_completion_proc
1149
- if result.is_a?(Array)
1150
- completion_occurs = true
1151
- process_insert
1152
- move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
1153
- end
1143
+ elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
1144
+ # In vi mode, move completed list even if autocompletion is off
1145
+ if not @config.disable_completion
1146
+ process_insert(force: true)
1147
+ @completion_state = CompletionState::NORMAL
1148
+ completion_occurs = move_completed_list("\C-p".ord == key.char ? :up : :down)
1154
1149
  end
1155
1150
  elsif Symbol === key.char and respond_to?(key.char, true)
1156
1151
  process_key(key.char, key.char)
1157
1152
  else
1158
1153
  normal_char(key)
1159
1154
  end
1155
+
1160
1156
  unless completion_occurs
1161
1157
  @completion_state = CompletionState::NORMAL
1162
- @completion_journey_data = nil
1158
+ @completion_journey_state = nil
1163
1159
  end
1160
+
1164
1161
  if @in_pasting
1165
1162
  clear_dialogs
1166
- else
1167
- return old_lines != @buffer_of_lines
1163
+ return
1168
1164
  end
1165
+
1166
+ modified = old_lines != @buffer_of_lines
1167
+ if !completion_occurs && modified && !@config.disable_completion && @config.autocompletion
1168
+ # Auto complete starts only when edited
1169
+ process_insert(force: true)
1170
+ @completion_journey_state = retrieve_completion_journey_state
1171
+ end
1172
+ modified
1169
1173
  end
1170
1174
 
1171
1175
  def scroll_into_view
1172
- _editor_cursor_x, editor_cursor_y = editor_cursor_position
1173
- if editor_cursor_y < screen_scroll_top
1174
- @scroll_partial_screen = editor_cursor_y
1176
+ _wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
1177
+ if wrapped_cursor_y < screen_scroll_top
1178
+ @scroll_partial_screen = wrapped_cursor_y
1175
1179
  end
1176
- if editor_cursor_y >= screen_scroll_top + screen_height
1177
- @scroll_partial_screen = editor_cursor_y - screen_height + 1
1180
+ if wrapped_cursor_y >= screen_scroll_top + screen_height
1181
+ @scroll_partial_screen = wrapped_cursor_y - screen_height + 1
1178
1182
  end
1179
1183
  end
1180
1184
 
@@ -1217,10 +1221,11 @@ class Reline::LineEditor
1217
1221
  new_indent = @auto_indent_proc.(@buffer_of_lines.take(line_index + 1).push(''), line_index, byte_pointer, add_newline)
1218
1222
  return unless new_indent
1219
1223
 
1220
- @buffer_of_lines[line_index] = ' ' * new_indent + line.lstrip
1224
+ new_line = ' ' * new_indent + line.lstrip
1225
+ @buffer_of_lines[line_index] = new_line
1221
1226
  if @line_index == line_index
1222
- old_indent = line[/\A */].size
1223
- @byte_pointer = [@byte_pointer + new_indent - old_indent, 0].max
1227
+ indent_diff = new_line.bytesize - line.bytesize
1228
+ @byte_pointer = [@byte_pointer + indent_diff, 0].max
1224
1229
  end
1225
1230
  end
1226
1231
 
@@ -1233,7 +1238,6 @@ class Reline::LineEditor
1233
1238
  end
1234
1239
 
1235
1240
  def set_current_line(line, byte_pointer = nil)
1236
- @modified = true
1237
1241
  cursor = current_byte_pointer_cursor
1238
1242
  @buffer_of_lines[@line_index] = line
1239
1243
  if byte_pointer
@@ -1993,6 +1997,13 @@ class Reline::LineEditor
1993
1997
  end
1994
1998
  alias_method :kill_line, :ed_kill_line
1995
1999
 
2000
+ # Editline:: +vi_change_to_eol+ (vi command: +C+) + Kill and change from the cursor to the end of the line.
2001
+ private def vi_change_to_eol(key)
2002
+ ed_kill_line(key)
2003
+
2004
+ @config.editing_mode = :vi_insert
2005
+ end
2006
+
1996
2007
  # Editline:: +vi-kill-line-prev+ (vi: +Ctrl-U+) Delete the string from the
1997
2008
  # beginning of the edit buffer to the cursor and save it to the
1998
2009
  # cut buffer.
@@ -2037,7 +2048,7 @@ class Reline::LineEditor
2037
2048
  private def em_delete_or_list(key)
2038
2049
  if current_line.empty? or @byte_pointer < current_line.bytesize
2039
2050
  em_delete(key)
2040
- else # show completed list
2051
+ elsif !@config.autocompletion # show completed list
2041
2052
  result = call_completion_proc
2042
2053
  if result.is_a?(Array)
2043
2054
  complete(result, true)
@@ -2063,7 +2074,11 @@ class Reline::LineEditor
2063
2074
  alias_method :yank_pop, :em_yank_pop
2064
2075
 
2065
2076
  private def ed_clear_screen(key)
2066
- @cleared = true
2077
+ Reline::IOGate.clear_screen
2078
+ @screen_size = Reline::IOGate.get_screen_size
2079
+ @rendered_screen.lines = []
2080
+ @rendered_screen.base_y = 0
2081
+ @rendered_screen.cursor_y = 0
2067
2082
  end
2068
2083
  alias_method :clear_screen, :ed_clear_screen
2069
2084
 
@@ -2291,7 +2306,7 @@ class Reline::LineEditor
2291
2306
  end
2292
2307
 
2293
2308
  private def ed_delete_prev_char(key, arg: 1)
2294
- deleted = ''
2309
+ deleted = +''
2295
2310
  arg.times do
2296
2311
  if @byte_pointer > 0
2297
2312
  byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
@@ -80,23 +80,11 @@ module Reline::Terminfo
80
80
  def self.setupterm(term, fildes)
81
81
  errret_int = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT)
82
82
  ret = @setupterm.(term, fildes, errret_int)
83
- errret = errret_int[0, Fiddle::SIZEOF_INT].unpack1('i')
84
83
  case ret
85
84
  when 0 # OK
86
- 0
85
+ @term_supported = true
87
86
  when -1 # ERR
88
- case errret
89
- when 1
90
- raise TerminfoError.new('The terminal is hardcopy, cannot be used for curses applications.')
91
- when 0
92
- raise TerminfoError.new('The terminal could not be found, or that it is a generic type, having too little information for curses applications to run.')
93
- when -1
94
- raise TerminfoError.new('The terminfo database could not be found.')
95
- else # unknown
96
- -1
97
- end
98
- else # unknown
99
- -2
87
+ @term_supported = false
100
88
  end
101
89
  end
102
90
 
@@ -148,9 +136,14 @@ module Reline::Terminfo
148
136
  num
149
137
  end
150
138
 
139
+ # NOTE: This means Fiddle and curses are enabled.
151
140
  def self.enabled?
152
141
  true
153
142
  end
143
+
144
+ def self.term_supported?
145
+ @term_supported
146
+ end
154
147
  end if Reline::Terminfo.curses_dl
155
148
 
156
149
  module Reline::Terminfo
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.5.0.pre.1'
2
+ VERSION = '0.5.1'
3
3
  end
@@ -1,6 +1,8 @@
1
1
  require 'fiddle/import'
2
2
 
3
3
  class Reline::Windows
4
+ RESET_COLOR = "\e[0m"
5
+
4
6
  def self.encoding
5
7
  Encoding::UTF_8
6
8
  end
@@ -85,7 +87,7 @@ class Reline::Windows
85
87
  def call(*args)
86
88
  import = @proto.split("")
87
89
  args.each_with_index do |x, i|
88
- args[i], = [x == 0 ? nil : x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
90
+ args[i], = [x == 0 ? nil : +x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
89
91
  args[i], = [x].pack("I").unpack("i") if import[i] == "I"
90
92
  end
91
93
  ret, = @func.call(*args)
@@ -257,7 +259,7 @@ class Reline::Windows
257
259
  def self.check_input_event
258
260
  num_of_events = 0.chr * 8
259
261
  while @@output_buf.empty?
260
- Reline.core.line_editor.resize
262
+ Reline.core.line_editor.handle_signal
261
263
  if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
262
264
  # prevent for background consolemode change
263
265
  @@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
data/lib/reline.rb CHANGED
@@ -75,10 +75,10 @@ module Reline
75
75
 
76
76
  def initialize
77
77
  self.output = STDOUT
78
+ @mutex = Mutex.new
78
79
  @dialog_proc_list = {}
79
80
  yield self
80
81
  @completion_quote_character = nil
81
- @bracketed_paste_finished = false
82
82
  end
83
83
 
84
84
  def io_gate
@@ -220,26 +220,16 @@ module Reline
220
220
 
221
221
  Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
222
222
  # autocomplete
223
- return nil unless config.autocompletion
224
- if just_cursor_moving and completion_journey_data.nil?
225
- # Auto complete starts only when edited
226
- return nil
227
- end
228
- pre, target, post = retrieve_completion_block(true)
229
- if target.nil? or target.empty? or (completion_journey_data&.pointer == -1 and target.size <= 3)
230
- return nil
231
- end
232
- if completion_journey_data and completion_journey_data.list
233
- result = completion_journey_data.list.dup
234
- result.shift
235
- pointer = completion_journey_data.pointer - 1
236
- else
237
- result = call_completion_proc_with_checking_args(pre, target, post)
238
- pointer = nil
239
- end
240
- if result and result.size == 1 and result[0] == target and pointer != 0
241
- result = nil
242
- end
223
+ return unless config.autocompletion
224
+
225
+ journey_data = completion_journey_data
226
+ return unless journey_data
227
+
228
+ target = journey_data.list[journey_data.pointer]
229
+ result = journey_data.list.drop(1)
230
+ pointer = journey_data.pointer - 1
231
+ return if target.empty? || (result == [target] && pointer < 0)
232
+
243
233
  target_width = Reline::Unicode.calculate_width(target)
244
234
  x = cursor_pos.x - target_width
245
235
  if x < 0
@@ -265,12 +255,15 @@ module Reline
265
255
  Reline::DEFAULT_DIALOG_CONTEXT = Array.new
266
256
 
267
257
  def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
268
- Reline.update_iogate
269
- io_gate.with_raw_input do
258
+ @mutex.synchronize do
270
259
  unless confirm_multiline_termination
271
260
  raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
272
261
  end
273
- inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
262
+
263
+ Reline.update_iogate
264
+ io_gate.with_raw_input do
265
+ inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
266
+ end
274
267
 
275
268
  whole_buffer = line_editor.whole_buffer.dup
276
269
  whole_buffer.taint if RUBY_VERSION < '2.7'
@@ -280,6 +273,7 @@ module Reline
280
273
 
281
274
  if line_editor.eof?
282
275
  line_editor.reset_line
276
+ # Return nil if the input is aborted by C-d.
283
277
  nil
284
278
  else
285
279
  whole_buffer
@@ -288,17 +282,21 @@ module Reline
288
282
  end
289
283
 
290
284
  def readline(prompt = '', add_hist = false)
291
- Reline.update_iogate
292
- inner_readline(prompt, add_hist, false)
285
+ @mutex.synchronize do
286
+ Reline.update_iogate
287
+ io_gate.with_raw_input do
288
+ inner_readline(prompt, add_hist, false)
289
+ end
293
290
 
294
- line = line_editor.line.dup
295
- line.taint if RUBY_VERSION < '2.7'
296
- if add_hist and line and line.chomp("\n").size > 0
297
- Reline::HISTORY << line.chomp("\n")
298
- end
291
+ line = line_editor.line.dup
292
+ line.taint if RUBY_VERSION < '2.7'
293
+ if add_hist and line and line.chomp("\n").size > 0
294
+ Reline::HISTORY << line.chomp("\n")
295
+ end
299
296
 
300
- line_editor.reset_line if line_editor.line.nil?
301
- line
297
+ line_editor.reset_line if line_editor.line.nil?
298
+ line
299
+ end
302
300
  end
303
301
 
304
302
  private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
@@ -331,8 +329,10 @@ module Reline
331
329
  line_editor.auto_indent_proc = auto_indent_proc
332
330
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
333
331
  pre_input_hook&.call
334
- @dialog_proc_list.each_pair do |name_sym, d|
335
- line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
332
+ unless Reline::IOGate == Reline::GeneralIO
333
+ @dialog_proc_list.each_pair do |name_sym, d|
334
+ line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
335
+ end
336
336
  end
337
337
 
338
338
  unless config.test_mode
@@ -342,45 +342,31 @@ module Reline
342
342
  end
343
343
 
344
344
  line_editor.print_nomultiline_prompt(prompt)
345
+ line_editor.update_dialogs
345
346
  line_editor.rerender
346
347
 
347
348
  begin
348
349
  line_editor.set_signal_handlers
349
- prev_pasting_state = false
350
350
  loop do
351
- prev_pasting_state = io_gate.in_pasting?
352
351
  read_io(config.keyseq_timeout) { |inputs|
353
352
  line_editor.set_pasting_state(io_gate.in_pasting?)
354
- inputs.each { |c| line_editor.update(c) }
355
- line_editor.rerender
356
- if @bracketed_paste_finished
357
- line_editor.rerender_all
358
- @bracketed_paste_finished = false
359
- end
353
+ inputs.each { |key| line_editor.update(key) }
360
354
  }
361
- if prev_pasting_state == true and not io_gate.in_pasting? and not line_editor.finished?
362
- line_editor.set_pasting_state(false)
363
- prev_pasting_state = false
364
- line_editor.rerender_all
355
+ if line_editor.finished?
356
+ line_editor.render_finished
357
+ break
358
+ else
359
+ line_editor.set_pasting_state(io_gate.in_pasting?)
360
+ line_editor.rerender
365
361
  end
366
- break if line_editor.finished?
367
362
  end
368
363
  io_gate.move_cursor_column(0)
369
364
  rescue Errno::EIO
370
365
  # Maybe the I/O has been closed.
371
- rescue StandardError => e
366
+ ensure
372
367
  line_editor.finalize
373
368
  io_gate.deprep(otio)
374
- raise e
375
- rescue Exception
376
- # Including Interrupt
377
- line_editor.finalize
378
- io_gate.deprep(otio)
379
- raise
380
369
  end
381
-
382
- line_editor.finalize
383
- io_gate.deprep(otio)
384
370
  end
385
371
 
386
372
  # GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
@@ -398,7 +384,6 @@ module Reline
398
384
  c = io_gate.getc(Float::INFINITY)
399
385
  if c == -1
400
386
  result = :unmatched
401
- @bracketed_paste_finished = true
402
387
  else
403
388
  buffer << c
404
389
  result = key_stroke.match_status(buffer)
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.5.0.pre.1
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-01 00:00:00.000000000 Z
11
+ date: 2024-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -57,7 +57,10 @@ files:
57
57
  homepage: https://github.com/ruby/reline
58
58
  licenses:
59
59
  - Ruby
60
- metadata: {}
60
+ metadata:
61
+ bug_tracker_uri: https://github.com/ruby/reline/issues
62
+ changelog_uri: https://github.com/ruby/reline/releases
63
+ source_code_uri: https://github.com/ruby/reline
61
64
  post_install_message:
62
65
  rdoc_options: []
63
66
  require_paths:
@@ -69,11 +72,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
69
72
  version: '2.6'
70
73
  required_rubygems_version: !ruby/object:Gem::Requirement
71
74
  requirements:
72
- - - ">"
75
+ - - ">="
73
76
  - !ruby/object:Gem::Version
74
- version: 1.3.1
77
+ version: '0'
75
78
  requirements: []
76
- rubygems_version: 3.4.10
79
+ rubygems_version: 3.5.3
77
80
  signing_key:
78
81
  specification_version: 4
79
82
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.