From 807c4f7613cffd270df23dfd175504397dd4cce1 Mon Sep 17 00:00:00 2001 From: Anthony Eid Date: Thu, 13 Feb 2025 16:18:35 -0500 Subject: [PATCH] Add threads reqeust to debug session Co-authored-by: Piotr --- Cargo.lock | 1 + crates/dap/src/proto_conversions.rs | 19 ++++++ crates/debugger_ui/src/debugger_panel.rs | 1 - crates/debugger_ui/src/stack_frame_list.rs | 4 +- crates/debugger_ui/src/tests/variable_list.rs | 2 +- crates/project/Cargo.toml | 1 + crates/project/src/debugger/dap_command.rs | 59 +++++++++++++++++++ crates/project/src/debugger/dap_session.rs | 41 +++++++++++-- crates/proto/proto/zed.proto | 18 +++++- crates/proto/src/proto.rs | 4 ++ 10 files changed, 139 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94d8414491aa3b..e4d62324dbb3e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10228,6 +10228,7 @@ dependencies = [ "gpui", "http_client", "image", + "indexmap", "itertools 0.14.0", "language", "log", diff --git a/crates/dap/src/proto_conversions.rs b/crates/dap/src/proto_conversions.rs index 66041a4457557d..153247dfc98bda 100644 --- a/crates/dap/src/proto_conversions.rs +++ b/crates/dap/src/proto_conversions.rs @@ -619,3 +619,22 @@ impl ProtoConversion for dap_types::CompletionItemType { } } } + +impl ProtoConversion for dap_types::Thread { + type ProtoType = proto::DapThread; + type Output = Self; + + fn to_proto(&self) -> Self::ProtoType { + proto::DapThread { + id: self.id, + name: self.name.clone(), + } + } + + fn from_proto(payload: Self::ProtoType) -> Self { + Self { + id: payload.id, + name: payload.name, + } + } +} diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs index ba490f1ab249ee..7ba512592bc7e7 100644 --- a/crates/debugger_ui/src/debugger_panel.rs +++ b/crates/debugger_ui/src/debugger_panel.rs @@ -364,7 +364,6 @@ impl DebugPanel { let thread_panel = item.downcast::().unwrap(); let thread_id = thread_panel.read(cx).thread_id(); - let session_id = thread_panel.read(cx).session().read(cx).id(); let client_id = thread_panel.read(cx).client_id(); self.thread_states.remove(&(client_id, thread_id)); diff --git a/crates/debugger_ui/src/stack_frame_list.rs b/crates/debugger_ui/src/stack_frame_list.rs index bb8db8f6a0462f..b8197234def060 100644 --- a/crates/debugger_ui/src/stack_frame_list.rs +++ b/crates/debugger_ui/src/stack_frame_list.rs @@ -44,7 +44,7 @@ impl StackFrameList { session: Entity, client_id: DebugAdapterClientId, thread_id: ThreadId, - window: &Window, + _window: &Window, cx: &mut Context, ) -> Self { let weak_entity = cx.weak_entity(); @@ -67,7 +67,7 @@ impl StackFrameList { let client_state = session.read(cx).client_state(client_id).unwrap(); let _subscription = cx.observe(&client_state, |stack_frame_list, state, cx| { - let frame_len = state.update(cx, |state, cx| { + let _frame_len = state.update(cx, |state, cx| { state.stack_frames(stack_frame_list.thread_id, cx).len() }); diff --git a/crates/debugger_ui/src/tests/variable_list.rs b/crates/debugger_ui/src/tests/variable_list.rs index 887c43557cadd3..9173b6c5864719 100644 --- a/crates/debugger_ui/src/tests/variable_list.rs +++ b/crates/debugger_ui/src/tests/variable_list.rs @@ -1355,7 +1355,7 @@ async fn test_it_only_fetches_scopes_and_variables_for_the_first_stack_frame( assert_eq!(1, stack_frame_id); assert_eq!(stack_frames, stack_frame_list); - let stack_frame_list = debug_panel_item.stack_frame_list().read(cx); + let variable_list = debug_panel_item.variable_list().read(cx); assert_eq!( diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 5c83fd04b45163..10be9c3c55c7ce 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -44,6 +44,7 @@ globset.workspace = true gpui.workspace = true http_client.workspace = true itertools.workspace = true +indexmap.workspace = true language.workspace = true log.workspace = true lsp.workspace = true diff --git a/crates/project/src/debugger/dap_command.rs b/crates/project/src/debugger/dap_command.rs index da0df5b9c89020..b4b2e7f5424a4e 100644 --- a/crates/project/src/debugger/dap_command.rs +++ b/crates/project/src/debugger/dap_command.rs @@ -1527,3 +1527,62 @@ impl DapCommand for EvaluateCommand { } } } + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub(crate) struct ThreadsCommand; + +impl DapCommand for ThreadsCommand { + type Response = Vec; + type DapRequest = dap::requests::Threads; + type ProtoRequest = proto::DapThreadsRequest; + + fn to_dap(&self) -> ::Arguments { + () + } + + fn response_from_dap( + &self, + message: ::Response, + ) -> Result { + Ok(message.threads) + } + + fn is_supported(&self, _capabilities: &Capabilities) -> bool { + true + } + + fn to_proto( + &self, + debug_client_id: DebugAdapterClientId, + upstream_project_id: u64, + ) -> Self::ProtoRequest { + proto::DapThreadsRequest { + project_id: upstream_project_id, + client_id: debug_client_id.to_proto(), + } + } + + fn from_proto(_request: &Self::ProtoRequest) -> Self { + Self {} + } + + fn client_id_from_proto(request: &Self::ProtoRequest) -> DebugAdapterClientId { + DebugAdapterClientId::from_proto(request.client_id) + } + + fn response_from_proto( + &self, + message: ::Response, + ) -> Result { + Ok(Vec::from_proto(message.threads)) + } + + fn response_to_proto( + _debug_client_id: DebugAdapterClientId, + message: Self::Response, + ) -> ::Response { + proto::DapThreadsResponse { + threads: message.to_proto(), + } + } +} diff --git a/crates/project/src/debugger/dap_session.rs b/crates/project/src/debugger/dap_session.rs index 26afb889bd8434..ce9bb72ffd3d67 100644 --- a/crates/project/src/debugger/dap_session.rs +++ b/crates/project/src/debugger/dap_session.rs @@ -5,7 +5,7 @@ use super::dap_command::{ TerminateThreadsCommand, VariablesCommand, }; use anyhow::{anyhow, Result}; -use collections::{BTreeMap, HashMap}; +use collections::{BTreeMap, HashMap, IndexMap}; use dap::client::{DebugAdapterClient, DebugAdapterClientId}; use dap::requests::Request; use dap::{ @@ -42,7 +42,7 @@ impl DebugSessionId { } } -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)] #[repr(transparent)] pub struct ThreadId(pub u64); @@ -106,12 +106,23 @@ pub enum ThreadStatus { } pub struct Thread { - _thread: dap::Thread, + dap: dap::Thread, stack_frames: Vec, _status: ThreadStatus, _has_stopped: bool, } +impl From for Thread { + fn from(dap: dap::Thread) -> Self { + Self { + dap, + stack_frames: vec![], + _status: ThreadStatus::default(), + _has_stopped: false, + } + } +} + type UpstreamProjectId = u64; pub struct RemoteConnection { @@ -227,7 +238,7 @@ pub struct Client { client_id: DebugAdapterClientId, modules: Vec, loaded_sources: Vec, - threads: BTreeMap, + threads: IndexMap, requests: HashMap>>>, } @@ -391,6 +402,24 @@ impl Client { cx.notify(); } + pub fn threads(&mut self, cx: &mut Context) -> Vec { + self.fetch( + dap_command::ThreadsCommand, + |this, result, cx| { + this.threads.extend( + result + .iter() + .map(|thread| (ThreadId(thread.id), Thread::from(thread.clone()))), + ); + }, + cx, + ); + self.threads + .values() + .map(|thread| thread.dap.clone()) + .collect() + } + pub fn modules(&mut self, cx: &mut Context) -> &[Module] { self.fetch( dap_command::ModulesCommand, @@ -681,7 +710,7 @@ impl Client { thread.stack_frames = stack_frames.iter().cloned().map(From::from).collect(); }); debug_assert!( - matches!(entry, BTreeMapEntry::Occupied(_)), + matches!(entry, indexmap::map::Entry::Occupied(_)), "Sent request for thread_id that doesn't exist" ); @@ -978,7 +1007,7 @@ impl DebugSession { client_id, modules: Vec::default(), loaded_sources: Vec::default(), - threads: BTreeMap::default(), + threads: IndexMap::default(), requests: HashMap::default(), capabilities: Default::default(), mode, diff --git a/crates/proto/proto/zed.proto b/crates/proto/proto/zed.proto index 4180c347c15220..1489618e21ee38 100644 --- a/crates/proto/proto/zed.proto +++ b/crates/proto/proto/zed.proto @@ -365,7 +365,9 @@ message Envelope { DapEvaluateRequest dap_evaluate_request = 341; DapEvaluateResponse dap_evaluate_response = 342; DapCompletionRequest dap_completion_request = 343; - DapCompletionResponse dap_completion_response = 344; // current max + DapCompletionResponse dap_completion_response = 344; + DapThreadsRequest dap_threads_request = 345; + DapThreadsResponse dap_threads_response = 346; // current max } reserved 87 to 88; @@ -2880,6 +2882,15 @@ message DapTerminateThreadsRequest { repeated uint64 thread_ids = 3; } +message DapThreadsRequest { + uint64 project_id = 1; + uint64 client_id = 2; +} + +message DapThreadsResponse { + repeated DapThread threads = 1; +} + message DapTerminateRequest { uint64 project_id = 1; uint64 client_id = 2; @@ -3069,6 +3080,11 @@ message DapVariable { optional string memory_reference = 9; } +message DapThread { + uint64 id = 1; + string name = 2; +} + message DapScope { string name = 1; optional DapScopePresentationHint presentation_hint = 2; diff --git a/crates/proto/src/proto.rs b/crates/proto/src/proto.rs index 2e8aa2ae94c62d..d7fc560522f0e1 100644 --- a/crates/proto/src/proto.rs +++ b/crates/proto/src/proto.rs @@ -486,6 +486,8 @@ messages!( (DapEvaluateResponse, Background), (DapCompletionRequest, Background), (DapCompletionResponse, Background), + (DapThreadsRequest, Background), + (DapThreadsResponse, Background), ); request_messages!( @@ -643,6 +645,7 @@ request_messages!( (DapSetVariableValueRequest, DapSetVariableValueResponse), (DapEvaluateRequest, DapEvaluateResponse), (DapCompletionRequest, DapCompletionResponse), + (DapThreadsRequest, DapThreadsResponse), ); entity_messages!( @@ -768,6 +771,7 @@ entity_messages!( DapSetVariableValueRequest, DapEvaluateRequest, DapCompletionRequest, + DapThreadsRequest, ); entity_messages!(