diff --git a/lib/irb.rb b/lib/irb.rb index ba12bdbcab..a7e7944cc4 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -750,8 +750,8 @@ class Binding # # Potato.new # - # Running ruby potato.rb will open an IRB session where - # +binding.irb+ is called, and you will see the following: + # Running +ruby potato.rb+ will open an IRB session where +binding.irb+ is + # called, and you will see the following: # # $ ruby potato.rb # @@ -781,7 +781,7 @@ class Binding # irb(#):004:0> @cooked = true # => true # - # You can exit the IRB session with the +exit+ command. Note that exiting will + # You can exit the IRB session with the `exit` command. Note that exiting will # resume execution where +binding.irb+ had paused it, as you can see from the # output printed to standard output in this example: # diff --git a/lib/irb/cmd/fork.rb b/lib/irb/cmd/fork.rb index 31d53dcaba..ae4d51b5d1 100644 --- a/lib/irb/cmd/fork.rb +++ b/lib/irb/cmd/fork.rb @@ -21,7 +21,7 @@ module IRB class << self alias_method :exit, ExtendCommand.irb_original_method_name('exit') end - if block_given? + if iterator? begin yield ensure diff --git a/lib/irb/color.rb b/lib/irb/color.rb index 9606c7b68c..2e7a3e8bab 100644 --- a/lib/irb/color.rb +++ b/lib/irb/color.rb @@ -17,24 +17,28 @@ module IRB # :nodoc: on_const: ['ENV'], } - TOKEN_SEQ_EXPRS = { - on_CHAR: [[BLUE, BOLD], [Ripper::EXPR_END]], - on_const: [[BLUE, BOLD, UNDERLINE], [Ripper::EXPR_ARG, Ripper::EXPR_CMDARG]], - on_embexpr_beg: [[RED], [Ripper::EXPR_BEG, Ripper::EXPR_END]], - on_embexpr_end: [[RED], [Ripper::EXPR_END, Ripper::EXPR_ENDFN]], - on_ident: [[BLUE, BOLD], [Ripper::EXPR_ENDFN]], - on_int: [[BLUE, BOLD], [Ripper::EXPR_END]], - on_float: [[MAGENTA, BOLD], [Ripper::EXPR_END]], - on_kw: [[GREEN], [Ripper::EXPR_ARG, Ripper::EXPR_CLASS, Ripper::EXPR_BEG, Ripper::EXPR_END, Ripper::EXPR_FNAME]], - on_label: [[MAGENTA], [Ripper::EXPR_LABELED]], - on_qwords_beg: [[RED], [Ripper::EXPR_BEG]], - on_regexp_beg: [[RED, BOLD], [Ripper::EXPR_BEG]], - on_regexp_end: [[RED, BOLD], [Ripper::EXPR_BEG]], - on_symbeg: [[BLUE, BOLD], [Ripper::EXPR_FNAME]], - on_tstring_beg: [[RED], [Ripper::EXPR_BEG, Ripper::EXPR_END, Ripper::EXPR_ARG, Ripper::EXPR_CMDARG]], - on_tstring_content: [[RED], [Ripper::EXPR_BEG, Ripper::EXPR_ARG, Ripper::EXPR_CMDARG]], - on_tstring_end: [[RED], [Ripper::EXPR_END]], - } + begin + TOKEN_SEQ_EXPRS = { + on_CHAR: [[BLUE, BOLD], [Ripper::EXPR_END]], + on_const: [[BLUE, BOLD, UNDERLINE], [Ripper::EXPR_ARG, Ripper::EXPR_CMDARG]], + on_embexpr_beg: [[RED], [Ripper::EXPR_BEG, Ripper::EXPR_END]], + on_embexpr_end: [[RED], [Ripper::EXPR_END, Ripper::EXPR_ENDFN]], + on_ident: [[BLUE, BOLD], [Ripper::EXPR_ENDFN]], + on_int: [[BLUE, BOLD], [Ripper::EXPR_END]], + on_float: [[MAGENTA, BOLD], [Ripper::EXPR_END]], + on_kw: [[GREEN], [Ripper::EXPR_ARG, Ripper::EXPR_CLASS, Ripper::EXPR_BEG, Ripper::EXPR_END, Ripper::EXPR_FNAME]], + on_label: [[MAGENTA], [Ripper::EXPR_LABELED]], + on_qwords_beg: [[RED], [Ripper::EXPR_BEG]], + on_regexp_beg: [[RED, BOLD], [Ripper::EXPR_BEG]], + on_regexp_end: [[RED, BOLD], [Ripper::EXPR_BEG]], + on_symbeg: [[BLUE, BOLD], [Ripper::EXPR_FNAME]], + on_tstring_beg: [[RED], [Ripper::EXPR_BEG, Ripper::EXPR_END, Ripper::EXPR_ARG, Ripper::EXPR_CMDARG]], + on_tstring_content: [[RED], [Ripper::EXPR_BEG, Ripper::EXPR_ARG, Ripper::EXPR_CMDARG]], + on_tstring_end: [[RED], [Ripper::EXPR_END]], + } + rescue NameError + TOKEN_SEQ_EXPRS = {} + end class << self def colorable? @@ -85,7 +89,7 @@ module IRB # :nodoc: [BLUE, BOLD] elsif TOKEN_KEYWORDS.fetch(token, []).include?(str) [CYAN, BOLD] - elsif (seq, exprs = TOKEN_SEQ_EXPRS[token]; exprs&.any? { |e| (expr & e) != Ripper::EXPR_NONE }) + elsif (seq, exprs = TOKEN_SEQ_EXPRS[token]; exprs&.any? { |e| (expr & e) != 0 }) seq else nil diff --git a/lib/irb/irb.gemspec b/lib/irb/irb.gemspec index 57a44fecb7..d16d6b0ecc 100644 --- a/lib/irb/irb.gemspec +++ b/lib/irb/irb.gemspec @@ -16,7 +16,7 @@ Gem::Specification.new do |spec| spec.homepage = "https://github.com/ruby/irb" spec.license = "BSD-2-Clause" - spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "exe/irb", "irb.gemspec", "lib/irb.rb", "lib/irb/cmd/chws.rb", "lib/irb/cmd/fork.rb", "lib/irb/cmd/help.rb", "lib/irb/cmd/load.rb", "lib/irb/cmd/nop.rb", "lib/irb/cmd/pushws.rb", "lib/irb/cmd/subirb.rb", "lib/irb/completion.rb", "lib/irb/context.rb", "lib/irb/ext/change-ws.rb", "lib/irb/ext/history.rb", "lib/irb/ext/loader.rb", "lib/irb/ext/multi-irb.rb", "lib/irb/ext/save-history.rb", "lib/irb/ext/tracer.rb", "lib/irb/ext/use-loader.rb", "lib/irb/ext/workspaces.rb", "lib/irb/extend-command.rb", "lib/irb/frame.rb", "lib/irb/help.rb", "lib/irb/init.rb", "lib/irb/input-method.rb", "lib/irb/inspector.rb", "lib/irb/lc/.document", "lib/irb/lc/error.rb", "lib/irb/lc/help-message", "lib/irb/lc/ja/encoding_aliases.rb", "lib/irb/lc/ja/error.rb", "lib/irb/lc/ja/help-message", "lib/irb/locale.rb", "lib/irb/magic-file.rb", "lib/irb/notifier.rb", "lib/irb/output-method.rb", "lib/irb/ruby-lex.rb", "lib/irb/ruby-token.rb", "lib/irb/slex.rb", "lib/irb/src_encoding.rb", "lib/irb/version.rb", "lib/irb/workspace.rb", "lib/irb/ws-for-case-2.rb", "lib/irb/xmp.rb"] + spec.files = ["LICENSE.txt", "README.md", "exe/irb", "irb.gemspec", "lib/irb.rb", "lib/irb/cmd/chws.rb", "lib/irb/cmd/fork.rb", "lib/irb/cmd/help.rb", "lib/irb/cmd/load.rb", "lib/irb/cmd/nop.rb", "lib/irb/cmd/pushws.rb", "lib/irb/cmd/subirb.rb", "lib/irb/completion.rb", "lib/irb/context.rb", "lib/irb/ext/change-ws.rb", "lib/irb/ext/history.rb", "lib/irb/ext/loader.rb", "lib/irb/ext/multi-irb.rb", "lib/irb/ext/save-history.rb", "lib/irb/ext/tracer.rb", "lib/irb/ext/use-loader.rb", "lib/irb/ext/workspaces.rb", "lib/irb/extend-command.rb", "lib/irb/frame.rb", "lib/irb/help.rb", "lib/irb/init.rb", "lib/irb/input-method.rb", "lib/irb/inspector.rb", "lib/irb/lc/.document", "lib/irb/lc/error.rb", "lib/irb/lc/help-message", "lib/irb/lc/ja/encoding_aliases.rb", "lib/irb/lc/ja/error.rb", "lib/irb/lc/ja/help-message", "lib/irb/locale.rb", "lib/irb/magic-file.rb", "lib/irb/notifier.rb", "lib/irb/output-method.rb", "lib/irb/ruby-lex.rb", "lib/irb/ruby-token.rb", "lib/irb/slex.rb", "lib/irb/src_encoding.rb", "lib/irb/version.rb", "lib/irb/workspace.rb", "lib/irb/ws-for-case-2.rb", "lib/irb/xmp.rb"] spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] diff --git a/lib/irb/workspace.rb b/lib/irb/workspace.rb index ff8f5da64f..bee59b9c3f 100644 --- a/lib/irb/workspace.rb +++ b/lib/irb/workspace.rb @@ -49,7 +49,7 @@ EOF @binding = BINDING_QUEUE.pop when 3 # binding in function on TOPLEVEL_BINDING(default) - @binding = eval("self.class.remove_method(:irb_binding) if defined?(irb_binding); def irb_binding; private; binding; end; irb_binding", + @binding = eval("def irb_binding; private; binding; end; irb_binding", TOPLEVEL_BINDING, __FILE__, __LINE__ - 3) @@ -116,7 +116,11 @@ EOF end def code_around_binding - file, pos = @binding.source_location + if @binding.respond_to?(:source_location) + file, pos = @binding.source_location + else + file, pos = @binding.eval('[__FILE__, __LINE__]') + end if defined?(::SCRIPT_LINES__[file]) && lines = ::SCRIPT_LINES__[file] code = ::SCRIPT_LINES__[file].join('') diff --git a/test/irb/test_color.rb b/test/irb/test_color.rb index 4b1878545e..8439fd54f5 100644 --- a/test/irb/test_color.rb +++ b/test/irb/test_color.rb @@ -15,6 +15,11 @@ module TestIRB CYAN = "\e[36m" def test_colorize_code + if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.5.0') + assert_equal({}, IRB::Color::TOKEN_SEQ_EXPRS) + skip "this Ripper version is not supported" + end + { "1" => "#{BLUE}#{BOLD}1#{CLEAR}", "2.3" => "#{MAGENTA}#{BOLD}2.3#{CLEAR}", @@ -23,11 +28,22 @@ module TestIRB "def self.foo; bar; end" => "#{GREEN}def#{CLEAR} #{CYAN}#{BOLD}self#{CLEAR}.#{BLUE}#{BOLD}foo#{CLEAR}; bar; #{GREEN}end#{CLEAR}", 'ERB.new("a#{nil}b", trim_mode: "-")' => "#{BLUE}#{BOLD}#{UNDERLINE}ERB#{CLEAR}.new(#{RED}\"#{CLEAR}#{RED}a#{CLEAR}#{RED}\#{#{CLEAR}#{CYAN}#{BOLD}nil#{CLEAR}#{RED}}#{CLEAR}#{RED}b#{CLEAR}#{RED}\"#{CLEAR}, #{MAGENTA}trim_mode:#{CLEAR} #{RED}\"#{CLEAR}#{RED}-#{CLEAR}#{RED}\"#{CLEAR})", "# comment" => "#{BLUE}#{BOLD}# comment#{CLEAR}", - '/r#{e}g/' => "#{RED}#{BOLD}/#{CLEAR}#{RED}r#{CLEAR}#{RED}\#{#{CLEAR}e}#{RED}g#{CLEAR}#{RED}#{BOLD}/#{CLEAR}", "yield(hello)" => "#{GREEN}yield#{CLEAR}(hello)", }.each do |code, result| assert_equal(result, with_term { IRB::Color.colorize_code(code) }) end + + if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6.0') + { + '/r#{e}g/' => "#{RED}#{BOLD}/#{CLEAR}#{RED}r#{CLEAR}#{RED}\#{#{CLEAR}e}#{RED}g#{CLEAR}#{RED}#{BOLD}/#{CLEAR}", + } + else + { + '/r#{e}g/' => "#{RED}#{BOLD}/#{CLEAR}#{RED}r#{CLEAR}#{RED}\#{#{CLEAR}e#{RED}}#{CLEAR}#{RED}g#{CLEAR}#{RED}#{BOLD}/#{CLEAR}", + } + end.each do |code, result| + assert_equal(result, with_term { IRB::Color.colorize_code(code) }) + end end def test_inspect_colorable diff --git a/test/irb/test_completion.rb b/test/irb/test_completion.rb new file mode 100644 index 0000000000..608c41bad9 --- /dev/null +++ b/test/irb/test_completion.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: false +require 'test/unit' + +module TestIRB + class TestCompletion < Test::Unit::TestCase + def test_nonstring_module_name + begin + require "irb/completion" + bug5938 = '[ruby-core:42244]' + cmds = %W[-rirb -rirb/completion -e IRB.setup(__FILE__) + -e IRB.conf[:MAIN_CONTEXT]=IRB::Irb.new.context + -e module\sFoo;def\sself.name;//;end;end + -e IRB::InputCompletor::CompletionProc.call("[1].first.") + -- -f --] + status = assert_in_out_err(cmds, "", //, [], bug5938) + assert(status.success?, bug5938) + rescue LoadError + skip "cannot load irb/completion" + end + end + end +end diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb index 469599c11b..fa2432b3f3 100644 --- a/test/irb/test_context.rb +++ b/test/irb/test_context.rb @@ -55,7 +55,6 @@ module TestIRB end def test_eval_input - verbose, $VERBOSE = $VERBOSE, nil input = TestInputMethod.new([ "raise 'Foo'\n", "_\n", @@ -72,8 +71,6 @@ module TestIRB :*, /0$/, :*, /0$/, /\s*/], out) - ensure - $VERBOSE = verbose end end end diff --git a/test/irb/test_raise_no_backtrace_exception.rb b/test/irb/test_raise_no_backtrace_exception.rb index 72e82bf7f7..d3882a427c 100644 --- a/test/irb/test_raise_no_backtrace_exception.rb +++ b/test/irb/test_raise_no_backtrace_exception.rb @@ -4,7 +4,7 @@ require 'test/unit' module TestIRB class TestRaiseNoBacktraceException < Test::Unit::TestCase def test_raise_exception - assert_in_out_err(%w[-rirb -W1 -e IRB.start(__FILE__) -- -f --], <<-IRB, /Exception: foo/, [], success: true) + status = assert_in_out_err(%w[-rirb -e IRB.start(__FILE__) -- -f --], <<-IRB, /Exception: foo/, []) e = Exception.new("foo") def e.backtrace; nil; end raise e diff --git a/test/irb/test_ruby-lex.rb b/test/irb/test_ruby-lex.rb new file mode 100644 index 0000000000..b07b4a2eb6 --- /dev/null +++ b/test/irb/test_ruby-lex.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: false +require 'test/unit' +require 'irb/ruby-lex' +require 'stringio' + +module TestIRB + class TestRubyLex < Test::Unit::TestCase + def setup + @scanner = RubyLex.new + end + + def teardown + RubyLex.debug_level = 0 + end + + def test_set_input_proc + called = false + @scanner.set_input(nil) {called = true; nil} + @scanner.each_top_level_statement {} + assert(called) + end + + def test_comment + assert_equal([["#\n", 1]], top_level_statement("#\n")) + end + + def test_top_level_statement + result = top_level_statement("#{<<-"begin;"}#{<<~"end;"}") + begin; + begin + end + begin + end + end; + assert_equal([ + ["begin\n""end\n", 1], + ["begin\n""end\n", 3], + ], + result) + end + + def test_immature_statement + src = "if false\n" + assert_equal([[src, 1]], top_level_statement(src)) + end + + def test_prompt + prompts = [] + @scanner.set_prompt {|*a| + a << @scanner.instance_variable_get(:@lex_state) + unless prompts.last == a + prompts << a + end + } + src, lineno = "#{<<-"begin;"}#{<<~'end;'}", __LINE__+1 + begin; + # #;# LTYPE:INDENT:CONTINUE + x #;# -:0:- + x( #;# -:0:- + ) #;# -:1:* + a \ #;# -:0:- + #;# -:0:* + a; #;# -:0:- + a #;# -:0:- + #;# -:0:- + a #;# -:0:- + a = #;# -:0:- + ' #;# -:0:* + ' #;# ':0:* + if false or #;# -:0:- + true #;# -:1:* + a #;# -:1:- + " #;# -:1:- + " #;# ":1:- + begin #;# -:1:- + a #;# -:2:- + a #;# -:2:- + end #;# -:2:- + else #;# -:1:- + nil #;# -:1:- + end #;# -:1:- + end; + top_level_statement(src.gsub(/[ \t]*#;#.*/, '')) + src.each_line.with_index(1) do |line, i| + p = prompts.shift + next unless /#;#\s*(?:-|(?\S)):(?\d+):(?:(?\*)|-)(?:.*FIXME:(?.*))?/ =~ line + indent = indent.to_i + cont = (fixme && /`continue'/.match?(fixme)) ^ cont + assert_equal([ltype, indent, cont, i], p[0..3], "#{lineno+i}:#{p[4]}: #{line}") + end + end + + def top_level_statement(lines) + input = InputLines.new(lines, "r") + scanned = [] + @scanner.set_input(input) + @scanner.each_top_level_statement {|*e| + scanned << e + yield(*e) if defined?(yield) + } + scanned + end + + class InputLines < StringIO + alias encoding external_encoding + end + end +end diff --git a/test/irb/test_workspace.rb b/test/irb/test_workspace.rb index 9c87468cf7..fe63c3c225 100644 --- a/test/irb/test_workspace.rb +++ b/test/irb/test_workspace.rb @@ -7,7 +7,7 @@ require 'irb/color' module TestIRB class TestWorkSpace < Test::Unit::TestCase def test_code_around_binding - Tempfile.create do |f| + Tempfile.create('irb') do |f| code = <<~RUBY # 1 # 2 @@ -37,7 +37,7 @@ module TestIRB skip 'chmod cannot make file unreadable on windows' if windows? skip 'skipped in root privilege' if Process.uid == 0 - Tempfile.create do |f| + Tempfile.create('irb') do |f| code = "IRB::WorkSpace.new(binding)\n" f.print(code) f.close @@ -51,7 +51,7 @@ module TestIRB def test_code_around_binding_with_script_lines__ with_script_lines do |script_lines| - Tempfile.create do |f| + Tempfile.create('irb') do |f| code = "IRB::WorkSpace.new(binding)\n" script_lines[f.path] = code.split(/^/)