reline 0.5.0.pre.1 → 0.5.1

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: 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.