use super::*;
impl DebugAdapter {
pub(super) fn handle_stack_trace(
&self,
seq: i64,
request_seq: i64,
arguments: Option<Value>,
) -> DapMessage {
let args: Option<StackTraceArguments> =
arguments.and_then(|v| serde_json::from_value(v).ok());
let start_frame =
args.as_ref().and_then(|value| value.start_frame).unwrap_or(0).max(0) as usize;
let levels = args.as_ref().and_then(|value| value.levels).unwrap_or(0);
let requested_count = if levels <= 0 { None } else { Some(levels as usize) };
let mut framed_output_lines = None;
if let Some(ref mut session) = *lock_or_recover(&self.session, "debug_adapter.session")
&& let Some(stdin) = session.process.stdin.as_mut()
{
let commands = vec!["T".to_string()];
match self.send_framed_debugger_commands(stdin, &commands) {
Ok((begin, end)) => {
framed_output_lines = self.capture_framed_debugger_output(
&begin,
&end,
DEBUGGER_QUERY_WAIT_MS * 8,
);
}
Err(error) => {
tracing::warn!(%error, "Failed to send framed stackTrace command, falling back");
let _ = stdin.write_all(b"T\n");
let _ = stdin.flush();
Self::wait_for_debugger_output_window(DEBUGGER_QUERY_WAIT_MS as u32);
}
}
}
let parsed_frames = if let Some(lines) = framed_output_lines.as_ref() {
let output = lines.join("\n");
let framed_frames =
Self::filter_user_visible_frames(Self::parse_stack_frames_from_text(&output));
if framed_frames.is_empty() {
let output_lines = self.snapshot_recent_output_lines();
if output_lines.is_empty() {
Vec::new()
} else {
let output = output_lines.join("\n");
Self::filter_user_visible_frames(Self::parse_stack_frames_from_text(&output))
}
} else {
framed_frames
}
} else {
let output_lines = self.snapshot_recent_output_lines();
if output_lines.is_empty() {
Vec::new()
} else {
let output = output_lines.join("\n");
Self::filter_user_visible_frames(Self::parse_stack_frames_from_text(&output))
}
};
let stack_frames = if !parsed_frames.is_empty() {
if let Some(ref mut session) = *lock_or_recover(&self.session, "debug_adapter.session")
{
session.stack_frames = parsed_frames.clone();
}
parsed_frames
} else if let Some(ref session) = *lock_or_recover(&self.session, "debug_adapter.session") {
Self::filter_user_visible_frames(session.stack_frames.clone())
} else if let Some(pid) = *lock_or_recover(&self.attached_pid, "debug_adapter.attached_pid")
{
vec![StackFrame {
id: Self::i64_to_i32_saturating(i64::from(pid)),
name: format!("attached::process::{pid}"),
source: Source {
name: Some(format!("pid:{pid}")),
path: format!("pid://{pid}"),
source_reference: None,
},
line: 1,
column: 1,
end_line: None,
end_column: None,
}]
} else {
vec![StackFrame {
id: 1,
name: "main::hello".to_string(),
source: Source {
name: Some("hello.pl".to_string()),
path: "/tmp/hello.pl".to_string(),
source_reference: None,
},
line: 10,
column: 1,
end_line: None,
end_column: None,
}]
};
let stack_frames = Self::paginate_stack_frames(stack_frames, start_frame, requested_count);
DapMessage::Response {
seq,
request_seq,
success: true,
command: "stackTrace".to_string(),
body: Some(json!({
"stackFrames": stack_frames,
"totalFrames": stack_frames.len()
})),
message: None,
}
}
pub(super) fn handle_scopes(
&self,
seq: i64,
request_seq: i64,
arguments: Option<Value>,
) -> DapMessage {
let args: ScopesArguments = match arguments.and_then(|v| serde_json::from_value(v).ok()) {
Some(a) => a,
None => {
return DapMessage::Response {
seq,
request_seq,
success: false,
command: "scopes".to_string(),
body: None,
message: Some("Missing frameId".to_string()),
};
}
};
let frame_id = args.frame_id as i32;
let locals_ref = frame_id * 10 + 1;
let package_ref = frame_id * 10 + 2;
let globals_ref = frame_id * 10 + 3;
let scopes_body = ScopesResponseBody {
scopes: vec![
Scope {
name: "Locals".to_string(),
presentation_hint: Some("locals".to_string()),
variables_reference: i64::from(locals_ref),
expensive: false,
},
Scope {
name: "Package".to_string(),
presentation_hint: None,
variables_reference: i64::from(package_ref),
expensive: true,
},
Scope {
name: "Globals".to_string(),
presentation_hint: None,
variables_reference: i64::from(globals_ref),
expensive: true,
},
],
};
DapMessage::Response {
seq,
request_seq,
success: true,
command: "scopes".to_string(),
body: serde_json::to_value(&scopes_body).ok(),
message: None,
}
}
}
impl DebugAdapter {
fn paginate_stack_frames(
stack_frames: Vec<StackFrame>,
start_frame: usize,
levels: Option<usize>,
) -> Vec<StackFrame> {
let iter = stack_frames.into_iter().skip(start_frame);
match levels {
Some(limit) => iter.take(limit).collect(),
None => iter.collect(),
}
}
}