From a0bfe4d293fc71964e0498ef22076daa411b405f Mon Sep 17 00:00:00 2001 From: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com> Date: Tue, 6 May 2025 08:54:57 +0200 Subject: [PATCH] debugger: Fix debug scenario's defined in debug.json not using passed in build task (#29973) There were two bugs that caused user-defined debug scenarios from being able to run a build task. 1. DebugRequest would be deserialized to `Attach` even when `process_id` wasn't defined in a user's configuration file. This has been fixed by adding our own deserializer that defaults to None if there are no fields present instead of `Attach`, and I added tests to prevent regressions. 2. Debug scenario resolve phase never got the active buffer when spawning a debug session from the new session modal. This has been worked around by passing in the worktree_id of a debug scenario in the scenario picker and the active worktree_id otherwise. Release Notes: - N/A --- crates/debugger_ui/src/attach_modal.rs | 2 +- crates/debugger_ui/src/debugger_panel.rs | 6 +- crates/debugger_ui/src/new_session_modal.rs | 16 ++++- crates/debugger_ui/src/session/running.rs | 5 +- crates/project/src/task_inventory.rs | 5 +- crates/task/src/debug_format.rs | 75 ++++++++++++++++++++- 6 files changed, 97 insertions(+), 12 deletions(-) diff --git a/crates/debugger_ui/src/attach_modal.rs b/crates/debugger_ui/src/attach_modal.rs index 1afe8ba275..e054985f92 100644 --- a/crates/debugger_ui/src/attach_modal.rs +++ b/crates/debugger_ui/src/attach_modal.rs @@ -237,7 +237,7 @@ impl PickerDelegate for AttachModalDelegate { .flatten(); if let Some(panel) = panel { panel.update(cx, |panel, cx| { - panel.start_session(scenario, Default::default(), None, window, cx); + panel.start_session(scenario, Default::default(), None, None, window, cx); }); } diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs index 846c374b27..4c6367bd25 100644 --- a/crates/debugger_ui/src/debugger_panel.rs +++ b/crates/debugger_ui/src/debugger_panel.rs @@ -22,8 +22,8 @@ use gpui::{ }; use language::Buffer; -use project::Fs; use project::debugger::session::{Session, SessionStateEvent}; +use project::{Fs, WorktreeId}; use project::{Project, debugger::session::ThreadStatus}; use rpc::proto::{self}; use settings::Settings; @@ -208,6 +208,7 @@ impl DebugPanel { scenario: DebugScenario, task_context: TaskContext, active_buffer: Option>, + worktree_id: Option, window: &mut Window, cx: &mut Context, ) { @@ -233,6 +234,7 @@ impl DebugPanel { scenario, task_context, active_buffer, + worktree_id, window, cx, ) @@ -1283,7 +1285,7 @@ impl workspace::DebuggerProvider for DebuggerProvider { ) { self.0.update(cx, |_, cx| { cx.defer_in(window, |this, window, cx| { - this.start_session(definition, context, buffer, window, cx); + this.start_session(definition, context, buffer, None, window, cx); }) }) } diff --git a/crates/debugger_ui/src/new_session_modal.rs b/crates/debugger_ui/src/new_session_modal.rs index 06398336bc..73aa4b095e 100644 --- a/crates/debugger_ui/src/new_session_modal.rs +++ b/crates/debugger_ui/src/new_session_modal.rs @@ -150,6 +150,7 @@ impl NewSessionModal { let task_contexts = workspace .update_in(cx, |this, window, cx| task_contexts(this, window, cx))? .await; + let worktree_id = task_contexts.worktree(); let task_context = task_contexts .active_item_context .map(|(_, _, context)| context) @@ -159,8 +160,9 @@ impl NewSessionModal { .map(|(_, context)| context) }) .unwrap_or_default(); + debug_panel.update_in(cx, |debug_panel, window, cx| { - debug_panel.start_session(config, task_context, None, window, cx) + debug_panel.start_session(config, task_context, None, worktree_id, window, cx) })?; this.update(cx, |_, cx| { cx.emit(DismissEvent); @@ -937,19 +939,27 @@ impl PickerDelegate for DebugScenarioDelegate { .await .task_context_for_worktree_id(worktree_id) .cloned() + .map(|context| (context, Some(worktree_id))) }) } else { gpui::Task::ready(None) }; cx.spawn_in(window, async move |this, cx| { - let task_context = task_context.await.unwrap_or_default(); + let (task_context, worktree_id) = task_context.await.unwrap_or_default(); this.update_in(cx, |this, window, cx| { this.delegate .debug_panel .update(cx, |panel, cx| { - panel.start_session(debug_scenario, task_context, None, window, cx); + panel.start_session( + debug_scenario, + task_context, + None, + worktree_id, + window, + cx, + ); }) .ok(); diff --git a/crates/debugger_ui/src/session/running.rs b/crates/debugger_ui/src/session/running.rs index caf227cf9c..34baa44caa 100644 --- a/crates/debugger_ui/src/session/running.rs +++ b/crates/debugger_ui/src/session/running.rs @@ -29,7 +29,7 @@ use language::Buffer; use loaded_source_list::LoadedSourceList; use module_list::ModuleList; use project::{ - Project, + Project, WorktreeId, debugger::session::{Session, SessionEvent, ThreadId, ThreadStatus}, terminals::TerminalKind, }; @@ -684,6 +684,7 @@ impl RunningState { scenario: DebugScenario, task_context: TaskContext, buffer: Option>, + worktree_id: Option, window: &Window, cx: &mut Context, ) -> Task> { @@ -712,7 +713,7 @@ impl RunningState { this.task_inventory().and_then(|inventory| { inventory .read(cx) - .task_template_by_label(buffer, &build, cx) + .task_template_by_label(buffer, worktree_id, &build, cx) }) })? else { diff --git a/crates/project/src/task_inventory.rs b/crates/project/src/task_inventory.rs index 6bb7fd0d1c..642ee2c086 100644 --- a/crates/project/src/task_inventory.rs +++ b/crates/project/src/task_inventory.rs @@ -229,10 +229,11 @@ impl Inventory { pub fn task_template_by_label( &self, buffer: Option>, + worktree_id: Option, label: &str, cx: &App, ) -> Option { - let (worktree_id, file, language) = buffer + let (buffer_worktree_id, file, language) = buffer .map(|buffer| { let buffer = buffer.read(cx); let file = buffer.file().cloned(); @@ -244,7 +245,7 @@ impl Inventory { }) .unwrap_or((None, None, None)); - self.list_tasks(file, language, worktree_id, cx) + self.list_tasks(file, language, worktree_id.or(buffer_worktree_id), cx) .iter() .find(|(_, template)| template.label == label) .map(|val| val.1.clone()) diff --git a/crates/task/src/debug_format.rs b/crates/task/src/debug_format.rs index f25243df87..068b0885b0 100644 --- a/crates/task/src/debug_format.rs +++ b/crates/task/src/debug_format.rs @@ -47,12 +47,35 @@ impl TcpArgumentsTemplate { } /// Represents the attach request information of the debug adapter -#[derive(Default, Deserialize, Serialize, PartialEq, Eq, JsonSchema, Clone, Debug)] +#[derive(Default, Serialize, PartialEq, Eq, JsonSchema, Clone, Debug)] pub struct AttachRequest { /// The processId to attach to, if left empty we will show a process picker pub process_id: Option, } +impl<'de> Deserialize<'de> for AttachRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + process_id: Option, + } + + let helper = Helper::deserialize(deserializer)?; + + // Skip creating an AttachRequest if process_id is None + if helper.process_id.is_none() { + return Err(serde::de::Error::custom("process_id is required")); + } + + Ok(AttachRequest { + process_id: helper.process_id, + }) + } +} + /// Represents the launch request information of the debug adapter #[derive(Deserialize, Serialize, Default, PartialEq, Eq, JsonSchema, Clone, Debug)] pub struct LaunchRequest { @@ -204,7 +227,7 @@ impl DebugTaskFile { #[cfg(test)] mod tests { - use crate::{DebugRequest, LaunchRequest}; + use crate::{DebugRequest, DebugScenario, LaunchRequest}; #[test] fn test_can_deserialize_non_attach_task() { @@ -218,4 +241,52 @@ mod tests { }) ); } + + #[test] + fn test_empty_scenario_has_none_request() { + let json = r#"{ + "label": "Build & debug rust", + "build": "rust", + "adapter": "CodeLLDB" + }"#; + + let deserialized: DebugScenario = serde_json::from_str(json).unwrap(); + assert_eq!(deserialized.request, None); + } + + #[test] + fn test_launch_scenario_deserialization() { + let json = r#"{ + "label": "Launch program", + "adapter": "CodeLLDB", + "program": "target/debug/myapp", + "args": ["--test"] + }"#; + + let deserialized: DebugScenario = serde_json::from_str(json).unwrap(); + match deserialized.request { + Some(DebugRequest::Launch(launch)) => { + assert_eq!(launch.program, "target/debug/myapp"); + assert_eq!(launch.args, vec!["--test"]); + } + _ => panic!("Expected Launch request"), + } + } + + #[test] + fn test_attach_scenario_deserialization() { + let json = r#"{ + "label": "Attach to process", + "adapter": "CodeLLDB", + "process_id": 1234 + }"#; + + let deserialized: DebugScenario = serde_json::from_str(json).unwrap(); + match deserialized.request { + Some(DebugRequest::Attach(attach)) => { + assert_eq!(attach.process_id, Some(1234)); + } + _ => panic!("Expected Attach request"), + } + } }