Skip to content

Commit

Permalink
Store IO gate in Config and avoid referencing the Reline::IOGate
Browse files Browse the repository at this point in the history
…directly

IO gate is a component that's widely used in Reline. However, it's not
supposed to be assigned right when `Reline` is loaded, which has been
causing problems described in #559

And the need to lazily assign it when `readline`/`readmultiline` being called
means it should not be a constant but an attribute of either `Core` or `Config`.

Since it's widely used in `LineEditor`, I think making it an attribute of
`Config` is better than `Core`.

Note:

- The goal is to drop the `Reline::IOGate` constant completely. But I want
  to avoid making breaking change here as it's supposed to be a refactoring.
  • Loading branch information
st0012 committed Jul 5, 2023
1 parent 45acc2f commit 7ceae36
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 101 deletions.
62 changes: 33 additions & 29 deletions lib/reline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,19 @@ class Core
:autocompletion=

def initialize
self.output = STDOUT
@dialog_proc_list = {}
yield self
self.output = STDOUT
@completion_quote_character = nil
@bracketed_paste_finished = false
end

def io_gate
@config.io_gate
end

def encoding
Reline::IOGate.encoding
io_gate.encoding
end

def completion_append_character=(val)
Expand Down Expand Up @@ -181,16 +185,16 @@ def dialog_proc(name_sym)

def input=(val)
raise TypeError unless val.respond_to?(:getc) or val.nil?
if val.respond_to?(:getc) && Reline::IOGate.respond_to?(:input=)
Reline::IOGate.input = val
if val.respond_to?(:getc) && io_gate.respond_to?(:input=)
io_gate.input = val
end
end

def output=(val)
raise TypeError unless val.respond_to?(:write) or val.nil?
@output = val
if Reline::IOGate.respond_to?(:output=)
Reline::IOGate.output = val
if io_gate.respond_to?(:output=)
io_gate.output = val
end
end

Expand All @@ -213,7 +217,7 @@ def emacs_editing_mode?
end

def get_screen_size
Reline::IOGate.get_screen_size
io_gate.get_screen_size
end

Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
Expand Down Expand Up @@ -266,7 +270,7 @@ def get_screen_size
Reline::DEFAULT_DIALOG_CONTEXT = Array.new

def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
Reline::IOGate.with_raw_input do
io_gate.with_raw_input do
unless confirm_multiline_termination
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
end
Expand Down Expand Up @@ -298,15 +302,15 @@ def readline(prompt = '', add_hist = false)

private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
if ENV['RELINE_STDERR_TTY']
if Reline::IOGate.win?
if io_gate.win?
$stderr = File.open(ENV['RELINE_STDERR_TTY'], 'a')
else
$stderr.reopen(ENV['RELINE_STDERR_TTY'], 'w')
end
$stderr.sync = true
$stderr.puts "Reline is used by #{Process.pid}"
end
otio = Reline::IOGate.prep
otio = io_gate.prep

may_req_ambiguous_char_width
line_editor.reset(prompt, encoding: encoding)
Expand All @@ -333,7 +337,7 @@ def readline(prompt = '', add_hist = false)
unless config.test_mode
config.read
config.reset_default_key_bindings
Reline::IOGate.set_default_key_bindings(config)
io_gate.set_default_key_bindings(config)
end

line_editor.rerender
Expand All @@ -342,9 +346,9 @@ def readline(prompt = '', add_hist = false)
line_editor.set_signal_handlers
prev_pasting_state = false
loop do
prev_pasting_state = Reline::IOGate.in_pasting?
prev_pasting_state = io_gate.in_pasting?
read_io(config.keyseq_timeout) { |inputs|
line_editor.set_pasting_state(Reline::IOGate.in_pasting?)
line_editor.set_pasting_state(io_gate.in_pasting?)
inputs.each { |c|
line_editor.input_key(c)
line_editor.rerender
Expand All @@ -354,29 +358,29 @@ def readline(prompt = '', add_hist = false)
@bracketed_paste_finished = false
end
}
if prev_pasting_state == true and not Reline::IOGate.in_pasting? and not line_editor.finished?
if prev_pasting_state == true and not io_gate.in_pasting? and not line_editor.finished?
line_editor.set_pasting_state(false)
prev_pasting_state = false
line_editor.rerender_all
end
break if line_editor.finished?
end
Reline::IOGate.move_cursor_column(0)
io_gate.move_cursor_column(0)
rescue Errno::EIO
# Maybe the I/O has been closed.
rescue StandardError => e
line_editor.finalize
Reline::IOGate.deprep(otio)
io_gate.deprep(otio)
raise e
rescue Exception
# Including Interrupt
line_editor.finalize
Reline::IOGate.deprep(otio)
io_gate.deprep(otio)
raise
end

line_editor.finalize
Reline::IOGate.deprep(otio)
io_gate.deprep(otio)
end

# GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
Expand All @@ -391,7 +395,7 @@ def readline(prompt = '', add_hist = false)
private def read_io(keyseq_timeout, &block)
buffer = []
loop do
c = Reline::IOGate.getc
c = io_gate.getc
if c == -1
result = :unmatched
@bracketed_paste_finished = true
Expand Down Expand Up @@ -431,7 +435,7 @@ def readline(prompt = '', add_hist = false)
begin
succ_c = nil
Timeout.timeout(keyseq_timeout / 1000.0) {
succ_c = Reline::IOGate.getc
succ_c = io_gate.getc
}
rescue Timeout::Error # cancel matching only when first byte
block.([Reline::Key.new(c, c, false)])
Expand All @@ -446,7 +450,7 @@ def readline(prompt = '', add_hist = false)
end
return :break
when :matching
Reline::IOGate.ungetc(succ_c)
io_gate.ungetc(succ_c)
return :next
when :matched
buffer << succ_c
Expand All @@ -463,7 +467,7 @@ def readline(prompt = '', add_hist = false)
begin
escaped_c = nil
Timeout.timeout(keyseq_timeout / 1000.0) {
escaped_c = Reline::IOGate.getc
escaped_c = io_gate.getc
}
rescue Timeout::Error # independent ESC
block.([Reline::Key.new(c, c, false)])
Expand All @@ -486,19 +490,19 @@ def ambiguous_width
end

private def may_req_ambiguous_char_width
@ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or !STDOUT.tty?
@ambiguous_width = 2 if io_gate == Reline::GeneralIO or !STDOUT.tty?
return if defined? @ambiguous_width
Reline::IOGate.move_cursor_column(0)
io_gate.move_cursor_column(0)
begin
output.write "\u{25bd}"
rescue Encoding::UndefinedConversionError
# LANG=C
@ambiguous_width = 1
else
@ambiguous_width = Reline::IOGate.cursor_pos.x
@ambiguous_width = io_gate.cursor_pos.x
end
Reline::IOGate.move_cursor_column(0)
Reline::IOGate.erase_after_cursor
io_gate.move_cursor_column(0)
io_gate.erase_after_cursor
end
end

Expand Down Expand Up @@ -559,7 +563,7 @@ def self.encoding_system_needs

def self.core
@core ||= Core.new { |core|
core.config = Reline::Config.new
core.config = Reline::Config.new(Reline::IOGate)
core.key_stroke = Reline::KeyStroke.new(core.config)
core.line_editor = Reline::LineEditor.new(core.config, core.encoding)

Expand All @@ -574,7 +578,7 @@ def self.core
end

def self.ungetc(c)
Reline::IOGate.ungetc(c)
io_gate.ungetc(c)
end

def self.line_editor
Expand Down
6 changes: 4 additions & 2 deletions lib/reline/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ class InvalidInputrc < RuntimeError
end

attr_accessor :autocompletion
attr_accessor :io_gate

def initialize
def initialize(io_gate)
@additional_key_bindings = {} # from inputrc
@additional_key_bindings[:emacs] = {}
@additional_key_bindings[:vi_insert] = {}
Expand All @@ -70,7 +71,8 @@ def initialize
@keyseq_timeout = 500
@test_mode = false
@autocompletion = false
@convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
@io_gate = io_gate
@convert_meta = true if seven_bit_encoding?(@io_gate.encoding)
end

def reset
Expand Down
Loading

0 comments on commit 7ceae36

Please sign in to comment.