#!/usr/bin/env ruby require 'bundler' Bundler.require require 'reline' require 'optparse' require_relative 'termination_checker' opt = OptionParser.new opt.on('--dynamic-prompt') { Reline.prompt_proc = proc { |lines| lines.each_with_index.map { |l, i| "\e[1m[%04d]>\e[m " % i } } } opt.on('--broken-dynamic-prompt') { Reline.prompt_proc = proc { |lines| range = lines.size > 1 ? (0..(lines.size - 2)) : (0..0) lines[range].each_with_index.map { |l, i| '[%04d]> ' % i } } } 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') { 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 { return nil if v.include?('nil') if v.include?('simple') contents = <<~RUBY.split("\n") 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. RUBY elsif v.include?('long') contents = <<~RUBY.split("\n") 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. RUBY elsif v.include?('fullwidth') contents = <<~RUBY.split("\n") Rubyとは... オープンソースの動的なプログラミン グ言語で、シンプルさと高い生産性を 備えています。エレガントな文法を持 ち、自然に読み書きができます。 RUBY end if v.include?('scrollkey') dialog.trap_key = nil if key and key.match?(dialog.name) if dialog.pointer.nil? dialog.pointer = 0 elsif dialog.pointer >= (contents.size - 1) dialog.pointer = 0 else dialog.pointer += 1 end end dialog.trap_key = [?j.ord] height = 4 end scrollbar = false if v.include?('scrollbar') scrollbar = true end 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) } } } 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) begin stty_save = `stty -g`.chomp rescue end begin prompt = ENV['RELINE_TEST_PROMPT'] || "\e[1mprompt>\e[m " puts 'Multiline REPL.' while code = Reline.readmultiline(prompt, true) { |code| TerminationChecker.terminated?(code) } case code.chomp when 'exit', 'quit', 'q' exit 0 when '' # NOOP else begin result = eval(code) puts "=> #{result.inspect}" rescue ScriptError, StandardError => e puts "Traceback (most recent call last):" e.backtrace.reverse_each do |f| puts " #{f}" end puts e.message end end end rescue Interrupt puts '^C' `stty #{stty_save}` if stty_save exit 0 ensure `stty #{stty_save}` if stty_save end begin puts rescue Errno::EIO # Maybe the I/O has been closed. end