Send debug adapter event through project and listen in debug panel

This commit is contained in:
Remco Smits
2024-06-23 14:27:07 +02:00
parent 14b913fb4b
commit 0d97e9e579
7 changed files with 124 additions and 31 deletions

1
Cargo.lock generated
View File

@@ -3265,6 +3265,7 @@ dependencies = [
"editor",
"futures 0.3.28",
"gpui",
"project",
"serde",
"serde_derive",
"ui",

View File

@@ -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<Payload>,
request_count: AtomicU64,
thread_id: Option<ThreadId>,
client_rx: UnboundedReceiver<Payload>,
capabilities: Option<DebuggerCapabilities>,
}
impl DebugAdapterClient {
pub async fn new(
pub async fn new<F>(
transport_type: TransportType,
command: &str,
args: Vec<&str>,
port: u16,
project_path: PathBuf,
cx: &mut AsyncAppContext,
) -> Result<Self> {
event_handler: F,
) -> Result<Self>
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<F>(
command: &str,
args: Vec<&str>,
port: u16,
project_path: PathBuf,
cx: &mut AsyncAppContext,
) -> Result<Self> {
event_handler: F,
) -> Result<Self>
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<F>(
rx: Box<dyn AsyncBufRead + Unpin + Send>,
tx: Box<dyn AsyncWrite + Unpin + Send>,
err: Option<Box<dyn AsyncBufRead + Unpin + Send>>,
process: Option<Child>,
cx: &mut AsyncAppContext,
) -> Result<Self> {
event_handler: F,
) -> Result<Self>
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::<Payload>();
@@ -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<F>(
mut client_rx: UnboundedReceiver<Payload>,
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<Payload>,
mut server_tx: UnboundedSender<Payload>,
mut client_tx: UnboundedSender<Payload>,
@@ -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<DebuggerCapabilities> {
let args = InitializeArguments {
client_id: Some("zed".to_owned()),

View File

@@ -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

View File

@@ -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

View File

@@ -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<Workspace>,
_subscriptions: Vec<Subscription>,
}
impl DebugPanel {
@@ -38,6 +35,35 @@ impl DebugPanel {
workspace: WeakView<Workspace>,
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,
}
}

View File

@@ -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;

View File

@@ -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<Buffer>),
DebugClientStarted(DebugAdapterClientId),
DebugClientEvent {
client_id: DebugAdapterClientId,
event: dap::events::Event,
},
ActiveEntryChanged(Option<ProjectEntryId>),
ActivateProjectPanel,
WorktreeAdded,
@@ -1042,22 +1047,41 @@ impl Project {
})
}
pub fn debug_adapter_by_id(
&self,
id: DebugAdapterClientId,
) -> Option<&Arc<DebugAdapterClient>> {
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<Self>,
) {
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();