Skip to content

Commit

Permalink
[ruby/irb] Support repeating debugger input by passing empty input
Browse files Browse the repository at this point in the history
to it
(ruby/irb#856)

* Test IRB's behaviour with empty input

* Handle empty input and pass it to debugger

Since `rdbg` accepts empty input to repeat the previous command, IRB
should take empty input in `irb:rdbg` sessions and pass them to the
debugger.

Currently, IRB simply ignores empty input and does nothing. This commit
creates `EmptyInput` to represent empty input so it can fit into the
current IRB's input processing flow in `Irb#eval_input`.

ruby/irb@0e9db419be
  • Loading branch information
st0012 authored and matzbot committed Feb 16, 2024
1 parent ff4f5c0 commit b315826
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 5 deletions.
9 changes: 5 additions & 4 deletions lib/irb.rb
Expand Up @@ -1082,16 +1082,17 @@ def each_top_level_statement
loop do
code = readmultiline
break unless code

if code != "\n"
yield build_statement(code), @line_no
end
yield build_statement(code), @line_no
@line_no += code.count("\n")
rescue RubyLex::TerminateLineInput
end
end

def build_statement(code)
if code.match?(/\A\n*\z/)
return Statement::EmptyInput.new
end

code.force_encoding(@context.io.encoding)
command_or_alias, arg = code.split(/\s/, 2)
# Transform a non-identifier alias (@, $) or keywords (next, break)
Expand Down
23 changes: 23 additions & 0 deletions lib/irb/statement.rb
Expand Up @@ -20,6 +20,29 @@ def evaluable_code
raise NotImplementedError
end

class EmptyInput < Statement
def is_assignment?
false
end

def suppresses_echo?
true
end

# Debugger takes empty input to repeat the last command
def should_be_handled_by_debugger?
true
end

def code
""
end

def evaluable_code
code
end
end

class Expression < Statement
def initialize(code, is_assignment)
@code = code
Expand Down
Expand Up @@ -6,7 +6,7 @@
require_relative "helper"

module TestIRB
class DebugCommandTest < IntegrationTestCase
class DebuggerIntegrationTest < IntegrationTestCase
def setup
super

Expand Down Expand Up @@ -434,5 +434,29 @@ def test_multi_irb_commands_are_not_available_after_activating_the_debugger
assert_match(/irb\(main\):001> next/, output)
assert_include(output, "Multi-IRB commands are not available when the debugger is enabled.")
end

def test_irb_passes_empty_input_to_debugger_to_repeat_the_last_command
write_ruby <<~'ruby'
binding.irb
puts "foo"
puts "bar"
puts "baz"
ruby

output = run_ruby_file do
type "next"
type ""
# Test that empty input doesn't repeat expressions
type "123"
type ""
type "next"
type ""
type ""
end

assert_include(output, "=> 2\| puts \"foo\"")
assert_include(output, "=> 3\| puts \"bar\"")
assert_include(output, "=> 4\| puts \"baz\"")
end
end
end
18 changes: 18 additions & 0 deletions test/irb/test_irb.rb
Expand Up @@ -58,6 +58,24 @@ def test_symbol_aliases_dont_affect_ruby_syntax
assert_include output, "=> \"It's a foo\""
assert_include output, "=> \"It's a bar\""
end

def test_empty_input_echoing_behaviour
write_ruby <<~'RUBY'
binding.irb
RUBY

output = run_ruby_file do
type ""
type " "
type "exit"
end

# Input cramped together due to how Reline's Reline::GeneralIO works
assert_include(
output,
"irb(main):001> irb(main):002> irb(main):002> irb(main):002> => nil\r\n"
)
end
end

class IrbIOConfigurationTest < TestCase
Expand Down

0 comments on commit b315826

Please sign in to comment.