From 98d001bad57c89728767680f09a53213654a5279 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 15 Apr 2025 14:13:19 +0200 Subject: [PATCH] debugger: Always show process list in attach (#28685) Closes #ISSUE Release Notes: - N/A --- Cargo.lock | 2 - crates/dap/Cargo.toml | 1 - crates/dap/src/adapters.rs | 15 +-- crates/dap/src/registry.rs | 2 +- crates/dap_adapters/Cargo.toml | 1 - crates/dap_adapters/src/dap_adapters.rs | 2 +- crates/dap_adapters/src/javascript.rs | 17 +-- crates/debugger_ui/src/attach_modal.rs | 24 ++--- crates/debugger_ui/src/new_session_modal.rs | 103 +++++++++---------- crates/debugger_ui/src/tests/attach_modal.rs | 25 ++++- 10 files changed, 79 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c9513970fcebb10d0599ee464ecc55fae205aa4..9ee0f5c3ea60322e27025b86c658424ae850bff0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4001,7 +4001,6 @@ dependencies = [ "node_runtime", "parking_lot", "paths", - "regex", "schemars", "serde", "serde_json", @@ -4033,7 +4032,6 @@ dependencies = [ "gpui", "language", "paths", - "regex", "serde", "serde_json", "task", diff --git a/crates/dap/Cargo.toml b/crates/dap/Cargo.toml index 5a44d6b9468c62df4b410dbe7cc2d8302c75adf8..0fdd19c93edab1c8736cdbfa6312a57ac7c7212d 100644 --- a/crates/dap/Cargo.toml +++ b/crates/dap/Cargo.toml @@ -39,7 +39,6 @@ log.workspace = true node_runtime.workspace = true parking_lot.workspace = true paths.workspace = true -regex.workspace = true schemars.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/crates/dap/src/adapters.rs b/crates/dap/src/adapters.rs index 175fdd8c2dbfff1237cc2d910837fc770c43d222..5f1083d0d6a346d3607e599678dcc2171a4c63e0 100644 --- a/crates/dap/src/adapters.rs +++ b/crates/dap/src/adapters.rs @@ -20,7 +20,7 @@ use std::{ net::Ipv4Addr, ops::Deref, path::PathBuf, - sync::{Arc, LazyLock}, + sync::Arc, }; use task::{DebugAdapterConfig, DebugTaskDefinition}; use util::ResultExt; @@ -291,14 +291,7 @@ pub trait DebugAdapter: 'static + Send + Sync { /// Should return base configuration to make the debug adapter work fn request_args(&self, config: &DebugTaskDefinition) -> Value; - - fn attach_processes_filter(&self) -> regex::Regex { - EMPTY_REGEX.clone() - } } - -static EMPTY_REGEX: LazyLock = - LazyLock::new(|| regex::Regex::new("").expect("Regex compilation to succeed")); #[cfg(any(test, feature = "test-support"))] pub struct FakeAdapter {} @@ -375,10 +368,4 @@ impl DebugAdapter for FakeAdapter { }, }) } - - fn attach_processes_filter(&self) -> regex::Regex { - static REGEX: LazyLock = - LazyLock::new(|| regex::Regex::new("^fake-binary").unwrap()); - REGEX.clone() - } } diff --git a/crates/dap/src/registry.rs b/crates/dap/src/registry.rs index 2a3f0869fbcaba6564f1d05289c9f300641eea0c..b6c8efea40cecb5f48f3f7bddb114e6a87068e24 100644 --- a/crates/dap/src/registry.rs +++ b/crates/dap/src/registry.rs @@ -8,7 +8,7 @@ struct DapRegistryState { adapters: BTreeMap>, } -#[derive(Default)] +#[derive(Clone, Default)] /// Stores available debug adapters. pub struct DapRegistry(Arc>); diff --git a/crates/dap_adapters/Cargo.toml b/crates/dap_adapters/Cargo.toml index 40ca634a26a767e4f3948c4fb0a042ff0bcd1e34..0a11724aa2a130f215b3584daf18894078b3c509 100644 --- a/crates/dap_adapters/Cargo.toml +++ b/crates/dap_adapters/Cargo.toml @@ -27,7 +27,6 @@ dap.workspace = true gpui.workspace = true language.workspace = true paths.workspace = true -regex.workspace = true serde.workspace = true serde_json.workspace = true task.workspace = true diff --git a/crates/dap_adapters/src/dap_adapters.rs b/crates/dap_adapters/src/dap_adapters.rs index 320b5336fcd3761593232c8fb6e7c4f48def8961..f6c6f7844c853f3d76acb471d48150505aa666d6 100644 --- a/crates/dap_adapters/src/dap_adapters.rs +++ b/crates/dap_adapters/src/dap_adapters.rs @@ -31,7 +31,7 @@ pub fn init(registry: Arc) { registry.add_adapter(Arc::from(CodeLldbDebugAdapter::default())); registry.add_adapter(Arc::from(PythonDebugAdapter)); registry.add_adapter(Arc::from(PhpDebugAdapter)); - registry.add_adapter(Arc::from(JsDebugAdapter::default())); + registry.add_adapter(Arc::from(JsDebugAdapter)); registry.add_adapter(Arc::from(LldbDebugAdapter)); registry.add_adapter(Arc::from(GoDebugAdapter)); registry.add_adapter(Arc::from(GdbDebugAdapter)); diff --git a/crates/dap_adapters/src/javascript.rs b/crates/dap_adapters/src/javascript.rs index 5022f0ac76c0e4628e7b757bdcc9adf24f9deefc..11dee971b1e2b03d27a3fa32d66455dbe3e1806f 100644 --- a/crates/dap_adapters/src/javascript.rs +++ b/crates/dap_adapters/src/javascript.rs @@ -1,24 +1,13 @@ use adapters::latest_github_release; use gpui::AsyncApp; -use regex::Regex; use std::path::PathBuf; use task::{DebugRequestType, DebugTaskDefinition}; use crate::*; #[derive(Debug)] -pub(crate) struct JsDebugAdapter { - attach_processes: Regex, -} +pub(crate) struct JsDebugAdapter; -impl Default for JsDebugAdapter { - fn default() -> Self { - Self { - attach_processes: Regex::new(r"(?i)^(?:node|bun|iojs)(?:$|\b)") - .expect("Regex compilation to succeed"), - } - } -} impl JsDebugAdapter { const ADAPTER_NAME: &'static str = "JavaScript"; const ADAPTER_NPM_NAME: &'static str = "vscode-js-debug"; @@ -149,8 +138,4 @@ impl DebugAdapter for JsDebugAdapter { } args } - - fn attach_processes_filter(&self) -> Regex { - self.attach_processes.clone() - } } diff --git a/crates/debugger_ui/src/attach_modal.rs b/crates/debugger_ui/src/attach_modal.rs index 0ba8efc5e3a2847c57180df9d2de2a283eca5dd6..870d09aa3fc4d8c7a87ea8d02fde15a0743edf21 100644 --- a/crates/debugger_ui/src/attach_modal.rs +++ b/crates/debugger_ui/src/attach_modal.rs @@ -4,7 +4,6 @@ use gpui::Subscription; use gpui::{DismissEvent, Entity, EventEmitter, Focusable, Render}; use picker::{Picker, PickerDelegate}; -use std::cell::LazyCell; use std::sync::Arc; use sysinfo::System; use ui::{Context, Tooltip, prelude::*}; @@ -24,7 +23,7 @@ pub(crate) struct AttachModalDelegate { matches: Vec, placeholder_text: Arc, project: Entity, - debug_config: task::DebugTaskDefinition, + pub(crate) debug_config: task::DebugTaskDefinition, candidates: Arc<[Candidate]>, } @@ -58,7 +57,7 @@ impl AttachModal { window: &mut Window, cx: &mut Context, ) -> Self { - let mut processes: Vec<_> = System::new_all() + let mut processes: Box<[_]> = System::new_all() .processes() .values() .map(|process| { @@ -75,30 +74,18 @@ impl AttachModal { }) .collect(); processes.sort_by_key(|k| k.name.clone()); + let processes = processes.into_iter().collect(); Self::with_processes(project, debug_config, processes, modal, window, cx) } pub(super) fn with_processes( project: Entity, debug_config: task::DebugTaskDefinition, - processes: Vec, + processes: Arc<[Candidate]>, modal: bool, window: &mut Window, cx: &mut Context, ) -> Self { - let adapter = project - .read(cx) - .debug_adapters() - .adapter(&debug_config.adapter); - let filter = LazyCell::new(|| adapter.map(|adapter| adapter.attach_processes_filter())); - let processes = processes - .into_iter() - .filter(|process| { - filter - .as_ref() - .map_or(false, |filter| filter.is_match(&process.name)) - }) - .collect(); let picker = cx.new(|cx| { Picker::uniform_list( AttachModalDelegate::new(project, debug_config, processes), @@ -117,9 +104,10 @@ impl AttachModal { } impl Render for AttachModal { - fn render(&mut self, _window: &mut Window, _: &mut Context) -> impl ui::IntoElement { + fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl ui::IntoElement { v_flex() .key_context("AttachModal") + .track_focus(&self.focus_handle(cx)) .w(rems(34.)) .child(self.picker.clone()) } diff --git a/crates/debugger_ui/src/new_session_modal.rs b/crates/debugger_ui/src/new_session_modal.rs index b97af68fd66630de450785bdc881925a075a463b..c6f10bae25222e1117451331f6e77b301a1a9b85 100644 --- a/crates/debugger_ui/src/new_session_modal.rs +++ b/crates/debugger_ui/src/new_session_modal.rs @@ -11,6 +11,7 @@ use gpui::{ App, AppContext, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Render, TextStyle, WeakEntity, }; +use project::Project; use settings::Settings; use task::{DebugTaskDefinition, LaunchConfig}; use theme::ThemeSettings; @@ -59,7 +60,7 @@ impl NewSessionModal { debug_panel: WeakEntity, workspace: WeakEntity, window: &mut Window, - cx: &mut App, + cx: &mut Context, ) -> Self { let debugger = past_debug_definition .as_ref() @@ -171,25 +172,13 @@ impl NewSessionModal { attach.update(cx, |this, cx| { if selected_debugger != this.debug_definition.adapter { this.debug_definition.adapter = selected_debugger.into(); - if let Some(project) = this - .workspace - .read_with(cx, |workspace, _| workspace.project().clone()) - .ok() - { - this.attach_picker = Some(cx.new(|cx| { - let modal = AttachModal::new( - project, - this.debug_definition.clone(), - false, - window, - cx, - ); - - window.focus(&modal.focus_handle(cx)); - - modal - })); - } + + this.attach_picker.update(cx, |this, cx| { + this.picker.update(cx, |this, cx| { + this.delegate.debug_config.adapter = selected_debugger.into(); + this.focus(window, cx); + }) + }); } cx.notify(); @@ -256,7 +245,6 @@ impl NewSessionModal { ContextMenu::build(window, cx, move |mut menu, _, cx| { let setter_for_name = |task: DebugTaskDefinition| { let weak = weak.clone(); - let workspace = workspace.clone(); move |window: &mut Window, cx: &mut App| { weak.update(cx, |this, cx| { this.last_selected_profile_name = Some(SharedString::from(&task.label)); @@ -271,12 +259,19 @@ impl NewSessionModal { ); } DebugRequestType::Attach(_) => { + let Ok(project) = this + .workspace + .read_with(cx, |this, _| this.project().clone()) + else { + return; + }; this.mode = NewSessionMode::attach( this.debugger.clone(), - workspace.clone(), + project, window, cx, ); + this.mode.focus_handle(cx).focus(window); if let Some((debugger, attach)) = this.debugger.as_ref().zip(this.mode.as_attach()) { @@ -365,18 +360,16 @@ impl LaunchMode { #[derive(Clone)] struct AttachMode { - workspace: WeakEntity, debug_definition: DebugTaskDefinition, - attach_picker: Option>, - focus_handle: FocusHandle, + attach_picker: Entity, } impl AttachMode { fn new( debugger: Option, - workspace: WeakEntity, + project: Entity, window: &mut Window, - cx: &mut App, + cx: &mut Context, ) -> Entity { let debug_definition = DebugTaskDefinition { label: "Attach New Session Setup".into(), @@ -387,27 +380,15 @@ impl AttachMode { initialize_args: None, stop_on_entry: Some(false), }; + let attach_picker = cx.new(|cx| { + let modal = AttachModal::new(project, debug_definition.clone(), false, window, cx); + window.focus(&modal.focus_handle(cx)); - let attach_picker = if let Some(project) = debugger.and( - workspace - .read_with(cx, |workspace, _| workspace.project().clone()) - .ok(), - ) { - Some(cx.new(|cx| { - let modal = AttachModal::new(project, debug_definition.clone(), false, window, cx); - window.focus(&modal.focus_handle(cx)); - - modal - })) - } else { - None - }; - - cx.new(|cx| Self { - workspace, + modal + }); + cx.new(|_| Self { debug_definition, attach_picker, - focus_handle: cx.focus_handle(), }) } fn debug_task(&self) -> task::AttachConfig { @@ -444,7 +425,7 @@ impl Focusable for NewSessionMode { fn focus_handle(&self, cx: &App) -> FocusHandle { match &self { NewSessionMode::Launch(entity) => entity.read(cx).program.focus_handle(cx), - NewSessionMode::Attach(entity) => entity.read(cx).focus_handle.clone(), + NewSessionMode::Attach(entity) => entity.read(cx).attach_picker.focus_handle(cx), } } } @@ -476,8 +457,11 @@ impl RenderOnce for LaunchMode { } impl RenderOnce for AttachMode { - fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement { - v_flex().w_full().children(self.attach_picker.clone()) + fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { + v_flex() + .w_full() + .track_focus(&self.attach_picker.focus_handle(cx)) + .child(self.attach_picker.clone()) } } @@ -497,13 +481,17 @@ impl RenderOnce for NewSessionMode { impl NewSessionMode { fn attach( debugger: Option, - workspace: WeakEntity, + project: Entity, window: &mut Window, - cx: &mut App, + cx: &mut Context, ) -> Self { - Self::Attach(AttachMode::new(debugger, workspace, window, cx)) + Self::Attach(AttachMode::new(debugger, project, window, cx)) } - fn launch(past_launch_config: Option, window: &mut Window, cx: &mut App) -> Self { + fn launch( + past_launch_config: Option, + window: &mut Window, + cx: &mut Context, + ) -> Self { Self::Launch(LaunchMode::new(past_launch_config, window, cx)) } } @@ -592,18 +580,25 @@ impl Render for NewSessionModal { .toggle_state(matches!(self.mode, NewSessionMode::Attach(_))) .style(ui::ButtonStyle::Subtle) .on_click(cx.listener(|this, _, window, cx| { + let Ok(project) = this + .workspace + .read_with(cx, |this, _| this.project().clone()) + else { + return; + }; this.mode = NewSessionMode::attach( this.debugger.clone(), - this.workspace.clone(), + project, window, cx, ); + this.mode.focus_handle(cx).focus(window); if let Some((debugger, attach)) = this.debugger.as_ref().zip(this.mode.as_attach()) { Self::update_attach_picker(&attach, &debugger, window, cx); } - this.mode.focus_handle(cx).focus(window); + cx.notify(); })) .last(), diff --git a/crates/debugger_ui/src/tests/attach_modal.rs b/crates/debugger_ui/src/tests/attach_modal.rs index 868191f22de446501542e8a14c2d75c9ab20497c..0c7465ca262254731d693b1c00e32dd980b348a3 100644 --- a/crates/debugger_ui/src/tests/attach_modal.rs +++ b/crates/debugger_ui/src/tests/attach_modal.rs @@ -100,7 +100,7 @@ async fn test_show_attach_modal_and_select_process( }, Candidate { pid: 3, - name: "non-fake-binary-1".into(), + name: "real-binary-1".into(), command: vec![], }, Candidate { @@ -108,7 +108,9 @@ async fn test_show_attach_modal_and_select_process( name: "fake-binary-2".into(), command: vec![], }, - ], + ] + .into_iter() + .collect(), true, window, cx, @@ -121,17 +123,30 @@ async fn test_show_attach_modal_and_select_process( cx.run_until_parked(); + // assert we got the expected processes + workspace + .update(cx, |_, window, cx| { + let names = + attach_modal.update(cx, |modal, cx| attach_modal::_process_names(&modal, cx)); + // Initially all processes are visible. + assert_eq!(3, names.len()); + attach_modal.update(cx, |this, cx| { + this.picker.update(cx, |this, cx| { + this.set_query("fakb", window, cx); + }) + }) + }) + .unwrap(); + cx.run_until_parked(); // assert we got the expected processes workspace .update(cx, |_, _, cx| { let names = attach_modal.update(cx, |modal, cx| attach_modal::_process_names(&modal, cx)); - - // we filtered out all processes that are not starting with `fake-binary` + // Initially all processes are visible. assert_eq!(2, names.len()); }) .unwrap(); - // select the only existing process cx.dispatch_action(Confirm);