diff --git a/lib/irb.rb b/lib/irb.rb index 99fd1c5df..723035f15 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -1028,21 +1028,7 @@ def eval_input return statement.code end - case statement - when Statement::EmptyInput - # Do nothing - when Statement::Expression - @context.evaluate(statement.code, line_no) - when Statement::Command - ret = statement.command_class.execute(@context, statement.arg) - # TODO: Remove this output once we have a better way to handle it - # This is to notify `debug`'s test framework that the current input has been processed - # We also need to have a way to restart/stop threads around command execution - # when being used as `debug`'s console. - # https://github.com/ruby/debug/blob/master/lib/debug/irb_integration.rb#L8-L13 - puts "INTERNAL_INFO: {}" if @context.with_debugger && ENV['RUBY_DEBUG_TEST_UI'] == 'terminal' - @context.set_last_value(ret) - end + @context.evaluate(statement, line_no) if @context.echo? && !statement.suppresses_echo? if statement.is_assignment? diff --git a/lib/irb/context.rb b/lib/irb/context.rb index e3c419245..836b8d262 100644 --- a/lib/irb/context.rb +++ b/lib/irb/context.rb @@ -585,31 +585,44 @@ def inspect_mode=(opt) @inspect_mode end - def evaluate(line, line_no) # :nodoc: + def evaluate(statement, line_no) # :nodoc: @line_no = line_no result = nil + case statement + when Statement::EmptyInput + return + when Statement::Expression + result = evaluate_expression(statement.code, line_no) + when Statement::Command + result = statement.command_class.execute(self, statement.arg) + end + + set_last_value(result) + end + + def evaluate_expression(code, line_no) # :nodoc: + result = nil if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty? IRB.set_measure_callback end if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty? last_proc = proc do - result = workspace.evaluate(line, @eval_path, line_no) + result = workspace.evaluate(code, @eval_path, line_no) end IRB.conf[:MEASURE_CALLBACKS].inject(last_proc) do |chain, item| _name, callback, arg = item proc do - callback.(self, line, line_no, arg) do + callback.(self, code, line_no, arg) do chain.call end end end.call else - result = workspace.evaluate(line, @eval_path, line_no) + result = workspace.evaluate(code, @eval_path, line_no) end - - set_last_value(result) + result end def inspect_last_value # :nodoc: diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb index 5812ea041..aff4b5b67 100644 --- a/test/irb/test_context.rb +++ b/test/irb/test_context.rb @@ -28,35 +28,6 @@ def teardown restore_encodings end - def test_last_value - assert_nil(@context.last_value) - assert_nil(@context.evaluate('_', 1)) - obj = Object.new - @context.set_last_value(obj) - assert_same(obj, @context.last_value) - assert_same(obj, @context.evaluate('_', 1)) - end - - def test_evaluate_with_encoding_error_without_lineno - if RUBY_ENGINE == 'truffleruby' - omit "Remove me after https://github.com/ruby/prism/issues/2129 is addressed and adopted in TruffleRuby" - end - - if RUBY_VERSION >= "3.4." - omit "Now raises SyntaxError" - end - - assert_raise_with_message(EncodingError, /invalid symbol/) { - @context.evaluate(%q[:"\xAE"], 1) - # The backtrace of this invalid encoding hash doesn't contain lineno. - } - end - - def test_evaluate_still_emits_warning - assert_warning("(irb):1: warning: END in method; use at_exit\n") do - @context.evaluate(%q[def foo; END {}; end], 1) - end - end def test_eval_input verbose, $VERBOSE = $VERBOSE, nil @@ -382,7 +353,7 @@ def test_omit_multiline_on_assignment end assert_empty err assert_equal("=> \n#{value}\n", out) - irb.context.evaluate('A.remove_method(:inspect)', 0) + irb.context.evaluate_expression('A.remove_method(:inspect)', 0) input.reset irb.context.echo = true @@ -392,7 +363,7 @@ def test_omit_multiline_on_assignment end assert_empty err assert_equal("=> #{value_first_line[0..(input.winsize.last - 9)]}...\n=> \n#{value}\n", out) - irb.context.evaluate('A.remove_method(:inspect)', 0) + irb.context.evaluate_expression('A.remove_method(:inspect)', 0) input.reset irb.context.echo = true @@ -402,7 +373,7 @@ def test_omit_multiline_on_assignment end assert_empty err assert_equal("=> \n#{value}\n=> \n#{value}\n", out) - irb.context.evaluate('A.remove_method(:inspect)', 0) + irb.context.evaluate_expression('A.remove_method(:inspect)', 0) input.reset irb.context.echo = false @@ -412,7 +383,7 @@ def test_omit_multiline_on_assignment end assert_empty err assert_equal("", out) - irb.context.evaluate('A.remove_method(:inspect)', 0) + irb.context.evaluate_expression('A.remove_method(:inspect)', 0) input.reset irb.context.echo = false @@ -422,7 +393,7 @@ def test_omit_multiline_on_assignment end assert_empty err assert_equal("", out) - irb.context.evaluate('A.remove_method(:inspect)', 0) + irb.context.evaluate_expression('A.remove_method(:inspect)', 0) input.reset irb.context.echo = false @@ -432,7 +403,7 @@ def test_omit_multiline_on_assignment end assert_empty err assert_equal("", out) - irb.context.evaluate('A.remove_method(:inspect)', 0) + irb.context.evaluate_expression('A.remove_method(:inspect)', 0) end end diff --git a/test/irb/test_irb.rb b/test/irb/test_irb.rb index 966c84013..84b9ee364 100644 --- a/test/irb/test_irb.rb +++ b/test/irb/test_irb.rb @@ -42,6 +42,56 @@ class Foo assert_include output, "From: #{@ruby_file.path}:1" end + def test_underscore_stores_last_result + write_ruby <<~'RUBY' + binding.irb + RUBY + + output = run_ruby_file do + type "1 + 1" + type "_ + 10" + type "exit!" + end + + assert_include output, "=> 12" + end + + def test_evaluate_with_encoding_error_without_lineno + if RUBY_ENGINE == 'truffleruby' + omit "Remove me after https://github.com/ruby/prism/issues/2129 is addressed and adopted in TruffleRuby" + end + + if RUBY_VERSION >= "3.4." + omit "Now raises SyntaxError" + end + + write_ruby <<~'RUBY' + binding.irb + RUBY + + output = run_ruby_file do + type %q[:"\xAE"] + type "exit!" + end + + assert_include output, 'invalid symbol in encoding UTF-8 :"\xAE"' + # EncodingError would be wrapped with ANSI escape sequences, so we assert it separately + assert_include output, "EncodingError" + end + + def test_evaluate_still_emits_warning + write_ruby <<~'RUBY' + binding.irb + RUBY + + output = run_ruby_file do + type %q[def foo; END {}; end] + type "exit!" + end + + assert_include output, '(irb):1: warning: END in method; use at_exit' + end + def test_symbol_aliases_dont_affect_ruby_syntax write_ruby <<~'RUBY' $foo = "It's a foo"