diff --git a/lib/debug/console.rb b/lib/debug/console.rb index b228086d9..e0a9835a4 100644 --- a/lib/debug/console.rb +++ b/lib/debug/console.rb @@ -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 @@ -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 @@ -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(?\s*)(?[^\s]+)(?.*)/ =~ buff.chomp cmd = colorize(c, [:CYAN, :UNDERLINE]) if commands[c] == c @@ -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 @@ -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 } diff --git a/lib/debug/session.rb b/lib/debug/session.rb index 05dc7f508..cfdc155aa 100644 --- a/lib/debug/session.rb +++ b/lib/debug/session.rb @@ -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 @@ -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 diff --git a/test/console/session_test.rb b/test/console/session_test.rb index d17a38ac2..9a7398092 100644 --- a/test/console/session_test.rb +++ b/test/console/session_test.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'debug/session' require_relative '../support/console_test_case' module DEBUGGER__ @@ -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