From 0d97e9e5797fa410f25fe2f01f79fe87fbfdd205 Mon Sep 17 00:00:00 2001 From: Remco Smits Date: Sun, 23 Jun 2024 14:27:07 +0200 Subject: [PATCH] Send debug adapter event through project and listen in debug panel --- Cargo.lock | 1 + crates/dap/src/client.rs | 68 ++++++++++++++++++------ crates/dap/src/events.rs | 2 +- crates/debugger_ui/Cargo.toml | 1 + crates/debugger_ui/src/debugger_panel.rs | 41 +++++++++++--- crates/editor/src/editor.rs | 2 +- crates/project/src/project.rs | 40 +++++++++++--- 7 files changed, 124 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b974f72382..cf8a20db5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3265,6 +3265,7 @@ dependencies = [ "editor", "futures 0.3.28", "gpui", + "project", "serde", "serde_derive", "ui", diff --git a/crates/dap/src/client.rs b/crates/dap/src/client.rs index acd911227b..b74302e522 100644 --- a/crates/dap/src/client.rs +++ b/crates/dap/src/client.rs @@ -1,9 +1,9 @@ use crate::{ + events::{self, Event}, requests::{ ConfigurationDone, Continue, ContinueArguments, Initialize, InitializeArguments, Launch, - LaunchRequestArguments, Next, NextArguments, ScopesResponse, SetBreakpoints, - SetBreakpointsArguments, SetBreakpointsResponse, StepIn, StepInArguments, StepOut, - StepOutArguments, + LaunchRequestArguments, Next, NextArguments, SetBreakpoints, SetBreakpointsArguments, + SetBreakpointsResponse, StepIn, StepInArguments, StepOut, StepOutArguments, }, transport::{self, Payload, Request, Transport}, types::{DebuggerCapabilities, Source, SourceBreakpoint, ThreadId}, @@ -13,8 +13,7 @@ use futures::{ channel::mpsc::{channel, unbounded, UnboundedReceiver, UnboundedSender}, AsyncBufRead, AsyncReadExt, AsyncWrite, SinkExt as _, StreamExt, }; -use gpui::AsyncAppContext; -use serde_json::Value; +use gpui::{AppContext, AsyncAppContext}; use smol::{ io::BufReader, net::TcpStream, @@ -22,6 +21,7 @@ use smol::{ }; use std::{ net::{Ipv4Addr, SocketAddrV4}, + ops::DerefMut, path::PathBuf, process::Stdio, sync::atomic::{AtomicU64, Ordering}, @@ -43,22 +43,25 @@ pub struct DebugAdapterClient { server_tx: UnboundedSender, request_count: AtomicU64, thread_id: Option, - client_rx: UnboundedReceiver, capabilities: Option, } impl DebugAdapterClient { - pub async fn new( + pub async fn new( transport_type: TransportType, command: &str, args: Vec<&str>, port: u16, project_path: PathBuf, cx: &mut AsyncAppContext, - ) -> Result { + event_handler: F, + ) -> Result + where + F: FnMut(events::Event, &mut AppContext) + 'static + Send + Sync + Clone, + { match transport_type { TransportType::TCP => { - Self::create_tcp_client(command, args, port, project_path, cx).await + Self::create_tcp_client(command, args, port, project_path, cx, event_handler).await } TransportType::STDIO => { Self::create_stdio_client(command, args, port, project_path, cx).await @@ -66,13 +69,17 @@ impl DebugAdapterClient { } } - async fn create_tcp_client( + async fn create_tcp_client( command: &str, args: Vec<&str>, port: u16, project_path: PathBuf, cx: &mut AsyncAppContext, - ) -> Result { + event_handler: F, + ) -> Result + where + F: FnMut(events::Event, &mut AppContext) + 'static + Send + Sync + Clone, + { let mut command = process::Command::new(command); command .current_dir(project_path) @@ -101,6 +108,7 @@ impl DebugAdapterClient { None, Some(process), cx, + event_handler, ) } @@ -114,13 +122,17 @@ impl DebugAdapterClient { todo!("not implemented") } - pub fn handle_transport( + pub fn handle_transport( rx: Box, tx: Box, err: Option>, process: Option, cx: &mut AsyncAppContext, - ) -> Result { + event_handler: F, + ) -> Result + where + F: FnMut(events::Event, &mut AppContext) + 'static + Send + Sync + Clone, + { let (server_rx, server_tx) = Transport::start(rx, tx, err, cx); let (client_tx, client_rx) = unbounded::(); @@ -129,17 +141,37 @@ impl DebugAdapterClient { _process: process, request_count: AtomicU64::new(0), thread_id: Some(ThreadId(1)), - client_rx, // TODO: remove this here capabilities: None, }; - cx.spawn(move |_| Self::recv(server_rx, server_tx, client_tx)) + cx.spawn(move |cx| Self::handle_events(client_rx, event_handler, cx)) + .detach(); + + cx.spawn(move |_| Self::handle_recv(server_rx, server_tx, client_tx)) .detach(); Ok(client) } - async fn recv( + async fn handle_events( + mut client_rx: UnboundedReceiver, + mut event_handler: F, + cx: AsyncAppContext, + ) -> Result<()> + where + F: FnMut(events::Event, &mut AppContext) + 'static + Send + Sync + Clone, + { + while let Some(payload) = client_rx.next().await { + cx.update(|cx| match payload { + Payload::Event(event) => event_handler(*event, cx), + _ => unreachable!(), + })?; + } + + anyhow::Ok(()) + } + + async fn handle_recv( mut server_rx: UnboundedReceiver, mut server_tx: UnboundedSender, mut client_tx: UnboundedSender, @@ -185,6 +217,10 @@ impl DebugAdapterClient { self.request_count.fetch_add(1, Ordering::Relaxed) } + pub fn update_thread_id(&mut self, thread_id: ThreadId) { + self.thread_id = Some(thread_id); + } + pub async fn initialize(&mut self) -> Result { let args = InitializeArguments { client_id: Some("zed".to_owned()), diff --git a/crates/dap/src/events.rs b/crates/dap/src/events.rs index 5f8de379bb..052514098d 100644 --- a/crates/dap/src/events.rs +++ b/crates/dap/src/events.rs @@ -2,7 +2,7 @@ use crate::types::{DebuggerCapabilities, Source, ThreadId}; use serde::{Deserialize, Serialize}; use serde_json::Value; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] #[serde(tag = "event", content = "body")] // seq is omitted as unused and is not sent by some implementations diff --git a/crates/debugger_ui/Cargo.toml b/crates/debugger_ui/Cargo.toml index 21ad84afc3..2f70db0ce0 100644 --- a/crates/debugger_ui/Cargo.toml +++ b/crates/debugger_ui/Cargo.toml @@ -15,6 +15,7 @@ db.workspace = true editor.workspace = true futures.workspace = true gpui.workspace = true +project.workspace = true serde.workspace = true serde_derive.workspace = true ui.workspace = true diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs index bb24660260..365503b6e2 100644 --- a/crates/debugger_ui/src/debugger_panel.rs +++ b/crates/debugger_ui/src/debugger_panel.rs @@ -1,14 +1,10 @@ use std::sync::Arc; -use anyhow::{anyhow, Result}; -use dap::{ - client::{DebugAdapterClient, DebugAdapterClientId, TransportType}, - transport::Payload, -}; -use futures::channel::mpsc::UnboundedReceiver; +use anyhow::Result; +use dap::client::{DebugAdapterClient, DebugAdapterClientId}; use gpui::{ actions, Action, AppContext, AsyncWindowContext, EventEmitter, FocusHandle, FocusableView, - View, ViewContext, WeakView, + Subscription, View, ViewContext, WeakView, }; use ui::{ div, h_flex, @@ -30,6 +26,7 @@ pub struct DebugPanel { pub focus_handle: FocusHandle, pub size: Pixels, pub workspace: WeakView, + _subscriptions: Vec, } impl DebugPanel { @@ -38,6 +35,35 @@ impl DebugPanel { workspace: WeakView, cx: &mut WindowContext, ) -> Self { + let project = workspace + .update(cx, |workspace, cx| workspace.project().clone()) + .unwrap(); + + let _subscriptions = vec![cx.subscribe(&project, { + move |this, event, cx| { + if let project::Event::DebugClientStarted(client_id) = event { + dbg!(&event, &client_id); + } + if let project::Event::DebugClientEvent { client_id, event } = event { + match event { + dap::events::Event::Initialized(_) => todo!(), + dap::events::Event::Stopped(_) => todo!(), + dap::events::Event::Continued(_) => todo!(), + dap::events::Event::Exited(_) => todo!(), + dap::events::Event::Terminated(_) => todo!(), + dap::events::Event::Thread(_) => todo!(), + dap::events::Event::Output(_) => todo!(), + dap::events::Event::Breakpoint(_) => todo!(), + dap::events::Event::Module(_) => todo!(), + dap::events::Event::LoadedSource(_) => todo!(), + dap::events::Event::Process(_) => todo!(), + dap::events::Event::Capabilities(_) => todo!(), + dap::events::Event::Memory(_) => todo!(), + } + } + } + })]; + Self { position, zoomed: false, @@ -45,6 +71,7 @@ impl DebugPanel { focus_handle: cx.focus_handle(), size: px(300.), workspace, + _subscriptions, } } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 997d1c473d..a816d52670 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -148,7 +148,7 @@ use workspace::notifications::{DetachAndPromptErr, NotificationId}; use workspace::{ searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId, }; -use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast,ToggleBreakpoint}; +use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast, ToggleBreakpoint}; use crate::hover_links::find_url; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 0af1d233c5..73dd1ac39f 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -19,8 +19,8 @@ use client::{ TypedEnvelope, UserStore, }; use clock::ReplicaId; -use dap::client::{DebugAdapterClient, DebugAdapterClientId, TransportType}; use collections::{btree_map, hash_map, BTreeMap, HashMap, HashSet, VecDeque}; +use dap::client::{DebugAdapterClient, DebugAdapterClientId, TransportType}; use debounced_delay::DebouncedDelay; use futures::{ channel::{ @@ -86,7 +86,7 @@ use smol::channel::{Receiver, Sender}; use smol::lock::Semaphore; use snippet::Snippet; use std::{ - borrow::{BorrowMut, Cow}, + borrow::Cow, cmp::{self, Ordering}, convert::TryInto, env, @@ -333,6 +333,11 @@ pub enum Event { Notification(String), LanguageServerPrompt(LanguageServerPromptRequest), LanguageNotFound(Model), + DebugClientStarted(DebugAdapterClientId), + DebugClientEvent { + client_id: DebugAdapterClientId, + event: dap::events::Event, + }, ActiveEntryChanged(Option), ActivateProjectPanel, WorktreeAdded, @@ -1042,22 +1047,41 @@ impl Project { }) } + pub fn debug_adapter_by_id( + &self, + id: DebugAdapterClientId, + ) -> Option<&Arc> { + self.debug_adapters.get(&id).and_then(|state| match state { + DebugAdapterClientState::Starting(_) => None, + DebugAdapterClientState::Running(client) => Some(client), + }) + } + pub fn start_debug_adapter_client( &mut self, id: DebugAdapterClientId, cx: &mut ModelContext, ) { let task = cx.spawn(|this, mut cx| async move { + let this2 = this.clone(); let mut client = DebugAdapterClient::new( TransportType::TCP, "bun", vec![ "/Users/remcosmits/Documents/code/vscode-php-debug/out/phpDebug.js", - "--server=8130", + "--server=8131", ], - 8130, + 8131, "/Users/remcosmits/Documents/code/symfony_demo".into(), &mut cx, + move |event, cx| { + this2.update(cx, |_, cx| { + cx.emit(Event::DebugClientEvent { + client_id: id, + event, + }) + }); + }, ) .await .log_err()?; @@ -1071,7 +1095,8 @@ impl Project { "/Users/remcosmits/Documents/code/symfony_demo/src/Kernel.php".into(), 14, ) - .await; + .await + .log_err(); // configuration done client.configuration_done().await.log_err()?; @@ -1081,12 +1106,15 @@ impl Project { let client = Arc::new(client); - this.update(&mut cx, |this, _| { + this.update(&mut cx, |this, cx| { let handle = this .debug_adapters .get_mut(&id) .with_context(|| "Failed to find debug adapter with given id")?; *handle = DebugAdapterClientState::Running(client.clone()); + + cx.emit(Event::DebugClientStarted(id)); + anyhow::Ok(()) }) .log_err();