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
This commit is contained in:
parent
52ea501f4f
commit
a0bfe4d293
6 changed files with 97 additions and 12 deletions
|
@ -237,7 +237,7 @@ impl PickerDelegate for AttachModalDelegate {
|
||||||
.flatten();
|
.flatten();
|
||||||
if let Some(panel) = panel {
|
if let Some(panel) = panel {
|
||||||
panel.update(cx, |panel, cx| {
|
panel.update(cx, |panel, cx| {
|
||||||
panel.start_session(scenario, Default::default(), None, window, cx);
|
panel.start_session(scenario, Default::default(), None, None, window, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@ use gpui::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use language::Buffer;
|
use language::Buffer;
|
||||||
use project::Fs;
|
|
||||||
use project::debugger::session::{Session, SessionStateEvent};
|
use project::debugger::session::{Session, SessionStateEvent};
|
||||||
|
use project::{Fs, WorktreeId};
|
||||||
use project::{Project, debugger::session::ThreadStatus};
|
use project::{Project, debugger::session::ThreadStatus};
|
||||||
use rpc::proto::{self};
|
use rpc::proto::{self};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
@ -208,6 +208,7 @@ impl DebugPanel {
|
||||||
scenario: DebugScenario,
|
scenario: DebugScenario,
|
||||||
task_context: TaskContext,
|
task_context: TaskContext,
|
||||||
active_buffer: Option<Entity<Buffer>>,
|
active_buffer: Option<Entity<Buffer>>,
|
||||||
|
worktree_id: Option<WorktreeId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
|
@ -233,6 +234,7 @@ impl DebugPanel {
|
||||||
scenario,
|
scenario,
|
||||||
task_context,
|
task_context,
|
||||||
active_buffer,
|
active_buffer,
|
||||||
|
worktree_id,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -1283,7 +1285,7 @@ impl workspace::DebuggerProvider for DebuggerProvider {
|
||||||
) {
|
) {
|
||||||
self.0.update(cx, |_, cx| {
|
self.0.update(cx, |_, cx| {
|
||||||
cx.defer_in(window, |this, window, 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);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,7 @@ impl NewSessionModal {
|
||||||
let task_contexts = workspace
|
let task_contexts = workspace
|
||||||
.update_in(cx, |this, window, cx| task_contexts(this, window, cx))?
|
.update_in(cx, |this, window, cx| task_contexts(this, window, cx))?
|
||||||
.await;
|
.await;
|
||||||
|
let worktree_id = task_contexts.worktree();
|
||||||
let task_context = task_contexts
|
let task_context = task_contexts
|
||||||
.active_item_context
|
.active_item_context
|
||||||
.map(|(_, _, context)| context)
|
.map(|(_, _, context)| context)
|
||||||
|
@ -159,8 +160,9 @@ impl NewSessionModal {
|
||||||
.map(|(_, context)| context)
|
.map(|(_, context)| context)
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
debug_panel.update_in(cx, |debug_panel, window, cx| {
|
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| {
|
this.update(cx, |_, cx| {
|
||||||
cx.emit(DismissEvent);
|
cx.emit(DismissEvent);
|
||||||
|
@ -937,19 +939,27 @@ impl PickerDelegate for DebugScenarioDelegate {
|
||||||
.await
|
.await
|
||||||
.task_context_for_worktree_id(worktree_id)
|
.task_context_for_worktree_id(worktree_id)
|
||||||
.cloned()
|
.cloned()
|
||||||
|
.map(|context| (context, Some(worktree_id)))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
gpui::Task::ready(None)
|
gpui::Task::ready(None)
|
||||||
};
|
};
|
||||||
|
|
||||||
cx.spawn_in(window, async move |this, cx| {
|
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.update_in(cx, |this, window, cx| {
|
||||||
this.delegate
|
this.delegate
|
||||||
.debug_panel
|
.debug_panel
|
||||||
.update(cx, |panel, cx| {
|
.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();
|
.ok();
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ use language::Buffer;
|
||||||
use loaded_source_list::LoadedSourceList;
|
use loaded_source_list::LoadedSourceList;
|
||||||
use module_list::ModuleList;
|
use module_list::ModuleList;
|
||||||
use project::{
|
use project::{
|
||||||
Project,
|
Project, WorktreeId,
|
||||||
debugger::session::{Session, SessionEvent, ThreadId, ThreadStatus},
|
debugger::session::{Session, SessionEvent, ThreadId, ThreadStatus},
|
||||||
terminals::TerminalKind,
|
terminals::TerminalKind,
|
||||||
};
|
};
|
||||||
|
@ -684,6 +684,7 @@ impl RunningState {
|
||||||
scenario: DebugScenario,
|
scenario: DebugScenario,
|
||||||
task_context: TaskContext,
|
task_context: TaskContext,
|
||||||
buffer: Option<Entity<Buffer>>,
|
buffer: Option<Entity<Buffer>>,
|
||||||
|
worktree_id: Option<WorktreeId>,
|
||||||
window: &Window,
|
window: &Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<DebugTaskDefinition>> {
|
) -> Task<Result<DebugTaskDefinition>> {
|
||||||
|
@ -712,7 +713,7 @@ impl RunningState {
|
||||||
this.task_inventory().and_then(|inventory| {
|
this.task_inventory().and_then(|inventory| {
|
||||||
inventory
|
inventory
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.task_template_by_label(buffer, &build, cx)
|
.task_template_by_label(buffer, worktree_id, &build, cx)
|
||||||
})
|
})
|
||||||
})?
|
})?
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -229,10 +229,11 @@ impl Inventory {
|
||||||
pub fn task_template_by_label(
|
pub fn task_template_by_label(
|
||||||
&self,
|
&self,
|
||||||
buffer: Option<Entity<Buffer>>,
|
buffer: Option<Entity<Buffer>>,
|
||||||
|
worktree_id: Option<WorktreeId>,
|
||||||
label: &str,
|
label: &str,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Option<TaskTemplate> {
|
) -> Option<TaskTemplate> {
|
||||||
let (worktree_id, file, language) = buffer
|
let (buffer_worktree_id, file, language) = buffer
|
||||||
.map(|buffer| {
|
.map(|buffer| {
|
||||||
let buffer = buffer.read(cx);
|
let buffer = buffer.read(cx);
|
||||||
let file = buffer.file().cloned();
|
let file = buffer.file().cloned();
|
||||||
|
@ -244,7 +245,7 @@ impl Inventory {
|
||||||
})
|
})
|
||||||
.unwrap_or((None, None, None));
|
.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()
|
.iter()
|
||||||
.find(|(_, template)| template.label == label)
|
.find(|(_, template)| template.label == label)
|
||||||
.map(|val| val.1.clone())
|
.map(|val| val.1.clone())
|
||||||
|
|
|
@ -47,12 +47,35 @@ impl TcpArgumentsTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the attach request information of the debug adapter
|
/// 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 {
|
pub struct AttachRequest {
|
||||||
/// The processId to attach to, if left empty we will show a process picker
|
/// The processId to attach to, if left empty we will show a process picker
|
||||||
pub process_id: Option<u32>,
|
pub process_id: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for AttachRequest {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Helper {
|
||||||
|
process_id: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
/// Represents the launch request information of the debug adapter
|
||||||
#[derive(Deserialize, Serialize, Default, PartialEq, Eq, JsonSchema, Clone, Debug)]
|
#[derive(Deserialize, Serialize, Default, PartialEq, Eq, JsonSchema, Clone, Debug)]
|
||||||
pub struct LaunchRequest {
|
pub struct LaunchRequest {
|
||||||
|
@ -204,7 +227,7 @@ impl DebugTaskFile {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{DebugRequest, LaunchRequest};
|
use crate::{DebugRequest, DebugScenario, LaunchRequest};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_can_deserialize_non_attach_task() {
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue