Skip to content

Commit bc39870

Browse files
Anthony-EidRemcoSmitsDev
authored andcommitted
debugger: Allow users to include PickProcessId in debug tasks and resolve Pid (#42913)
Closes #33286 This PR adds support for Zed's `$ZED_PICK_PID` command in debug configurations, which allows users to select a process to attach to at debug time. When this variable is present in a debug configuration, Zed automatically opens a process picker modal. Follow up for this will be integrating this variable in the task system instead of just the debug configuration system. Release Notes: - Added `$ZED_PICK_PID` variable for debug configurations, allowing users to select which process to attach the debugger to at runtime --------- Co-authored-by: Remco Smits <djsmits12@gmail.com>
1 parent ebdc848 commit bc39870

File tree

6 files changed

+410
-87
lines changed

6 files changed

+410
-87
lines changed

crates/debugger_ui/src/attach_modal.rs

Lines changed: 103 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use dap::{DapRegistry, DebugRequest};
2+
use futures::channel::oneshot;
23
use fuzzy::{StringMatch, StringMatchCandidate};
34
use gpui::{AppContext, DismissEvent, Entity, EventEmitter, Focusable, Render, Task};
45
use gpui::{Subscription, WeakEntity};
@@ -9,6 +10,7 @@ use task::ZedDebugConfig;
910
use util::debug_panic;
1011

1112
use std::sync::Arc;
13+
1214
use sysinfo::{ProcessRefreshKind, RefreshKind, System, UpdateKind};
1315
use ui::{Context, Tooltip, prelude::*};
1416
use ui::{ListItem, ListItemSpacing};
@@ -23,25 +25,30 @@ pub(super) struct Candidate {
2325
pub(super) command: Vec<String>,
2426
}
2527

28+
pub(crate) enum ModalIntent {
29+
ResolveProcessId(Option<oneshot::Sender<Option<i32>>>),
30+
AttachToProcess(ZedDebugConfig),
31+
}
32+
2633
pub(crate) struct AttachModalDelegate {
2734
selected_index: usize,
2835
matches: Vec<StringMatch>,
2936
placeholder_text: Arc<str>,
30-
pub(crate) definition: ZedDebugConfig,
37+
pub(crate) intent: ModalIntent,
3138
workspace: WeakEntity<Workspace>,
3239
candidates: Arc<[Candidate]>,
3340
}
3441

3542
impl AttachModalDelegate {
3643
fn new(
3744
workspace: WeakEntity<Workspace>,
38-
definition: ZedDebugConfig,
45+
intent: ModalIntent,
3946
candidates: Arc<[Candidate]>,
4047
) -> Self {
4148
Self {
4249
workspace,
43-
definition,
4450
candidates,
51+
intent,
4552
selected_index: 0,
4653
matches: Vec::default(),
4754
placeholder_text: Arc::from("Select the process you want to attach the debugger to"),
@@ -55,8 +62,8 @@ pub struct AttachModal {
5562
}
5663

5764
impl AttachModal {
58-
pub fn new(
59-
definition: ZedDebugConfig,
65+
pub(crate) fn new(
66+
intent: ModalIntent,
6067
workspace: WeakEntity<Workspace>,
6168
project: Entity<Project>,
6269
modal: bool,
@@ -65,7 +72,7 @@ impl AttachModal {
6572
) -> Self {
6673
let processes_task = get_processes_for_project(&project, cx);
6774

68-
let modal = Self::with_processes(workspace, definition, Arc::new([]), modal, window, cx);
75+
let modal = Self::with_processes(workspace, Arc::new([]), modal, intent, window, cx);
6976

7077
cx.spawn_in(window, async move |this, cx| {
7178
let processes = processes_task.await;
@@ -84,15 +91,15 @@ impl AttachModal {
8491

8592
pub(super) fn with_processes(
8693
workspace: WeakEntity<Workspace>,
87-
definition: ZedDebugConfig,
8894
processes: Arc<[Candidate]>,
8995
modal: bool,
96+
intent: ModalIntent,
9097
window: &mut Window,
9198
cx: &mut Context<Self>,
9299
) -> Self {
93100
let picker = cx.new(|cx| {
94101
Picker::uniform_list(
95-
AttachModalDelegate::new(workspace, definition, processes),
102+
AttachModalDelegate::new(workspace, intent, processes),
96103
window,
97104
cx,
98105
)
@@ -207,7 +214,7 @@ impl PickerDelegate for AttachModalDelegate {
207214
})
208215
}
209216

210-
fn confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
217+
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
211218
let candidate = self
212219
.matches
213220
.get(self.selected_index())
@@ -216,69 +223,86 @@ impl PickerDelegate for AttachModalDelegate {
216223
self.candidates.get(ix)
217224
});
218225

219-
let Some(candidate) = candidate else {
220-
return cx.emit(DismissEvent);
221-
};
226+
match &mut self.intent {
227+
ModalIntent::ResolveProcessId(sender) => {
228+
cx.emit(DismissEvent);
222229

223-
match &mut self.definition.request {
224-
DebugRequest::Attach(config) => {
225-
config.process_id = Some(candidate.pid);
226-
}
227-
DebugRequest::Launch(_) => {
228-
debug_panic!("Debugger attach modal used on launch debug config");
229-
return;
230+
if let Some(sender) = sender.take() {
231+
sender
232+
.send(candidate.map(|candidate| candidate.pid as i32))
233+
.ok();
234+
}
230235
}
231-
}
232-
233-
let workspace = self.workspace.clone();
234-
let Some(panel) = workspace
235-
.update(cx, |workspace, cx| workspace.panel::<DebugPanel>(cx))
236-
.ok()
237-
.flatten()
238-
else {
239-
return;
240-
};
241-
242-
if secondary {
243-
// let Some(id) = worktree_id else { return };
244-
// cx.spawn_in(window, async move |_, cx| {
245-
// panel
246-
// .update_in(cx, |debug_panel, window, cx| {
247-
// debug_panel.save_scenario(&debug_scenario, id, window, cx)
248-
// })?
249-
// .await?;
250-
// anyhow::Ok(())
251-
// })
252-
// .detach_and_log_err(cx);
253-
}
254-
let Some(adapter) = cx.read_global::<DapRegistry, _>(|registry, _| {
255-
registry.adapter(&self.definition.adapter)
256-
}) else {
257-
return;
258-
};
259-
260-
let definition = self.definition.clone();
261-
cx.spawn_in(window, async move |this, cx| {
262-
let Ok(scenario) = adapter.config_from_zed_format(definition).await else {
263-
return;
264-
};
236+
ModalIntent::AttachToProcess(definition) => {
237+
let Some(candidate) = candidate else {
238+
return cx.emit(DismissEvent);
239+
};
240+
241+
match &mut definition.request {
242+
DebugRequest::Attach(config) => {
243+
config.process_id = Some(candidate.pid);
244+
}
245+
DebugRequest::Launch(_) => {
246+
debug_panic!("Debugger attach modal used on launch debug config");
247+
return;
248+
}
249+
}
265250

266-
panel
267-
.update_in(cx, |panel, window, cx| {
268-
panel.start_session(scenario, Default::default(), None, None, window, cx);
251+
let workspace = self.workspace.clone();
252+
let Some(panel) = workspace
253+
.update(cx, |workspace, cx| workspace.panel::<DebugPanel>(cx))
254+
.ok()
255+
.flatten()
256+
else {
257+
return;
258+
};
259+
260+
let Some(adapter) = cx.read_global::<DapRegistry, _>(|registry, _| {
261+
registry.adapter(&definition.adapter)
262+
}) else {
263+
return;
264+
};
265+
266+
let definition = definition.clone();
267+
cx.spawn_in(window, async move |this, cx| {
268+
let Ok(scenario) = adapter.config_from_zed_format(definition).await else {
269+
return;
270+
};
271+
272+
panel
273+
.update_in(cx, |panel, window, cx| {
274+
panel.start_session(
275+
scenario,
276+
Default::default(),
277+
None,
278+
None,
279+
window,
280+
cx,
281+
);
282+
})
283+
.ok();
284+
this.update(cx, |_, cx| {
285+
cx.emit(DismissEvent);
286+
})
287+
.ok();
269288
})
270-
.ok();
271-
this.update(cx, |_, cx| {
272-
cx.emit(DismissEvent);
273-
})
274-
.ok();
275-
})
276-
.detach();
289+
.detach();
290+
}
291+
}
277292
}
278293

279294
fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
280295
self.selected_index = 0;
281296

297+
match &mut self.intent {
298+
ModalIntent::ResolveProcessId(sender) => {
299+
if let Some(sender) = sender.take() {
300+
sender.send(None).ok();
301+
}
302+
}
303+
ModalIntent::AttachToProcess(_) => {}
304+
}
305+
282306
cx.emit(DismissEvent);
283307
}
284308

@@ -338,7 +362,7 @@ fn get_processes_for_project(project: &Entity<Project>, cx: &mut App) -> Task<Ar
338362

339363
if let Some(remote_client) = project.remote_client() {
340364
let proto_client = remote_client.read(cx).proto_client();
341-
cx.spawn(async move |_cx| {
365+
cx.background_spawn(async move {
342366
let response = proto_client
343367
.request(proto::GetProcesses {
344368
project_id: proto::REMOTE_SERVER_PROJECT_ID,
@@ -389,8 +413,21 @@ fn get_processes_for_project(project: &Entity<Project>, cx: &mut App) -> Task<Ar
389413
}
390414
}
391415

392-
#[cfg(any(test, feature = "test-support"))]
393-
pub(crate) fn _process_names(modal: &AttachModal, cx: &mut Context<AttachModal>) -> Vec<String> {
416+
#[cfg(test)]
417+
pub(crate) fn set_candidates(
418+
modal: &AttachModal,
419+
candidates: Arc<[Candidate]>,
420+
window: &mut Window,
421+
cx: &mut Context<AttachModal>,
422+
) {
423+
modal.picker.update(cx, |picker, cx| {
424+
picker.delegate.candidates = candidates;
425+
picker.refresh(window, cx);
426+
});
427+
}
428+
429+
#[cfg(test)]
430+
pub(crate) fn process_names(modal: &AttachModal, cx: &mut Context<AttachModal>) -> Vec<String> {
394431
modal.picker.read_with(cx, |picker, _| {
395432
picker
396433
.delegate

crates/debugger_ui/src/new_process_modal.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ use ui::{
2929
KeyBinding, ListItem, ListItemSpacing, ToggleButtonGroup, ToggleButtonSimple, ToggleState,
3030
Tooltip, prelude::*,
3131
};
32-
use util::{ResultExt, rel_path::RelPath, shell::ShellKind};
32+
use util::{ResultExt, debug_panic, rel_path::RelPath, shell::ShellKind};
3333
use workspace::{ModalView, Workspace, notifications::DetachAndPromptErr, pane};
3434

35-
use crate::{attach_modal::AttachModal, debugger_panel::DebugPanel};
35+
use crate::{
36+
attach_modal::{AttachModal, ModalIntent},
37+
debugger_panel::DebugPanel,
38+
};
3639

3740
pub(super) struct NewProcessModal {
3841
workspace: WeakEntity<Workspace>,
@@ -395,8 +398,15 @@ impl NewProcessModal {
395398

396399
this.attach_picker.update(cx, |this, cx| {
397400
this.picker.update(cx, |this, cx| {
398-
this.delegate.definition.adapter = adapter.0.clone();
399-
this.focus(window, cx);
401+
match &mut this.delegate.intent {
402+
ModalIntent::AttachToProcess(definition) => {
403+
definition.adapter = adapter.0.clone();
404+
this.focus(window, cx);
405+
},
406+
ModalIntent::ResolveProcessId(_) => {
407+
debug_panic!("Attach picker attempted to update config when in resolve Process ID mode");
408+
}
409+
}
400410
})
401411
});
402412
}
@@ -942,7 +952,14 @@ impl AttachMode {
942952
stop_on_entry: Some(false),
943953
};
944954
let attach_picker = cx.new(|cx| {
945-
let modal = AttachModal::new(definition.clone(), workspace, project, false, window, cx);
955+
let modal = AttachModal::new(
956+
ModalIntent::AttachToProcess(definition.clone()),
957+
workspace,
958+
project,
959+
false,
960+
window,
961+
cx,
962+
);
946963
window.focus(&modal.focus_handle(cx));
947964

948965
modal

0 commit comments

Comments
 (0)