77require 'reline/line_editor'
88require 'reline/history'
99require 'reline/terminfo'
10+ require 'reline/io'
1011require 'reline/face'
1112require '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
602517Reline ::Face . load_initial_configs
603518
0 commit comments