Skip to content

Commit

Permalink
Remove Timeout usage
Browse files Browse the repository at this point in the history
Timeout's implementation relies on Thread, which would conflict with
`ruby/debug`'s thread-freezing implementation and has casued issues like

- ruby/debug#877
- ruby/debug#934
- ruby/debug#1000

This commit avoids the issue by completely removing the use of Timeout.
  • Loading branch information
st0012 committed Aug 14, 2023
1 parent 0f80004 commit e3fdbf5
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 48 deletions.
41 changes: 15 additions & 26 deletions lib/reline.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
require 'io/console'
require 'timeout'
require 'forwardable'
require 'reline/version'
require 'reline/config'
Expand Down Expand Up @@ -397,7 +396,7 @@ def readline(prompt = '', add_hist = false)
private def read_io(keyseq_timeout, &block)
buffer = []
loop do
c = io_gate.getc
c = io_gate.getc(Float::INFINITY)
if c == -1
result = :unmatched
@bracketed_paste_finished = true
Expand Down Expand Up @@ -434,15 +433,9 @@ def readline(prompt = '', add_hist = false)
end

private def read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
begin
succ_c = nil
Timeout.timeout(keyseq_timeout / 1000.0) {
succ_c = io_gate.getc
}
rescue Timeout::Error # cancel matching only when first byte
block.([Reline::Key.new(c, c, false)])
return :break
else
succ_c = io_gate.getc(keyseq_timeout / 100)

if succ_c
case key_stroke.match_status(buffer.dup.push(succ_c))
when :unmatched
if c == "\e".ord
Expand All @@ -462,27 +455,23 @@ def readline(prompt = '', add_hist = false)
block.(expanded)
return :break
end
else
block.([Reline::Key.new(c, c, false)])
return :break
end
end

private def read_escaped_key(keyseq_timeout, c, block)
begin
escaped_c = nil
Timeout.timeout(keyseq_timeout / 1000.0) {
escaped_c = io_gate.getc
}
rescue Timeout::Error # independent ESC
escaped_c = io_gate.getc(keyseq_timeout / 100)

if escaped_c.nil?
block.([Reline::Key.new(c, c, false)])
elsif escaped_c >= 128 # maybe, first byte of multi byte
block.([Reline::Key.new(c, c, false), Reline::Key.new(escaped_c, escaped_c, false)])
elsif escaped_c == "\e".ord # escape twice
block.([Reline::Key.new(c, c, false), Reline::Key.new(c, c, false)])
else
if escaped_c.nil?
block.([Reline::Key.new(c, c, false)])
elsif escaped_c >= 128 # maybe, first byte of multi byte
block.([Reline::Key.new(c, c, false), Reline::Key.new(escaped_c, escaped_c, false)])
elsif escaped_c == "\e".ord # escape twice
block.([Reline::Key.new(c, c, false), Reline::Key.new(c, c, false)])
else
block.([Reline::Key.new(escaped_c, escaped_c | 0b10000000, true)])
end
block.([Reline::Key.new(escaped_c, escaped_c | 0b10000000, true)])
end
end

Expand Down
38 changes: 20 additions & 18 deletions lib/reline/ansi.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
require 'io/console'
require 'io/wait'
require 'timeout'
require_relative 'terminfo'

class Reline::ANSI
Expand Down Expand Up @@ -154,11 +153,14 @@ def self.with_raw_input
end

@@buf = []
def self.inner_getc
# a single count is 0.1 sec
def self.inner_getc(count)
unless @@buf.empty?
return @@buf.shift
end
until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte }
count -= 1
return nil if count <= 0
Reline.core.line_editor.resize
end
(c == 0x16 && @@input.raw(min: 0, time: 0, &:getbyte)) || c
Expand All @@ -172,40 +174,40 @@ def self.inner_getc
@@in_bracketed_paste_mode = false
START_BRACKETED_PASTE = String.new("\e[200~,", encoding: Encoding::ASCII_8BIT)
END_BRACKETED_PASTE = String.new("\e[200~.", encoding: Encoding::ASCII_8BIT)
def self.getc_with_bracketed_paste
def self.getc_with_bracketed_paste(count)
buffer = String.new(encoding: Encoding::ASCII_8BIT)
buffer << inner_getc
buffer << inner_getc(count)
while START_BRACKETED_PASTE.start_with?(buffer) or END_BRACKETED_PASTE.start_with?(buffer) do
if START_BRACKETED_PASTE == buffer
@@in_bracketed_paste_mode = true
return inner_getc
return inner_getc(count)
elsif END_BRACKETED_PASTE == buffer
@@in_bracketed_paste_mode = false
ungetc(-1)
return inner_getc
return inner_getc(count)
end
begin
succ_c = nil
Timeout.timeout(Reline.core.config.keyseq_timeout * 100) {
succ_c = inner_getc
}
rescue Timeout::Error
break
else
succ_c = inner_getc(Reline.core.config.keyseq_timeout)

if succ_c
buffer << succ_c
else
break
end
end
buffer.bytes.reverse_each do |ch|
ungetc ch
end
inner_getc
inner_getc(count)
end

def self.getc
# a single count is 0.1 sec
# if the usage expects 1 sec timeout, count should be 10
# if the usage expects to wait indefinitely, count should be Float::INFINITY
def self.getc(count)
if Reline.core.config.enable_bracketed_paste
getc_with_bracketed_paste
getc_with_bracketed_paste(count)
else
inner_getc
inner_getc(count)
end
end

Expand Down
3 changes: 1 addition & 2 deletions lib/reline/general_io.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
require 'timeout'
require 'io/wait'

class Reline::GeneralIO
Expand Down Expand Up @@ -35,7 +34,7 @@ def self.with_raw_input
yield
end

def self.getc
def self.getc(_count)
unless @@buf.empty?
return @@buf.shift
end
Expand Down
2 changes: 1 addition & 1 deletion lib/reline/windows.rb
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ def self.with_raw_input
yield
end

def self.getc
def self.getc(_count)
check_input_event
@@output_buf.shift
end
Expand Down
2 changes: 1 addition & 1 deletion test/reline/yamatanooroti/test_rendering.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
begin
require 'yamatanooroti'

class Reline::TestRendering < Yamatanooroti::TestCase
class Reline::RenderingTest < Yamatanooroti::TestCase
def setup
@pwd = Dir.pwd
suffix = '%010d' % Random.rand(0..65535)
Expand Down

0 comments on commit e3fdbf5

Please sign in to comment.