Skip to content

Commit

Permalink
Treat assignment like input as ruby, not as command
Browse files Browse the repository at this point in the history
`n = 1` `n += 1` will be a ruby expression, not a shorthand of `next` command.
  • Loading branch information
tompng committed Jul 1, 2024
1 parent 8abc50a commit 2c7a707
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 26 deletions.
32 changes: 10 additions & 22 deletions lib/debug/console.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ class Console
include Color

def parse_input buff, commands
c, rest = get_command buff
case
when commands.keys.include?(c)
when Session.command?(buff, commands: commands)
:command
when !rest && /\A\s*[a-z]*\z/ =~ c
when buff.lines.size <= 1 && buff.match?(/\A\s*[a-z]*\s*\z/)
nil
else
:ruby
Expand All @@ -29,16 +28,14 @@ def readline_setup prompt
prev_output_modifier_proc = Reline.output_modifier_proc
prev_prompt_proc = Reline.prompt_proc

# prompt state
state = nil # :command, :ruby, nil (unknown)

Reline.prompt_proc = -> args, *kw do
case state = parse_input(args.first, commands)
Reline.prompt_proc = -> lines, *kw do
input = lines.map { |line| "#{line}\n" }.join
case parse_input(input, commands)
when nil, :command
[prompt]
when :ruby
[prompt.sub('rdbg'){colorize('ruby', [:RED])}]
end * args.size
end * lines.size
end

Reline.completion_proc = -> given do
Expand All @@ -58,10 +55,10 @@ def readline_setup prompt
end

Reline.output_modifier_proc = -> buff, **kw do
c, rest = get_command buff

case state
case parse_input(buff, commands)
when :command
# buff is single line if it's command
/\A(?<leading_spaces>\s*)(?<c>[^\s]+)(?<rest>.*)/ =~ buff.chomp
cmd = colorize(c, [:CYAN, :UNDERLINE])

if commands[c] == c
Expand All @@ -71,7 +68,7 @@ def readline_setup prompt
end

rest = rest ? colorize_code(rest) : ''
cmd + rest + rprompt
[leading_spaces, cmd, rest, rprompt].join
when nil
buff
when :ruby
Expand All @@ -87,15 +84,6 @@ def readline_setup prompt
Reline.prompt_proc = prev_prompt_proc
end

private def get_command line
case line.chomp
when /\A(\s*[a-z]+)(\s.*)?\z$/
return $1.strip, $2
else
line.strip
end
end

def readline prompt
readline_setup prompt do
Reline.readmultiline(prompt, true){ true }
Expand Down
20 changes: 16 additions & 4 deletions lib/debug/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,18 @@ def register_default_command
end
end

ASSIGN_OPERATORS_REGEXP = Regexp.union(%w[= += -= *= /= %= **= &= |= &&= ||= ^= <<= >>=])

def self.command?(line, commands:)
return false if line.lines.size != 1

command, arg = line.lstrip.split(/\s+/, 2)
return false unless commands.has_key? command

# assignment-like expression should be treated as ruby code, not as commands.
!arg || !arg.start_with?(ASSIGN_OPERATORS_REGEXP) || arg.start_with?(/==|=~/)
end

def process_command line
if line.empty?
if @repl_prev_line
Expand All @@ -1153,10 +1165,10 @@ def process_command line
@repl_prev_line = line
end

/([^\s]+)(?:\s+(.+))?/ =~ line
cmd_name, cmd_arg = $1, $2

if cmd = @commands[cmd_name]
if Session.command?(line, commands: @commands)
/([^\s]+)(?:\s+(.+))?/ =~ line
cmd_name, cmd_arg = $1, $2
cmd = @commands[cmd_name]
check_postmortem if !cmd.postmortem
check_unsafe if cmd.unsafe
cancel_auto_continue if cmd.cancel_auto_continue
Expand Down
43 changes: 43 additions & 0 deletions test/console/session_test.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require 'debug/session'
require_relative '../support/console_test_case'

module DEBUGGER__
Expand Down Expand Up @@ -70,4 +71,46 @@ def test_debugger_session_starts_correctly
end
end
end

class CommandRecognizeTest < ConsoleTestCase
def program
<<~RUBY
1| a = 1
2| b = 2
3| a = b
4| "foo"
RUBY
end

def test_is_command
commands = { 'n' => true, 'p' => true }
command_expressions = ['n', 'p 1', " n \n", " p 1 \n", 'n + 1', 'p == 1']
non_command_expressions = ["n\n\n", 'n = 1', 'n ||= 1', 'n =1', "p 1\n.itself"]
command_expressions.each do |s|
assert_equal true, DEBUGGER__::Session.command?(s, commands: commands)
end
non_command_expressions.each do |s|
assert_equal false, DEBUGGER__::Session.command?(s, commands: commands)
end
end

def test_assign_expression_conflicting_with_command_treated_as_expression
run_ruby(program, options: "-r debug/start") do
assert_line_num(1)
type 'n'
assert_line_num(2)
type ' n '
assert_line_num(3)
type 'n = 123000'
assert_line_num(3)
type 'n += 456'
assert_line_num(3)
type 'p n'
assert_line_text('123456')
type 'next'
assert_line_num(4)
type 'c'
end
end
end
end

0 comments on commit 2c7a707

Please sign in to comment.