Skip to content

Commit c3f7041

Browse files
authored
Merge reline-0.5.10 (ruby#11558)
* Merge reline-0.5.8 * Merge reline-0.5.9 * Merge reline-0.5.10
1 parent 4eb51df commit c3f7041

31 files changed

+2420
-2121
lines changed

lib/reline.rb

Lines changed: 42 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
require 'reline/line_editor'
88
require 'reline/history'
99
require 'reline/terminfo'
10+
require 'reline/io'
1011
require 'reline/face'
1112
require 'rbconfig'
1213

@@ -18,20 +19,10 @@ module Reline
1819
class ConfigEncodingConversionError < StandardError; end
1920

2021
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
3325
end
34-
alias_method :==, :match?
3526
end
3627
CursorPos = Struct.new(:x, :y)
3728
DialogRenderInfo = Struct.new(
@@ -263,7 +254,6 @@ def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
263254
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
264255
end
265256

266-
Reline.update_iogate
267257
io_gate.with_raw_input do
268258
inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
269259
end
@@ -286,7 +276,6 @@ def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
286276

287277
def readline(prompt = '', add_hist = false)
288278
@mutex.synchronize do
289-
Reline.update_iogate
290279
io_gate.with_raw_input do
291280
inner_readline(prompt, add_hist, false)
292281
end
@@ -335,14 +324,17 @@ def readline(prompt = '', add_hist = false)
335324
line_editor.prompt_proc = prompt_proc
336325
line_editor.auto_indent_proc = auto_indent_proc
337326
line_editor.dig_perfect_match_proc = dig_perfect_match_proc
327+
328+
# Readline calls pre_input_hook just after printing the first prompt.
329+
line_editor.print_nomultiline_prompt
338330
pre_input_hook&.call
339-
unless Reline::IOGate == Reline::GeneralIO
331+
332+
unless Reline::IOGate.dumb?
340333
@dialog_proc_list.each_pair do |name_sym, d|
341334
line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
342335
end
343336
end
344337

345-
line_editor.print_nomultiline_prompt(prompt)
346338
line_editor.update_dialogs
347339
line_editor.rerender
348340

@@ -354,7 +346,7 @@ def readline(prompt = '', add_hist = false)
354346
inputs.each do |key|
355347
if key.char == :bracketed_paste_start
356348
text = io_gate.read_bracketed_paste
357-
line_editor.insert_pasted_text(text)
349+
line_editor.insert_multiline_text(text)
358350
line_editor.scroll_into_view
359351
else
360352
line_editor.update(key)
@@ -378,92 +370,39 @@ def readline(prompt = '', add_hist = false)
378370
end
379371
end
380372

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.
373+
# GNU Readline watis for "keyseq-timeout" milliseconds when the input is
374+
# ambiguous whether it is matching or matched.
375+
# If the next character does not arrive within the specified timeout, input
376+
# is considered as matched.
377+
# `ESC` is ambiguous because it can be a standalone ESC (matched) or part of
378+
# `ESC char` or part of CSI sequence (matching).
390379
private def read_io(keyseq_timeout, &block)
391380
buffer = []
381+
status = KeyStroke::MATCHING
392382
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)
383+
timeout = status == KeyStroke::MATCHING_MATCHED ? keyseq_timeout.fdiv(1000) : Float::INFINITY
384+
c = io_gate.getc(timeout)
385+
if c.nil? || c == -1
386+
if status == KeyStroke::MATCHING_MATCHED
387+
status = KeyStroke::MATCHED
388+
elsif buffer.empty?
389+
# io_gate is closed and reached EOF
390+
block.call([Key.new(nil, nil, false)])
391+
return
417392
else
418-
expanded = buffer.map{ |expanded_c|
419-
Reline::Key.new(expanded_c, expanded_c, false)
420-
}
421-
block.(expanded)
393+
status = KeyStroke::UNMATCHED
422394
end
423-
break
395+
else
396+
buffer << c
397+
status = key_stroke.match_status(buffer)
424398
end
425-
end
426-
end
427399

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
400+
if status == KeyStroke::MATCHED || status == KeyStroke::UNMATCHED
401+
expanded, rest_bytes = key_stroke.expand(buffer)
402+
rest_bytes.reverse_each { |c| io_gate.ungetc(c) }
403+
block.call(expanded)
404+
return
449405
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)])
467406
end
468407
end
469408

@@ -473,7 +412,7 @@ def ambiguous_width
473412
end
474413

475414
private def may_req_ambiguous_char_width
476-
@ambiguous_width = 2 if io_gate == Reline::GeneralIO or !STDOUT.tty?
415+
@ambiguous_width = 2 if io_gate.dumb? || !STDIN.tty? || !STDOUT.tty?
477416
return if defined? @ambiguous_width
478417
io_gate.move_cursor_column(0)
479418
begin
@@ -521,8 +460,8 @@ def ambiguous_width
521460
def_single_delegator :line_editor, :byte_pointer, :point
522461
def_single_delegator :line_editor, :byte_pointer=, :point=
523462

524-
def self.insert_text(*args, &block)
525-
line_editor.insert_text(*args, &block)
463+
def self.insert_text(text)
464+
line_editor.insert_multiline_text(text)
526465
self
527466
end
528467

@@ -567,37 +506,13 @@ def self.ungetc(c)
567506
def self.line_editor
568507
core.line_editor
569508
end
509+
end
570510

571-
def self.update_iogate
572-
return if core.config.test_mode
573511

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
512+
Reline::IOGate = Reline::IO.decide_io_gate
583513

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
514+
# Deprecated
515+
Reline::GeneralIO = Reline::Dumb.new
601516

602517
Reline::Face.load_initial_configs
603518

0 commit comments

Comments
 (0)