def process_dap args
# pp tc: self, args: args
type = args.shift
req = args.shift
case type
when :backtrace
start_frame = req.dig('arguments', 'startFrame') || 0
levels = req.dig('arguments', 'levels') || 1_000
frames = []
@target_frames.each_with_index do |frame, i|
next if i < start_frame
path = frame.realpath || frame.path
next if skip_path?(path) && !SESSION.stop_stepping?(path, frame.location.lineno)
break if (levels -= 1) < 0
source_name = path ? File.basename(path) : frame.location.to_s
if (path && File.exist?(path)) && (local_path = UI_DAP.remote_to_local_path(path))
# ok
else
ref = frame.file_lines
end
frames << {
id: i, # id is refilled by SESSION
name: frame.name,
line: frame.location.lineno,
column: 1,
source: {
name: source_name,
path: (local_path || path),
sourceReference: ref,
},
}
end
event! :protocol_result, :backtrace, req, {
stackFrames: frames,
totalFrames: @target_frames.size,
}
when :scopes
fid = args.shift
frame = get_frame(fid)
lnum =
if frame.binding
frame.binding.local_variables.size
elsif vars = frame.local_variables
vars.size
else
0
end
event! :protocol_result, :scopes, req, scopes: [{
name: 'Local variables',
presentationHint: 'locals',
# variablesReference: N, # filled by SESSION
namedVariables: lnum,
indexedVariables: 0,
expensive: false,
}, {
name: 'Global variables',
presentationHint: 'globals',
variablesReference: 1, # GLOBAL
namedVariables: safe_global_variables.size,
indexedVariables: 0,
expensive: false,
}]
when :scope
fid = args.shift
frame = get_frame(fid)
vars = collect_locals(frame).map do |var, val|
variable(var, val)
end
event! :protocol_result, :scope, req, variables: vars, tid: self.id
when :variable
vid = args.shift
obj = @var_map[vid]
if obj
case req.dig('arguments', 'filter')
when 'indexed'
start = req.dig('arguments', 'start') || 0
count = req.dig('arguments', 'count') || obj.size
vars = (start ... (start + count)).map{|i|
variable(i.to_s, obj[i])
}
else
vars = []
case obj
when Hash
vars = obj.map{|k, v|
variable(value_inspect(k), v,)
}
when Struct
vars = obj.members.map{|m|
variable(m, obj[m])
}
when String
vars = [
variable('#length', obj.length),
variable('#encoding', obj.encoding),
]
printed_str = value_inspect(obj)
vars << variable('#dump', NaiveString.new(obj)) if printed_str.end_with?('...')
when Class, Module
vars << variable('%ancestors', obj.ancestors[1..])
when Range
vars = [
variable('#begin', obj.begin),
variable('#end', obj.end),
]
end
unless NaiveString === obj
vars += M_INSTANCE_VARIABLES.bind_call(obj).sort.map{|iv|
variable(iv, M_INSTANCE_VARIABLE_GET.bind_call(obj, iv))
}
vars.unshift variable('#class', M_CLASS.bind_call(obj))
end
end
end
event! :protocol_result, :variable, req, variables: (vars || []), tid: self.id
when :evaluate
fid, expr, context = args
frame = get_frame(fid)
message = nil
if frame && (b = frame.eval_binding)
special_local_variables frame do |name, var|
b.local_variable_set(name, var) if /\%/ !~ name
end
case context
when 'repl', 'watch'
result = dap_eval b, expr, context, prompt: '(DEBUG CONSOLE)'
when 'hover'
case expr
when /\A\@\S/
begin
result = M_INSTANCE_VARIABLE_GET.bind_call(b.receiver, expr)
rescue NameError
message = "Error: Not defined instance variable: #{expr.inspect}"
end
when /\A\$\S/
safe_global_variables.each{|gvar|
if gvar.to_s == expr
result = eval(gvar.to_s)
break false
end
} and (message = "Error: Not defined global variable: #{expr.inspect}")
when /\Aself$/
result = b.receiver
when /(\A((::[A-Z]|[A-Z])\w*)+)/
unless result = search_const(b, $1)
message = "Error: Not defined constants: #{expr.inspect}"
end
else
begin
result = b.local_variable_get(expr)
rescue NameError
# try to check method
if M_RESPOND_TO_P.bind_call(b.receiver, expr, include_all: true)
result = M_METHOD.bind_call(b.receiver, expr)
else
message = "Error: Can not evaluate: #{expr.inspect}"
end
end
end
else
message = "Error: unknown context: #{context}"
end
else
result = 'Error: Can not evaluate on this frame'
end
event! :protocol_result, :evaluate, req, message: message, tid: self.id, **evaluate_result(result)
when :completions
fid, text = args
frame = get_frame(fid)
if (b = frame&.binding) && word = text&.split(/[\s\{]/)&.last
words = IRB::InputCompletor::retrieve_completion_data(word, bind: b).compact
end
event! :protocol_result, :completions, req, targets: (words || []).map{|phrase|
detail = nil
if /\b([_a-zA-Z]\w*[!\?]?)\z/ =~ phrase
w = $1
else
w = phrase
end
begin
v = b.local_variable_get(w)
detail ="(variable: #{value_inspect(v)})"
rescue NameError
end
{
label: phrase,
text: w,
detail: detail,
}
}
else
if respond_to? mid = "custom_dap_request_#{type}"
__send__ mid, req
else
raise "Unknown request: #{args.inspect}"
end
end
end