debugger/tasks: Remove TaskType enum (#29208)

Closes #ISSUE

Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Anthony <anthony@zed.dev>
Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
Piotr Osiewicz 2025-04-26 01:44:56 +02:00 committed by GitHub
parent 053fafa90e
commit 67615b968b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 1272 additions and 1114 deletions

View file

@ -1,6 +1,6 @@
use super::{
breakpoint_store::BreakpointStore,
locators::DapLocator,
locators,
session::{self, Session, SessionStateEvent},
};
use crate::{
@ -13,10 +13,12 @@ use anyhow::{Result, anyhow};
use async_trait::async_trait;
use collections::HashMap;
use dap::{
Capabilities, CompletionItem, CompletionsArguments, DapRegistry, EvaluateArguments,
EvaluateArgumentsContext, EvaluateResponse, RunInTerminalRequestArguments, Source,
StackFrameId, StartDebuggingRequestArguments,
adapters::{DapStatus, DebugAdapterBinary, DebugAdapterName, TcpArguments},
Capabilities, CompletionItem, CompletionsArguments, DapRegistry, DebugRequest,
EvaluateArguments, EvaluateArgumentsContext, EvaluateResponse, RunInTerminalRequestArguments,
Source, StackFrameId, StartDebuggingRequestArguments,
adapters::{
DapStatus, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition, TcpArguments,
},
client::SessionId,
messages::Message,
requests::{Completions, Evaluate, Request as _, RunInTerminal, StartDebugging},
@ -49,9 +51,9 @@ use std::{
ffi::OsStr,
net::Ipv4Addr,
path::{Path, PathBuf},
sync::Arc,
sync::{Arc, Once},
};
use task::{DebugTaskDefinition, DebugTaskTemplate};
use task::{DebugScenario, SpawnInTerminal};
use util::ResultExt as _;
use worktree::Worktree;
@ -95,7 +97,6 @@ pub struct LocalDapStore {
environment: Entity<ProjectEnvironment>,
language_registry: Arc<LanguageRegistry>,
toolchain_store: Arc<dyn LanguageToolchainStore>,
locators: HashMap<String, Arc<dyn DapLocator>>,
}
pub struct SshDapStore {
@ -118,9 +119,14 @@ pub struct DapStore {
impl EventEmitter<DapStoreEvent> for DapStore {}
impl DapStore {
pub fn init(client: &AnyProtoClient) {
pub fn init(client: &AnyProtoClient, cx: &mut App) {
static ADD_LOCATORS: Once = Once::new();
client.add_entity_request_handler(Self::handle_run_debug_locator);
client.add_entity_request_handler(Self::handle_get_debug_adapter_binary);
ADD_LOCATORS.call_once(|| {
DapRegistry::global(cx)
.add_locator("cargo".into(), Arc::new(locators::cargo::CargoLocator {}))
});
}
#[expect(clippy::too_many_arguments)]
@ -135,11 +141,6 @@ impl DapStore {
breakpoint_store: Entity<BreakpointStore>,
cx: &mut Context<Self>,
) -> Self {
let locators = HashMap::from_iter([(
"cargo".to_string(),
Arc::new(super::locators::cargo::CargoLocator {}) as _,
)]);
let mode = DapStoreMode::Local(LocalDapStore {
fs,
environment,
@ -147,7 +148,6 @@ impl DapStore {
node_runtime,
toolchain_store,
language_registry,
locators,
});
Self::new(mode, breakpoint_store, worktree_store, cx)
@ -273,7 +273,7 @@ impl DapStore {
DapStoreMode::Ssh(ssh) => {
let request = ssh.upstream_client.request(proto::GetDebugAdapterBinary {
project_id: ssh.upstream_project_id,
task: Some(definition.to_proto()),
definition: Some(definition.to_proto()),
});
let ssh_client = ssh.ssh_client.clone();
@ -326,34 +326,100 @@ impl DapStore {
}
}
pub fn debug_scenario_for_build_task(
&self,
mut build: SpawnInTerminal,
unresoved_label: SharedString,
adapter: SharedString,
cx: &mut App,
) -> Option<DebugScenario> {
build.args = build
.args
.into_iter()
.map(|arg| {
if arg.starts_with("$") {
arg.strip_prefix("$")
.and_then(|arg| build.env.get(arg).map(ToOwned::to_owned))
.unwrap_or_else(|| arg)
} else {
arg
}
})
.collect();
DapRegistry::global(cx)
.locators()
.values()
.find(|locator| locator.accepts(&build))
.map(|_| DebugScenario {
adapter,
label: format!("Debug `{}`", build.label).into(),
build: Some(unresoved_label),
request: None,
initialize_args: None,
tcp_connection: None,
stop_on_entry: None,
})
}
pub fn run_debug_locator(
&mut self,
template: DebugTaskTemplate,
mut build_command: SpawnInTerminal,
cx: &mut Context<Self>,
) -> Task<Result<DebugTaskDefinition>> {
let Some(locator_name) = template.locator else {
return Task::ready(Ok(template.definition));
};
) -> Task<Result<DebugRequest>> {
match &self.mode {
DapStoreMode::Local(local) => {
if let Some(locator) = local.locators.get(&locator_name).cloned() {
cx.background_spawn(
async move { locator.run_locator(template.definition).await },
)
DapStoreMode::Local(_) => {
// Pre-resolve args with existing environment.
build_command.args = build_command
.args
.into_iter()
.map(|arg| {
if arg.starts_with("$") {
arg.strip_prefix("$")
.and_then(|arg| build_command.env.get(arg).map(ToOwned::to_owned))
.unwrap_or_else(|| arg)
} else {
arg
}
})
.collect();
let locators = DapRegistry::global(cx)
.locators()
.values()
.filter(|locator| locator.accepts(&build_command))
.cloned()
.collect::<Vec<_>>();
if !locators.is_empty() {
cx.background_spawn(async move {
for locator in locators {
let result = locator
.run(build_command.clone())
.await
.log_with_level(log::Level::Error);
if let Some(result) = result {
return Ok(result);
}
}
Err(anyhow!(
"None of the locators for task `{}` completed successfully",
build_command.label
))
})
} else {
Task::ready(Err(anyhow!("Couldn't find locator {}", locator_name)))
Task::ready(Err(anyhow!(
"Couldn't find any locator for task `{}`. Specify the `attach` or `launch` arguments in your debug scenario definition",
build_command.label
)))
}
}
DapStoreMode::Ssh(ssh) => {
let request = ssh.upstream_client.request(proto::RunDebugLocator {
let request = ssh.upstream_client.request(proto::RunDebugLocators {
project_id: ssh.upstream_project_id,
locator: locator_name,
task: Some(template.definition.to_proto()),
build_command: Some(build_command.to_proto()),
});
cx.background_spawn(async move {
let response = request.await?;
DebugTaskDefinition::from_proto(response)
DebugRequest::from_proto(response)
})
}
DapStoreMode::Collab => {
@ -943,22 +1009,19 @@ impl DapStore {
async fn handle_run_debug_locator(
this: Entity<Self>,
envelope: TypedEnvelope<proto::RunDebugLocator>,
envelope: TypedEnvelope<proto::RunDebugLocators>,
mut cx: AsyncApp,
) -> Result<proto::DebugTaskDefinition> {
let template = DebugTaskTemplate {
locator: Some(envelope.payload.locator),
definition: DebugTaskDefinition::from_proto(
envelope
.payload
.task
.ok_or_else(|| anyhow!("missing definition"))?,
)?,
};
let definition = this
.update(&mut cx, |this, cx| this.run_debug_locator(template, cx))?
) -> Result<proto::DebugRequest> {
let task = envelope
.payload
.build_command
.ok_or_else(|| anyhow!("missing definition"))?;
let build_task = SpawnInTerminal::from_proto(task);
let request = this
.update(&mut cx, |this, cx| this.run_debug_locator(build_task, cx))?
.await?;
Ok(definition.to_proto())
Ok(request.to_proto())
}
async fn handle_get_debug_adapter_binary(
@ -969,7 +1032,7 @@ impl DapStore {
let definition = DebugTaskDefinition::from_proto(
envelope
.payload
.task
.definition
.ok_or_else(|| anyhow!("missing definition"))?,
)?;
let binary = this

View file

@ -1,34 +0,0 @@
use anyhow::{Result, anyhow};
use cargo::CargoLocator;
use collections::HashMap;
use gpui::SharedString;
use locators::DapLocator;
use task::{DebugTaskDefinition, DebugTaskTemplate};
mod cargo;
pub mod locators;
pub(super) struct LocatorStore {
locators: HashMap<SharedString, Box<dyn DapLocator>>,
}
impl LocatorStore {
pub(super) fn new() -> Self {
Self { locators }
}
pub(super) async fn resolve_debug_config(
&self,
template: DebugTaskTemplate,
) -> Result<DebugTaskDefinition> {
let Some(locator_name) = &template.locator else {
return Ok(template.definition);
};
if let Some(locator) = self.locators.get(locator_name as &str) {
locator.run_locator(template.definition).await
} else {
Err(anyhow!("Couldn't find locator {}", locator_name))
}
}
}

View file

@ -1,10 +1 @@
use anyhow::Result;
use async_trait::async_trait;
use task::DebugTaskDefinition;
pub(crate) mod cargo;
#[async_trait]
pub(super) trait DapLocator: Send + Sync {
async fn run_locator(&self, debug_config: DebugTaskDefinition) -> Result<DebugTaskDefinition>;
}

View file

@ -1,12 +1,12 @@
use super::DapLocator;
use anyhow::{Result, anyhow};
use async_trait::async_trait;
use dap::{DapLocator, DebugRequest};
use serde_json::Value;
use smol::{
io::AsyncReadExt,
process::{Command, Stdio},
};
use task::DebugTaskDefinition;
use task::SpawnInTerminal;
pub(crate) struct CargoLocator;
@ -37,26 +37,31 @@ async fn find_best_executable(executables: &[String], test_name: &str) -> Option
}
#[async_trait]
impl DapLocator for CargoLocator {
async fn run_locator(
&self,
mut debug_config: DebugTaskDefinition,
) -> Result<DebugTaskDefinition> {
let Some(launch_config) = (match &mut debug_config.request {
task::DebugRequest::Launch(launch_config) => Some(launch_config),
_ => None,
}) else {
return Err(anyhow!("Couldn't get launch config in locator"));
fn accepts(&self, build_config: &SpawnInTerminal) -> bool {
if build_config.command != "cargo" {
return false;
}
let Some(command) = build_config.args.first().map(|s| s.as_str()) else {
return false;
};
if matches!(command, "check" | "run") {
return false;
}
!matches!(command, "test" | "bench")
|| build_config.args.iter().any(|arg| arg == "--no-run")
}
let Some(cwd) = launch_config.cwd.clone() else {
async fn run(&self, build_config: SpawnInTerminal) -> Result<DebugRequest> {
let Some(cwd) = build_config.cwd.clone() else {
return Err(anyhow!(
"Couldn't get cwd from debug config which is needed for locators"
));
};
let mut child = Command::new("cargo")
.args(&launch_config.args)
.args(&build_config.args)
.arg("--message-format=json")
.envs(build_config.env.iter().map(|(k, v)| (k.clone(), v.clone())))
.current_dir(cwd)
.stdout(Stdio::piped())
.spawn()?;
@ -85,19 +90,16 @@ impl DapLocator for CargoLocator {
return Err(anyhow!("Couldn't get executable in cargo locator"));
};
let is_test = launch_config
.args
.first()
.map_or(false, |arg| arg == "test");
let is_test = build_config.args.first().map_or(false, |arg| arg == "test");
let mut test_name = None;
if is_test {
if let Some(package_index) = launch_config
if let Some(package_index) = build_config
.args
.iter()
.position(|arg| arg == "-p" || arg == "--package")
{
test_name = launch_config
test_name = build_config
.args
.get(package_index + 2)
.filter(|name| !name.starts_with("--"))
@ -116,12 +118,17 @@ impl DapLocator for CargoLocator {
return Err(anyhow!("Couldn't get executable in cargo locator"));
};
launch_config.program = executable;
let args = test_name.into_iter().collect();
launch_config.args.clear();
if let Some(test_name) = test_name {
launch_config.args.push(test_name);
}
Ok(debug_config)
Ok(DebugRequest::Launch(task::LaunchRequest {
program: executable,
cwd: build_config.cwd.clone(),
args,
env: build_config
.env
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect(),
}))
}
}

View file

@ -12,7 +12,7 @@ use super::dap_command::{
use super::dap_store::DapStore;
use anyhow::{Context as _, Result, anyhow};
use collections::{HashMap, HashSet, IndexMap, IndexSet};
use dap::adapters::DebugAdapterBinary;
use dap::adapters::{DebugAdapterBinary, DebugTaskDefinition};
use dap::messages::Response;
use dap::{
Capabilities, ContinueArguments, EvaluateArgumentsContext, Module, Source, StackFrameId,
@ -42,7 +42,6 @@ use std::{
path::Path,
sync::Arc,
};
use task::DebugTaskDefinition;
use text::{PointUtf16, ToPointUtf16};
use util::{ResultExt, merge_json_value_into};
use worktree::Worktree;
@ -125,7 +124,6 @@ enum Mode {
pub struct LocalMode {
client: Arc<DebugAdapterClient>,
binary: DebugAdapterBinary,
root_binary: Option<Arc<DebugAdapterBinary>>,
pub(crate) breakpoint_store: Entity<BreakpointStore>,
tmp_breakpoint: Option<SourceBreakpoint>,
worktree: WeakEntity<Worktree>,
@ -160,12 +158,6 @@ impl LocalMode {
messages_tx.unbounded_send(message).ok();
});
let root_binary = if let Some(parent_session) = parent_session.as_ref() {
Some(parent_session.read_with(&cx, |session, _| session.root_binary().clone())?)
} else {
None
};
let client = Arc::new(
if let Some(client) = parent_session
.and_then(|session| cx.update(|cx| session.read(cx).adapter_client()).ok())
@ -186,7 +178,6 @@ impl LocalMode {
breakpoint_store,
worktree,
tmp_breakpoint: None,
root_binary,
binary,
})
}
@ -834,19 +825,6 @@ impl Session {
&self.capabilities
}
pub(crate) fn root_binary(&self) -> Arc<DebugAdapterBinary> {
match &self.mode {
Mode::Building => {
// todo(debugger): Implement root_binary for building mode
unimplemented!()
}
Mode::Running(running) => running
.root_binary
.clone()
.unwrap_or_else(|| Arc::new(running.binary.clone())),
}
}
pub fn binary(&self) -> &DebugAdapterBinary {
let Mode::Running(local_mode) = &self.mode else {
panic!("Session is not local");
@ -855,10 +833,10 @@ impl Session {
}
pub fn adapter_name(&self) -> SharedString {
self.definition.adapter.clone().into()
self.definition.adapter.clone()
}
pub fn label(&self) -> String {
pub fn label(&self) -> SharedString {
self.definition.label.clone()
}
@ -889,7 +867,7 @@ impl Session {
}
pub(super) fn request_initialize(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
let adapter_id = self.definition.adapter.clone();
let adapter_id = String::from(self.definition.adapter.clone());
let request = Initialize { adapter_id };
match &self.mode {
Mode::Running(local_mode) => {