diff options
Diffstat (limited to 'test/reline/yamatanooroti')
-rwxr-xr-x | test/reline/yamatanooroti/multiline_repl | 137 | ||||
-rw-r--r-- | test/reline/yamatanooroti/termination_checker.rb | 38 | ||||
-rw-r--r-- | test/reline/yamatanooroti/test_rendering.rb | 875 |
3 files changed, 951 insertions, 99 deletions
diff --git a/test/reline/yamatanooroti/multiline_repl b/test/reline/yamatanooroti/multiline_repl index 473d9d0f00..eba410f6dd 100755 --- a/test/reline/yamatanooroti/multiline_repl +++ b/test/reline/yamatanooroti/multiline_repl @@ -1,14 +1,14 @@ #!/usr/bin/env ruby + +require 'bundler' +Bundler.require + require 'reline' require 'optparse' require_relative 'termination_checker' opt = OptionParser.new -opt.on('--prompt-list-cache-timeout VAL') { |v| - Reline::LineEditor.__send__(:remove_const, :PROMPT_LIST_CACHE_TIMEOUT) - Reline::LineEditor::PROMPT_LIST_CACHE_TIMEOUT = v.to_f -} opt.on('--dynamic-prompt') { Reline.prompt_proc = proc { |lines| lines.each_with_index.map { |l, i| @@ -27,8 +27,54 @@ opt.on('--broken-dynamic-prompt') { opt.on('--dynamic-prompt-returns-empty') { Reline.prompt_proc = proc { |l| [] } } +opt.on('--dynamic-prompt-with-newline') { + Reline.prompt_proc = proc { |lines| + range = lines.size > 1 ? (0..(lines.size - 2)) : (0..0) + lines[range].each_with_index.map { |l, i| + '[%04d\n]> ' % i + } + } +} +opt.on('--broken-dynamic-prompt-assert-no-escape-sequence') { + Reline.prompt_proc = proc { |lines| + has_escape_sequence = lines.join.include?("\e") + (lines.size + 1).times.map { |i| + has_escape_sequence ? 'error>' : '[%04d]> ' % i + } + } +} +opt.on('--color-bold') { + Reline.output_modifier_proc = ->(output, complete:){ + output.gsub(/./) { |c| "\e[1m#{c}\e[0m" } + } +} +opt.on('--dynamic-prompt-show-line') { + Reline.prompt_proc = proc { |lines| + lines.map { |l| + '[%4.4s]> ' % l + } + } +} + +def assert_auto_indent_params(lines, line_index, byte_pointer, is_newline) + raise 'Wrong lines type' unless lines.all?(String) + + line = lines[line_index] + raise 'Wrong line_index value' unless line + + # The condition `byte_pointer <= line.bytesize` is not satisfied. Maybe bug. + # Instead, loose constraint `byte_pointer <= line.bytesize + 1` seems to be satisfied when is_newline is false. + return if is_newline + + raise 'byte_pointer out of bounds' unless byte_pointer <= line.bytesize + 1 + raise 'Invalid byte_pointer' unless line.byteslice(0, byte_pointer).valid_encoding? +end + opt.on('--auto-indent') { - AutoIndent.new + Reline.auto_indent_proc = lambda do |lines, line_index, byte_pointer, is_newline| + assert_auto_indent_params(lines, line_index, byte_pointer, is_newline) + AutoIndent.calculate_indent(lines, line_index, byte_pointer, is_newline) + end } opt.on('--dialog VAL') { |v| Reline.add_dialog_proc(:simple_dialog, lambda { @@ -55,6 +101,15 @@ opt.on('--dialog VAL') { |v| natural to read and easy to write. RUBY + elsif v.include?('fullwidth') + contents = <<~RUBY.split("\n") + Rubyとは... + + オープンソースの動的なプログラミン + グ言語で、シンプルさと高い生産性を + 備えています。エレガントな文法を持 + ち、自然に読み書きができます。 + RUBY end if v.include?('scrollkey') dialog.trap_key = nil @@ -74,24 +129,89 @@ opt.on('--dialog VAL') { |v| if v.include?('scrollbar') scrollbar = true end - Reline::DialogRenderInfo.new(pos: cursor_pos, contents: contents, height: height, scrollbar: scrollbar) + if v.include?('alt-scrollbar') + scrollbar = true + end + Reline::DialogRenderInfo.new(pos: cursor_pos, contents: contents, height: height, scrollbar: scrollbar, face: :completion_dialog) }) + if v.include?('alt-scrollbar') + ENV['RELINE_ALT_SCROLLBAR'] = '1' + end } opt.on('--complete') { Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil| %w{String ScriptError SyntaxError Signal}.select{ |c| c.start_with?(target) } } } +opt.on('--complete-menu-with-perfect-match') { + Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil| + %w{abs abs2}.select{ |c| c.start_with?(target) } + } +} opt.on('--autocomplete') { Reline.autocompletion = true Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil| %w{String Struct Symbol ScriptError SyntaxError Signal}.select{ |c| c.start_with?(target) } } } +opt.on('--autocomplete-empty') { + Reline.autocompletion = true + Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil| [] } +} opt.on('--autocomplete-long') { Reline.autocompletion = true Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil| - %w{String Struct Symbol StopIteration SystemCallError SystemExit SystemStackError ScriptError SyntaxError Signal SizedQueue Set SecureRandom Socket StringIO StringScanner Shellwords Syslog Singleton SDBM}.select{ |c| c.start_with?(target) } + %w{ + String + Struct + Symbol + StopIteration + SystemCallError + SystemExit + SystemStackError + ScriptError + SyntaxError + Signal + SizedQueue + Set + SecureRandom + Socket + StringIO + StringScanner + Shellwords + Syslog + Singleton + SDBM + }.select{ |c| c.start_with?(target) } + } +} +opt.on('--autocomplete-super-long') { + Reline.autocompletion = true + Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil| + c = +'A' + 2000.times.map{ s = "Str_#{c}"; c.succ!; s }.select{ |c| c.start_with?(target) } + } +} + +opt.on('--autocomplete-width-long') { + Reline.autocompletion = true + Reline.completion_proc = lambda { |target, preposing = nil, postposing = nil| + %w{ + remove_instance_variable + respond_to? + ruby2_keywords + rand + readline + readlines + require + require_relative + raise + respond_to_missing? + redo + rescue + retry + return + }.select{ |c| c.start_with?(target) } } } opt.parse!(ARGV) @@ -104,8 +224,7 @@ end begin prompt = ENV['RELINE_TEST_PROMPT'] || 'prompt> ' puts 'Multiline REPL.' - checker = TerminationChecker.new - while code = Reline.readmultiline(prompt, true) { |code| checker.terminated?(code) } + while code = Reline.readmultiline(prompt, true) { |code| TerminationChecker.terminated?(code) } case code.chomp when 'exit', 'quit', 'q' exit 0 diff --git a/test/reline/yamatanooroti/termination_checker.rb b/test/reline/yamatanooroti/termination_checker.rb index 9c2c3ae740..b97c798c59 100644 --- a/test/reline/yamatanooroti/termination_checker.rb +++ b/test/reline/yamatanooroti/termination_checker.rb @@ -1,30 +1,26 @@ require 'ripper' -require 'irb/ruby-lex' -class TerminationChecker < RubyLex - def terminated?(code) - code.gsub!(/\n*$/, '').concat("\n") - @tokens = Ripper.lex(code) - continue = process_continue - code_block_open = check_code_block(code) - indent = process_nesting_level - ltype = process_literal_type - if code_block_open or ltype or continue or indent > 0 - false - else - true - end +module TerminationChecker + def self.terminated?(code) + Ripper.sexp(code) ? true : false end end -class AutoIndent < RubyLex - def initialize - set_input(self) - context = Struct.new(:auto_indent_mode, :workspace).new(true, nil) - set_auto_indent(context) +module AutoIndent + def self.calculate_indent(lines, line_index, byte_pointer, is_newline) + if is_newline + 2 * nesting_level(lines[0..line_index - 1]) + else + lines = lines.dup + lines[line_index] = lines[line_index]&.byteslice(0, byte_pointer) + prev_level = nesting_level(lines[0..line_index - 1]) + level = nesting_level(lines[0..line_index]) + 2 * level if level < prev_level + end end - def auto_indent(&block) - Reline.auto_indent_proc = block + def self.nesting_level(lines) + code = lines.join("\n") + code.scan(/if|def|\(|\[|\{/).size - code.scan(/end|\)|\]|\}/).size end end diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb index 58d23a40fd..74798c338f 100644 --- a/test/reline/yamatanooroti/test_rendering.rb +++ b/test/reline/yamatanooroti/test_rendering.rb @@ -3,7 +3,34 @@ require 'reline' begin require 'yamatanooroti' - class Reline::TestRendering < Yamatanooroti::TestCase + class Reline::RenderingTest < Yamatanooroti::TestCase + + FACE_CONFIGS = { no_config: "", valid_config: <<~VALID_CONFIG, incomplete_config: <<~INCOMPLETE_CONFIG } + require "reline" + Reline::Face.config(:completion_dialog) do |face| + face.define :default, foreground: :white, background: :blue + face.define :enhanced, foreground: :white, background: :magenta + face.define :scrollbar, foreground: :white, background: :blue + end + VALID_CONFIG + require "reline" + Reline::Face.config(:completion_dialog) do |face| + face.define :default, foreground: :white, background: :black + face.define :scrollbar, foreground: :white, background: :cyan + end + INCOMPLETE_CONFIG + + def iterate_over_face_configs(&block) + FACE_CONFIGS.each do |config_name, face_config| + config_file = Tempfile.create(%w{face_config- .rb}) + config_file.write face_config + block.call(config_name, config_file) + config_file.close + ensure + File.delete(config_file) + end + end + def setup @pwd = Dir.pwd suffix = '%010d' % Random.rand(0..65535) @@ -121,6 +148,7 @@ begin end def test_finish_autowrapped_line_in_the_middle_of_multilines + omit if RUBY_VERSION < '2.7' start_terminal(30, 16, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') write("<<~EOM\n ABCDEFG\nEOM\n") close @@ -169,9 +197,12 @@ begin LINES start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') write(":a\n\C-[k") + write("i\n:a") + write("\C-[h") close assert_screen(<<~EOC) - Multiline REPL. + (ins)prompt> :a + => :a (ins)prompt> :a => :a (cmd)prompt> :a @@ -236,6 +267,21 @@ begin EOC end + def test_esc_input + omit if Reline::IOGate.win? + start_terminal(5, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') + write("def\C-aabc") + write("\e") # single ESC + sleep 1 + write("A") + write("B\eAC") # ESC + A (M-A, specified ed_unassigned in Reline::KeyActor::Emacs) + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> abcABCdef + EOC + end + def test_prompt_with_escape_sequence ENV['RELINE_TEST_PROMPT'] = "\1\e[30m\2prompt> \1\e[m\2" start_terminal(5, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') @@ -263,6 +309,21 @@ begin EOC end + def test_readline_with_multiline_input + start_terminal(5, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dynamic-prompt}, startup_message: 'Multiline REPL.') + write("def foo\n bar\nend\n") + write("Reline.readline('prompt> ')\n") + write("\C-p\C-p") + close + assert_screen(<<~EOC) + => :foo + [0000]> Reline.readline('prompt> ') + prompt> def foo + bar + end + EOC + end + def test_multiline_and_autowrap start_terminal(10, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') write("def aaaaaaaaaa\n 33333333\n end\C-a\C-pputs\C-e\e\C-m888888888888888") @@ -280,6 +341,23 @@ begin EOC end + def test_multiline_add_new_line_and_autowrap + start_terminal(10, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') + write("def aaaaaaaaaa") + write("\n") + write(" bbbbbbbbbbbb") + write("\n") + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> def aaaaaaaa + aa + prompt> bbbbbbbbbb + bb + prompt> + EOC + end + def test_clear start_terminal(10, 15, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') write("3\C-l") @@ -404,6 +482,9 @@ begin write("def a\n 8\nend\ndef b\n 3\nend\C-s8") close assert_screen(<<~EOC) + prompt> 8 + prompt> end + => :a (i-search)`8'def a (i-search)`8' 8 (i-search)`8'end @@ -415,6 +496,9 @@ begin write("def a\n 8\nend\ndef b\n 3\nend\C-r8\C-j") close assert_screen(<<~EOC) + prompt> 8 + prompt> end + => :a prompt> def a prompt> 8 prompt> end @@ -424,10 +508,10 @@ begin def test_binding_for_vi_movement_mode write_inputrc <<~LINES set editing-mode vi - "\\C-j": vi-movement-mode + "\\C-a": vi-movement-mode LINES start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') - write(":1234\C-jhhhi0") + write(":1234\C-ahhhi0") close assert_screen(<<~EOC) Multiline REPL. @@ -435,32 +519,32 @@ begin EOC end - def test_prompt_list_caching - start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --prompt-list-cache-timeout 10 --dynamic-prompt}, startup_message: 'Multiline REPL.') + def test_broken_prompt_list + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --broken-dynamic-prompt}, startup_message: 'Multiline REPL.') write("def hoge\n 3\nend") close assert_screen(<<~EOC) Multiline REPL. [0000]> def hoge [0001]> 3 - [0002]> end + [0001]> end EOC end - def test_broken_prompt_list - start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --broken-dynamic-prompt}, startup_message: 'Multiline REPL.') - write("def hoge\n 3\nend") + def test_no_escape_sequence_passed_to_dynamic_prompt + start_terminal(10, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete --color-bold --broken-dynamic-prompt-assert-no-escape-sequence}, startup_message: 'Multiline REPL.') + write("%[ S") + write("\n") close assert_screen(<<~EOC) Multiline REPL. - [0000]> def hoge - [0001]> 3 - [0001]> end + [0000]> %[ S + [0001]> EOC end def test_enable_bracketed_paste - omit if Reline::IOGate.win? + omit if Reline.core.io_gate.win? write_inputrc <<~LINES set enable-bracketed-paste on LINES @@ -631,6 +715,31 @@ begin EOC end + def test_longer_than_screen_height_nearest_cursor_with_scroll_back + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') + write(<<~EOC.chomp) + if 1 + if 2 + if 3 + if 4 + puts + end + end + end + end + EOC + write("\C-p" * 4 + "\C-e" + "\C-p" * 4) + write("2") + close + assert_screen(<<~EOC) + prompt> if 12 + prompt> if 2 + prompt> if 3 + prompt> if 4 + prompt> puts + EOC + end + def test_update_cursor_correctly_when_just_cursor_moving start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') write("def hoge\n 01234678") @@ -648,6 +757,66 @@ begin EOC end + def test_auto_indent + start_terminal(10, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.') + "def hoge\nputs(\n1,\n2\n)\nend".lines do |line| + write line + end + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> def hoge + prompt> puts( + prompt> 1, + prompt> 2 + prompt> ) + prompt> end + EOC + end + + def test_auto_indent_when_inserting_line + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.') + write 'aa(bb(cc(dd(ee(' + write "\C-b" * 5 + "\n" + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> aa(bb(cc(d + prompt> d(ee( + EOC + end + + def test_auto_indent_multibyte_insert_line + start_terminal(10, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.') + write "if true\n" + write "あいうえお\n" + 4.times { write "\C-b\C-b\C-b\C-b\e\r" } + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> if true + prompt> あ + prompt> い + prompt> う + prompt> え + prompt> お + prompt> + EOC + end + + def test_newline_after_wrong_indent + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.') + write "if 1\n aa" + write "\n" + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> if 1 + prompt> aa + prompt> + EOC + end + def test_suppress_auto_indent_just_after_pasted start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.') write("def hoge\n [[\n 3]]\ned") @@ -677,6 +846,20 @@ begin EOC end + def test_auto_indent_with_various_spaces + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.') + write "(\n\C-v" + write "\C-k\n\C-v" + write "\C-k)" + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> ( + prompt> ^K + prompt> ) + EOC + end + def test_autowrap_in_the_middle_of_a_line start_terminal(5, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') write("def abcdefg; end\C-b\C-b\C-b\C-b\C-b") @@ -730,7 +913,7 @@ begin end def test_meta_key - start_terminal(50, 200, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') + start_terminal(30, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') write("def ge\M-bho") close assert_screen(<<~EOC) @@ -739,8 +922,18 @@ begin EOC end + def test_not_meta_key + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') + write("おだんご") # "だ" in UTF-8 contains "\xA0" + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> おだんご + EOC + end + def test_force_enter - start_terminal(50, 200, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') + start_terminal(30, 120, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') write("def hoge\nend\C-p\C-e") write("\M-\x0D") close @@ -752,36 +945,27 @@ begin EOC end - def test_cyrillic_chars - omit unless Reline::IOGate.win? - start_terminal(50, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') - write("`chcp 850`\n") - write("`chcp`\n") - write("def гопота; 3; end\n") - write("гопота\n") - close + def test_eof_with_newline + omit if Reline.core.io_gate.win? + cmd = %Q{ruby -e 'print(%Q{abc def \\e\\r})' | ruby -I#{@pwd}/lib -rreline -e 'p Reline.readline(%{> })'} + start_terminal(40, 50, ['bash', '-c', cmd]) + sleep 1 + close rescue nil assert_screen(<<~'EOC') - Multiline REPL. - prompt> `chcp 850` - => "Active code page: 850\n" - prompt> `chcp` - => "Active code page: 850\n" - prompt> def гопота; 3; end - => :гопота - prompt> гопота - => 3 - prompt> + > abc def + "abc def " EOC end - def test_with_newline - omit if Reline::IOGate.win? - cmd = %Q{ruby -e 'print(%Q{abc def \\e\\r})' | ruby -I#{@pwd}/lib -rreline -e 'p Reline.readline(%{> })'} - start_terminal(50, 50, ['bash', '-c', cmd]) - close + def test_eof_without_newline + omit if Reline.core.io_gate.win? + cmd = %Q{ruby -e 'print(%{hello})' | ruby -I#{@pwd}/lib -rreline -e 'p Reline.readline(%{> })'} + start_terminal(40, 50, ['bash', '-c', cmd]) + sleep 1 + close rescue nil assert_screen(<<~'EOC') - > abc def - "abc def " + > hello + "hello" EOC end @@ -795,6 +979,18 @@ begin EOC end + def test_multiline_completion + start_terminal(10, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --complete}, startup_message: 'Multiline REPL.') + write("def hoge\n St\n St\C-p\t") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> def hoge + prompt> String + prompt> St + EOC + end + def test_completion_journey_2nd_line write_inputrc <<~LINES set editing-mode vi @@ -822,44 +1018,127 @@ begin EOC end - def test_simple_dialog - start_terminal(20, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.') - write('a') - write('b') - write('c') - write("\C-h") + def test_completion_menu_is_displayed_horizontally + start_terminal(20, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --complete}, startup_message: 'Multiline REPL.') + write("S\t\t") close assert_screen(<<~'EOC') Multiline REPL. - prompt> ab - Ruby is... - A dynamic, open source programming - language with a focus on simplicity - and productivity. It has an elegant - syntax that is natural to read and - easy to write. + prompt> S + ScriptError String + Signal SyntaxError EOC end - def test_simple_dialog_at_right_edge - start_terminal(20, 40, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.') - write('a') - write('b') - write('c') - write("\C-h") + def test_show_all_if_ambiguous_on + write_inputrc <<~LINES + set show-all-if-ambiguous on + LINES + start_terminal(20, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --complete}, startup_message: 'Multiline REPL.') + write("S\t") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> S + ScriptError String + Signal SyntaxError + EOC + end + + def test_show_all_if_ambiguous_on_and_menu_with_perfect_match + write_inputrc <<~LINES + set show-all-if-ambiguous on + LINES + start_terminal(20, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --complete-menu-with-perfect-match}, startup_message: 'Multiline REPL.') + write("a\t") close assert_screen(<<~'EOC') Multiline REPL. - prompt> ab - Ruby is... - A dynamic, open source programming - language with a focus on simplicity - and productivity. It has an elegant - syntax that is natural to read and - easy to write. + prompt> abs + abs abs2 EOC end + def test_simple_dialog + iterate_over_face_configs do |config_name, config_file| + start_terminal(20, 50, %W{ruby -I#{@pwd}/lib -r#{config_file.path} #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.') + write('a') + write('b') + write('c') + write("\C-h") + close + assert_screen(<<~'EOC', "Failed with `#{config_name}` in Face") + Multiline REPL. + prompt> ab + Ruby is... + A dynamic, open source programming + language with a focus on simplicity + and productivity. It has an elegant + syntax that is natural to read and + easy to write. + EOC + end + end + + def test_simple_dialog_at_right_edge + iterate_over_face_configs do |config_name, config_file| + start_terminal(20, 40, %W{ruby -I#{@pwd}/lib -r#{config_file.path} #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.') + write('a') + write('b') + write('c') + write("\C-h") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> ab + Ruby is... + A dynamic, open source programming + language with a focus on simplicity + and productivity. It has an elegant + syntax that is natural to read and + easy to write. + EOC + end + end + + def test_dialog_scroll_pushup_condition + iterate_over_face_configs do |config_name, config_file| + start_terminal(10, 50, %W{ruby -I#{@pwd}/lib -r#{config_file.path} #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write("\n" * 10) + write("if 1\n sSts\nend") + write("\C-p\C-h\C-e\C-h") + close + assert_screen(<<~'EOC') + prompt> + prompt> + prompt> + prompt> + prompt> + prompt> + prompt> if 1 + prompt> St + prompt> enString + Struct + EOC + end + end + + def test_simple_dialog_with_scroll_screen + iterate_over_face_configs do |config_name, config_file| + start_terminal(5, 50, %W{ruby -I#{@pwd}/lib -r#{config_file.path} #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.') + write("if 1\n 2\n 3\n 4\n 5\n 6") + write("\C-p\C-n\C-p\C-p\C-p#") + close + assert_screen(<<~'EOC') + prompt> 2 + prompt> 3# + prompt> 4 + prompt> 5 Ruby is... + prompt> 6 A dynamic, open source programming + EOC + end + end + def test_autocomplete_at_bottom start_terminal(15, 50, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') write('def hoge' + "\C-m" * 10 + "end\C-p ") @@ -906,8 +1185,50 @@ begin assert_screen(<<~'EOC') Multiline REPL. prompt> St - r String - Struct + r + String + Struct + EOC + end + + def test_autocomplete_target_at_end_of_line + start_terminal(20, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write(' ') + write('Str') + write("\C-i") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> Str + ing String + Struct + EOC + end + + def test_autocomplete_completed_input_is_wrapped + start_terminal(20, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write(' ') + write('Str') + write("\C-i") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> Stri + ng String + Struct + EOC + end + + def test_force_insert_before_autocomplete + start_terminal(20, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write('Sy') + write(";St\t\t") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> Sy;Struct + String + Struct EOC end @@ -957,6 +1278,40 @@ begin EOC end + def test_dialog_with_fullwidth_chars + ENV['RELINE_TEST_PROMPT'] = '> ' + start_terminal(20, 5, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog fullwidth,scrollkey,scrollbar}, startup_message: 'Multiline REPL.') + 6.times{ write('j') } + close + assert_screen(<<~'EOC') + Multi + line + REPL. + > + オー + グ言▄ + 備え█ + ち、█ + EOC + end + + def test_dialog_with_fullwidth_chars_split + ENV['RELINE_TEST_PROMPT'] = '> ' + start_terminal(20, 6, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog fullwidth,scrollkey,scrollbar}, startup_message: 'Multiline REPL.') + 6.times{ write('j') } + close + assert_screen(<<~'EOC') + Multil + ine RE + PL. + > + オー + グ言 ▄ + 備え █ + ち、 █ + EOC + end + def test_autocomplete_empty start_terminal(20, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') write('Street') @@ -979,6 +1334,32 @@ begin EOC end + def test_autocomplete_empty_string + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write("\C-i") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> String + String █ + Struct ▀ + Symbol + EOC + end + + def test_paste_code_with_tab_indent_does_not_fail + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete-empty}, startup_message: 'Multiline REPL.') + write("2.times do\n\tputs\n\tputs\nend") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> 2.times do + prompt> puts + prompt> puts + prompt> end + EOC + end + def test_autocomplete_after_2nd_line start_terminal(20, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') write("def hoge\n Str") @@ -1007,6 +1388,23 @@ begin EOC end + def test_rerender_multiple_dialog + start_terminal(20, 60, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete --dialog simple}, startup_message: 'Multiline REPL.') + write("if\n abcdef\n 123456\n 456789\nend\C-p\C-p\C-p\C-p Str") + write("\t") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> if String + prompt> aStringRuby is... + prompt> 1StructA dynamic, open source programming + prompt> 456789 language with a focus on simplicity + prompt> end and productivity. It has an elegant + syntax that is natural to read and + easy to write. + EOC + end + def test_autocomplete_long_with_scrollbar start_terminal(20, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete-long}, startup_message: 'Multiline REPL.') write('S') @@ -1057,6 +1455,59 @@ begin EOC end + def test_autocomplete_super_long_scroll_to_bottom + start_terminal(20, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete-super-long}, startup_message: 'Multiline REPL.') + shift_tab = [27, 91, 90] + write('S' + shift_tab.map(&:chr).join) + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> Str_BXX + Str_BXJ + Str_BXK + Str_BXL + Str_BXM + Str_BXN + Str_BXO + Str_BXP + Str_BXQ + Str_BXR + Str_BXS + Str_BXT + Str_BXU + Str_BXV + Str_BXW + Str_BXX▄ + EOC + end + + def test_autocomplete_super_long_and_backspace + start_terminal(20, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete-super-long}, startup_message: 'Multiline REPL.') + shift_tab = [27, 91, 90] + write('S' + shift_tab.map(&:chr).join) + write("\C-h") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> Str_BX + Str_BX █ + Str_BXA█ + Str_BXB█ + Str_BXC█ + Str_BXD█ + Str_BXE█ + Str_BXF█ + Str_BXG█ + Str_BXH█ + Str_BXI + Str_BXJ + Str_BXK + Str_BXL + Str_BXM + Str_BXN + EOC + end + def test_dialog_callback_returns_nil start_terminal(20, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog nil}, startup_message: 'Multiline REPL.') write('a') @@ -1067,6 +1518,292 @@ begin EOC end + def test_dialog_narrower_than_screen + start_terminal(20, 11, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple}, startup_message: 'Multiline REPL.') + close + assert_screen(<<~'EOC') + Multiline R + EPL. + prompt> + Ruby is... + A dynamic, + language wi + and product + syntax that + easy to wri + EOC + end + + def test_dialog_narrower_than_screen_with_scrollbar + start_terminal(20, 11, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete-long}, startup_message: 'Multiline REPL.') + write('S' + "\C-i" * 3) + close + assert_screen(<<~'EOC') + Multiline R + EPL. + prompt> Sym + String █ + Struct █ + Symbol █ + StopIterat█ + SystemCall█ + SystemExit█ + SystemStac█ + ScriptErro█ + SyntaxErro█ + Signal █ + SizedQueue█ + Set + SecureRand + Socket + StringIO + EOC + end + + def test_dialog_with_fullwidth_scrollbar + start_terminal(20, 40, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dialog simple,scrollkey,alt-scrollbar}, startup_message: 'Multiline REPL.') + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> + Ruby is... :: + A dynamic, open source programming :: + language with a focus on simplicity'' + and productivity. It has an elegant + EOC + end + + def test_rerender_argument_prompt_after_pasting + start_terminal(20, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') + write('abcdef') + write("\M-3\C-h") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> abc + EOC + end + + def test_autocomplete_old_dialog_width_greater_than_dialog_width + start_terminal(40, 40, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete-width-long}, startup_message: 'Multiline REPL.') + write("0+ \n12345678901234") + write("\C-p") + write("r") + write("a") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> 0+ ra + prompt> 123rand 901234 + raise + EOC + end + + def test_scroll_at_bottom_for_dialog + start_terminal(10, 40, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write("\n\n\n\n\n\n\n\n\n\n\n") + write("def hoge\n\nend\C-p\C-e") + write(" S") + close + assert_screen(<<~'EOC') + prompt> + prompt> + prompt> + prompt> + prompt> + prompt> def hoge + prompt> S + prompt> enString █ + Struct ▀ + Symbol + EOC + end + + def test_clear_dialog_in_pasting + start_terminal(10, 40, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write("S") + write("tring ") + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> String + EOC + end + + def test_prompt_with_newline + ENV['RELINE_TEST_PROMPT'] = "::\n> " + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') + write("def hoge\n 3\nend") + close + assert_screen(<<~'EOC') + Multiline REPL. + ::\n> def hoge + ::\n> 3 + ::\n> end + EOC + end + + def test_dynamic_prompt_with_newline + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dynamic-prompt-with-newline}, startup_message: 'Multiline REPL.') + write("def hoge\n 3\nend") + close + assert_screen(<<~'EOC') + Multiline REPL. + [0000\n]> def hoge + [0001\n]> 3 + [0001\n]> end + EOC + end + + def test_lines_passed_to_dynamic_prompt + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --dynamic-prompt-show-line}, startup_message: 'Multiline REPL.') + write("if true") + write("\n") + close + assert_screen(<<~EOC) + Multiline REPL. + [if t]> if true + [ ]> + EOC + end + + def test_clear_dialog_when_just_move_cursor_at_last_line + start_terminal(10, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write("class A\n 3\nend\n\n\n") + write("\C-p\C-p\C-e; S") + write("\C-n") + write(";") + close + assert_screen(<<~'EOC') + prompt> 3 + prompt> end + => 3 + prompt> + prompt> + prompt> class A + prompt> 3; S + prompt> end; + EOC + end + + def test_clear_dialog_when_adding_new_line_to_end_of_buffer + start_terminal(10, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write("class A\n def a\n 3\n 3\n end\nend") + write("\n") + write("class S") + write("\n") + write(" 3") + close + assert_screen(<<~'EOC') + prompt> def a + prompt> 3 + prompt> 3 + prompt> end + prompt> end + => :a + prompt> class S + prompt> 3 + EOC + end + + def test_insert_newline_in_the_middle_of_buffer_just_after_dialog + start_terminal(10, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write("class A\n def a\n 3\n end\nend") + write("\n") + write("\C-p\C-p\C-p\C-p\C-p\C-e\C-hS") + write("\M-\x0D") + write(" 3") + close + assert_screen(<<~'EOC') + prompt> 3 + prompt> end + prompt> end + => :a + prompt> class S + prompt> 3 + prompt> def a + prompt> 3 + prompt> end + prompt> end + EOC + end + + def test_incremental_search_on_not_last_line + start_terminal(10, 40, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.') + write("def abc\nend\n") + write("def def\nend\n") + write("\C-p\C-p\C-e") + write("\C-r") + write("a") + write("\n\n") + close + assert_screen(<<~'EOC') + prompt> def abc + prompt> end + => :abc + prompt> def def + prompt> end + => :def + prompt> def abc + prompt> end + => :abc + prompt> + EOC + end + + def test_bracket_newline_indent + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.') + write("[\n") + write("1") + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> [ + prompt> 1 + EOC + end + + def test_repeated_input_delete + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') + write("a\C-h" * 4000) + close + assert_screen(<<~'EOC') + Multiline REPL. + prompt> + EOC + end + + def test_exit_with_ctrl_d + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.') + begin + write("\C-d") + close + rescue EOFError + # EOFError is raised when process terminated. + end + assert_screen(<<~EOC) + Multiline REPL. + prompt> + EOC + end + + def test_thread_safe + start_terminal(6, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.') + write("[Thread.new{Reline.readline'>'},Thread.new{Reline.readmultiline('>'){true}}].map(&:join).size\n") + write("exit\n") + write("exit\n") + write("42\n") + close + assert_screen(<<~EOC) + >exit + >exit + => 2 + prompt> 42 + => 42 + prompt> + EOC + end + def write_inputrc(content) File.open(@inputrc_file, 'w') do |f| f.write content |