Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow assigning to local variable conflicting with command name #1108

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading