reline 0.5.8 → 0.5.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,138 +7,99 @@ class Reline::KeyStroke
7
7
  @config = config
8
8
  end
9
9
 
10
- def compress_meta_key(ary)
11
- return ary unless @config.convert_meta
12
- ary.inject([]) { |result, key|
13
- if result.size > 0 and result.last == "\e".ord
14
- result[result.size - 1] = Reline::Key.new(key, key | 0b10000000, true)
15
- else
16
- result << key
17
- end
18
- result
19
- }
20
- end
10
+ # Input exactly matches to a key sequence
11
+ MATCHING = :matching
12
+ # Input partially matches to a key sequence
13
+ MATCHED = :matched
14
+ # Input matches to a key sequence and the key sequence is a prefix of another key sequence
15
+ MATCHING_MATCHED = :matching_matched
16
+ # Input does not match to any key sequence
17
+ UNMATCHED = :unmatched
21
18
 
22
- def start_with?(me, other)
23
- compressed_me = compress_meta_key(me)
24
- compressed_other = compress_meta_key(other)
25
- i = 0
26
- loop do
27
- my_c = compressed_me[i]
28
- other_c = compressed_other[i]
29
- other_is_last = (i + 1) == compressed_other.size
30
- me_is_last = (i + 1) == compressed_me.size
31
- if my_c != other_c
32
- if other_c == "\e".ord and other_is_last and my_c.is_a?(Reline::Key) and my_c.with_meta
33
- return true
34
- else
35
- return false
36
- end
37
- elsif other_is_last
38
- return true
39
- elsif me_is_last
40
- return false
41
- end
42
- i += 1
43
- end
44
- end
19
+ def match_status(input)
20
+ matching = key_mapping.matching?(input)
21
+ matched = key_mapping.get(input)
45
22
 
46
- def equal?(me, other)
47
- case me
48
- when Array
49
- compressed_me = compress_meta_key(me)
50
- compressed_other = compress_meta_key(other)
51
- compressed_me.size == compressed_other.size and [compressed_me, compressed_other].transpose.all?{ |i| equal?(i[0], i[1]) }
52
- when Integer
53
- if other.is_a?(Reline::Key)
54
- if other.combined_char == "\e".ord
55
- false
56
- else
57
- other.combined_char == me
58
- end
59
- else
60
- me == other
61
- end
62
- when Reline::Key
63
- if other.is_a?(Integer)
64
- me.combined_char == other
65
- else
66
- me == other
67
- end
68
- end
69
- end
23
+ # FIXME: Workaround for single byte. remove this after MAPPING is merged into KeyActor.
24
+ matched ||= input.size == 1
25
+ matching ||= input == [ESC_BYTE]
70
26
 
71
- def match_status(input)
72
- key_mapping.keys.select { |lhs|
73
- start_with?(lhs, input)
74
- }.tap { |it|
75
- return :matched if it.size == 1 && equal?(it[0], input)
76
- return :matching if it.size == 1 && !equal?(it[0], input)
77
- return :matched if it.max_by(&:size)&.size&.< input.size
78
- return :matching if it.size > 1
79
- }
80
- if key_mapping.keys.any? { |lhs| start_with?(input, lhs) }
81
- :matched
27
+ if matching && matched
28
+ MATCHING_MATCHED
29
+ elsif matching
30
+ MATCHING
31
+ elsif matched
32
+ MATCHED
33
+ elsif input[0] == ESC_BYTE
34
+ match_unknown_escape_sequence(input, vi_mode: @config.editing_mode_is?(:vi_insert, :vi_command))
35
+ elsif input.size == 1
36
+ MATCHED
82
37
  else
83
- match_unknown_escape_sequence(input).first
38
+ UNMATCHED
84
39
  end
85
40
  end
86
41
 
87
42
  def expand(input)
88
- lhs = key_mapping.keys.select { |item| start_with?(input, item) }.sort_by(&:size).last
89
- unless lhs
90
- status, size = match_unknown_escape_sequence(input)
91
- case status
92
- when :matched
93
- return [:ed_unassigned] + expand(input.drop(size))
94
- when :matching
95
- return [:ed_unassigned]
96
- else
97
- return input
98
- end
43
+ matched_bytes = nil
44
+ (1..input.size).each do |i|
45
+ bytes = input.take(i)
46
+ status = match_status(bytes)
47
+ matched_bytes = bytes if status == MATCHED || status == MATCHING_MATCHED
99
48
  end
100
- rhs = key_mapping[lhs]
49
+ return [[], []] unless matched_bytes
101
50
 
102
- case rhs
103
- when String
104
- rhs_bytes = rhs.bytes
105
- expand(expand(rhs_bytes) + expand(input.drop(lhs.size)))
106
- when Symbol
107
- [rhs] + expand(input.drop(lhs.size))
108
- when Array
109
- rhs
51
+ func = key_mapping.get(matched_bytes)
52
+ if func.is_a?(Array)
53
+ keys = func.map { |c| Reline::Key.new(c, c, false) }
54
+ elsif func
55
+ keys = [Reline::Key.new(func, func, false)]
56
+ elsif matched_bytes.size == 1
57
+ keys = [Reline::Key.new(matched_bytes.first, matched_bytes.first, false)]
58
+ elsif matched_bytes.size == 2 && matched_bytes[0] == ESC_BYTE
59
+ keys = [Reline::Key.new(matched_bytes[1], matched_bytes[1] | 0b10000000, true)]
60
+ else
61
+ keys = []
110
62
  end
63
+
64
+ [keys, input.drop(matched_bytes.size)]
111
65
  end
112
66
 
113
67
  private
114
68
 
115
69
  # returns match status of CSI/SS3 sequence and matched length
116
- def match_unknown_escape_sequence(input)
70
+ def match_unknown_escape_sequence(input, vi_mode: false)
117
71
  idx = 0
118
- return [:unmatched, nil] unless input[idx] == ESC_BYTE
72
+ return UNMATCHED unless input[idx] == ESC_BYTE
119
73
  idx += 1
120
74
  idx += 1 if input[idx] == ESC_BYTE
121
75
 
122
76
  case input[idx]
123
77
  when nil
124
- return [:matching, nil]
78
+ if idx == 1 # `ESC`
79
+ return MATCHING_MATCHED
80
+ else # `ESC ESC`
81
+ return MATCHING
82
+ end
125
83
  when 91 # == '['.ord
126
- # CSI sequence
84
+ # CSI sequence `ESC [ ... char`
127
85
  idx += 1
128
86
  idx += 1 while idx < input.size && CSI_PARAMETER_BYTES_RANGE.cover?(input[idx])
129
87
  idx += 1 while idx < input.size && CSI_INTERMEDIATE_BYTES_RANGE.cover?(input[idx])
130
- input[idx] ? [:matched, idx + 1] : [:matching, nil]
131
88
  when 79 # == 'O'.ord
132
- # SS3 sequence
133
- input[idx + 1] ? [:matched, idx + 2] : [:matching, nil]
89
+ # SS3 sequence `ESC O char`
90
+ idx += 1
134
91
  else
135
- if idx == 1
136
- # `ESC char`, make it :unmatched so that it will be handled correctly in `read_2nd_character_of_key_sequence`
137
- [:unmatched, nil]
138
- else
139
- # `ESC ESC char`
140
- [:matched, idx + 1]
141
- end
92
+ # `ESC char` or `ESC ESC char`
93
+ return UNMATCHED if vi_mode
94
+ end
95
+
96
+ case input.size
97
+ when idx
98
+ MATCHING
99
+ when idx + 1
100
+ MATCHED
101
+ else
102
+ UNMATCHED
142
103
  end
143
104
  end
144
105
 
@@ -45,6 +45,7 @@ class Reline::LineEditor
45
45
  RenderedScreen = Struct.new(:base_y, :lines, :cursor_y, keyword_init: true)
46
46
 
47
47
  CompletionJourneyState = Struct.new(:line_index, :pre, :target, :post, :list, :pointer)
48
+ NullActionState = [nil, nil].freeze
48
49
 
49
50
  class MenuInfo
50
51
  attr_reader :list
@@ -237,7 +238,6 @@ class Reline::LineEditor
237
238
  @perfect_matched = nil
238
239
  @menu_info = nil
239
240
  @searching_prompt = nil
240
- @first_char = true
241
241
  @just_cursor_moving = false
242
242
  @eof = false
243
243
  @continuous_insertion_buffer = String.new(encoding: @encoding)
@@ -253,6 +253,8 @@ class Reline::LineEditor
253
253
  @input_lines = [[[""], 0, 0]]
254
254
  @input_lines_position = 0
255
255
  @undoing = false
256
+ @prev_action_state = NullActionState
257
+ @next_action_state = NullActionState
256
258
  reset_line
257
259
  end
258
260
 
@@ -412,7 +414,7 @@ class Reline::LineEditor
412
414
  # do nothing
413
415
  elsif level == :blank
414
416
  Reline::IOGate.move_cursor_column base_x
415
- @output.write "#{Reline::IOGate::RESET_COLOR}#{' ' * width}"
417
+ @output.write "#{Reline::IOGate.reset_color_sequence}#{' ' * width}"
416
418
  else
417
419
  x, w, content = new_items[level]
418
420
  cover_begin = base_x != 0 && new_levels[base_x - 1] == level
@@ -422,7 +424,7 @@ class Reline::LineEditor
422
424
  content, pos = Reline::Unicode.take_mbchar_range(content, base_x - x, width, cover_begin: cover_begin, cover_end: cover_end, padding: true)
423
425
  end
424
426
  Reline::IOGate.move_cursor_column x + pos
425
- @output.write "#{Reline::IOGate::RESET_COLOR}#{content}#{Reline::IOGate::RESET_COLOR}"
427
+ @output.write "#{Reline::IOGate.reset_color_sequence}#{content}#{Reline::IOGate.reset_color_sequence}"
426
428
  end
427
429
  base_x += width
428
430
  end
@@ -684,10 +686,8 @@ class Reline::LineEditor
684
686
  @trap_key.each do |t|
685
687
  @config.add_oneshot_key_binding(t, @name)
686
688
  end
687
- elsif @trap_key.is_a?(Array)
689
+ else
688
690
  @config.add_oneshot_key_binding(@trap_key, @name)
689
- elsif @trap_key.is_a?(Integer) or @trap_key.is_a?(Reline::Key)
690
- @config.add_oneshot_key_binding([@trap_key], @name)
691
691
  end
692
692
  end
693
693
  dialog_render_info
@@ -1080,17 +1080,7 @@ class Reline::LineEditor
1080
1080
  else # single byte
1081
1081
  return if key.char >= 128 # maybe, first byte of multi byte
1082
1082
  method_symbol = @config.editing_mode.get_method(key.combined_char)
1083
- if key.with_meta and method_symbol == :ed_unassigned
1084
- if @config.editing_mode_is?(:vi_command, :vi_insert)
1085
- # split ESC + key in vi mode
1086
- method_symbol = @config.editing_mode.get_method("\e".ord)
1087
- process_key("\e".ord, method_symbol)
1088
- method_symbol = @config.editing_mode.get_method(key.char)
1089
- process_key(key.char, method_symbol)
1090
- end
1091
- else
1092
- process_key(key.combined_char, method_symbol)
1093
- end
1083
+ process_key(key.combined_char, method_symbol)
1094
1084
  @multibyte_buffer.clear
1095
1085
  end
1096
1086
  if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
@@ -1119,13 +1109,10 @@ class Reline::LineEditor
1119
1109
  end
1120
1110
  if key.char.nil?
1121
1111
  process_insert(force: true)
1122
- if @first_char
1123
- @eof = true
1124
- end
1112
+ @eof = buffer_empty?
1125
1113
  finish
1126
1114
  return
1127
1115
  end
1128
- @first_char = false
1129
1116
  @completion_occurs = false
1130
1117
 
1131
1118
  if key.char.is_a?(Symbol)
@@ -1133,6 +1120,9 @@ class Reline::LineEditor
1133
1120
  else
1134
1121
  normal_char(key)
1135
1122
  end
1123
+
1124
+ @prev_action_state, @next_action_state = @next_action_state, NullActionState
1125
+
1136
1126
  unless @completion_occurs
1137
1127
  @completion_state = CompletionState::NORMAL
1138
1128
  @completion_journey_state = nil
@@ -1415,6 +1405,10 @@ class Reline::LineEditor
1415
1405
  whole_lines.join("\n")
1416
1406
  end
1417
1407
 
1408
+ private def buffer_empty?
1409
+ current_line.empty? and @buffer_of_lines.size == 1
1410
+ end
1411
+
1418
1412
  def finished?
1419
1413
  @finished
1420
1414
  end
@@ -1763,29 +1757,31 @@ class Reline::LineEditor
1763
1757
  end
1764
1758
 
1765
1759
  private def ed_search_prev_history(key, arg: 1)
1766
- substr = current_line.byteslice(0, @byte_pointer)
1760
+ substr = prev_action_state_value(:search_history) == :empty ? '' : current_line.byteslice(0, @byte_pointer)
1767
1761
  return if @history_pointer == 0
1768
1762
  return if @history_pointer.nil? && substr.empty? && !current_line.empty?
1769
1763
 
1770
1764
  history_range = 0...(@history_pointer || Reline::HISTORY.size)
1771
1765
  h_pointer, line_index = search_history(substr, history_range.reverse_each)
1772
1766
  return unless h_pointer
1773
- move_history(h_pointer, line: line_index || :start, cursor: @byte_pointer)
1767
+ move_history(h_pointer, line: line_index || :start, cursor: substr.empty? ? :end : @byte_pointer)
1774
1768
  arg -= 1
1769
+ set_next_action_state(:search_history, :empty) if substr.empty?
1775
1770
  ed_search_prev_history(key, arg: arg) if arg > 0
1776
1771
  end
1777
1772
  alias_method :history_search_backward, :ed_search_prev_history
1778
1773
 
1779
1774
  private def ed_search_next_history(key, arg: 1)
1780
- substr = current_line.byteslice(0, @byte_pointer)
1775
+ substr = prev_action_state_value(:search_history) == :empty ? '' : current_line.byteslice(0, @byte_pointer)
1781
1776
  return if @history_pointer.nil?
1782
1777
 
1783
1778
  history_range = @history_pointer + 1...Reline::HISTORY.size
1784
1779
  h_pointer, line_index = search_history(substr, history_range)
1785
1780
  return if h_pointer.nil? and not substr.empty?
1786
1781
 
1787
- move_history(h_pointer, line: line_index || :start, cursor: @byte_pointer)
1782
+ move_history(h_pointer, line: line_index || :start, cursor: substr.empty? ? :end : @byte_pointer)
1788
1783
  arg -= 1
1784
+ set_next_action_state(:search_history, :empty) if substr.empty?
1789
1785
  ed_search_next_history(key, arg: arg) if arg > 0
1790
1786
  end
1791
1787
  alias_method :history_search_forward, :ed_search_next_history
@@ -1941,7 +1937,7 @@ class Reline::LineEditor
1941
1937
  alias_method :kill_whole_line, :em_kill_line
1942
1938
 
1943
1939
  private def em_delete(key)
1944
- if current_line.empty? and @buffer_of_lines.size == 1 and key == "\C-d".ord
1940
+ if buffer_empty? and key == "\C-d".ord
1945
1941
  @eof = true
1946
1942
  finish
1947
1943
  elsif @byte_pointer < current_line.bytesize
@@ -2289,8 +2285,7 @@ class Reline::LineEditor
2289
2285
  end
2290
2286
 
2291
2287
  private def vi_list_or_eof(key)
2292
- if current_line.empty? and @buffer_of_lines.size == 1
2293
- set_current_line('', 0)
2288
+ if buffer_empty?
2294
2289
  @eof = true
2295
2290
  finish
2296
2291
  else
@@ -2551,4 +2546,12 @@ class Reline::LineEditor
2551
2546
  target_lines, target_cursor_x, target_cursor_y = @input_lines[@input_lines_position]
2552
2547
  set_current_lines(target_lines.dup, target_cursor_x, target_cursor_y)
2553
2548
  end
2549
+
2550
+ private def prev_action_state_value(type)
2551
+ @prev_action_state[0] == type ? @prev_action_state[1] : nil
2552
+ end
2553
+
2554
+ private def set_next_action_state(type, value)
2555
+ @next_action_state = [type, value]
2556
+ end
2554
2557
  end
@@ -1,4 +1,7 @@
1
1
  begin
2
+ # Ignore warning `Add fiddle to your Gemfile or gemspec` in Ruby 3.4.
3
+ # terminfo.rb and ansi.rb supports fiddle unavailable environment.
4
+ verbose, $VERBOSE = $VERBOSE, nil
2
5
  require 'fiddle'
3
6
  require 'fiddle/import'
4
7
  rescue LoadError
@@ -7,6 +10,8 @@ rescue LoadError
7
10
  false
8
11
  end
9
12
  end
13
+ ensure
14
+ $VERBOSE = verbose
10
15
  end
11
16
 
12
17
  module Reline::Terminfo
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.5.8'
2
+ VERSION = '0.5.9'
3
3
  end
data/lib/reline.rb CHANGED
@@ -7,6 +7,7 @@ require 'reline/key_stroke'
7
7
  require 'reline/line_editor'
8
8
  require 'reline/history'
9
9
  require 'reline/terminfo'
10
+ require 'reline/io'
10
11
  require 'reline/face'
11
12
  require 'rbconfig'
12
13
 
@@ -18,20 +19,10 @@ module Reline
18
19
  class ConfigEncodingConversionError < StandardError; end
19
20
 
20
21
  Key = Struct.new(:char, :combined_char, :with_meta) do
21
- def match?(other)
22
- case other
23
- when Reline::Key
24
- (other.char.nil? or char.nil? or char == other.char) and
25
- (other.combined_char.nil? or combined_char.nil? or combined_char == other.combined_char) and
26
- (other.with_meta.nil? or with_meta.nil? or with_meta == other.with_meta)
27
- when Integer, Symbol
28
- (combined_char and combined_char == other) or
29
- (combined_char.nil? and char and char == other)
30
- else
31
- false
32
- end
22
+ # For dialog_proc `key.match?(dialog.name)`
23
+ def match?(sym)
24
+ combined_char.is_a?(Symbol) && combined_char == sym
33
25
  end
34
- alias_method :==, :match?
35
26
  end
36
27
  CursorPos = Struct.new(:x, :y)
37
28
  DialogRenderInfo = Struct.new(
@@ -263,7 +254,6 @@ module Reline
263
254
  raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
264
255
  end
265
256
 
266
- Reline.update_iogate
267
257
  io_gate.with_raw_input do
268
258
  inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
269
259
  end
@@ -286,7 +276,6 @@ module Reline
286
276
 
287
277
  def readline(prompt = '', add_hist = false)
288
278
  @mutex.synchronize do
289
- Reline.update_iogate
290
279
  io_gate.with_raw_input do
291
280
  inner_readline(prompt, add_hist, false)
292
281
  end
@@ -336,7 +325,7 @@ module Reline
336
325
  line_editor.auto_indent_proc = auto_indent_proc
337
326
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
338
327
  pre_input_hook&.call
339
- unless Reline::IOGate == Reline::GeneralIO
328
+ unless Reline::IOGate.dumb?
340
329
  @dialog_proc_list.each_pair do |name_sym, d|
341
330
  line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
342
331
  end
@@ -378,92 +367,39 @@ module Reline
378
367
  end
379
368
  end
380
369
 
381
- # GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
382
- # is followed by a character, and times out and treats it as a standalone
383
- # ESC if the second character does not arrive. If the second character
384
- # comes before timed out, it is treated as a modifier key with the
385
- # meta-property of meta-key, so that it can be distinguished from
386
- # multibyte characters with the 8th bit turned on.
387
- #
388
- # GNU Readline will wait for the 2nd character with "keyseq-timeout"
389
- # milli-seconds but wait forever after 3rd characters.
370
+ # GNU Readline watis for "keyseq-timeout" milliseconds when the input is
371
+ # ambiguous whether it is matching or matched.
372
+ # If the next character does not arrive within the specified timeout, input
373
+ # is considered as matched.
374
+ # `ESC` is ambiguous because it can be a standalone ESC (matched) or part of
375
+ # `ESC char` or part of CSI sequence (matching).
390
376
  private def read_io(keyseq_timeout, &block)
391
377
  buffer = []
378
+ status = KeyStroke::MATCHING
392
379
  loop do
393
- c = io_gate.getc(Float::INFINITY)
394
- if c == -1
395
- result = :unmatched
396
- else
397
- buffer << c
398
- result = key_stroke.match_status(buffer)
399
- end
400
- case result
401
- when :matched
402
- expanded = key_stroke.expand(buffer).map{ |expanded_c|
403
- Reline::Key.new(expanded_c, expanded_c, false)
404
- }
405
- block.(expanded)
406
- break
407
- when :matching
408
- if buffer.size == 1
409
- case read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
410
- when :break then break
411
- when :next then next
412
- end
413
- end
414
- when :unmatched
415
- if buffer.size == 1 and c == "\e".ord
416
- read_escaped_key(keyseq_timeout, c, block)
380
+ timeout = status == KeyStroke::MATCHING_MATCHED ? keyseq_timeout.fdiv(1000) : Float::INFINITY
381
+ c = io_gate.getc(timeout)
382
+ if c.nil? || c == -1
383
+ if status == KeyStroke::MATCHING_MATCHED
384
+ status = KeyStroke::MATCHED
385
+ elsif buffer.empty?
386
+ # io_gate is closed and reached EOF
387
+ block.call([Key.new(nil, nil, false)])
388
+ return
417
389
  else
418
- expanded = buffer.map{ |expanded_c|
419
- Reline::Key.new(expanded_c, expanded_c, false)
420
- }
421
- block.(expanded)
390
+ status = KeyStroke::UNMATCHED
422
391
  end
423
- break
392
+ else
393
+ buffer << c
394
+ status = key_stroke.match_status(buffer)
424
395
  end
425
- end
426
- end
427
396
 
428
- private def read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
429
- succ_c = io_gate.getc(keyseq_timeout.fdiv(1000))
430
- if succ_c
431
- case key_stroke.match_status(buffer.dup.push(succ_c))
432
- when :unmatched
433
- if c == "\e".ord
434
- block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
435
- else
436
- block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
437
- end
438
- return :break
439
- when :matching
440
- io_gate.ungetc(succ_c)
441
- return :next
442
- when :matched
443
- buffer << succ_c
444
- expanded = key_stroke.expand(buffer).map{ |expanded_c|
445
- Reline::Key.new(expanded_c, expanded_c, false)
446
- }
447
- block.(expanded)
448
- return :break
397
+ if status == KeyStroke::MATCHED || status == KeyStroke::UNMATCHED
398
+ expanded, rest_bytes = key_stroke.expand(buffer)
399
+ rest_bytes.reverse_each { |c| io_gate.ungetc(c) }
400
+ block.call(expanded)
401
+ return
449
402
  end
450
- else
451
- block.([Reline::Key.new(c, c, false)])
452
- return :break
453
- end
454
- end
455
-
456
- private def read_escaped_key(keyseq_timeout, c, block)
457
- escaped_c = io_gate.getc(keyseq_timeout.fdiv(1000))
458
-
459
- if escaped_c.nil?
460
- block.([Reline::Key.new(c, c, false)])
461
- elsif escaped_c >= 128 # maybe, first byte of multi byte
462
- block.([Reline::Key.new(c, c, false), Reline::Key.new(escaped_c, escaped_c, false)])
463
- elsif escaped_c == "\e".ord # escape twice
464
- block.([Reline::Key.new(c, c, false), Reline::Key.new(c, c, false)])
465
- else
466
- block.([Reline::Key.new(escaped_c, escaped_c | 0b10000000, true)])
467
403
  end
468
404
  end
469
405
 
@@ -473,7 +409,7 @@ module Reline
473
409
  end
474
410
 
475
411
  private def may_req_ambiguous_char_width
476
- @ambiguous_width = 2 if io_gate == Reline::GeneralIO or !STDOUT.tty?
412
+ @ambiguous_width = 2 if io_gate.dumb? || !STDIN.tty? || !STDOUT.tty?
477
413
  return if defined? @ambiguous_width
478
414
  io_gate.move_cursor_column(0)
479
415
  begin
@@ -567,37 +503,13 @@ module Reline
567
503
  def self.line_editor
568
504
  core.line_editor
569
505
  end
506
+ end
570
507
 
571
- def self.update_iogate
572
- return if core.config.test_mode
573
508
 
574
- # Need to change IOGate when `$stdout.tty?` change from false to true by `$stdout.reopen`
575
- # Example: rails/spring boot the application in non-tty, then run console in tty.
576
- if ENV['TERM'] != 'dumb' && core.io_gate == Reline::GeneralIO && $stdout.tty?
577
- require 'reline/ansi'
578
- remove_const(:IOGate)
579
- const_set(:IOGate, Reline::ANSI)
580
- end
581
- end
582
- end
509
+ Reline::IOGate = Reline::IO.decide_io_gate
583
510
 
584
- require 'reline/general_io'
585
- io = Reline::GeneralIO
586
- unless ENV['TERM'] == 'dumb'
587
- case RbConfig::CONFIG['host_os']
588
- when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
589
- require 'reline/windows'
590
- tty = (io = Reline::Windows).msys_tty?
591
- else
592
- tty = $stdout.tty?
593
- end
594
- end
595
- Reline::IOGate = if tty
596
- require 'reline/ansi'
597
- Reline::ANSI
598
- else
599
- io
600
- end
511
+ # Deprecated
512
+ Reline::GeneralIO = Reline::Dumb.new
601
513
 
602
514
  Reline::Face.load_initial_configs
603
515
 
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.8
4
+ version: 0.5.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-29 00:00:00.000000000 Z
11
+ date: 2024-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -35,13 +35,16 @@ files:
35
35
  - COPYING
36
36
  - README.md
37
37
  - lib/reline.rb
38
- - lib/reline/ansi.rb
39
38
  - lib/reline/config.rb
40
39
  - lib/reline/face.rb
41
- - lib/reline/general_io.rb
42
40
  - lib/reline/history.rb
41
+ - lib/reline/io.rb
42
+ - lib/reline/io/ansi.rb
43
+ - lib/reline/io/dumb.rb
44
+ - lib/reline/io/windows.rb
43
45
  - lib/reline/key_actor.rb
44
46
  - lib/reline/key_actor/base.rb
47
+ - lib/reline/key_actor/composite.rb
45
48
  - lib/reline/key_actor/emacs.rb
46
49
  - lib/reline/key_actor/vi_command.rb
47
50
  - lib/reline/key_actor/vi_insert.rb
@@ -52,7 +55,6 @@ files:
52
55
  - lib/reline/unicode.rb
53
56
  - lib/reline/unicode/east_asian_width.rb
54
57
  - lib/reline/version.rb
55
- - lib/reline/windows.rb
56
58
  - license_of_rb-readline
57
59
  homepage: https://github.com/ruby/reline
58
60
  licenses: