debugger: Update New Session Modal (#30018)

This PR simplifies the new session modal by flattening its three modes
and updating the UI to be less noisy. The new UI also defaults to the
Debug Scenario Picker, and allows users to save debug scenarios created
in the UI to the active worktree's .zed/debug.json file.


Release Notes:

- N/A
This commit is contained in:
Anthony Eid 2025-05-08 18:19:14 +02:00 committed by GitHub
parent e9a756b5fc
commit dc01aef0cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 460 additions and 527 deletions

View file

@ -43,6 +43,7 @@ language.workspace = true
log.workspace = true
menu.workspace = true
parking_lot.workspace = true
paths.workspace = true
picker.workspace = true
pretty_assertions.workspace = true
project.workspace = true

View file

@ -32,12 +32,12 @@ pub(crate) struct AttachModalDelegate {
impl AttachModalDelegate {
fn new(
workspace: Entity<Workspace>,
workspace: WeakEntity<Workspace>,
definition: DebugTaskDefinition,
candidates: Arc<[Candidate]>,
) -> Self {
Self {
workspace: workspace.downgrade(),
workspace,
definition,
candidates,
selected_index: 0,
@ -55,7 +55,7 @@ pub struct AttachModal {
impl AttachModal {
pub fn new(
definition: DebugTaskDefinition,
workspace: Entity<Workspace>,
workspace: WeakEntity<Workspace>,
modal: bool,
window: &mut Window,
cx: &mut Context<Self>,
@ -82,7 +82,7 @@ impl AttachModal {
}
pub(super) fn with_processes(
workspace: Entity<Workspace>,
workspace: WeakEntity<Workspace>,
definition: DebugTaskDefinition,
processes: Arc<[Candidate]>,
modal: bool,

View file

@ -5,15 +5,15 @@ use crate::{
FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables, Pause, Restart, StepBack,
StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints, persistence,
};
use anyhow::Result;
use anyhow::{Result, anyhow};
use command_palette_hooks::CommandPaletteFilter;
use dap::StartDebuggingRequestArguments;
use dap::adapters::DebugAdapterName;
use dap::debugger_settings::DebugPanelDockPosition;
use dap::{
ContinuedEvent, LoadedSourceEvent, ModuleEvent, OutputEvent, StoppedEvent, ThreadEvent,
client::SessionId, debugger_settings::DebuggerSettings,
};
use dap::{StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
use gpui::{
Action, App, AsyncWindowContext, Context, DismissEvent, Entity, EntityId, EventEmitter,
FocusHandle, Focusable, MouseButton, MouseDownEvent, Point, Subscription, Task, WeakEntity,
@ -54,12 +54,11 @@ pub enum DebugPanelEvent {
}
actions!(debug_panel, [ToggleFocus]);
pub struct DebugPanel {
size: Pixels,
sessions: Vec<Entity<DebugSession>>,
active_session: Option<Entity<DebugSession>>,
/// This represents the last debug definition that was created in the new session modal
pub(crate) past_debug_definition: Option<DebugTaskDefinition>,
project: Entity<Project>,
workspace: WeakEntity<Workspace>,
focus_handle: FocusHandle,
@ -80,7 +79,6 @@ impl DebugPanel {
size: px(300.),
sessions: vec![],
active_session: None,
past_debug_definition: None,
focus_handle: cx.focus_handle(),
project,
workspace: workspace.weak_handle(),
@ -992,6 +990,69 @@ impl DebugPanel {
self.active_session = Some(session_item);
cx.notify();
}
pub(crate) fn save_scenario(
&self,
scenario: &DebugScenario,
worktree_id: WorktreeId,
window: &mut Window,
cx: &mut App,
) -> Task<Result<()>> {
self.workspace
.update(cx, |workspace, cx| {
let Some(mut path) = workspace.absolute_path_of_worktree(worktree_id, cx) else {
return Task::ready(Err(anyhow!("Couldn't get worktree path")));
};
let serialized_scenario = serde_json::to_value(scenario);
path.push(paths::local_debug_file_relative_path());
cx.spawn_in(window, async move |workspace, cx| {
let serialized_scenario = serialized_scenario?;
let path = path.as_path();
let fs =
workspace.update(cx, |workspace, _| workspace.app_state().fs.clone())?;
if !fs.is_file(path).await {
let content =
serde_json::to_string_pretty(&serde_json::Value::Array(vec![
serialized_scenario,
]))?;
fs.create_file(path, Default::default()).await?;
fs.save(path, &content.into(), Default::default()).await?;
} else {
let content = fs.load(path).await?;
let mut values = serde_json::from_str::<Vec<serde_json::Value>>(&content)?;
values.push(serialized_scenario);
fs.save(
path,
&serde_json::to_string_pretty(&values).map(Into::into)?,
Default::default(),
)
.await?;
}
workspace.update_in(cx, |workspace, window, cx| {
if let Some(project_path) = workspace
.project()
.read(cx)
.project_path_for_absolute_path(&path, cx)
{
workspace.open_path(project_path, None, true, window, cx)
} else {
Task::ready(Err(anyhow!(
"Couldn't get project path for .zed/debug.json in active worktree"
)))
}
})?.await?;
anyhow::Ok(())
})
})
.unwrap_or_else(|err| Task::ready(Err(err)))
}
}
impl EventEmitter<PanelEvent> for DebugPanel {}

View file

@ -147,36 +147,7 @@ pub fn init(cx: &mut App) {
},
)
.register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
let weak_panel = debug_panel.downgrade();
let weak_workspace = cx.weak_entity();
let task_store = workspace.project().read(cx).task_store().clone();
cx.spawn_in(window, async move |this, cx| {
let task_contexts = this
.update_in(cx, |workspace, window, cx| {
tasks_ui::task_contexts(workspace, window, cx)
})?
.await;
this.update_in(cx, |workspace, window, cx| {
workspace.toggle_modal(window, cx, |window, cx| {
NewSessionModal::new(
debug_panel.read(cx).past_debug_definition.clone(),
weak_panel,
weak_workspace,
Some(task_store),
task_contexts,
window,
cx,
)
});
})?;
anyhow::Ok(())
})
.detach()
}
NewSessionModal::show(workspace, window, cx);
});
})
})

File diff suppressed because it is too large Load diff

View file

@ -864,7 +864,7 @@ impl RunningState {
dap::DebugRequest::Launch(new_launch_request)
}
request @ dap::DebugRequest::Attach(_) => request,
request @ dap::DebugRequest::Attach(_) => request, // todo(debugger): We should check that process_id is valid and if not show the modal
};
Ok(DebugTaskDefinition {
label,

View file

@ -103,7 +103,7 @@ async fn test_show_attach_modal_and_select_process(
});
let attach_modal = workspace
.update(cx, |workspace, window, cx| {
let workspace_handle = cx.entity();
let workspace_handle = cx.weak_entity();
workspace.toggle_modal(window, cx, |window, cx| {
AttachModal::with_processes(
workspace_handle,