debugger: Fix up Rust test tasks definitions (#30232)

Closes #ISSUE

Release Notes:

- N/A

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
Piotr Osiewicz 2025-05-08 14:39:56 +02:00 committed by GitHub
parent 3cc8850a58
commit ee56706d15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 53 additions and 27 deletions

View File

@ -78,6 +78,11 @@ impl From<DebugAdapterName> for SharedString {
name.0 name.0
} }
} }
impl From<SharedString> for DebugAdapterName {
fn from(name: SharedString) -> Self {
DebugAdapterName(name)
}
}
impl<'a> From<&'a str> for DebugAdapterName { impl<'a> From<&'a str> for DebugAdapterName {
fn from(str: &'a str) -> DebugAdapterName { fn from(str: &'a str) -> DebugAdapterName {

View File

@ -16,7 +16,12 @@ use std::{collections::BTreeMap, sync::Arc};
pub trait DapLocator: Send + Sync { pub trait DapLocator: Send + Sync {
fn name(&self) -> SharedString; fn name(&self) -> SharedString;
/// Determines whether this locator can generate debug target for given task. /// Determines whether this locator can generate debug target for given task.
fn create_scenario(&self, build_config: &TaskTemplate, adapter: &str) -> Option<DebugScenario>; fn create_scenario(
&self,
build_config: &TaskTemplate,
resolved_label: &str,
adapter: DebugAdapterName,
) -> Option<DebugScenario>;
async fn run(&self, build_config: SpawnInTerminal) -> Result<DebugRequest>; async fn run(&self, build_config: SpawnInTerminal) -> Result<DebugRequest>;
} }

View File

@ -731,19 +731,30 @@ impl RunningState {
(task, None) (task, None)
} }
}; };
let Some(task) = task.resolve_task("debug-build-task", &task_context) else {
anyhow::bail!("Could not resolve task variables within a debug scenario");
};
let locator_name = if let Some(locator_name) = locator_name { let locator_name = if let Some(locator_name) = locator_name {
debug_assert!(request.is_none()); debug_assert!(request.is_none());
Some(locator_name) Some(locator_name)
} else if request.is_none() { } else if request.is_none() {
dap_store dap_store
.update(cx, |this, cx| { .update(cx, |this, cx| {
this.debug_scenario_for_build_task(task.clone(), adapter.clone(), cx) this.debug_scenario_for_build_task(
.and_then(|scenario| match scenario.build { task.original_task().clone(),
adapter.clone().into(),
task.display_label().to_owned().into(),
cx,
)
.and_then(|scenario| {
match scenario.build {
Some(BuildTaskDefinition::Template { Some(BuildTaskDefinition::Template {
locator_name, .. locator_name, ..
}) => locator_name, }) => locator_name,
_ => None, _ => None,
}) }
})
}) })
.ok() .ok()
.flatten() .flatten()
@ -751,10 +762,6 @@ impl RunningState {
None None
}; };
let Some(task) = task.resolve_task("debug-build-task", &task_context) else {
anyhow::bail!("Could not resolve task variables within a debug scenario");
};
let builder = ShellBuilder::new(is_local, &task.resolved.shell); let builder = ShellBuilder::new(is_local, &task.resolved.shell);
let command_label = builder.command_label(&task.resolved.command_label); let command_label = builder.command_label(&task.resolved.command_label);
let (command, args) = let (command, args) =

View File

@ -5276,7 +5276,8 @@ impl Editor {
if let Some(scenario) = this if let Some(scenario) = this
.debug_scenario_for_build_task( .debug_scenario_for_build_task(
task.original_task().clone(), task.original_task().clone(),
debug_adapter.clone(), debug_adapter.clone().into(),
task.display_label().to_owned().into(),
cx, cx,
) )
{ {

View File

@ -685,8 +685,8 @@ impl ContextProvider for RustContextProvider {
"-p".into(), "-p".into(),
RUST_PACKAGE_TASK_VARIABLE.template_value(), RUST_PACKAGE_TASK_VARIABLE.template_value(),
"--".into(), "--".into(),
RUST_TEST_NAME_TASK_VARIABLE.template_value(),
"--nocapture".into(), "--nocapture".into(),
RUST_TEST_NAME_TASK_VARIABLE.template_value(),
], ],
tags: vec!["rust-test".to_owned()], tags: vec!["rust-test".to_owned()],
cwd: Some("$ZED_DIRNAME".to_owned()), cwd: Some("$ZED_DIRNAME".to_owned()),

View File

@ -283,13 +283,14 @@ impl DapStore {
pub fn debug_scenario_for_build_task( pub fn debug_scenario_for_build_task(
&self, &self,
build: TaskTemplate, build: TaskTemplate,
adapter: SharedString, adapter: DebugAdapterName,
label: SharedString,
cx: &mut App, cx: &mut App,
) -> Option<DebugScenario> { ) -> Option<DebugScenario> {
DapRegistry::global(cx) DapRegistry::global(cx)
.locators() .locators()
.values() .values()
.find_map(|locator| locator.create_scenario(&build, &adapter)) .find_map(|locator| locator.create_scenario(&build, &label, adapter.clone()))
} }
pub fn run_debug_locator( pub fn run_debug_locator(

View File

@ -1,6 +1,6 @@
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use async_trait::async_trait; use async_trait::async_trait;
use dap::{DapLocator, DebugRequest}; use dap::{DapLocator, DebugRequest, adapters::DebugAdapterName};
use gpui::SharedString; use gpui::SharedString;
use serde_json::Value; use serde_json::Value;
use smol::{ use smol::{
@ -41,7 +41,12 @@ impl DapLocator for CargoLocator {
fn name(&self) -> SharedString { fn name(&self) -> SharedString {
SharedString::new_static("rust-cargo-locator") SharedString::new_static("rust-cargo-locator")
} }
fn create_scenario(&self, build_config: &TaskTemplate, adapter: &str) -> Option<DebugScenario> { fn create_scenario(
&self,
build_config: &TaskTemplate,
resolved_label: &str,
adapter: DebugAdapterName,
) -> Option<DebugScenario> {
if build_config.command != "cargo" { if build_config.command != "cargo" {
return None; return None;
} }
@ -70,9 +75,9 @@ impl DapLocator for CargoLocator {
} }
_ => {} _ => {}
} }
let label = format!("Debug `{}`", build_config.label); let label = format!("Debug `{resolved_label}`");
Some(DebugScenario { Some(DebugScenario {
adapter: adapter.to_owned().into(), adapter: adapter.0,
label: SharedString::from(label), label: SharedString::from(label),
build: Some(BuildTaskDefinition::Template { build: Some(BuildTaskDefinition::Template {
task_template, task_template,
@ -136,20 +141,20 @@ impl DapLocator for CargoLocator {
let mut test_name = None; let mut test_name = None;
if is_test { if is_test {
if let Some(package_index) = build_config test_name = build_config
.args .args
.iter() .iter()
.position(|arg| arg == "-p" || arg == "--package") .rev()
{ .take_while(|name| "--" != name.as_str())
test_name = build_config .find(|name| !name.starts_with("-"))
.args .cloned();
.get(package_index + 2)
.filter(|name| !name.starts_with("--"))
.cloned();
}
} }
let executable = { let executable = {
if let Some(ref name) = test_name { if let Some(ref name) = test_name.as_ref().and_then(|name| {
name.strip_prefix('$')
.map(|name| build_config.env.get(name))
.unwrap_or(Some(name))
}) {
find_best_executable(&executables, &name).await find_best_executable(&executables, &name).await
} else { } else {
None None

View File

@ -663,7 +663,9 @@ impl LspCommand for GetLspRunnables {
// We cannot escape all shell arguments unconditionally, as we use this for ssh commands, which may involve paths starting with `~`. // We cannot escape all shell arguments unconditionally, as we use this for ssh commands, which may involve paths starting with `~`.
// That bit is not auto-expanded when using single quotes. // That bit is not auto-expanded when using single quotes.
// Escape extra cargo args unconditionally as those are unlikely to contain `~`. // Escape extra cargo args unconditionally as those are unlikely to contain `~`.
.map(|extra_arg| format!("'{extra_arg}'")), .flat_map(|extra_arg| {
shlex::try_quote(&extra_arg).ok().map(|s| s.to_string())
}),
); );
} }
} }