lib/byebug/dap/command.rb in byebug-dap-0.1.3 vs lib/byebug/dap/command.rb in byebug-dap-0.1.4
- old
+ new
@@ -1,11 +1,16 @@
module Byebug::DAP
+ # Implementation of a DAP command.
+ # @abstract Subclasses must implement {#execute}
class Command
+ # The error message returned when a variable or expression cannot be evaluated.
EVAL_ERROR = "*Error in evaluation*"
include SafeHelpers
+ # The DAP command assocated with the receiver.
+ # @return [std:String]
def self.command
return @command_name if defined?(@command_name)
raise "Not a command" if self == Byebug::DAP::Command
raise "Not a command" unless self < Byebug::DAP::Command
@@ -13,36 +18,54 @@
last = self.name.split('::').last
@command_name = "#{last[0].downcase}#{last[1..]}"
end
+ # Register the receiver as a DAP command.
def self.register!
(@@commands ||= {})[command] = self
end
+ # Resolve the requested command. Calls {Session#respond!} indicating a
+ # failed request if the command cannot be found.
+ # @param session [Session] the debug session
+ # @param request [Protocol::Request] the DAP request
+ # @return [std:Class] the {Command} class
def self.resolve!(session, request)
cls = @@commands[request.command]
return cls if cls
session.respond! request, success: false, message: 'Invalid command'
end
+ # Resolve and execute the requested command. The command is {.resolve!
+ # resolved}, {#initialize initialized}, and {#safe_execute safely executed}.
+ # @param session [Session] the debug session
+ # @param request [Protocol::Request] the DAP request
+ # @param args [std:Array] additional arguments for {#initialize}
+ # @return the return value of {#safe_execute}
def self.execute(session, request, *args)
return unless command = resolve!(session, request)
command.new(session, request, *args).safe_execute
end
+ # Create a new instance of the receiver.
+ # @param session [Session] the debug session
+ # @param request [Protocol::Request] the DAP request
def initialize(session, request)
@session = session
@request = request
end
+ # (see Session#log)
def log(*args)
@session.log(*args)
end
+ # Call {#execute} safely, handling any errors that arise.
+ # @return the return value of {#execute}
def safe_execute
execute
rescue InvalidRequestArgumentError => e
message =
@@ -89,16 +112,22 @@
@session.respond! @request, *args, **values
return
end
+ # Raises an error if the debugger is running
+ # @api private
+ # @!visibility public
def stopped!
return if !Byebug.started?
respond! success: false, message: "Cannot #{@request.command} - debugger is already running"
end
+ # Raises an error unless the debugger is running
+ # @api private
+ # @!visibility public
def started!
return if Byebug.started?
respond! success: false, message: "Cannot #{@request.command} - debugger is not running"
end
@@ -109,10 +138,17 @@
def exception_description(ex)
safe(-> { "#{ex.message} (#{ex.class.name})" }, :call) { EVAL_ERROR }
end
+ # Execute a code block on the specified thread, {SafeHelpers#safe safely}.
+ # @param thnum [std:Integer] the thread number
+ # @param block [std:Proc] the code block
+ # @yield called on error
+ # @yieldparam ex [std:Exception] the execution error
+ # @api private
+ # @!visibility public
def execute_on_thread(thnum, block, &on_error)
return safe(block, :call, &on_error) if thnum == 0 || @context&.thnum == thnum
p = find_thread(thnum).processor
safe(-> { p.execute(&block) }, :call, &on_error)
@@ -205,11 +241,13 @@
return nil unless condition.is_a?(String)
return condition
end
def convert_breakpoint_hit_condition(condition)
- return nil if condition.nil? || condition.empty?
- return nil unless condition.is_a?(String)
+ # Because of https://github.com/deivid-rodriguez/byebug/issues/739,
+ # Breakpoint#hit_condition can't be set to nil.
+ return :ge, 0 if condition.nil? || condition.empty?
+ return :ge, 0 unless condition.is_a?(String)
m = /^(?<op><|<=|=|==|===|=>|>|%)?\s*(?<value>[0-9]+)$/.match(condition)
raise InvalidRequestArgumentError.new("'#{condition}' is not a valid hit condition") unless m
v = m[:value].to_i