Skip to content

Commit

Permalink
Move I/O access from Reline::KeyStroke to Reline
Browse files Browse the repository at this point in the history
  • Loading branch information
aycabta committed Jun 5, 2019
1 parent 7c77603 commit c75a335
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 77 deletions.
77 changes: 75 additions & 2 deletions lib/reline.rb
Expand Up @@ -22,6 +22,7 @@ module Reline
CursorPos = Struct.new(:x, :y)

@@config = Reline::Config.new
@@key_stroke = Reline::KeyStroke.new(@@config)
@@line_editor = Reline::LineEditor.new(@@config)
@@ambiguous_width = nil

Expand Down Expand Up @@ -331,10 +332,9 @@ def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
end
end

key_stroke = Reline::KeyStroke.new(@@config)
begin
loop do
key_stroke.read_io(@@config.keyseq_timeout) { |inputs|
read_io(@@config.keyseq_timeout) { |inputs|
inputs.each { |c|
@@line_editor.input_key(c)
@@line_editor.rerender
Expand All @@ -353,6 +353,79 @@ def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
Reline::IOGate.deprep(otio)
end

# Keystrokes of GNU Readline will timeout it with the specification of
# "keyseq-timeout" when waiting for the 2nd character after the 1st one.
# If the 2nd character comes after 1st ESC without timeout it has a
# meta-property of meta-key to discriminate modified key with meta-key
# from multibyte characters that come with 8th bit on.
#
# GNU Readline will wait for the 2nd character with "keyseq-timeout"
# milli-seconds but wait forever after 3rd characters.
def read_io(keyseq_timeout, &block)
buffer = []
loop do
c = Reline::IOGate.getc
buffer << c
result = @@key_stroke.match_status(buffer)
case result
when :matched
block.(@@key_stroke.expand(buffer).map{ |c| Reline::Key.new(c, c, false) })
break
when :matching
if buffer.size == 1
begin
succ_c = nil
Timeout.timeout(keyseq_timeout / 1000.0) {
succ_c = Reline::IOGate.getc
}
rescue Timeout::Error # cancel matching only when first byte
block.([Reline::Key.new(c, c, false)])
break
else
if @@key_stroke.match_status(buffer.dup.push(succ_c)) == :unmatched
if c == "\e".ord
block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
else
block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
end
break
else
Reline::IOGate.ungetc(succ_c)
end
end
end
when :unmatched
if buffer.size == 1 and c == "\e".ord
read_escaped_key(keyseq_timeout, buffer, block)
else
block.(buffer.map{ |c| Reline::Key.new(c, c, false) })
end
break
end
end
end

def read_escaped_key(keyseq_timeout, buffer, block)
begin
escaped_c = nil
Timeout.timeout(keyseq_timeout / 1000.0) {
escaped_c = Reline::IOGate.getc
}
rescue Timeout::Error # independent ESC
block.([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
end
end

def may_req_ambiguous_char_width
@@ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
return if @@ambiguous_width
Expand Down
77 changes: 2 additions & 75 deletions lib/reline/key_stroke.rb
Expand Up @@ -15,79 +15,6 @@ def initialize(config)
@config = config
end

# Keystrokes of GNU Readline will timeout it with the specification of
# "keyseq-timeout" when waiting for the 2nd character after the 1st one.
# If the 2nd character comes after 1st ESC without timeout it has a
# meta-property of meta-key to discriminate modified key with meta-key
# from multibyte characters that come with 8th bit on.
#
# GNU Readline will wait for the 2nd character with "keyseq-timeout"
# milli-seconds but wait forever after 3rd characters.
def read_io(keyseq_timeout, &block)
buffer = []
loop do
c = Reline::IOGate.getc
buffer << c
result = match_status(buffer)
case result
when :matched
block.(expand(buffer).map{ |c| Reline::Key.new(c, c, false) })
break
when :matching
if buffer.size == 1
begin
succ_c = nil
Timeout.timeout(keyseq_timeout / 1000.0) {
succ_c = Reline::IOGate.getc
}
rescue Timeout::Error # cancel matching only when first byte
block.([Reline::Key.new(c, c, false)])
break
else
if match_status(buffer.dup.push(succ_c)) == :unmatched
if c == "\e".ord
block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
else
block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
end
break
else
Reline::IOGate.ungetc(succ_c)
end
end
end
when :unmatched
if buffer.size == 1 and c == "\e".ord
read_escaped_key(keyseq_timeout, buffer, block)
else
block.(buffer.map{ |c| Reline::Key.new(c, c, false) })
end
break
end
end
end

def read_escaped_key(keyseq_timeout, buffer, block)
begin
escaped_c = nil
Timeout.timeout(keyseq_timeout / 1000.0) {
escaped_c = Reline::IOGate.getc
}
rescue Timeout::Error # independent ESC
block.([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
end
end

def match_status(input)
key_mapping.keys.select { |lhs|
lhs.start_with? input
Expand All @@ -104,8 +31,6 @@ def match_status(input)
}
end

private

def expand(input)
lhs = key_mapping.keys.select { |lhs| input.start_with? lhs }.sort_by(&:size).reverse.first
return input unless lhs
Expand All @@ -120,6 +45,8 @@ def expand(input)
end
end

private

def key_mapping
@config.key_bindings
end
Expand Down

0 comments on commit c75a335

Please sign in to comment.