reline 0.5.8 → 0.5.9

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