debugger: Add an action to copy debuggee info and initialization args (#32647)

Release Notes:

- Debugger Beta: added the `dev: copy debug adapter arguments` action to
help troubleshoot debug configurations.
This commit is contained in:
Cole Miller 2025-06-12 21:38:25 -04:00 committed by GitHub
parent 1078f929aa
commit f227c2ff0c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 52 additions and 20 deletions

View file

@ -671,7 +671,7 @@ async fn test_remote_server_debugger(
}); });
session.update(cx_a, |session, _| { session.update(cx_a, |session, _| {
assert_eq!(session.binary().command.as_deref(), Some("ssh")); assert_eq!(session.binary().unwrap().command.as_deref(), Some("ssh"));
}); });
let shutdown_session = workspace.update(cx_a, |workspace, cx| { let shutdown_session = workspace.update(cx_a, |workspace, cx| {

View file

@ -93,7 +93,7 @@ impl<'a> From<&'a str> for DebugAdapterName {
} }
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Serialize)]
pub struct TcpArguments { pub struct TcpArguments {
pub host: Ipv4Addr, pub host: Ipv4Addr,
pub port: u16, pub port: u16,
@ -179,7 +179,7 @@ impl DebugTaskDefinition {
} }
/// Created from a [DebugTaskDefinition], this struct describes how to spawn the debugger to create a previously-configured debug session. /// Created from a [DebugTaskDefinition], this struct describes how to spawn the debugger to create a previously-configured debug session.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Serialize)]
pub struct DebugAdapterBinary { pub struct DebugAdapterBinary {
pub command: Option<String>, pub command: Option<String>,
pub arguments: Vec<String>, pub arguments: Vec<String>,

View file

@ -2,10 +2,10 @@ use crate::persistence::DebuggerPaneItem;
use crate::session::DebugSession; use crate::session::DebugSession;
use crate::session::running::RunningState; use crate::session::running::RunningState;
use crate::{ use crate::{
ClearAllBreakpoints, Continue, Detach, FocusBreakpointList, FocusConsole, FocusFrames, ClearAllBreakpoints, Continue, CopyDebugAdapterArguments, Detach, FocusBreakpointList,
FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables, NewProcessModal, FocusConsole, FocusFrames, FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables,
NewProcessMode, Pause, Restart, StepInto, StepOut, StepOver, Stop, ToggleExpandItem, NewProcessModal, NewProcessMode, Pause, Restart, StepInto, StepOut, StepOver, Stop,
ToggleSessionPicker, ToggleThreadPicker, persistence, spawn_task_or_modal, ToggleExpandItem, ToggleSessionPicker, ToggleThreadPicker, persistence, spawn_task_or_modal,
}; };
use anyhow::Result; use anyhow::Result;
use dap::adapters::DebugAdapterName; use dap::adapters::DebugAdapterName;
@ -16,9 +16,9 @@ use dap::{
}; };
use dap::{DapRegistry, StartDebuggingRequestArguments}; use dap::{DapRegistry, StartDebuggingRequestArguments};
use gpui::{ use gpui::{
Action, App, AsyncWindowContext, Context, DismissEvent, Entity, EntityId, EventEmitter, Action, App, AsyncWindowContext, ClipboardItem, Context, DismissEvent, Entity, EntityId,
FocusHandle, Focusable, MouseButton, MouseDownEvent, Point, Subscription, Task, WeakEntity, EventEmitter, FocusHandle, Focusable, MouseButton, MouseDownEvent, Point, Subscription, Task,
actions, anchored, deferred, WeakEntity, actions, anchored, deferred,
}; };
use language::Buffer; use language::Buffer;
@ -30,6 +30,7 @@ use settings::Settings;
use std::sync::Arc; use std::sync::Arc;
use task::{DebugScenario, TaskContext}; use task::{DebugScenario, TaskContext};
use ui::{ContextMenu, Divider, PopoverMenuHandle, Tooltip, prelude::*}; use ui::{ContextMenu, Divider, PopoverMenuHandle, Tooltip, prelude::*};
use util::maybe;
use workspace::SplitDirection; use workspace::SplitDirection;
use workspace::{ use workspace::{
Pane, Workspace, Pane, Workspace,
@ -335,7 +336,7 @@ impl DebugPanel {
let dap_store_handle = self.project.read(cx).dap_store().clone(); let dap_store_handle = self.project.read(cx).dap_store().clone();
let label = curr_session.read(cx).label().clone(); let label = curr_session.read(cx).label().clone();
let adapter = curr_session.read(cx).adapter().clone(); let adapter = curr_session.read(cx).adapter().clone();
let binary = curr_session.read(cx).binary().clone(); let binary = curr_session.read(cx).binary().cloned().unwrap();
let task = curr_session.update(cx, |session, cx| session.shutdown(cx)); let task = curr_session.update(cx, |session, cx| session.shutdown(cx));
cx.spawn_in(window, async move |this, cx| { cx.spawn_in(window, async move |this, cx| {
@ -388,7 +389,10 @@ impl DebugPanel {
let dap_store_handle = self.project.read(cx).dap_store().clone(); let dap_store_handle = self.project.read(cx).dap_store().clone();
let label = self.label_for_child_session(&parent_session, request, cx); let label = self.label_for_child_session(&parent_session, request, cx);
let adapter = parent_session.read(cx).adapter().clone(); let adapter = parent_session.read(cx).adapter().clone();
let mut binary = parent_session.read(cx).binary().clone(); let Some(mut binary) = parent_session.read(cx).binary().cloned() else {
log::error!("Attempted to start a child-session without a binary");
return;
};
binary.request_args = request.clone(); binary.request_args = request.clone();
cx.spawn_in(window, async move |this, cx| { cx.spawn_in(window, async move |this, cx| {
let (session, task) = dap_store_handle.update(cx, |dap_store, cx| { let (session, task) = dap_store_handle.update(cx, |dap_store, cx| {
@ -517,6 +521,26 @@ impl DebugPanel {
} }
} }
fn copy_debug_adapter_arguments(
&mut self,
_: &CopyDebugAdapterArguments,
_window: &mut Window,
cx: &mut Context<Self>,
) {
let content = maybe!({
let mut session = self.active_session()?.read(cx).session(cx);
while let Some(parent) = session.read(cx).parent_session().cloned() {
session = parent;
}
let binary = session.read(cx).binary()?;
let content = serde_json::to_string_pretty(&binary).ok()?;
Some(content)
});
if let Some(content) = content {
cx.write_to_clipboard(ClipboardItem::new_string(content));
}
}
pub(crate) fn top_controls_strip( pub(crate) fn top_controls_strip(
&mut self, &mut self,
window: &mut Window, window: &mut Window,
@ -1348,6 +1372,7 @@ impl Render for DebugPanel {
}); });
cx.notify(); cx.notify();
})) }))
.on_action(cx.listener(Self::copy_debug_adapter_arguments))
.when(self.active_session.is_some(), |this| { .when(self.active_session.is_some(), |this| {
this.on_mouse_down( this.on_mouse_down(
MouseButton::Right, MouseButton::Right,

View file

@ -56,6 +56,8 @@ actions!(
] ]
); );
actions!(dev, [CopyDebugAdapterArguments]);
pub fn init(cx: &mut App) { pub fn init(cx: &mut App) {
DebuggerSettings::register(cx); DebuggerSettings::register(cx);
workspace::FollowableViewRegistry::register::<DebugSession>(cx); workspace::FollowableViewRegistry::register::<DebugSession>(cx);

View file

@ -993,7 +993,7 @@ impl RunningState {
let cwd = Some(&request.cwd) let cwd = Some(&request.cwd)
.filter(|cwd| cwd.len() > 0) .filter(|cwd| cwd.len() > 0)
.map(PathBuf::from) .map(PathBuf::from)
.or_else(|| session.binary().cwd.clone()); .or_else(|| session.binary().unwrap().cwd.clone());
let mut args = request.args.clone(); let mut args = request.args.clone();

View file

@ -469,14 +469,19 @@ async fn test_handle_start_debugging_request(
// We should preserve the original binary (params to spawn process etc.) except for launch params // We should preserve the original binary (params to spawn process etc.) except for launch params
// (as they come from reverse spawn request). // (as they come from reverse spawn request).
let mut original_binary = parent_session.read(cx).binary().clone(); let mut original_binary = parent_session.read(cx).binary().cloned().unwrap();
original_binary.request_args = StartDebuggingRequestArguments { original_binary.request_args = StartDebuggingRequestArguments {
request: StartDebuggingRequestArgumentsRequest::Launch, request: StartDebuggingRequestArgumentsRequest::Launch,
configuration: fake_config.clone(), configuration: fake_config.clone(),
}; };
assert_eq!( assert_eq!(
current_sessions[1].read(cx).session(cx).read(cx).binary(), current_sessions[1]
.read(cx)
.session(cx)
.read(cx)
.binary()
.unwrap(),
&original_binary &original_binary
); );
}) })

View file

@ -892,11 +892,11 @@ impl Session {
&self.capabilities &self.capabilities
} }
pub fn binary(&self) -> &DebugAdapterBinary { pub fn binary(&self) -> Option<&DebugAdapterBinary> {
let Mode::Running(local_mode) = &self.mode else { match &self.mode {
panic!("Session is not running"); Mode::Building => None,
}; Mode::Running(running_mode) => Some(&running_mode.binary),
&local_mode.binary }
} }
pub fn adapter(&self) -> DebugAdapterName { pub fn adapter(&self) -> DebugAdapterName {