Skip to content

Commit 8add90d

Browse files
authored
Set up Rust debugger code runner tasks (zed-industries#27571)
## Summary This PR starts the process of adding debug task locators to Zed's debugger system. A task locator is a secondary resolution phase that allows a debug task to run a command before starting a debug session and then uses the output of the run command to configure itself. Locators are most applicable when debugging a compiled language but will be helpful for any language as well. ## Architecture At a high level, this works by adding a debug task queue to `Workspace`. Which add's a debug configuration associated with a `TaskId` whenever a resolved task with a debug config is added to `TaskInventory`'s queue. Then, when the `SpawnInTerminal` task finishes running, it emits its task_id and the result of the ran task. When a ran task exits successfully, `Workspace` tells `Project` to start a debug session using its stored debug config, then `DapStore` queries the `LocatorStore` to configure the debug configuration if it has a valid locator argument. Release Notes: - N/A
1 parent 141a6c3 commit 8add90d

File tree

24 files changed

+440
-167
lines changed

24 files changed

+440
-167
lines changed

crates/dap_adapters/src/go.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use anyhow::bail;
21
use gpui::AsyncApp;
32
use std::{ffi::OsStr, path::PathBuf};
43
use task::DebugTaskDefinition;
@@ -63,9 +62,7 @@ impl DebugAdapter for GoDebugAdapter {
6362
.and_then(|p| p.to_str().map(|p| p.to_string()))
6463
.ok_or(anyhow!("Dlv not found in path"))?;
6564

66-
let Some(tcp_connection) = config.tcp_connection.clone() else {
67-
bail!("Go Debug Adapter expects tcp connection arguments to be provided");
68-
};
65+
let tcp_connection = config.tcp_connection.clone().unwrap_or_default();
6966
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
7067

7168
Ok(DebugAdapterBinary {

crates/dap_adapters/src/javascript.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,7 @@ impl DebugAdapter for JsDebugAdapter {
7878
.ok_or_else(|| anyhow!("Couldn't find JavaScript dap directory"))?
7979
};
8080

81-
let Some(tcp_connection) = config.tcp_connection.clone() else {
82-
anyhow::bail!(
83-
"Javascript Debug Adapter expects tcp connection arguments to be provided"
84-
);
85-
};
81+
let tcp_connection = config.tcp_connection.clone().unwrap_or_default();
8682
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
8783

8884
Ok(DebugAdapterBinary {

crates/dap_adapters/src/php.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use adapters::latest_github_release;
2-
use anyhow::bail;
32
use dap::adapters::TcpArguments;
43
use gpui::AsyncApp;
54
use std::path::PathBuf;
@@ -69,9 +68,7 @@ impl DebugAdapter for PhpDebugAdapter {
6968
.ok_or_else(|| anyhow!("Couldn't find PHP dap directory"))?
7069
};
7170

72-
let Some(tcp_connection) = config.tcp_connection.clone() else {
73-
bail!("PHP Debug Adapter expects tcp connection arguments to be provided");
74-
};
71+
let tcp_connection = config.tcp_connection.clone().unwrap_or_default();
7572
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
7673

7774
Ok(DebugAdapterBinary {

crates/dap_adapters/src/python.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::*;
2-
use anyhow::bail;
32
use dap::DebugRequestType;
43
use gpui::AsyncApp;
54
use std::{ffi::OsStr, path::PathBuf};
@@ -70,9 +69,7 @@ impl DebugAdapter for PythonDebugAdapter {
7069
cx: &mut AsyncApp,
7170
) -> Result<DebugAdapterBinary> {
7271
const BINARY_NAMES: [&str; 3] = ["python3", "python", "py"];
73-
let Some(tcp_connection) = config.tcp_connection.clone() else {
74-
bail!("Python Debug Adapter expects tcp connection arguments to be provided");
75-
};
72+
let tcp_connection = config.tcp_connection.clone().unwrap_or_default();
7673
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
7774

7875
let debugpy_dir = if let Some(user_installed_path) = user_installed_path {

crates/debugger_ui/src/session/inert.rs

+4
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ impl Render for InertState {
159159
}),
160160
tcp_connection: Some(TCPHost::default()),
161161
initialize_args: None,
162+
args: Default::default(),
163+
locator: None,
162164
},
163165
});
164166
} else {
@@ -319,6 +321,8 @@ impl InertState {
319321
adapter: kind,
320322
request: DebugRequestType::Attach(task::AttachConfig { process_id: None }),
321323
initialize_args: None,
324+
args: Default::default(),
325+
locator: None,
322326
tcp_connection: Some(TCPHost::default()),
323327
};
324328

crates/debugger_ui/src/tests/attach_modal.rs

+2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ async fn test_show_attach_modal_and_select_process(
8989
label: "attach example".into(),
9090
initialize_args: None,
9191
tcp_connection: Some(TCPHost::default()),
92+
locator: None,
93+
args: Default::default(),
9294
},
9395
vec![
9496
Candidate {

crates/editor/src/editor.rs

+43-11
Original file line numberDiff line numberDiff line change
@@ -4850,6 +4850,7 @@ impl Editor {
48504850
} else {
48514851
return None;
48524852
};
4853+
48534854
let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
48544855
let action = actions_menu.actions.get(action_ix)?;
48554856
let title = action.label();
@@ -4858,17 +4859,48 @@ impl Editor {
48584859

48594860
match action {
48604861
CodeActionsItem::Task(task_source_kind, resolved_task) => {
4861-
workspace.update(cx, |workspace, cx| {
4862-
workspace::tasks::schedule_resolved_task(
4863-
workspace,
4864-
task_source_kind,
4865-
resolved_task,
4866-
false,
4867-
cx,
4868-
);
4862+
match resolved_task.task_type() {
4863+
task::TaskType::Script => workspace.update(cx, |workspace, cx| {
4864+
workspace::tasks::schedule_resolved_task(
4865+
workspace,
4866+
task_source_kind,
4867+
resolved_task,
4868+
false,
4869+
cx,
4870+
);
48694871

4870-
Some(Task::ready(Ok(())))
4871-
})
4872+
Some(Task::ready(Ok(())))
4873+
}),
4874+
task::TaskType::Debug(debug_args) => {
4875+
if debug_args.locator.is_some() {
4876+
workspace.update(cx, |workspace, cx| {
4877+
workspace::tasks::schedule_resolved_task(
4878+
workspace,
4879+
task_source_kind,
4880+
resolved_task,
4881+
false,
4882+
cx,
4883+
);
4884+
});
4885+
4886+
return Some(Task::ready(Ok(())));
4887+
}
4888+
4889+
if let Some(project) = self.project.as_ref() {
4890+
project
4891+
.update(cx, |project, cx| {
4892+
project.start_debug_session(
4893+
resolved_task.resolved_debug_adapter_config().unwrap(),
4894+
cx,
4895+
)
4896+
})
4897+
.detach_and_log_err(cx);
4898+
Some(Task::ready(Ok(())))
4899+
} else {
4900+
Some(Task::ready(Ok(())))
4901+
}
4902+
}
4903+
}
48724904
}
48734905
CodeActionsItem::CodeAction {
48744906
excerpt_id,
@@ -8600,7 +8632,7 @@ impl Editor {
86008632
let line_len = snapshot.buffer_snapshot.line_len(MultiBufferRow(row));
86018633
let anchor_end = snapshot
86028634
.buffer_snapshot
8603-
.anchor_before(Point::new(row, line_len));
8635+
.anchor_after(Point::new(row, line_len));
86048636

86058637
let bp = self
86068638
.breakpoint_store

crates/languages/src/rust.rs

+47-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::{
1717
path::{Path, PathBuf},
1818
sync::{Arc, LazyLock},
1919
};
20-
use task::{TaskTemplate, TaskTemplates, TaskVariables, VariableName};
20+
use task::{TaskTemplate, TaskTemplates, TaskType, TaskVariables, VariableName};
2121
use util::{fs::remove_matching, maybe, ResultExt};
2222

2323
use crate::language_settings::language_settings;
@@ -574,11 +574,16 @@ impl ContextProvider for RustContextProvider {
574574
.variables
575575
.get(CUSTOM_TARGET_DIR)
576576
.cloned();
577-
let run_task_args = if let Some(package_to_run) = package_to_run {
577+
let run_task_args = if let Some(package_to_run) = package_to_run.clone() {
578578
vec!["run".into(), "-p".into(), package_to_run]
579579
} else {
580580
vec!["run".into()]
581581
};
582+
let debug_task_args = if let Some(package_to_run) = package_to_run {
583+
vec!["build".into(), "-p".into(), package_to_run]
584+
} else {
585+
vec!["build".into()]
586+
};
582587
let mut task_templates = vec![
583588
TaskTemplate {
584589
label: format!(
@@ -620,6 +625,31 @@ impl ContextProvider for RustContextProvider {
620625
cwd: Some("$ZED_DIRNAME".to_owned()),
621626
..TaskTemplate::default()
622627
},
628+
TaskTemplate {
629+
label: format!(
630+
"Debug Test '{}' (package: {})",
631+
RUST_TEST_NAME_TASK_VARIABLE.template_value(),
632+
RUST_PACKAGE_TASK_VARIABLE.template_value(),
633+
),
634+
task_type: TaskType::Debug(task::DebugArgs {
635+
adapter: "LLDB".to_owned(),
636+
request: task::DebugArgsRequest::Launch,
637+
locator: Some("cargo".into()),
638+
tcp_connection: None,
639+
initialize_args: None,
640+
}),
641+
command: "cargo".into(),
642+
args: vec![
643+
"test".into(),
644+
"-p".into(),
645+
RUST_PACKAGE_TASK_VARIABLE.template_value(),
646+
RUST_TEST_NAME_TASK_VARIABLE.template_value(),
647+
"--no-run".into(),
648+
],
649+
tags: vec!["rust-test".to_owned()],
650+
cwd: Some("$ZED_DIRNAME".to_owned()),
651+
..TaskTemplate::default()
652+
},
623653
TaskTemplate {
624654
label: format!(
625655
"Doc test '{}' (package: {})",
@@ -697,6 +727,21 @@ impl ContextProvider for RustContextProvider {
697727
cwd: Some("$ZED_DIRNAME".to_owned()),
698728
..TaskTemplate::default()
699729
},
730+
TaskTemplate {
731+
label: "Debug".into(),
732+
cwd: Some("$ZED_DIRNAME".to_owned()),
733+
command: "cargo".into(),
734+
task_type: TaskType::Debug(task::DebugArgs {
735+
request: task::DebugArgsRequest::Launch,
736+
adapter: "LLDB".to_owned(),
737+
initialize_args: None,
738+
locator: Some("cargo".into()),
739+
tcp_connection: None,
740+
}),
741+
args: debug_task_args,
742+
tags: vec!["rust-main".to_owned()],
743+
..TaskTemplate::default()
744+
},
700745
TaskTemplate {
701746
label: "Clean".into(),
702747
command: "cargo".into(),

crates/project/src/debugger.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@
1414
pub mod breakpoint_store;
1515
pub mod dap_command;
1616
pub mod dap_store;
17+
mod locator_store;
1718
pub mod session;

crates/project/src/debugger/breakpoint_store.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rpc::{
1212
AnyProtoClient, TypedEnvelope,
1313
};
1414
use std::{hash::Hash, ops::Range, path::Path, sync::Arc};
15-
use text::{Point, PointUtf16};
15+
use text::PointUtf16;
1616

1717
use crate::{buffer_store::BufferStore, worktree_store::WorktreeStore, Project, ProjectPath};
1818

@@ -494,7 +494,7 @@ impl BreakpointStore {
494494
this.update(cx, |_, cx| BreakpointsInFile::new(buffer, cx))?;
495495

496496
for bp in bps {
497-
let position = snapshot.anchor_after(Point::new(bp.row, 0));
497+
let position = snapshot.anchor_after(PointUtf16::new(bp.row, 0));
498498
breakpoints_for_file.breakpoints.push((
499499
position,
500500
Breakpoint {

crates/project/src/debugger/dap_store.rs

+36-19
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
use super::{
22
breakpoint_store::BreakpointStore,
3-
// Will need to uncomment this once we implement rpc message handler again
4-
// dap_command::{
5-
// ContinueCommand, DapCommand, DisconnectCommand, NextCommand, PauseCommand, RestartCommand,
6-
// RestartStackFrameCommand, StepBackCommand, StepCommand, StepInCommand, StepOutCommand,
7-
// TerminateCommand, TerminateThreadsCommand, VariablesCommand,
8-
// },
3+
locator_store::LocatorStore,
94
session::{self, Session},
105
};
116
use crate::{debugger, worktree_store::WorktreeStore, ProjectEnvironment};
@@ -87,6 +82,7 @@ pub struct LocalDapStore {
8782
language_registry: Arc<LanguageRegistry>,
8883
debug_adapters: Arc<DapRegistry>,
8984
toolchain_store: Arc<dyn LanguageToolchainStore>,
85+
locator_store: Arc<LocatorStore>,
9086
start_debugging_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
9187
_start_debugging_task: Task<()>,
9288
}
@@ -179,6 +175,7 @@ impl DapStore {
179175
debug_adapters,
180176
start_debugging_tx,
181177
_start_debugging_task,
178+
locator_store: Arc::from(LocatorStore::new()),
182179
next_session_id: Default::default(),
183180
}),
184181
downstream_client: None,
@@ -324,7 +321,7 @@ impl DapStore {
324321

325322
pub fn new_session(
326323
&mut self,
327-
config: DebugAdapterConfig,
324+
mut config: DebugAdapterConfig,
328325
worktree: &Entity<Worktree>,
329326
parent_session: Option<Entity<Session>>,
330327
cx: &mut Context<Self>,
@@ -354,22 +351,39 @@ impl DapStore {
354351
}
355352

356353
let (initialized_tx, initialized_rx) = oneshot::channel();
354+
let locator_store = local_store.locator_store.clone();
355+
let debug_adapters = local_store.debug_adapters.clone();
357356

358-
let start_client_task = Session::local(
359-
self.breakpoint_store.clone(),
360-
session_id,
361-
parent_session,
362-
delegate,
363-
config,
364-
local_store.start_debugging_tx.clone(),
365-
initialized_tx,
366-
local_store.debug_adapters.clone(),
367-
cx,
368-
);
357+
let start_debugging_tx = local_store.start_debugging_tx.clone();
358+
359+
let task = cx.spawn(async move |this, cx| {
360+
if config.locator.is_some() {
361+
locator_store.resolve_debug_config(&mut config).await?;
362+
}
363+
364+
let start_client_task = this.update(cx, |this, cx| {
365+
Session::local(
366+
this.breakpoint_store.clone(),
367+
session_id,
368+
parent_session,
369+
delegate,
370+
config,
371+
start_debugging_tx.clone(),
372+
initialized_tx,
373+
debug_adapters,
374+
cx,
375+
)
376+
})?;
377+
378+
this.update(cx, |_, cx| {
379+
create_new_session(session_id, initialized_rx, start_client_task, cx)
380+
})?
381+
.await
382+
});
369383

370-
let task = create_new_session(session_id, initialized_rx, start_client_task, cx);
371384
(session_id, task)
372385
}
386+
373387
#[cfg(any(test, feature = "test-support"))]
374388
pub fn new_fake_session(
375389
&mut self,
@@ -456,7 +470,10 @@ impl DapStore {
456470
request: DebugRequestDisposition::ReverseRequest(args),
457471
initialize_args: config.initialize_args.clone(),
458472
tcp_connection: config.tcp_connection.clone(),
473+
locator: None,
474+
args: Default::default(),
459475
};
476+
460477
#[cfg(any(test, feature = "test-support"))]
461478
let new_session_task = {
462479
let caps = parent_session.read(cx).capabilities.clone();

0 commit comments

Comments
 (0)