diff options
-rw-r--r-- | lib/irb.rb | 58 | ||||
-rw-r--r-- | lib/irb/cmd/fork.rb | 2 | ||||
-rw-r--r-- | lib/irb/cmd/info.rb | 24 | ||||
-rw-r--r-- | lib/irb/cmd/pushws.rb | 1 | ||||
-rw-r--r-- | lib/irb/color.rb | 44 | ||||
-rw-r--r-- | lib/irb/completion.rb | 17 | ||||
-rw-r--r-- | lib/irb/context.rb | 32 | ||||
-rw-r--r-- | lib/irb/ext/change-ws.rb | 1 | ||||
-rw-r--r-- | lib/irb/ext/history.rb | 2 | ||||
-rw-r--r-- | lib/irb/ext/loader.rb | 1 | ||||
-rw-r--r-- | lib/irb/ext/save-history.rb | 13 | ||||
-rw-r--r-- | lib/irb/ext/tracer.rb | 1 | ||||
-rw-r--r-- | lib/irb/ext/use-loader.rb | 4 | ||||
-rw-r--r-- | lib/irb/ext/workspaces.rb | 1 | ||||
-rw-r--r-- | lib/irb/extend-command.rb | 13 | ||||
-rw-r--r-- | lib/irb/help.rb | 1 | ||||
-rw-r--r-- | lib/irb/init.rb | 14 | ||||
-rw-r--r-- | lib/irb/input-method.rb | 61 | ||||
-rw-r--r-- | lib/irb/inspector.rb | 6 | ||||
-rw-r--r-- | lib/irb/irb.gemspec | 4 | ||||
-rw-r--r-- | lib/irb/ruby-lex.rb | 119 | ||||
-rw-r--r-- | lib/irb/ruby_logo.aa | 1 | ||||
-rw-r--r-- | lib/irb/version.rb | 4 | ||||
-rw-r--r-- | lib/irb/xmp.rb | 2 | ||||
-rw-r--r-- | test/irb/test_cmd.rb | 127 | ||||
-rw-r--r-- | test/irb/test_context.rb | 176 | ||||
-rw-r--r-- | test/irb/test_history.rb | 153 | ||||
-rw-r--r-- | test/irb/test_ruby_lex.rb | 136 | ||||
-rw-r--r-- | test/irb/test_workspace.rb | 1 |
29 files changed, 913 insertions, 106 deletions
diff --git a/lib/irb.rb b/lib/irb.rb index ee6979c6d0..d8e1209f2c 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -10,18 +10,19 @@ # # require "ripper" +require "reline" -require "irb/init" -require "irb/context" -require "irb/extend-command" +require_relative "irb/init" +require_relative "irb/context" +require_relative "irb/extend-command" -require "irb/ruby-lex" -require "irb/input-method" -require "irb/locale" -require "irb/color" +require_relative "irb/ruby-lex" +require_relative "irb/input-method" +require_relative "irb/locale" +require_relative "irb/color" -require "irb/version" -require "irb/easter-egg" +require_relative "irb/version" +require_relative "irb/easter-egg" # IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby # expressions read from the standard input. @@ -271,7 +272,7 @@ require "irb/easter-egg" # On the other hand, each conf in IRB@Command+line+options is used to # individually configure IRB.irb. # -# If a proc is set for IRB.conf[:IRB_RC], its will be invoked after execution +# If a proc is set for <code>IRB.conf[:IRB_RC]</code>, its will be invoked after execution # of that proc with the context of the current session as its argument. Each # session can be configured using this mechanism. # @@ -399,7 +400,7 @@ module IRB irb.run(@CONF) end - # Calls each event hook of IRB.conf[:AT_EXIT] when the current session quits. + # Calls each event hook of <code>IRB.conf[:TA_EXIT]</code> when the current session quits. def IRB.irb_at_exit @CONF[:AT_EXIT].each{|hook| hook.call} end @@ -538,7 +539,15 @@ module IRB begin line.untaint if RUBY_VERSION < '2.7' @context.evaluate(line, line_no, exception: exc) - output_value if @context.echo? && (@context.echo_on_assignment? || !assignment_expression?(line)) + if @context.echo? + if assignment_expression?(line) + if @context.echo_on_assignment? + output_value(@context.omit_on_assignment?) + end + else + output_value + end + end rescue Interrupt => exc rescue SystemExit, SignalException raise @@ -737,9 +746,32 @@ module IRB p end - def output_value # :nodoc: + def output_value(omit = false) # :nodoc: str = @context.inspect_last_value multiline_p = str.include?("\n") + if omit + winwidth = @context.io.winsize.last + if multiline_p + first_line = str.split("\n").first + result = @context.newline_before_multiline_output? ? (@context.return_format % first_line) : first_line + output_width = Reline::Unicode.calculate_width(result, true) + diff_size = output_width - Reline::Unicode.calculate_width(first_line, true) + if diff_size.positive? and output_width > winwidth + lines, _ = Reline::Unicode.split_by_width(first_line, winwidth - diff_size - 3) + str = "%s...\e[0m" % lines.first + multiline_p = false + else + str.gsub!(/(\A.*?\n).*/m, "\\1...") + end + else + output_width = Reline::Unicode.calculate_width(@context.return_format % str, true) + diff_size = output_width - Reline::Unicode.calculate_width(str, true) + if diff_size.positive? and output_width > winwidth + lines, _ = Reline::Unicode.split_by_width(str, winwidth - diff_size - 3) + str = "%s...\e[0m" % lines.first + end + end + end if multiline_p && @context.newline_before_multiline_output? printf @context.return_format, "\n#{str}" else diff --git a/lib/irb/cmd/fork.rb b/lib/irb/cmd/fork.rb index 31d53dcaba..19c78fc910 100644 --- a/lib/irb/cmd/fork.rb +++ b/lib/irb/cmd/fork.rb @@ -35,5 +35,3 @@ module IRB end end # :startdoc: - - diff --git a/lib/irb/cmd/info.rb b/lib/irb/cmd/info.rb new file mode 100644 index 0000000000..53ec71d754 --- /dev/null +++ b/lib/irb/cmd/info.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: false + +require_relative "nop" + +# :stopdoc: +module IRB + module ExtendCommand + class Info < Nop + def execute + Class.new { + def inspect + str = "Ruby version: #{RUBY_VERSION}\n" + str += "IRB version: #{IRB.version}\n" + str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n" + str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file) + str + end + alias_method :to_s, :inspect + }.new + end + end + end +end +# :startdoc: diff --git a/lib/irb/cmd/pushws.rb b/lib/irb/cmd/pushws.rb index 187b276e48..612157d8a0 100644 --- a/lib/irb/cmd/pushws.rb +++ b/lib/irb/cmd/pushws.rb @@ -38,4 +38,3 @@ module IRB end end # :startdoc: - diff --git a/lib/irb/color.rb b/lib/irb/color.rb index d2b9674a71..0f49291d85 100644 --- a/lib/irb/color.rb +++ b/lib/irb/color.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'reline' require 'ripper' +require 'irb/ruby-lex' module IRB # :nodoc: module Color @@ -145,37 +146,38 @@ module IRB # :nodoc: seen.delete(obj) end - # Ripper::Lexer::Elem#state is supported on Ruby 2.5+ def supported? return @supported if defined?(@supported) - @supported = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0') + @supported = Ripper::Lexer::Elem.method_defined?(:state) end def scan(code, allow_last_error:) pos = [1, 0] verbose, $VERBOSE = $VERBOSE, nil - lexer = Ripper::Lexer.new(code) - if lexer.respond_to?(:scan) # Ruby 2.7+ - lexer.scan.each do |elem| - str = elem.tok - next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message - next if ([elem.pos[0], elem.pos[1] + str.bytesize] <=> pos) <= 0 - - str.each_line do |line| - if line.end_with?("\n") - pos[0] += 1 - pos[1] = 0 - else - pos[1] += line.bytesize + RubyLex.compile_with_errors_suppressed(code) do |inner_code, line_no| + lexer = Ripper::Lexer.new(inner_code, '(ripper)', line_no) + if lexer.respond_to?(:scan) # Ruby 2.7+ + lexer.scan.each do |elem| + str = elem.tok + next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message + next if ([elem.pos[0], elem.pos[1] + str.bytesize] <=> pos) <= 0 + + str.each_line do |line| + if line.end_with?("\n") + pos[0] += 1 + pos[1] = 0 + else + pos[1] += line.bytesize + end end - end - yield(elem.event, str, elem.state) - end - else - lexer.parse.each do |elem| - yield(elem.event, elem.tok, elem.state) + yield(elem.event, str, elem.state) + end + else + lexer.parse.each do |elem| + yield(elem.event, elem.tok, elem.state) + end end end $VERBOSE = verbose diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb index c44aa9039e..c9328e5c5a 100644 --- a/lib/irb/completion.rb +++ b/lib/irb/completion.rb @@ -7,7 +7,6 @@ # From Original Idea of shugo@ruby-lang.org # -require "readline" autoload :RDoc, "rdoc" module IRB @@ -97,17 +96,13 @@ module IRB when /^(:[^:.]*)$/ # Symbol return nil if doc_namespace - if Symbol.respond_to?(:all_symbols) - sym = $1 - candidates = Symbol.all_symbols.collect do |s| - ":" + s.id2name.encode(Encoding.default_external) - rescue Encoding::UndefinedConversionError - # ignore - end - candidates.grep(/^#{Regexp.quote(sym)}/) - else - [] + sym = $1 + candidates = Symbol.all_symbols.collect do |s| + ":" + s.id2name.encode(Encoding.default_external) + rescue Encoding::UndefinedConversionError + # ignore end + candidates.grep(/^#{Regexp.quote(sym)}/) when /^::([A-Z][^:\.\(]*)$/ # Absolute Constant or class methods diff --git a/lib/irb/context.rb b/lib/irb/context.rb index 218f7c6037..4f001729e1 100644 --- a/lib/irb/context.rb +++ b/lib/irb/context.rb @@ -131,7 +131,12 @@ module IRB @echo_on_assignment = IRB.conf[:ECHO_ON_ASSIGNMENT] if @echo_on_assignment.nil? - @echo_on_assignment = false + @echo_on_assignment = true + end + + @omit_on_assignment = IRB.conf[:OMIT_ON_ASSIGNMENT] + if @omit_on_assignment.nil? + @omit_on_assignment = true end @newline_before_multiline_output = IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT] @@ -240,7 +245,7 @@ module IRB attr_accessor :ignore_eof # Whether to echo the return value to output or not. # - # Uses IRB.conf[:ECHO] if available, or defaults to +true+. + # Uses <code>IRB.conf[:ECHO]</code> if available, or defaults to +true+. # # puts "hello" # # hello @@ -251,16 +256,30 @@ module IRB attr_accessor :echo # Whether to echo for assignment expressions # - # Uses IRB.conf[:ECHO_ON_ASSIGNMENT] if available, or defaults to +false+. + # Uses <code>IRB.conf[:ECHO_ON_ASSIGNMENT]</code> if available, or defaults to +true+. # # a = "omg" - # IRB.CurrentContext.echo_on_assignment = true - # a = "omg" # #=> omg + # IRB.CurrentContext.echo_on_assignment = false + # a = "omg" attr_accessor :echo_on_assignment + # Whether to omit echo for assignment expressions + # + # Uses <code>IRB.conf[:OMIT_ON_ASSIGNMENT]</code> if available, or defaults to +true+. + # + # a = [1] * 10 + # #=> [1, 1, 1, 1, 1, 1, 1, 1, ... + # [1] * 10 + # #=> [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + # IRB.CurrentContext.omit_on_assignment = false + # a = [1] * 10 + # #=> [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + # [1] * 10 + # #=> [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + attr_accessor :omit_on_assignment # Whether a newline is put before multiline output. # - # Uses IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT] if available, + # Uses <code>IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT]</code> if available, # or defaults to +true+. # # "abc\ndef" @@ -306,6 +325,7 @@ module IRB alias ignore_eof? ignore_eof alias echo? echo alias echo_on_assignment? echo_on_assignment + alias omit_on_assignment? omit_on_assignment alias newline_before_multiline_output? newline_before_multiline_output # Returns whether messages are displayed or not. diff --git a/lib/irb/ext/change-ws.rb b/lib/irb/ext/change-ws.rb index 94bfe62bc0..4c57e44eab 100644 --- a/lib/irb/ext/change-ws.rb +++ b/lib/irb/ext/change-ws.rb @@ -43,4 +43,3 @@ module IRB # :nodoc: end end end - diff --git a/lib/irb/ext/history.rb b/lib/irb/ext/history.rb index 30e3fb901f..fc304c6f6c 100644 --- a/lib/irb/ext/history.rb +++ b/lib/irb/ext/history.rb @@ -153,5 +153,3 @@ module IRB # :nodoc: end end end - - diff --git a/lib/irb/ext/loader.rb b/lib/irb/ext/loader.rb index 840226db30..1b683d88e5 100644 --- a/lib/irb/ext/loader.rb +++ b/lib/irb/ext/loader.rb @@ -126,4 +126,3 @@ module IRB # :nodoc: end end end - diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb index edc4f234cc..ac358c8ccb 100644 --- a/lib/irb/ext/save-history.rb +++ b/lib/irb/ext/save-history.rb @@ -9,8 +9,6 @@ # # -require "readline" - module IRB module HistorySavingAbility # :nodoc: end @@ -27,7 +25,7 @@ module IRB IRB.conf[:SAVE_HISTORY] end - remove_method :save_history= if method_defined?(:save_history=) + remove_method(:save_history=) if method_defined?(:save_history=) # Sets <code>IRB.conf[:SAVE_HISTORY]</code> to the given +val+ and calls # #init_save_history with this context. # @@ -89,7 +87,7 @@ module IRB def save_history return unless self.class.const_defined?(:HISTORY) history = self.class::HISTORY - if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) > 0 + if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) != 0 if history_file = IRB.conf[:HISTORY_FILE] history_file = File.expand_path(history_file) end @@ -109,7 +107,12 @@ module IRB open(history_file, "w:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f| hist = history.map{ |l| l.split("\n").join("\\\n") } - f.puts(hist[-num..-1] || hist) + begin + hist = hist.last(num) if hist.size > num and num > 0 + rescue RangeError # bignum too big to convert into `long' + # Do nothing because the bignum should be treated as inifinity + end + f.puts(hist) end end end diff --git a/lib/irb/ext/tracer.rb b/lib/irb/ext/tracer.rb index e3e325e6e5..67ac4bb965 100644 --- a/lib/irb/ext/tracer.rb +++ b/lib/irb/ext/tracer.rb @@ -82,4 +82,3 @@ module IRB IRB.initialize_tracer end - diff --git a/lib/irb/ext/use-loader.rb b/lib/irb/ext/use-loader.rb index cb10e8a254..1897bc89e0 100644 --- a/lib/irb/ext/use-loader.rb +++ b/lib/irb/ext/use-loader.rb @@ -47,7 +47,7 @@ module IRB alias use_loader? use_loader remove_method :use_loader= if method_defined?(:use_loader=) - # Sets IRB.conf[:USE_LOADER] + # Sets <code>IRB.conf[:USE_LOADER]</code> # # See #use_loader for more information. def use_loader=(opt) @@ -73,5 +73,3 @@ module IRB end end end - - diff --git a/lib/irb/ext/workspaces.rb b/lib/irb/ext/workspaces.rb index 5bd72c194f..730b58e64d 100644 --- a/lib/irb/ext/workspaces.rb +++ b/lib/irb/ext/workspaces.rb @@ -64,4 +64,3 @@ module IRB # :nodoc: end end end - diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb index de145e962d..2f4fcfb5c6 100644 --- a/lib/irb/extend-command.rb +++ b/lib/irb/extend-command.rb @@ -121,6 +121,10 @@ module IRB # :nodoc: [:help, NO_OVERRIDE], ], + [ + :irb_info, :Info, "irb/cmd/info" + ], + ] # Installs the default irb commands: @@ -169,11 +173,14 @@ module IRB # :nodoc: args << "&block" args = args.join(", ") line = __LINE__; eval %[ - def #{cmd_name}(\#{args}) - ExtendCommand::#{cmd_class}.execute(irb_context, \#{args}) + unless self.class.class_variable_defined?(:@@#{cmd_name}_) + self.class.class_variable_set(:@@#{cmd_name}_, true) + def #{cmd_name}_(\#{args}) + ExtendCommand::#{cmd_class}.execute(irb_context, \#{args}) + end end ], nil, __FILE__, line - send :#{cmd_name}, *opts, &b + send :#{cmd_name}_, *opts, &b end ], nil, __FILE__, line else diff --git a/lib/irb/help.rb b/lib/irb/help.rb index 7868a70a6c..3eeaf841b0 100644 --- a/lib/irb/help.rb +++ b/lib/irb/help.rb @@ -34,4 +34,3 @@ module IRB } end end - diff --git a/lib/irb/init.rb b/lib/irb/init.rb index 37d1f8d609..44383609bd 100644 --- a/lib/irb/init.rb +++ b/lib/irb/init.rb @@ -52,6 +52,7 @@ module IRB # :nodoc: @CONF[:IGNORE_EOF] = false @CONF[:ECHO] = nil @CONF[:ECHO_ON_ASSIGNMENT] = nil + @CONF[:OMIT_ON_ASSIGNMENT] = nil @CONF[:VERBOSE] = nil @CONF[:EVAL_HISTORY] = nil @@ -177,6 +178,10 @@ module IRB # :nodoc: @CONF[:ECHO_ON_ASSIGNMENT] = true when "--noecho-on-assignment" @CONF[:ECHO_ON_ASSIGNMENT] = false + when "--omit-on-assignment" + @CONF[:OMIT_ON_ASSIGNMENT] = true + when "--noomit-on-assignment" + @CONF[:OMIT_ON_ASSIGNMENT] = false when "--verbose" @CONF[:VERBOSE] = true when "--noverbose" @@ -271,10 +276,19 @@ module IRB # :nodoc: if irbrc = ENV["IRBRC"] yield proc{|rc| rc == "rc" ? irbrc : irbrc+rc} end + if xdg_config_home = ENV["XDG_CONFIG_HOME"] + irb_home = File.join(xdg_config_home, "irb") + unless File.exist? irb_home + require 'fileutils' + FileUtils.mkdir_p irb_home + end + yield proc{|rc| irb_home + "/irb#{rc}"} + end if home = ENV["HOME"] yield proc{|rc| home+"/.irb#{rc}"} end current_dir = Dir.pwd + yield proc{|rc| current_dir+"/.config/irb/irb#{rc}"} yield proc{|rc| current_dir+"/.irb#{rc}"} yield proc{|rc| current_dir+"/irb#{rc.sub(/\A_?/, '.')}"} yield proc{|rc| current_dir+"/_irb#{rc}"} diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb index 9fbbaeb0f3..6e87488753 100644 --- a/lib/irb/input-method.rb +++ b/lib/irb/input-method.rb @@ -12,6 +12,7 @@ require_relative 'src_encoding' require_relative 'magic-file' require_relative 'completion' +require 'io/console' require 'reline' module IRB @@ -36,6 +37,14 @@ module IRB end public :gets + def winsize + if instance_variable_defined?(:@stdout) + @stdout.winsize + else + [24, 80] + end + end + # Whether this input method is still readable when there is no more data to # read. # @@ -43,6 +52,11 @@ module IRB def readable_after_eof? false end + + # For debug message + def inspect + 'Abstract InputMethod' + end end class StdioInputMethod < InputMethod @@ -93,6 +107,11 @@ module IRB def encoding @stdin.external_encoding end + + # For debug message + def inspect + 'StdioInputMethod' + end end # Use a File for IO with irb, see InputMethod @@ -125,14 +144,25 @@ module IRB def encoding @io.external_encoding end + + # For debug message + def inspect + 'FileInputMethod' + end end begin - require "readline" class ReadlineInputMethod < InputMethod - include Readline + def self.initialize_readline + require "readline" + rescue LoadError + else + include ::Readline + end + # Creates a new input method object using Readline def initialize + self.class.initialize_readline if Readline.respond_to?(:encoding_system_needs) IRB.__send__(:set_encoding, Readline.encoding_system_needs.name, override: false) end @@ -197,13 +227,15 @@ module IRB @stdin.external_encoding end - if Readline.respond_to?("basic_word_break_characters=") - Readline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS + # For debug message + def inspect + readline_impl = (defined?(Reline) && Readline == Reline) ? 'Reline' : 'ext/readline' + str = "ReadlineInputMethod with #{readline_impl} #{Readline::VERSION}" + inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc') + str += " and #{inputrc_path}" if File.exist?(inputrc_path) + str end - Readline.completion_append_character = nil - Readline.completion_proc = IRB::InputCompletor::CompletionProc end - rescue LoadError end class ReidlineInputMethod < InputMethod @@ -227,7 +259,7 @@ module IRB Reline.completion_proc = IRB::InputCompletor::CompletionProc Reline.output_modifier_proc = if IRB.conf[:USE_COLORIZE] - proc do |output, complete:| + proc do |output, complete: | next unless IRB::Color.colorable? IRB::Color.colorize_code(output, complete: complete) end @@ -297,5 +329,18 @@ module IRB def encoding @stdin.external_encoding end + + # For debug message + def inspect + config = Reline::Config.new + str = "ReidlineInputMethod with Reline #{Reline::VERSION}" + if config.respond_to?(:inputrc_path) + inputrc_path = File.expand_path(config.inputrc_path) + else + inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc') + end + str += " and #{inputrc_path}" if File.exist?(inputrc_path) + str + end end end diff --git a/lib/irb/inspector.rb b/lib/irb/inspector.rb index bc7fb0b125..671b32b6e8 100644 --- a/lib/irb/inspector.rb +++ b/lib/irb/inspector.rb @@ -113,6 +113,7 @@ module IRB # :nodoc: result rescue NoMethodError puts "(Object doesn't support #inspect)" + '' end } Inspector.def_inspector([:pp, :pretty_inspect], proc{require "pp"}){|v| @@ -135,8 +136,3 @@ module IRB # :nodoc: Marshal.dump(v) } end - - - - - diff --git a/lib/irb/irb.gemspec b/lib/irb/irb.gemspec index 658fe813f1..af40a32452 100644 --- a/lib/irb/irb.gemspec +++ b/lib/irb/irb.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |spec| spec.summary = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).} spec.description = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).} spec.homepage = "https://github.com/ruby/irb" - spec.license = "BSD-2-Clause" + spec.licenses = ["Ruby", "BSD-2-Clause"] spec.files = [ ".document", @@ -78,7 +78,7 @@ Gem::Specification.new do |spec| spec.required_ruby_version = Gem::Requirement.new(">= 2.5") - spec.add_dependency "reline", ">= 0.0.1" + spec.add_dependency "reline", ">= 0.1.5" spec.add_development_dependency "bundler" spec.add_development_dependency "rake" end diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb index d5630c8b52..7be77fd12e 100644 --- a/lib/irb/ruby-lex.rb +++ b/lib/irb/ruby-lex.rb @@ -11,6 +11,7 @@ # require "ripper" +require "jruby" if RUBY_ENGINE == "jruby" # :stopdoc: class RubyLex @@ -29,6 +30,18 @@ class RubyLex @prompt = nil end + def self.compile_with_errors_suppressed(code) + line_no = 1 + begin + result = yield code, line_no + rescue ArgumentError + code = ";\n#{code}" + line_no = 0 + result = yield code, line_no + end + result + end + # io functions def set_input(io, p = nil, &block) @io = io @@ -75,7 +88,10 @@ class RubyLex def ripper_lex_without_warning(code) verbose, $VERBOSE = $VERBOSE, nil - tokens = Ripper.lex(code) + tokens = nil + self.class.compile_with_errors_suppressed(code) do |inner_code, line_no| + tokens = Ripper.lex(inner_code, '-', line_no) + end $VERBOSE = verbose tokens end @@ -209,7 +225,9 @@ class RubyLex when 'jruby' JRuby.compile_ir(code) else - RubyVM::InstructionSequence.compile(code) + self.class.compile_with_errors_suppressed(code) do |inner_code, line_no| + RubyVM::InstructionSequence.compile(inner_code, nil, nil, line_no) + end end rescue EncodingError # This is for a hash with invalid encoding symbol, {"\xAE": 1} @@ -285,9 +303,33 @@ class RubyLex def process_nesting_level indent = 0 + in_oneliner_def = nil @tokens.each_with_index { |t, index| + # detecting one-liner method definition + if in_oneliner_def.nil? + if t[3].allbits?(Ripper::EXPR_ENDFN) + in_oneliner_def = :ENDFN + end + else + if t[3].allbits?(Ripper::EXPR_ENDFN) + # continuing + elsif t[3].allbits?(Ripper::EXPR_BEG) + if t[2] == '=' + in_oneliner_def = :BODY + end + elsif t[3].allbits?(Ripper::EXPR_END) + if in_oneliner_def == :BODY + # one-liner method definition + indent -= 1 + end + in_oneliner_def = nil + else + in_oneliner_def = nil + end + end + case t[1] - when :on_lbracket, :on_lbrace, :on_lparen + when :on_lbracket, :on_lbrace, :on_lparen, :on_tlambeg indent += 1 when :on_rbracket, :on_rbrace, :on_rparen indent -= 1 @@ -306,7 +348,7 @@ class RubyLex when 'def', 'case', 'for', 'begin', 'class', 'module' indent += 1 when 'if', 'unless', 'while', 'until' - # postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL + # postfix if/unless/while/until must be Ripper::EXPR_LABEL indent += 1 unless t[3].allbits?(Ripper::EXPR_LABEL) when 'end' indent -= 1 @@ -320,7 +362,31 @@ class RubyLex def check_newline_depth_difference depth_difference = 0 open_brace_on_line = 0 + in_oneliner_def = nil @tokens.each_with_index do |t, index| + # detecting one-liner method definition + if in_oneliner_def.nil? + if t[3].allbits?(Ripper::EXPR_ENDFN) + in_oneliner_def = :ENDFN + end + else + if t[3].allbits?(Ripper::EXPR_ENDFN) + # continuing + elsif t[3].allbits?(Ripper::EXPR_BEG) + if t[2] == '=' + in_oneliner_def = :BODY + end + elsif t[3].allbits?(Ripper::EXPR_END) + if in_oneliner_def == :BODY + # one[-liner method definition + depth_difference -= 1 + end + in_oneliner_def = nil + else + in_oneliner_def = nil + end + end + case t[1] when :on_ignored_nl, :on_nl, :on_comment if index != (@tokens.size - 1) @@ -332,7 +398,7 @@ class RubyLex next end case t[1] - when :on_lbracket, :on_lbrace, :on_lparen + when :on_lbracket, :on_lbrace, :on_lparen, :on_tlambeg depth_difference += 1 open_brace_on_line += 1 when :on_rbracket, :on_rbrace, :on_rparen @@ -351,12 +417,12 @@ class RubyLex end when 'def', 'case', 'for', 'begin', 'class', 'module' depth_difference += 1 - when 'if', 'unless', 'while', 'until' + when 'if', 'unless', 'while', 'until', 'rescue' # postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL unless t[3].allbits?(Ripper::EXPR_LABEL) depth_difference += 1 end - when 'else', 'elsif', 'rescue', 'ensure', 'when', 'in' + when 'else', 'elsif', 'ensure', 'when', 'in' depth_difference += 1 end end @@ -371,7 +437,36 @@ class RubyLex spaces_of_nest = [] spaces_at_line_head = 0 open_brace_on_line = 0 + in_oneliner_def = nil @tokens.each_with_index do |t, index| + # detecting one-liner method definition + if in_oneliner_def.nil? + if t[3].allbits?(Ripper::EXPR_ENDFN) + in_oneliner_def = :ENDFN + end + else + if t[3].allbits?(Ripper::EXPR_ENDFN) + # continuing + elsif t[3].allbits?(Ripper::EXPR_BEG) + if t[2] == '=' + in_oneliner_def = :BODY + end + elsif t[3].allbits?(Ripper::EXPR_END) + if in_oneliner_def == :BODY + # one-liner method definition + if is_first_printable_of_line + corresponding_token_depth = spaces_of_nest.pop + else + spaces_of_nest.pop + corresponding_token_depth = nil + end + end + in_oneliner_def = nil + else + in_oneliner_def = nil + end + end + case t[1] when :on_ignored_nl, :on_nl, :on_comment corresponding_token_depth = nil @@ -386,7 +481,7 @@ class RubyLex next end case t[1] - when :on_lbracket, :on_lbrace, :on_lparen + when :on_lbracket, :on_lbrace, :on_lparen, :on_tlambeg spaces_of_nest.push(spaces_at_line_head + open_brace_on_line * 2) open_brace_on_line += 1 when :on_rbracket, :on_rbrace, :on_rparen @@ -402,12 +497,16 @@ class RubyLex case t[2] when 'def', 'do', 'case', 'for', 'begin', 'class', 'module' spaces_of_nest.push(spaces_at_line_head) + when 'rescue' + unless t[3].allbits?(Ripper::EXPR_LABEL) + corresponding_token_depth = spaces_of_nest.last + end when 'if', 'unless', 'while', 'until' - # postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL + # postfix if/unless/while/until must be Ripper::EXPR_LABEL unless t[3].allbits?(Ripper::EXPR_LABEL) spaces_of_nest.push(spaces_at_line_head) end - when 'else', 'elsif', 'rescue', 'ensure', 'when', 'in' + when 'else', 'elsif', 'ensure', 'when', 'in' corresponding_token_depth = spaces_of_nest.last when 'end' if is_first_printable_of_line diff --git a/lib/irb/ruby_logo.aa b/lib/irb/ruby_logo.aa index 043e6f85fb..a34a3e2f28 100644 --- a/lib/irb/ruby_logo.aa +++ b/lib/irb/ruby_logo.aa @@ -35,4 +35,3 @@ m7 NW H N HSVO1z=?11- NgTH bB kH WBHWWHBHWmQgg&gggggNNN NNggggggNN - diff --git a/lib/irb/version.rb b/lib/irb/version.rb index 7669c1c535..8b61eb3ef9 100644 --- a/lib/irb/version.rb +++ b/lib/irb/version.rb @@ -11,7 +11,7 @@ # module IRB # :nodoc: - VERSION = "1.2.3" + VERSION = "1.2.6" @RELEASE_VERSION = VERSION - @LAST_UPDATE_DATE = "2020-02-15" + @LAST_UPDATE_DATE = "2020-09-14" end diff --git a/lib/irb/xmp.rb b/lib/irb/xmp.rb index 60cf3b4e4d..88cbd88525 100644 --- a/lib/irb/xmp.rb +++ b/lib/irb/xmp.rb @@ -10,7 +10,7 @@ # # -require "irb" +require_relative "../irb" require_relative "frame" # An example printer for irb. diff --git a/test/irb/test_cmd.rb b/test/irb/test_cmd.rb new file mode 100644 index 0000000000..bb33f535b6 --- /dev/null +++ b/test/irb/test_cmd.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: false +require "test/unit" +require "irb" +require "irb/extend-command" + +module TestIRB + class ExtendCommand < Test::Unit::TestCase + def setup + @pwd = Dir.pwd + @tmpdir = File.join(Dir.tmpdir, "test_reline_config_#{$$}") + begin + Dir.mkdir(@tmpdir) + rescue Errno::EEXIST + FileUtils.rm_rf(@tmpdir) + Dir.mkdir(@tmpdir) + end + Dir.chdir(@tmpdir) + @home_backup = ENV["HOME"] + ENV["HOME"] = @tmpdir + @default_encoding = [Encoding.default_external, Encoding.default_internal] + @stdio_encodings = [STDIN, STDOUT, STDERR].map {|io| [io.external_encoding, io.internal_encoding] } + IRB.instance_variable_get(:@CONF).clear + end + + def teardown + ENV["HOME"] = @home_backup + Dir.chdir(@pwd) + FileUtils.rm_rf(@tmpdir) + EnvUtil.suppress_warning { + Encoding.default_external, Encoding.default_internal = *@default_encoding + [STDIN, STDOUT, STDERR].zip(@stdio_encodings) do |io, encs| + io.set_encoding(*encs) + end + } + end + + def test_irb_info_multiline + FileUtils.touch("#{@tmpdir}/.inputrc") + FileUtils.touch("#{@tmpdir}/.irbrc") + IRB.setup(__FILE__, argv: []) + IRB.conf[:USE_MULTILINE] = true + IRB.conf[:USE_SINGLELINE] = false + IRB.conf[:VERBOSE] = false + workspace = IRB::WorkSpace.new(self) + irb = IRB::Irb.new(workspace) + IRB.conf[:MAIN_CONTEXT] = irb.context + expected = %r{ + Ruby\sversion: .+\n + IRB\sversion:\sirb .+\n + InputMethod:\sReidlineInputMethod\swith\sReline .+ and .+\n + \.irbrc\spath: .+ + }x + assert_match expected, irb.context.main.irb_info.to_s + end + + def test_irb_info_singleline + FileUtils.touch("#{@tmpdir}/.inputrc") + FileUtils.touch("#{@tmpdir}/.irbrc") + IRB.setup(__FILE__, argv: []) + IRB.conf[:USE_MULTILINE] = false + IRB.conf[:USE_SINGLELINE] = true + IRB.conf[:VERBOSE] = false + workspace = IRB::WorkSpace.new(self) + irb = IRB::Irb.new(workspace) + IRB.conf[:MAIN_CONTEXT] = irb.context + expected = %r{ + Ruby\sversion: .+\n + IRB\sversion:\sirb .+\n + InputMethod:\sReadlineInputMethod\swith .+ and .+\n + \.irbrc\spath: .+ + }x + assert_match expected, irb.context.main.irb_info.to_s + end + + def test_irb_info_multiline_without_rc_files + inputrc_backup = ENV["INPUTRC"] + ENV["INPUTRC"] = "unknown_inpurc" + ext_backup = IRB::IRBRC_EXT + IRB.__send__(:remove_const, :IRBRC_EXT) + IRB.const_set(:IRBRC_EXT, "unknown_ext") + IRB.setup(__FILE__, argv: []) + IRB.conf[:USE_MULTILINE] = true + IRB.conf[:USE_SINGLELINE] = false + IRB.conf[:VERBOSE] = false + workspace = IRB::WorkSpace.new(self) + irb = IRB::Irb.new(workspace) + IRB.conf[:MAIN_CONTEXT] = irb.context + expected = %r{ + Ruby\sversion: .+\n + IRB\sversion:\sirb .+\n + InputMethod:\sReidlineInputMethod\swith\sReline\s[^ ]+(?!\sand\s.+)\n + \z + }x + assert_match expected, irb.context.main.irb_info.to_s + ensure + ENV["INPUTRC"] = inputrc_backup + IRB.__send__(:remove_const, :IRBRC_EXT) + IRB.const_set(:IRBRC_EXT, ext_backup) + end + + def test_irb_info_singleline_without_rc_files + inputrc_backup = ENV["INPUTRC"] + ENV["INPUTRC"] = "unknown_inpurc" + ext_backup = IRB::IRBRC_EXT + IRB.__send__(:remove_const, :IRBRC_EXT) + IRB.const_set(:IRBRC_EXT, "unknown_ext") + IRB.setup(__FILE__, argv: []) + IRB.conf[:USE_MULTILINE] = false + IRB.conf[:USE_SINGLELINE] = true + IRB.conf[:VERBOSE] = false + workspace = IRB::WorkSpace.new(self) + irb = IRB::Irb.new(workspace) + IRB.conf[:MAIN_CONTEXT] = irb.context + expected = %r{ + Ruby\sversion: .+\n + IRB\sversion:\sirb .+\n + InputMethod:\sReadlineInputMethod\swith\s(?~.*\sand\s.+)\n + \z + }x + assert_match expected, irb.context.main.irb_info.to_s + ensure + ENV["INPUTRC"] = inputrc_backup + IRB.__send__(:remove_const, :IRBRC_EXT) + IRB.const_set(:IRBRC_EXT, ext_backup) + end + end +end diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb index d03cc30c78..fa628bba46 100644 --- a/test/irb/test_context.rb +++ b/test/irb/test_context.rb @@ -30,6 +30,10 @@ module TestIRB def reset @line_no = 0 end + + def winsize + [10, 20] + end end def setup @@ -98,6 +102,21 @@ module TestIRB $VERBOSE = verbose end + def test_eval_object_without_inspect_method + verbose, $VERBOSE = $VERBOSE, nil + input = TestInputMethod.new([ + "BasicObject.new\n", + ]) + irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) + out, err = capture_output do + irb.eval_input + end + assert_empty err + assert(/\(Object doesn't support #inspect\)\n(=> )?\n/, out) + ensure + $VERBOSE = verbose + end + def test_default_config assert_equal(true, @context.use_colorize?) end @@ -198,6 +217,151 @@ module TestIRB assert_equal("", out) end + def test_omit_on_assignment + input = TestInputMethod.new([ + "a = [1] * 100\n", + "a\n", + ]) + value = [1] * 100 + irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) + irb.context.return_format = "=> %s\n" + + irb.context.echo = true + irb.context.echo_on_assignment = false + irb.context.omit_on_assignment = true + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("=> #{value.inspect}\n", out) + + input.reset + irb.context.echo = true + irb.context.echo_on_assignment = true + irb.context.omit_on_assignment = true + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("=> #{value.inspect[0..(input.winsize.last - 9)]}...\e[0m\n=> #{value.inspect}\n", out) + + input.reset + irb.context.echo = true + irb.context.echo_on_assignment = true + irb.context.omit_on_assignment = false + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("=> #{value.inspect}\n=> #{value.inspect}\n", out) + + input.reset + irb.context.echo = false + irb.context.echo_on_assignment = false + irb.context.omit_on_assignment = true + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("", out) + + input.reset + irb.context.echo = false + irb.context.echo_on_assignment = true + irb.context.omit_on_assignment = true + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("", out) + + input.reset + irb.context.echo = false + irb.context.echo_on_assignment = true + irb.context.omit_on_assignment = false + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("", out) + end + + def test_omit_multiline_on_assignment + input = TestInputMethod.new([ + "class A; def inspect; ([?* * 1000] * 3).join(%{\\n}); end; end; a = A.new\n", + "a\n" + ]) + value = ([?* * 1000] * 3).join(%{\n}) + value_first_line = (?* * 1000).to_s + irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) + irb.context.return_format = "=> %s\n" + + irb.context.echo = true + irb.context.echo_on_assignment = false + irb.context.omit_on_assignment = true + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("=> \n#{value}\n", out) + irb.context.evaluate('A.remove_method(:inspect)', 0) + + input.reset + irb.context.echo = true + irb.context.echo_on_assignment = true + irb.context.omit_on_assignment = true + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("=> #{value_first_line[0..(input.winsize.last - 9)]}...\e[0m\n=> \n#{value}\n", out) + irb.context.evaluate('A.remove_method(:inspect)', 0) + + input.reset + irb.context.echo = true + irb.context.echo_on_assignment = true + irb.context.omit_on_assignment = false + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("=> \n#{value}\n=> \n#{value}\n", out) + irb.context.evaluate('A.remove_method(:inspect)', 0) + + input.reset + irb.context.echo = false + irb.context.echo_on_assignment = false + irb.context.omit_on_assignment = true + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("", out) + irb.context.evaluate('A.remove_method(:inspect)', 0) + + input.reset + irb.context.echo = false + irb.context.echo_on_assignment = true + irb.context.omit_on_assignment = true + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("", out) + irb.context.evaluate('A.remove_method(:inspect)', 0) + + input.reset + irb.context.echo = false + irb.context.echo_on_assignment = true + irb.context.omit_on_assignment = false + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("", out) + irb.context.evaluate('A.remove_method(:inspect)', 0) + end + def test_echo_on_assignment_conf # Default IRB.conf[:ECHO] = nil @@ -206,22 +370,26 @@ module TestIRB irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) assert(irb.context.echo?, "echo? should be true by default") - refute(irb.context.echo_on_assignment?, "echo_on_assignment? should be false by default") + assert(irb.context.echo_on_assignment?, "echo_on_assignment? should be true by default") + assert(irb.context.omit_on_assignment?, "omit_on_assignment? should be true by default") # Explicitly set :ECHO to false IRB.conf[:ECHO] = false irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) refute(irb.context.echo?, "echo? should be false when IRB.conf[:ECHO] is set to false") - refute(irb.context.echo_on_assignment?, "echo_on_assignment? should be false by default") + assert(irb.context.echo_on_assignment?, "echo_on_assignment? should be true by default") + assert(irb.context.omit_on_assignment?, "omit_on_assignment? should be true by default") # Explicitly set :ECHO_ON_ASSIGNMENT to true IRB.conf[:ECHO] = nil - IRB.conf[:ECHO_ON_ASSIGNMENT] = true + IRB.conf[:ECHO_ON_ASSIGNMENT] = false + IRB.conf[:OMIT_ON_ASSIGNMENT] = false irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) assert(irb.context.echo?, "echo? should be true by default") - assert(irb.context.echo_on_assignment?, "echo_on_assignment? should be true when IRB.conf[:ECHO_ON_ASSIGNMENT] is set to true") + refute(irb.context.echo_on_assignment?, "echo_on_assignment? should be false when IRB.conf[:ECHO_ON_ASSIGNMENT] is set to false") + refute(irb.context.omit_on_assignment?, "omit_on_assignment? should be false when IRB.conf[:OMIT_ON_ASSIGNMENT] is set to false") end def test_multiline_output_on_default_inspector diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb new file mode 100644 index 0000000000..3591f88f59 --- /dev/null +++ b/test/irb/test_history.rb @@ -0,0 +1,153 @@ +# frozen_string_literal: false +require 'test/unit' +require 'irb' +require 'readline' + +module TestIRB + class TestHistory < Test::Unit::TestCase + def setup + IRB.conf[:RC_NAME_GENERATOR] = nil + end + + def teardown + IRB.conf[:RC_NAME_GENERATOR] = nil + end + + def test_history_save_1 + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) + _result_output, result_history_file = launch_irb_with_irbrc_and_irb_history(<<~IRBRC, <<~IRB_HISTORY) do |stdin| + IRB.conf[:USE_READLINE] = true + IRB.conf[:SAVE_HISTORY] = 1 + IRB.conf[:USE_READLINE] = true + IRBRC + 1 + 2 + 3 + 4 + IRB_HISTORY + stdin.write("5\nexit\n") + end + + assert_equal(<<~HISTORY_FILE, result_history_file) + exit + HISTORY_FILE + end + + def test_history_save_100 + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) + _result_output, result_history_file = launch_irb_with_irbrc_and_irb_history(<<~IRBRC, <<~IRB_HISTORY) do |stdin| + IRB.conf[:USE_READLINE] = true + IRB.conf[:SAVE_HISTORY] = 100 + IRB.conf[:USE_READLINE] = true + IRBRC + 1 + 2 + 3 + 4 + IRB_HISTORY + stdin.write("5\nexit\n") + end + + assert_equal(<<~HISTORY_FILE, result_history_file) + 1 + 2 + 3 + 4 + 5 + exit + HISTORY_FILE + end + + def test_history_save_bignum + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) + _result_output, result_history_file = launch_irb_with_irbrc_and_irb_history(<<~IRBRC, <<~IRB_HISTORY) do |stdin| + IRB.conf[:USE_READLINE] = true + IRB.conf[:SAVE_HISTORY] = 10 ** 19 + IRB.conf[:USE_READLINE] = true + IRBRC + 1 + 2 + 3 + 4 + IRB_HISTORY + stdin.write("5\nexit\n") + end + + assert_equal(<<~HISTORY_FILE, result_history_file) + 1 + 2 + 3 + 4 + 5 + exit + HISTORY_FILE + end + + def test_history_save_minus_as_infinity + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) + _result_output, result_history_file = launch_irb_with_irbrc_and_irb_history(<<~IRBRC, <<~IRB_HISTORY) do |stdin| + IRB.conf[:USE_READLINE] = true + IRB.conf[:SAVE_HISTORY] = -1 # infinity + IRB.conf[:USE_READLINE] = true + IRBRC + 1 + 2 + 3 + 4 + IRB_HISTORY + stdin.write("5\nexit\n") + end + + assert_equal(<<~HISTORY_FILE, result_history_file) + 1 + 2 + 3 + 4 + 5 + exit + HISTORY_FILE + end + + private + + def launch_irb_with_irbrc_and_irb_history(irbrc, irb_history) + result = nil + result_history = nil + backup_irbrc = ENV.delete("IRBRC") + backup_home = ENV["HOME"] + Dir.mktmpdir("test_irb_history_#{$$}") do |tmpdir| + ENV["HOME"] = tmpdir + open(IRB.rc_file, "w") do |f| + f.write(irbrc) + end + open(IRB.rc_file("_history"), "w") do |f| + f.write(irb_history) + end + + with_temp_stdio do |stdin, stdout| + yield(stdin, stdout) + stdin.close + stdout.flush + system('ruby', '-Ilib', '-Itest', '-W0', '-rirb', '-e', 'IRB.start(__FILE__)', in: stdin.path, out: stdout.path) + result = stdout.read + stdout.close + end + open(IRB.rc_file("_history"), "r") do |f| + result_history = f.read + end + end + [result, result_history] + ensure + ENV["HOME"] = backup_home + ENV["IRBRC"] = backup_irbrc + end + + def with_temp_stdio + Tempfile.create("test_readline_stdin") do |stdin| + Tempfile.create("test_readline_stdout") do |stdout| + yield stdin, stdout + end + end + end + end +end if not RUBY_PLATFORM.match?(/solaris|mswin|mingw/i) diff --git a/test/irb/test_ruby_lex.rb b/test/irb/test_ruby_lex.rb index dd5a1f7ca5..14b43f5718 100644 --- a/test/irb/test_ruby_lex.rb +++ b/test/irb/test_ruby_lex.rb @@ -5,7 +5,7 @@ require 'ostruct' module TestIRB class TestRubyLex < Test::Unit::TestCase - Row = Struct.new(:content, :current_line_spaces, :new_line_spaces) + Row = Struct.new(:content, :current_line_spaces, :new_line_spaces, :nesting_level) class MockIO def initialize(params, &assertion) @@ -34,6 +34,15 @@ module TestIRB ruby_lex.set_auto_indent(context) end + def assert_nesting_level(lines, expected) + ruby_lex = RubyLex.new() + io = proc{ lines.join("\n") } + ruby_lex.set_input(io, io) + ruby_lex.lex + error_message = "Calculated the wrong number of nesting level for:\n #{lines.join("\n")}" + assert_equal(expected, ruby_lex.instance_variable_get(:@indent), error_message) + end + def test_auto_indent input_with_correct_indents = [ Row.new(%q(def each_top_level_statement), nil, 2), @@ -126,5 +135,130 @@ module TestIRB assert_indenting(lines, row.new_line_spaces, true) end end + + def test_incomplete_coding_magic_comment + input_with_correct_indents = [ + Row.new(%q(#coding:u), nil, 0), + ] + + lines = [] + input_with_correct_indents.each do |row| + lines << row.content + assert_indenting(lines, row.current_line_spaces, false) + assert_indenting(lines, row.new_line_spaces, true) + end + end + + def test_incomplete_encoding_magic_comment + input_with_correct_indents = [ + Row.new(%q(#encoding:u), nil, 0), + ] + + lines = [] + input_with_correct_indents.each do |row| + lines << row.content + assert_indenting(lines, row.current_line_spaces, false) + assert_indenting(lines, row.new_line_spaces, true) + end + end + + def test_incomplete_emacs_coding_magic_comment + input_with_correct_indents = [ + Row.new(%q(# -*- coding: u), nil, 0), + ] + + lines = [] + input_with_correct_indents.each do |row| + lines << row.content + assert_indenting(lines, row.current_line_spaces, false) + assert_indenting(lines, row.new_line_spaces, true) + end + end + + def test_incomplete_vim_coding_magic_comment + input_with_correct_indents = [ + Row.new(%q(# vim:set fileencoding=u), nil, 0), + ] + + lines = [] + input_with_correct_indents.each do |row| + lines << row.content + assert_indenting(lines, row.current_line_spaces, false) + assert_indenting(lines, row.new_line_spaces, true) + end + end + + def test_mixed_rescue + input_with_correct_indents = [ + Row.new(%q(def m), nil, 2), + Row.new(%q( begin), nil, 4), + Row.new(%q( begin), nil, 6), + Row.new(%q( x = a rescue 4), nil, 6), + Row.new(%q( y = [(a rescue 5)]), nil, 6), + Row.new(%q( [x, y]), nil, 6), + Row.new(%q( rescue => e), 4, 6), + Row.new(%q( raise e rescue 8), nil, 6), + Row.new(%q( end), 4, 4), + Row.new(%q( rescue), 2, 4), + Row.new(%q( raise rescue 11), nil, 4), + Row.new(%q( end), 2, 2), + Row.new(%q(rescue => e), 0, 2), + Row.new(%q( raise e rescue 14), nil, 2), + Row.new(%q(end), 0, 0), + ] + + lines = [] + input_with_correct_indents.each do |row| + lines << row.content + assert_indenting(lines, row.current_line_spaces, false) + assert_indenting(lines, row.new_line_spaces, true) + end + end + + def test_oneliner_method_definition + input_with_correct_indents = [ + Row.new(%q(class A), nil, 2), + Row.new(%q( def foo0), nil, 4), + Row.new(%q( 3), nil, 4), + Row.new(%q( end), 2, 2), + Row.new(%q( def foo1()), nil, 4), + Row.new(%q( 3), nil, 4), + Row.new(%q( end), 2, 2), + Row.new(%q( def foo2(a, b)), nil, 4), + Row.new(%q( a + b), nil, 4), + Row.new(%q( end), 2, 2), + Row.new(%q( def foo3 a, b), nil, 4), + Row.new(%q( a + b), nil, 4), + Row.new(%q( end), 2, 2), + Row.new(%q( def bar0() = 3), nil, 2), + Row.new(%q( def bar1(a) = a), nil, 2), + Row.new(%q( def bar2(a, b) = a + b), nil, 2), + Row.new(%q(end), 0, 0), + ] + + lines = [] + input_with_correct_indents.each do |row| + lines << row.content + assert_indenting(lines, row.current_line_spaces, false) + assert_indenting(lines, row.new_line_spaces, true) + end + end + + def test_tlambda + input_with_correct_indents = [ + Row.new(%q(if true), nil, 2, 1), + Row.new(%q( -> {), nil, 4, 2), + Row.new(%q( }), 2, 2, 1), + Row.new(%q(end), 0, 0, 0), + ] + + lines = [] + input_with_correct_indents.each do |row| + lines << row.content + assert_indenting(lines, row.current_line_spaces, false) + assert_indenting(lines, row.new_line_spaces, true) + assert_nesting_level(lines, row.nesting_level) + end + end end end diff --git a/test/irb/test_workspace.rb b/test/irb/test_workspace.rb index 992cdd4516..15c77315a8 100644 --- a/test/irb/test_workspace.rb +++ b/test/irb/test_workspace.rb @@ -1,6 +1,7 @@ # frozen_string_literal: false require 'test/unit' require 'tempfile' +require 'rubygems' require 'irb' require 'irb/workspace' require 'irb/color' |