History

Tidy up DAP initialization (#28730)

To make DAP work over SSH we want to create the binary at the project level (so we can wrap it in an `ssh` invocation transparently). This means not pushing the adapter down into the session, and resolving more information ahead-of-time. Co-authored-by: Anthony Eid <hello@anthonyeid.me> Co-authored-by: Piotr <piotr@zed.dev> Release Notes: - N/A ---------

Conrad Irwin committed 1 year ago

Commit aef78dc1 parent 6f0951f32 files · +1276 −1695Browse files

Unified · Split

32 files
  1. crates/dap/src/adapters.rs +36 −27
  2. crates/dap/src/client.rs +38 −19
  3. crates/dap/src/transport.rs +45 −50
  4. crates/dap_adapters/src/codelldb.rs +42 −35
  5. crates/dap_adapters/src/dap_adapters.rs +15 −2
  6. crates/dap_adapters/src/gdb.rs +44 −38
  7. crates/dap_adapters/src/go.rs +30 −25
  8. crates/dap_adapters/src/javascript.rs +38 −32
  9. crates/dap_adapters/src/lldb.rs +39 −33
  10. crates/dap_adapters/src/php.rs +25 −18
  11. crates/dap_adapters/src/python.rs +36 −31
  12. crates/debugger_ui/src/attach_modal.rs +1 −4
  13. crates/debugger_ui/src/new_session_modal.rs +1 −1
  14. crates/debugger_ui/src/tests/attach_modal.rs +30 −10
  15. crates/debugger_ui/src/tests/console.rs +8 −18
  16. crates/debugger_ui/src/tests/debugger_panel.rs +256 −355
  17. crates/debugger_ui/src/tests/module_list.rs +31 −54
  18. crates/debugger_ui/src/tests/stack_frame_list.rs +71 −102
  19. crates/debugger_ui/src/tests/variable_list.rs +254 −342
  20. crates/project/src/debugger.rs +3 −0
  21. crates/project/src/debugger/dap_store.rs +36 −127
  22. crates/project/src/debugger/locator_store.rs +2 −2
  23. crates/project/src/debugger/locator_store/cargo.rs +3 −5
  24. crates/project/src/debugger/locator_store/locators.rs +2 −2
  25. crates/project/src/debugger/session.rs +49 −298
  26. crates/project/src/debugger/test.rs +98 −0
  27. crates/project/src/project.rs +32 −51
  28. crates/project/src/project_tests.rs +1 −1
  29. crates/remote_server/src/headless_project.rs +1 −3
  30. crates/task/src/lib.rs +4 −4
  31. crates/tasks_ui/src/modal.rs +3 −4
  32. crates/workspace/src/workspace.rs +2 −2
crates/dap/src/adapters.rs+36−27
@@ -3,13 +3,13 @@ use anyhow::{Context as _, Ok, Result, anyhow}; · 2 unmodified lines
3use async_compression::futures::bufread::GzipDecoder;3use async_compression::futures::bufread::GzipDecoder;
4use async_tar::Archive;4use async_tar::Archive;
5use async_trait::async_trait;5use async_trait::async_trait;
6use dap_types::StartDebuggingRequestArguments;
6use futures::io::BufReader;7use futures::io::BufReader;
7use gpui::{AsyncApp, SharedString};8use gpui::{AsyncApp, SharedString};
8pub use http_client::{HttpClient, github::latest_github_release};9pub use http_client::{HttpClient, github::latest_github_release};
9use language::LanguageToolchainStore;10use language::LanguageToolchainStore;
10use node_runtime::NodeRuntime;11use node_runtime::NodeRuntime;
11use serde::{Deserialize, Serialize};12use serde::{Deserialize, Serialize};
12use serde_json::Value;
13use settings::WorktreeId;13use settings::WorktreeId;
14use smol::{self, fs::File, lock::Mutex};14use smol::{self, fs::File, lock::Mutex};
15use std::{15use std::{
@@ -22,7 +22,7 @@ use std::{ · 6 unmodified lines
22 path::PathBuf,22 path::PathBuf,
23 sync::Arc,23 sync::Arc,
24};24};
25use task::{DebugAdapterConfig, DebugTaskDefinition};25use task::DebugTaskDefinition;
26use util::ResultExt;26use util::ResultExt;
27 27
28#[derive(Clone, Debug, PartialEq, Eq)]28#[derive(Clone, Debug, PartialEq, Eq)]
@@ -93,13 +93,15 @@ pub struct TcpArguments { · 64 unmodified lines
93 pub port: u16,93 pub port: u16,
94 pub timeout: Option<u64>,94 pub timeout: Option<u64>,
95}95}
96#[derive(Default, Debug, Clone)]96#[derive(Debug, Clone)]
97pub struct DebugAdapterBinary {97pub struct DebugAdapterBinary {
98 pub adapter_name: DebugAdapterName,
98 pub command: String,99 pub command: String,
99 pub arguments: Option<Vec<OsString>>,100 pub arguments: Option<Vec<OsString>>,
100 pub envs: Option<HashMap<String, String>>,101 pub envs: Option<HashMap<String, String>>,
101 pub cwd: Option<PathBuf>,102 pub cwd: Option<PathBuf>,
102 pub connection: Option<TcpArguments>,103 pub connection: Option<TcpArguments>,
104 pub request_args: StartDebuggingRequestArguments,
103}105}
104 106
105#[derive(Debug)]107#[derive(Debug)]
@@ -220,7 +222,7 @@ pub trait DebugAdapter: 'static + Send + Sync { · 114 unmodified lines
220 async fn get_binary(222 async fn get_binary(
221 &self,223 &self,
222 delegate: &dyn DapDelegate,224 delegate: &dyn DapDelegate,
223 config: &DebugAdapterConfig,225 config: &DebugTaskDefinition,
224 user_installed_path: Option<PathBuf>,226 user_installed_path: Option<PathBuf>,
225 cx: &mut AsyncApp,227 cx: &mut AsyncApp,
226 ) -> Result<DebugAdapterBinary> {228 ) -> Result<DebugAdapterBinary> {
@@ -284,13 +286,10 @@ pub trait DebugAdapter: 'static + Send + Sync { · 57 unmodified lines
284 async fn get_installed_binary(286 async fn get_installed_binary(
285 &self,287 &self,
286 delegate: &dyn DapDelegate,288 delegate: &dyn DapDelegate,
287 config: &DebugAdapterConfig,289 config: &DebugTaskDefinition,
288 user_installed_path: Option<PathBuf>,290 user_installed_path: Option<PathBuf>,
289 cx: &mut AsyncApp,291 cx: &mut AsyncApp,
290 ) -> Result<DebugAdapterBinary>;292 ) -> Result<DebugAdapterBinary>;
291
292 /// Should return base configuration to make the debug adapter work
293 fn request_args(&self, config: &DebugTaskDefinition) -> Value;
294}293}
295#[cfg(any(test, feature = "test-support"))]294#[cfg(any(test, feature = "test-support"))]
296pub struct FakeAdapter {}295pub struct FakeAdapter {}
@@ -302,6 +301,31 @@ impl FakeAdapter { · 5 unmodified lines
302 pub fn new() -> Self {301 pub fn new() -> Self {
303 Self {}302 Self {}
304 }303 }
304
305 fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
306 use serde_json::json;
307 use task::DebugRequestType;
308
309 let value = json!({
310 "request": match config.request {
311 DebugRequestType::Launch(_) => "launch",
312 DebugRequestType::Attach(_) => "attach",
313 },
314 "process_id": if let DebugRequestType::Attach(attach_config) = &config.request {
315 attach_config.process_id
316 } else {
317 None
318 },
319 });
320 let request = match config.request {
321 DebugRequestType::Launch(_) => dap_types::StartDebuggingRequestArgumentsRequest::Launch,
322 DebugRequestType::Attach(_) => dap_types::StartDebuggingRequestArgumentsRequest::Attach,
323 };
324 StartDebuggingRequestArguments {
325 configuration: value,
326 request,
327 }
328 }
305}329}
306 330
307#[cfg(any(test, feature = "test-support"))]331#[cfg(any(test, feature = "test-support"))]
@@ -314,16 +338,18 @@ impl DebugAdapter for FakeAdapter { · 6 unmodified lines
314 async fn get_binary(338 async fn get_binary(
315 &self,339 &self,
316 _: &dyn DapDelegate,340 _: &dyn DapDelegate,
317 _: &DebugAdapterConfig,341 config: &DebugTaskDefinition,
318 _: Option<PathBuf>,342 _: Option<PathBuf>,
319 _: &mut AsyncApp,343 _: &mut AsyncApp,
320 ) -> Result<DebugAdapterBinary> {344 ) -> Result<DebugAdapterBinary> {
321 Ok(DebugAdapterBinary {345 Ok(DebugAdapterBinary {
346 adapter_name: Self::ADAPTER_NAME.into(),
322 command: "command".into(),347 command: "command".into(),
323 arguments: None,348 arguments: None,
324 connection: None,349 connection: None,
325 envs: None,350 envs: None,
326 cwd: None,351 cwd: None,
352 request_args: self.request_args(config),
327 })353 })
328 }354 }
329 355
@@ -345,27 +371,10 @@ impl DebugAdapter for FakeAdapter { · 15 unmodified lines
345 async fn get_installed_binary(371 async fn get_installed_binary(
346 &self,372 &self,
347 _: &dyn DapDelegate,373 _: &dyn DapDelegate,
348 _: &DebugAdapterConfig,374 _: &DebugTaskDefinition,
349 _: Option<PathBuf>,375 _: Option<PathBuf>,
350 _: &mut AsyncApp,376 _: &mut AsyncApp,
351 ) -> Result<DebugAdapterBinary> {377 ) -> Result<DebugAdapterBinary> {
352 unimplemented!("get installed binary");378 unimplemented!("get installed binary");
353 }379 }
354
355 fn request_args(&self, config: &DebugTaskDefinition) -> Value {
356 use serde_json::json;
357 use task::DebugRequestType;
358
359 json!({
360 "request": match config.request {
361 DebugRequestType::Launch(_) => "launch",
362 DebugRequestType::Attach(_) => "attach",
363 },
364 "process_id": if let DebugRequestType::Attach(attach_config) = &config.request {
365 attach_config.process_id
366 } else {
367 None
368 },
369 })
370 }
371}380}
crates/dap/src/client.rs+38−19
@@ -39,7 +39,6 @@ impl SessionId { · 38 unmodified lines
39/// Represents a connection to the debug adapter process, either via stdout/stdin or a socket.39/// Represents a connection to the debug adapter process, either via stdout/stdin or a socket.
40pub struct DebugAdapterClient {40pub struct DebugAdapterClient {
41 id: SessionId,41 id: SessionId,
42 name: DebugAdapterName,
43 sequence_count: AtomicU64,42 sequence_count: AtomicU64,
44 binary: DebugAdapterBinary,43 binary: DebugAdapterBinary,
45 executor: BackgroundExecutor,44 executor: BackgroundExecutor,
@@ -51,7 +50,6 @@ pub type DapMessageHandler = Box<dyn FnMut(Message) + 'static + Send + Sync>; · 5 unmodified lines
51impl DebugAdapterClient {50impl DebugAdapterClient {
52 pub async fn start(51 pub async fn start(
53 id: SessionId,52 id: SessionId,
54 name: DebugAdapterName,
55 binary: DebugAdapterBinary,53 binary: DebugAdapterBinary,
56 message_handler: DapMessageHandler,54 message_handler: DapMessageHandler,
57 cx: AsyncApp,55 cx: AsyncApp,
@@ -60,7 +58,6 @@ impl DebugAdapterClient { · 2 unmodified lines
60 TransportDelegate::start(&binary, cx.clone()).await?;58 TransportDelegate::start(&binary, cx.clone()).await?;
61 let this = Self {59 let this = Self {
62 id,60 id,
63 name,
64 binary,61 binary,
65 transport_delegate,62 transport_delegate,
66 sequence_count: AtomicU64::new(1),63 sequence_count: AtomicU64::new(1),
@@ -91,6 +88,7 @@ impl DebugAdapterClient { · 24 unmodified lines
91 ) -> Result<Self> {88 ) -> Result<Self> {
92 let binary = match self.transport_delegate.transport() {89 let binary = match self.transport_delegate.transport() {
93 crate::transport::Transport::Tcp(tcp_transport) => DebugAdapterBinary {90 crate::transport::Transport::Tcp(tcp_transport) => DebugAdapterBinary {
91 adapter_name: binary.adapter_name,
94 command: binary.command,92 command: binary.command,
95 arguments: binary.arguments,93 arguments: binary.arguments,
96 envs: binary.envs,94 envs: binary.envs,
@@ -100,11 +98,12 @@ impl DebugAdapterClient { · 3 unmodified lines
100 port: tcp_transport.port,98 port: tcp_transport.port,
101 timeout: Some(tcp_transport.timeout),99 timeout: Some(tcp_transport.timeout),
102 }),100 }),
101 request_args: binary.request_args,
103 },102 },
104 _ => self.binary.clone(),103 _ => self.binary.clone(),
105 };104 };
106 105
107 Self::start(session_id, self.name(), binary, message_handler, cx).await106 Self::start(session_id, binary, message_handler, cx).await
108 }107 }
109 108
110 async fn handle_receive_messages(109 async fn handle_receive_messages(
@@ -189,7 +188,17 @@ impl DebugAdapterClient { · 78 unmodified lines
189 188
190 let response = response??;189 let response = response??;
191 match response.success {190 match response.success {
192 true => Ok(serde_json::from_value(response.body.unwrap_or_default())?),191 true => {
192 if let Some(json) = response.body {
193 Ok(serde_json::from_value(json)?)
194 // Note: dap types configure themselves to return `None` when an empty object is received,
195 // which then fails here...
196 } else if let Ok(result) = serde_json::from_value(serde_json::Value::Object(Default::default())) {
197 Ok(result)
198 } else {
199 Ok(serde_json::from_value(Default::default())?)
200 }
201 }
193 false => Err(anyhow!("Request failed: {}", response.message.unwrap_or_default())),202 false => Err(anyhow!("Request failed: {}", response.message.unwrap_or_default())),
194 }203 }
195 }204 }
@@ -211,7 +220,7 @@ impl DebugAdapterClient { · 15 unmodified lines
211 }220 }
212 221
213 pub fn name(&self) -> DebugAdapterName {222 pub fn name(&self) -> DebugAdapterName {
214 self.name.clone()223 self.binary.adapter_name.clone()
215 }224 }
216 pub fn binary(&self) -> &DebugAdapterBinary {225 pub fn binary(&self) -> &DebugAdapterBinary {
217 &self.binary226 &self.binary
@@ -238,14 +247,14 @@ impl DebugAdapterClient { · 20 unmodified lines
238 }247 }
239 248
240 #[cfg(any(test, feature = "test-support"))]249 #[cfg(any(test, feature = "test-support"))]
241 pub async fn on_request<R: dap_types::requests::Request, F>(&self, handler: F)250 pub fn on_request<R: dap_types::requests::Request, F>(&self, handler: F)
242 where251 where
243 F: 'static252 F: 'static
244 + Send253 + Send
245 + FnMut(u64, R::Arguments) -> Result<R::Response, dap_types::ErrorResponse>,254 + FnMut(u64, R::Arguments) -> Result<R::Response, dap_types::ErrorResponse>,
246 {255 {
247 let transport = self.transport_delegate.transport().as_fake();256 let transport = self.transport_delegate.transport().as_fake();
248 transport.on_request::<R, F>(handler).await;257 transport.on_request::<R, F>(handler);
249 }258 }
250 259
251 #[cfg(any(test, feature = "test-support"))]260 #[cfg(any(test, feature = "test-support"))]
@@ -282,7 +291,7 @@ mod tests { · 30 unmodified lines
282 use crate::{client::DebugAdapterClient, debugger_settings::DebuggerSettings};291 use crate::{client::DebugAdapterClient, debugger_settings::DebuggerSettings};
283 use dap_types::{292 use dap_types::{
284 Capabilities, InitializeRequestArguments, InitializeRequestArgumentsPathFormat,293 Capabilities, InitializeRequestArguments, InitializeRequestArgumentsPathFormat,
285 RunInTerminalRequestArguments,294 RunInTerminalRequestArguments, StartDebuggingRequestArguments,
286 messages::Events,295 messages::Events,
287 requests::{Initialize, Request, RunInTerminal},296 requests::{Initialize, Request, RunInTerminal},
288 };297 };
@@ -312,13 +321,17 @@ mod tests { · 23 unmodified lines
312 321
313 let client = DebugAdapterClient::start(322 let client = DebugAdapterClient::start(
314 crate::client::SessionId(1),323 crate::client::SessionId(1),
315 DebugAdapterName("adapter".into()),
316 DebugAdapterBinary {324 DebugAdapterBinary {
325 adapter_name: "adapter".into(),
317 command: "command".into(),326 command: "command".into(),
318 arguments: Default::default(),327 arguments: Default::default(),
319 envs: Default::default(),328 envs: Default::default(),
320 connection: None,329 connection: None,
321 cwd: None,330 cwd: None,
331 request_args: StartDebuggingRequestArguments {
332 configuration: serde_json::Value::Null,
333 request: dap_types::StartDebuggingRequestArgumentsRequest::Launch,
334 },
322 },335 },
323 Box::new(|_| panic!("Did not expect to hit this code path")),336 Box::new(|_| panic!("Did not expect to hit this code path")),
324 cx.to_async(),337 cx.to_async(),
@@ -326,14 +339,12 @@ mod tests { · 1 unmodified line
326 .await339 .await
327 .unwrap();340 .unwrap();
328 341
329 client342 client.on_request::<Initialize, _>(move |_, _| {
330 .on_request::<Initialize, _>(move |_, _| {343 Ok(dap_types::Capabilities {
331 Ok(dap_types::Capabilities {344 supports_configuration_done_request: Some(true),
332 supports_configuration_done_request: Some(true),345 ..Default::default()
333 ..Default::default()
334 })
335 })346 })
336 .await;347 });
337 348
338 cx.run_until_parked();349 cx.run_until_parked();
339 350
@@ -381,13 +392,17 @@ mod tests { · 41 unmodified lines
381 392
382 let client = DebugAdapterClient::start(393 let client = DebugAdapterClient::start(
383 crate::client::SessionId(1),394 crate::client::SessionId(1),
384 DebugAdapterName("adapter".into()),
385 DebugAdapterBinary {395 DebugAdapterBinary {
396 adapter_name: "adapter".into(),
386 command: "command".into(),397 command: "command".into(),
387 arguments: Default::default(),398 arguments: Default::default(),
388 envs: Default::default(),399 envs: Default::default(),
389 connection: None,400 connection: None,
390 cwd: None,401 cwd: None,
402 request_args: StartDebuggingRequestArguments {
403 configuration: serde_json::Value::Null,
404 request: dap_types::StartDebuggingRequestArgumentsRequest::Launch,
405 },
391 },406 },
392 Box::new({407 Box::new({
393 let called_event_handler = called_event_handler.clone();408 let called_event_handler = called_event_handler.clone();
@@ -431,13 +446,17 @@ mod tests { · 37 unmodified lines
431 446
432 let client = DebugAdapterClient::start(447 let client = DebugAdapterClient::start(
433 crate::client::SessionId(1),448 crate::client::SessionId(1),
434 DebugAdapterName("test-adapter".into()),
435 DebugAdapterBinary {449 DebugAdapterBinary {
450 adapter_name: "test-adapter".into(),
436 command: "command".into(),451 command: "command".into(),
437 arguments: Default::default(),452 arguments: Default::default(),
438 envs: Default::default(),453 envs: Default::default(),
439 connection: None,454 connection: None,
440 cwd: None,455 cwd: None,
456 request_args: dap_types::StartDebuggingRequestArguments {
457 configuration: serde_json::Value::Null,
458 request: dap_types::StartDebuggingRequestArgumentsRequest::Launch,
459 },
441 },460 },
442 Box::new({461 Box::new({
443 let called_event_handler = called_event_handler.clone();462 let called_event_handler = called_event_handler.clone();
crates/dap/src/transport.rs+45−50
@@ -699,14 +699,8 @@ impl StdioTransport { · 698 unmodified lines
699}699}
700 700
701#[cfg(any(test, feature = "test-support"))]701#[cfg(any(test, feature = "test-support"))]
702type RequestHandler = Box<702type RequestHandler =
703 dyn Send703 Box<dyn Send + FnMut(u64, serde_json::Value) -> dap_types::messages::Response>;
704 + FnMut(
705 u64,
706 serde_json::Value,
707 Arc<Mutex<async_pipe::PipeWriter>>,
708 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>>,
709>;
710 704
711#[cfg(any(test, feature = "test-support"))]705#[cfg(any(test, feature = "test-support"))]
712type ResponseHandler = Box<dyn Send + Fn(Response)>;706type ResponseHandler = Box<dyn Send + Fn(Response)>;
@@ -714,45 +708,41 @@ type ResponseHandler = Box<dyn Send + Fn(Response)>; · 1 unmodified line
714#[cfg(any(test, feature = "test-support"))]708#[cfg(any(test, feature = "test-support"))]
715pub struct FakeTransport {709pub struct FakeTransport {
716 // for sending fake response back from adapter side710 // for sending fake response back from adapter side
717 request_handlers: Arc<Mutex<HashMap<&'static str, RequestHandler>>>,711 request_handlers: Arc<parking_lot::Mutex<HashMap<&'static str, RequestHandler>>>,
718 // for reverse request responses712 // for reverse request responses
719 response_handlers: Arc<Mutex<HashMap<&'static str, ResponseHandler>>>,713 response_handlers: Arc<parking_lot::Mutex<HashMap<&'static str, ResponseHandler>>>,
720}714}
721 715
722#[cfg(any(test, feature = "test-support"))]716#[cfg(any(test, feature = "test-support"))]
723impl FakeTransport {717impl FakeTransport {
724 pub async fn on_request<R: dap_types::requests::Request, F>(&self, mut handler: F)718 pub fn on_request<R: dap_types::requests::Request, F>(&self, mut handler: F)
725 where719 where
726 F: 'static + Send + FnMut(u64, R::Arguments) -> Result<R::Response, ErrorResponse>,720 F: 'static + Send + FnMut(u64, R::Arguments) -> Result<R::Response, ErrorResponse>,
727 {721 {
728 self.request_handlers.lock().await.insert(722 self.request_handlers.lock().insert(
729 R::COMMAND,723 R::COMMAND,
730 Box::new(724 Box::new(move |seq, args| {
731 move |seq, args, writer: Arc<Mutex<async_pipe::PipeWriter>>| {725 let result = handler(seq, serde_json::from_value(args).unwrap());
732 let response = handler(seq, serde_json::from_value(args).unwrap());726 let response = match result {
733 727 Ok(response) => Response {
734 let message = serde_json::to_string(&Message::Response(Response {
735 seq: seq + 1,728 seq: seq + 1,
736 request_seq: seq,729 request_seq: seq,
737 success: response.as_ref().is_ok(),730 success: true,
738 command: R::COMMAND.into(),731 command: R::COMMAND.into(),
739 body: util::maybe!({ serde_json::to_value(response.ok()?).ok() }),732 body: Some(serde_json::to_value(response).unwrap()),
740 message: None,733 message: None,
741 }))734 },
742 .unwrap();735 Err(response) => Response {
743 736 seq: seq + 1,
744 let writer = writer.clone();737 request_seq: seq,
745 738 success: false,
746 Box::pin(async move {739 command: R::COMMAND.into(),
747 let mut writer = writer.lock().await;740 body: Some(serde_json::to_value(response).unwrap()),
748 writer741 message: None,
749 .write_all(TransportDelegate::build_rpc_message(message).as_bytes())742 },
750 .await743 };
751 .unwrap();744 response
752 writer.flush().await.unwrap();745 }),
753 })
754 },
755 ),
756 );746 );
757 }747 }
758 748
@@ -762,14 +752,13 @@ impl FakeTransport { · 3 unmodified lines
762 {752 {
763 self.response_handlers753 self.response_handlers
764 .lock()754 .lock()
765 .await
766 .insert(R::COMMAND, Box::new(handler));755 .insert(R::COMMAND, Box::new(handler));
767 }756 }
768 757
769 async fn start(cx: AsyncApp) -> Result<(TransportPipe, Self)> {758 async fn start(cx: AsyncApp) -> Result<(TransportPipe, Self)> {
770 let this = Self {759 let this = Self {
771 request_handlers: Arc::new(Mutex::new(HashMap::default())),760 request_handlers: Arc::new(parking_lot::Mutex::new(HashMap::default())),
772 response_handlers: Arc::new(Mutex::new(HashMap::default())),761 response_handlers: Arc::new(parking_lot::Mutex::new(HashMap::default())),
773 };762 };
774 use dap_types::requests::{Request, RunInTerminal, StartDebugging};763 use dap_types::requests::{Request, RunInTerminal, StartDebugging};
775 use serde_json::json;764 use serde_json::json;
@@ -816,23 +805,31 @@ impl FakeTransport { · 40 unmodified lines
816 .unwrap();805 .unwrap();
817 writer.flush().await.unwrap();806 writer.flush().await.unwrap();
818 } else {807 } else {
819 if let Some(handle) = request_handlers808 let response = if let Some(handle) = request_handlers
820 .lock()809 .lock()
821 .await
822 .get_mut(request.command.as_str())810 .get_mut(request.command.as_str())
823 {811 {
824 handle(812 handle(
825 request.seq,813 request.seq,
826 request.arguments.unwrap_or(json!({})),814 request.arguments.unwrap_or(json!({})),
827 stdout_writer.clone(),
828 )815 )
829 .await;
830 } else {816 } else {
831 log::error!(817 panic!("No request handler for {}", request.command);
832 "No request handler for {}",818 };
833 request.command819 let message =
834 );820 serde_json::to_string(&Message::Response(response))
835 }821 .unwrap();
822
823 let mut writer = stdout_writer.lock().await;
824
825 writer
826 .write_all(
827 TransportDelegate::build_rpc_message(message)
828 .as_bytes(),
829 )
830 .await
831 .unwrap();
832 writer.flush().await.unwrap();
836 }833 }
837 }834 }
838 Message::Event(event) => {835 Message::Event(event) => {
@@ -850,10 +847,8 @@ impl FakeTransport { · 11 unmodified lines
850 writer.flush().await.unwrap();847 writer.flush().await.unwrap();
851 }848 }
852 Message::Response(response) => {849 Message::Response(response) => {
853 if let Some(handle) = response_handlers850 if let Some(handle) =
854 .lock()851 response_handlers.lock().get(response.command.as_str())
855 .await
856 .get(response.command.as_str())
857 {852 {
858 handle(response);853 handle(response);
859 } else {854 } else {
crates/dap_adapters/src/codelldb.rs+42−35
@@ -4,7 +4,7 @@ use anyhow::{Result, bail}; · 3 unmodified lines
4use async_trait::async_trait;4use async_trait::async_trait;
5use dap::adapters::latest_github_release;5use dap::adapters::latest_github_release;
6use gpui::AsyncApp;6use gpui::AsyncApp;
7use task::{DebugAdapterConfig, DebugRequestType, DebugTaskDefinition};7use task::{DebugRequestType, DebugTaskDefinition};
8 8
9use crate::*;9use crate::*;
10 10
@@ -15,6 +15,42 @@ pub(crate) struct CodeLldbDebugAdapter { · 4 unmodified lines
15 15
16impl CodeLldbDebugAdapter {16impl CodeLldbDebugAdapter {
17 const ADAPTER_NAME: &'static str = "CodeLLDB";17 const ADAPTER_NAME: &'static str = "CodeLLDB";
18
19 fn request_args(&self, config: &DebugTaskDefinition) -> dap::StartDebuggingRequestArguments {
20 let mut configuration = json!({
21 "request": match config.request {
22 DebugRequestType::Launch(_) => "launch",
23 DebugRequestType::Attach(_) => "attach",
24 },
25 });
26 let map = configuration.as_object_mut().unwrap();
27 // CodeLLDB uses `name` for a terminal label.
28 map.insert("name".into(), Value::String(config.label.clone()));
29 let request = config.request.to_dap();
30 match &config.request {
31 DebugRequestType::Attach(attach) => {
32 map.insert("pid".into(), attach.process_id.into());
33 }
34 DebugRequestType::Launch(launch) => {
35 map.insert("program".into(), launch.program.clone().into());
36
37 if !launch.args.is_empty() {
38 map.insert("args".into(), launch.args.clone().into());
39 }
40
41 if let Some(stop_on_entry) = config.stop_on_entry {
42 map.insert("stopOnEntry".into(), stop_on_entry.into());
43 }
44 if let Some(cwd) = launch.cwd.as_ref() {
45 map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
46 }
47 }
48 }
49 dap::StartDebuggingRequestArguments {
50 request,
51 configuration,
52 }
53 }
18}54}
19 55
20#[async_trait(?Send)]56#[async_trait(?Send)]
@@ -86,7 +122,7 @@ impl DebugAdapter for CodeLldbDebugAdapter { · 65 unmodified lines
86 async fn get_installed_binary(122 async fn get_installed_binary(
87 &self,123 &self,
88 _: &dyn DapDelegate,124 _: &dyn DapDelegate,
89 _: &DebugAdapterConfig,125 config: &DebugTaskDefinition,
90 _: Option<PathBuf>,126 _: Option<PathBuf>,
91 _: &mut AsyncApp,127 _: &mut AsyncApp,
92 ) -> Result<DebugAdapterBinary> {128 ) -> Result<DebugAdapterBinary> {
@@ -111,39 +147,10 @@ impl DebugAdapter for CodeLldbDebugAdapter { · 18 unmodified lines
111 .to_string()147 .to_string()
112 .into(),148 .into(),
113 ]),149 ]),
114 ..Default::default()150 request_args: self.request_args(config),
151 adapter_name: "test".into(),
152 envs: None,
153 connection: None,
115 })154 })
116 }155 }
117
118 fn request_args(&self, config: &DebugTaskDefinition) -> Value {
119 let mut args = json!({
120 "request": match config.request {
121 DebugRequestType::Launch(_) => "launch",
122 DebugRequestType::Attach(_) => "attach",
123 },
124 });
125 let map = args.as_object_mut().unwrap();
126 // CodeLLDB uses `name` for a terminal label.
127 map.insert("name".into(), Value::String(config.label.clone()));
128 match &config.request {
129 DebugRequestType::Attach(attach) => {
130 map.insert("pid".into(), attach.process_id.into());
131 }
132 DebugRequestType::Launch(launch) => {
133 map.insert("program".into(), launch.program.clone().into());
134
135 if !launch.args.is_empty() {
136 map.insert("args".into(), launch.args.clone().into());
137 }
138
139 if let Some(stop_on_entry) = config.stop_on_entry {
140 map.insert("stopOnEntry".into(), stop_on_entry.into());
141 }
142 if let Some(cwd) = launch.cwd.as_ref() {
143 map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
144 }
145 }
146 }
147 args
148 }
149}156}
crates/dap_adapters/src/dap_adapters.rs+15−2
@@ -12,7 +12,7 @@ use anyhow::{Result, anyhow}; · 11 unmodified lines
12use async_trait::async_trait;12use async_trait::async_trait;
13use codelldb::CodeLldbDebugAdapter;13use codelldb::CodeLldbDebugAdapter;
14use dap::{14use dap::{
15 DapRegistry,15 DapRegistry, DebugRequestType,
16 adapters::{16 adapters::{
17 self, AdapterVersion, DapDelegate, DebugAdapter, DebugAdapterBinary, DebugAdapterName,17 self, AdapterVersion, DapDelegate, DebugAdapter, DebugAdapterBinary, DebugAdapterName,
18 GithubRepo,18 GithubRepo,
@@ -25,7 +25,7 @@ use lldb::LldbDebugAdapter; · 6 unmodified lines
25use php::PhpDebugAdapter;25use php::PhpDebugAdapter;
26use python::PythonDebugAdapter;26use python::PythonDebugAdapter;
27use serde_json::{Value, json};27use serde_json::{Value, json};
28use task::{DebugAdapterConfig, TCPHost};28use task::TCPHost;
29 29
30pub fn init(registry: Arc<DapRegistry>) {30pub fn init(registry: Arc<DapRegistry>) {
31 registry.add_adapter(Arc::from(CodeLldbDebugAdapter::default()));31 registry.add_adapter(Arc::from(CodeLldbDebugAdapter::default()));
@@ -51,3 +51,16 @@ pub(crate) async fn configure_tcp_connection( · 19 unmodified lines
51 51
52 Ok((host, port, timeout))52 Ok((host, port, timeout))
53}53}
54
55trait ToDap {
56 fn to_dap(&self) -> dap::StartDebuggingRequestArgumentsRequest;
57}
58
59impl ToDap for DebugRequestType {
60 fn to_dap(&self) -> dap::StartDebuggingRequestArgumentsRequest {
61 match self {
62 Self::Launch(_) => dap::StartDebuggingRequestArgumentsRequest::Launch,
63 Self::Attach(_) => dap::StartDebuggingRequestArgumentsRequest::Attach,
64 }
65 }
66}
crates/dap_adapters/src/gdb.rs+44−38
@@ -2,8 +2,9 @@ use std::ffi::OsStr; · 1 unmodified line
2 2
3use anyhow::{Result, bail};3use anyhow::{Result, bail};
4use async_trait::async_trait;4use async_trait::async_trait;
5use dap::StartDebuggingRequestArguments;
5use gpui::AsyncApp;6use gpui::AsyncApp;
6use task::{DebugAdapterConfig, DebugRequestType, DebugTaskDefinition};7use task::{DebugRequestType, DebugTaskDefinition};
7 8
8use crate::*;9use crate::*;
9 10
@@ -12,6 +13,44 @@ pub(crate) struct GdbDebugAdapter; · 2 unmodified lines
12 13
13impl GdbDebugAdapter {14impl GdbDebugAdapter {
14 const ADAPTER_NAME: &'static str = "GDB";15 const ADAPTER_NAME: &'static str = "GDB";
16
17 fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
18 let mut args = json!({
19 "request": match config.request {
20 DebugRequestType::Launch(_) => "launch",
21 DebugRequestType::Attach(_) => "attach",
22 },
23 });
24
25 let map = args.as_object_mut().unwrap();
26 match &config.request {
27 DebugRequestType::Attach(attach) => {
28 map.insert("pid".into(), attach.process_id.into());
29 }
30
31 DebugRequestType::Launch(launch) => {
32 map.insert("program".into(), launch.program.clone().into());
33
34 if !launch.args.is_empty() {
35 map.insert("args".into(), launch.args.clone().into());
36 }
37
38 if let Some(stop_on_entry) = config.stop_on_entry {
39 map.insert(
40 "stopAtBeginningOfMainSubprogram".into(),
41 stop_on_entry.into(),
42 );
43 }
44 if let Some(cwd) = launch.cwd.as_ref() {
45 map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
46 }
47 }
48 }
49 StartDebuggingRequestArguments {
50 configuration: args,
51 request: config.request.to_dap(),
52 }
53 }
15}54}
16 55
17#[async_trait(?Send)]56#[async_trait(?Send)]
@@ -23,7 +62,7 @@ impl DebugAdapter for GdbDebugAdapter { · 5 unmodified lines
23 async fn get_binary(62 async fn get_binary(
24 &self,63 &self,
25 delegate: &dyn DapDelegate,64 delegate: &dyn DapDelegate,
26 _: &DebugAdapterConfig,65 config: &DebugTaskDefinition,
27 user_installed_path: Option<std::path::PathBuf>,66 user_installed_path: Option<std::path::PathBuf>,
28 _: &mut AsyncApp,67 _: &mut AsyncApp,
29 ) -> Result<DebugAdapterBinary> {68 ) -> Result<DebugAdapterBinary> {
@@ -43,11 +82,13 @@ impl DebugAdapter for GdbDebugAdapter { · 13 unmodified lines
43 let gdb_path = user_setting_path.unwrap_or(gdb_path?);82 let gdb_path = user_setting_path.unwrap_or(gdb_path?);
44 83
45 Ok(DebugAdapterBinary {84 Ok(DebugAdapterBinary {
85 adapter_name: Self::ADAPTER_NAME.into(),
46 command: gdb_path,86 command: gdb_path,
47 arguments: Some(vec!["-i=dap".into()]),87 arguments: Some(vec!["-i=dap".into()]),
48 envs: None,88 envs: None,
49 cwd: None,89 cwd: None,
50 connection: None,90 connection: None,
91 request_args: self.request_args(config),
51 })92 })
52 }93 }
53 94
@@ -66,45 +107,10 @@ impl DebugAdapter for GdbDebugAdapter { · 12 unmodified lines
66 async fn get_installed_binary(107 async fn get_installed_binary(
67 &self,108 &self,
68 _: &dyn DapDelegate,109 _: &dyn DapDelegate,
69 _: &DebugAdapterConfig,110 _: &DebugTaskDefinition,
70 _: Option<std::path::PathBuf>,111 _: Option<std::path::PathBuf>,
71 _: &mut AsyncApp,112 _: &mut AsyncApp,
72 ) -> Result<DebugAdapterBinary> {113 ) -> Result<DebugAdapterBinary> {
73 unimplemented!("GDB cannot be installed by Zed (yet)")114 unimplemented!("GDB cannot be installed by Zed (yet)")
74 }115 }
75
76 fn request_args(&self, config: &DebugTaskDefinition) -> Value {
77 let mut args = json!({
78 "request": match config.request {
79 DebugRequestType::Launch(_) => "launch",
80 DebugRequestType::Attach(_) => "attach",
81 },
82 });
83
84 let map = args.as_object_mut().unwrap();
85 match &config.request {
86 DebugRequestType::Attach(attach) => {
87 map.insert("pid".into(), attach.process_id.into());
88 }
89
90 DebugRequestType::Launch(launch) => {
91 map.insert("program".into(), launch.program.clone().into());
92
93 if !launch.args.is_empty() {
94 map.insert("args".into(), launch.args.clone().into());
95 }
96
97 if let Some(stop_on_entry) = config.stop_on_entry {
98 map.insert(
99 "stopAtBeginningOfMainSubprogram".into(),
100 stop_on_entry.into(),
101 );
102 }
103 if let Some(cwd) = launch.cwd.as_ref() {
104 map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
105 }
106 }
107 }
108 args
109 }
110}116}
crates/dap_adapters/src/go.rs+30−25
@@ -1,3 +1,4 @@
1use dap::StartDebuggingRequestArguments;
1use gpui::AsyncApp;2use gpui::AsyncApp;
2use std::{ffi::OsStr, path::PathBuf};3use std::{ffi::OsStr, path::PathBuf};
3use task::DebugTaskDefinition;4use task::DebugTaskDefinition;
@@ -9,6 +10,31 @@ pub(crate) struct GoDebugAdapter; · 5 unmodified lines
9 10
10impl GoDebugAdapter {11impl GoDebugAdapter {
11 const ADAPTER_NAME: &'static str = "Delve";12 const ADAPTER_NAME: &'static str = "Delve";
13 fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
14 let mut args = match &config.request {
15 dap::DebugRequestType::Attach(attach_config) => {
16 json!({
17 "processId": attach_config.process_id,
18 })
19 }
20 dap::DebugRequestType::Launch(launch_config) => json!({
21 "program": launch_config.program,
22 "cwd": launch_config.cwd,
23 "args": launch_config.args
24 }),
25 };
26
27 let map = args.as_object_mut().unwrap();
28
29 if let Some(stop_on_entry) = config.stop_on_entry {
30 map.insert("stopOnEntry".into(), stop_on_entry.into());
31 }
32
33 StartDebuggingRequestArguments {
34 configuration: args,
35 request: config.request.to_dap(),
36 }
37 }
12}38}
13 39
14#[async_trait(?Send)]40#[async_trait(?Send)]
@@ -20,7 +46,7 @@ impl DebugAdapter for GoDebugAdapter { · 5 unmodified lines
20 async fn get_binary(46 async fn get_binary(
21 &self,47 &self,
22 delegate: &dyn DapDelegate,48 delegate: &dyn DapDelegate,
23 config: &DebugAdapterConfig,49 config: &DebugTaskDefinition,
24 user_installed_path: Option<PathBuf>,50 user_installed_path: Option<PathBuf>,
25 cx: &mut AsyncApp,51 cx: &mut AsyncApp,
26 ) -> Result<DebugAdapterBinary> {52 ) -> Result<DebugAdapterBinary> {
@@ -53,7 +79,7 @@ impl DebugAdapter for GoDebugAdapter { · 26 unmodified lines
53 async fn get_installed_binary(79 async fn get_installed_binary(
54 &self,80 &self,
55 delegate: &dyn DapDelegate,81 delegate: &dyn DapDelegate,
56 config: &DebugAdapterConfig,82 config: &DebugTaskDefinition,
57 _: Option<PathBuf>,83 _: Option<PathBuf>,
58 _: &mut AsyncApp,84 _: &mut AsyncApp,
59 ) -> Result<DebugAdapterBinary> {85 ) -> Result<DebugAdapterBinary> {
@@ -66,6 +92,7 @@ impl DebugAdapter for GoDebugAdapter { · 6 unmodified lines
66 let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;92 let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
67 93
68 Ok(DebugAdapterBinary {94 Ok(DebugAdapterBinary {
95 adapter_name: self.name(),
69 command: delve_path,96 command: delve_path,
70 arguments: Some(vec![97 arguments: Some(vec![
71 "dap".into(),98 "dap".into(),
@@ -79,29 +106,7 @@ impl DebugAdapter for GoDebugAdapter { · 7 unmodified lines
79 port,106 port,
80 timeout,107 timeout,
81 }),108 }),
109 request_args: self.request_args(config),
82 })110 })
83 }111 }
84
85 fn request_args(&self, config: &DebugTaskDefinition) -> Value {
86 let mut args = match &config.request {
87 dap::DebugRequestType::Attach(attach_config) => {
88 json!({
89 "processId": attach_config.process_id,
90 })
91 }
92 dap::DebugRequestType::Launch(launch_config) => json!({
93 "program": launch_config.program,
94 "cwd": launch_config.cwd,
95 "args": launch_config.args
96 }),
97 };
98
99 let map = args.as_object_mut().unwrap();
100
101 if let Some(stop_on_entry) = config.stop_on_entry {
102 map.insert("stopOnEntry".into(), stop_on_entry.into());
103 }
104
105 args
106 }
107}112}
crates/dap_adapters/src/javascript.rs+38−32
@@ -1,4 +1,5 @@
1use adapters::latest_github_release;1use adapters::latest_github_release;
2use dap::StartDebuggingRequestArguments;
2use gpui::AsyncApp;3use gpui::AsyncApp;
3use std::path::PathBuf;4use std::path::PathBuf;
4use task::{DebugRequestType, DebugTaskDefinition};5use task::{DebugRequestType, DebugTaskDefinition};
@@ -12,6 +13,40 @@ impl JsDebugAdapter { · 7 unmodified lines
12 const ADAPTER_NAME: &'static str = "JavaScript";13 const ADAPTER_NAME: &'static str = "JavaScript";
13 const ADAPTER_NPM_NAME: &'static str = "vscode-js-debug";14 const ADAPTER_NPM_NAME: &'static str = "vscode-js-debug";
14 const ADAPTER_PATH: &'static str = "js-debug/src/dapDebugServer.js";15 const ADAPTER_PATH: &'static str = "js-debug/src/dapDebugServer.js";
16
17 fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
18 let mut args = json!({
19 "type": "pwa-node",
20 "request": match config.request {
21 DebugRequestType::Launch(_) => "launch",
22 DebugRequestType::Attach(_) => "attach",
23 },
24 });
25 let map = args.as_object_mut().unwrap();
26 match &config.request {
27 DebugRequestType::Attach(attach) => {
28 map.insert("processId".into(), attach.process_id.into());
29 }
30 DebugRequestType::Launch(launch) => {
31 map.insert("program".into(), launch.program.clone().into());
32
33 if !launch.args.is_empty() {
34 map.insert("args".into(), launch.args.clone().into());
35 }
36
37 if let Some(stop_on_entry) = config.stop_on_entry {
38 map.insert("stopOnEntry".into(), stop_on_entry.into());
39 }
40 if let Some(cwd) = launch.cwd.as_ref() {
41 map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
42 }
43 }
44 }
45 StartDebuggingRequestArguments {
46 configuration: args,
47 request: config.request.to_dap(),
48 }
49 }
15}50}
16 51
17#[async_trait(?Send)]52#[async_trait(?Send)]
@@ -49,7 +84,7 @@ impl DebugAdapter for JsDebugAdapter { · 31 unmodified lines
49 async fn get_installed_binary(84 async fn get_installed_binary(
50 &self,85 &self,
51 delegate: &dyn DapDelegate,86 delegate: &dyn DapDelegate,
52 config: &DebugAdapterConfig,87 config: &DebugTaskDefinition,
53 user_installed_path: Option<PathBuf>,88 user_installed_path: Option<PathBuf>,
54 _: &mut AsyncApp,89 _: &mut AsyncApp,
55 ) -> Result<DebugAdapterBinary> {90 ) -> Result<DebugAdapterBinary> {
@@ -71,6 +106,7 @@ impl DebugAdapter for JsDebugAdapter { · 15 unmodified lines
71 let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;106 let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
72 107
73 Ok(DebugAdapterBinary {108 Ok(DebugAdapterBinary {
109 adapter_name: self.name(),
74 command: delegate110 command: delegate
75 .node_runtime()111 .node_runtime()
76 .binary_path()112 .binary_path()
@@ -89,6 +125,7 @@ impl DebugAdapter for JsDebugAdapter { · 12 unmodified lines
89 port,125 port,
90 timeout,126 timeout,
91 }),127 }),
128 request_args: self.request_args(config),
92 })129 })
93 }130 }
94 131
@@ -107,35 +144,4 @@ impl DebugAdapter for JsDebugAdapter { · 12 unmodified lines
107 144
108 return Ok(());145 return Ok(());
109 }146 }
110
111 fn request_args(&self, config: &DebugTaskDefinition) -> Value {
112 let mut args = json!({
113 "type": "pwa-node",
114 "request": match config.request {
115 DebugRequestType::Launch(_) => "launch",
116 DebugRequestType::Attach(_) => "attach",
117 },
118 });
119 let map = args.as_object_mut().unwrap();
120 match &config.request {
121 DebugRequestType::Attach(attach) => {
122 map.insert("processId".into(), attach.process_id.into());
123 }
124 DebugRequestType::Launch(launch) => {
125 map.insert("program".into(), launch.program.clone().into());
126
127 if !launch.args.is_empty() {
128 map.insert("args".into(), launch.args.clone().into());
129 }
130
131 if let Some(stop_on_entry) = config.stop_on_entry {
132 map.insert("stopOnEntry".into(), stop_on_entry.into());
133 }
134 if let Some(cwd) = launch.cwd.as_ref() {
135 map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
136 }
137 }
138 }
139 args
140 }
141}147}
crates/dap_adapters/src/lldb.rs+39−33
@@ -3,7 +3,7 @@ use std::{ffi::OsStr, path::PathBuf}; · 2 unmodified lines
3use anyhow::Result;3use anyhow::Result;
4use async_trait::async_trait;4use async_trait::async_trait;
5use gpui::AsyncApp;5use gpui::AsyncApp;
6use task::{DebugAdapterConfig, DebugRequestType, DebugTaskDefinition};6use task::{DebugRequestType, DebugTaskDefinition};
7 7
8use crate::*;8use crate::*;
9 9
@@ -12,6 +12,40 @@ pub(crate) struct LldbDebugAdapter; · 2 unmodified lines
12 12
13impl LldbDebugAdapter {13impl LldbDebugAdapter {
14 const ADAPTER_NAME: &'static str = "LLDB";14 const ADAPTER_NAME: &'static str = "LLDB";
15
16 fn request_args(&self, config: &DebugTaskDefinition) -> dap::StartDebuggingRequestArguments {
17 let request = config.request.to_dap();
18 let mut args = json!({
19 "request": match config.request {
20 DebugRequestType::Launch(_) => "launch",
21 DebugRequestType::Attach(_) => "attach",
22 },
23 });
24 let map = args.as_object_mut().unwrap();
25 match &config.request {
26 DebugRequestType::Attach(attach) => {
27 map.insert("pid".into(), attach.process_id.into());
28 }
29 DebugRequestType::Launch(launch) => {
30 map.insert("program".into(), launch.program.clone().into());
31
32 if !launch.args.is_empty() {
33 map.insert("args".into(), launch.args.clone().into());
34 }
35
36 if let Some(stop_on_entry) = config.stop_on_entry {
37 map.insert("stopOnEntry".into(), stop_on_entry.into());
38 }
39 if let Some(cwd) = launch.cwd.as_ref() {
40 map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
41 }
42 }
43 }
44 dap::StartDebuggingRequestArguments {
45 request,
46 configuration: args,
47 }
48 }
15}49}
16 50
17#[async_trait(?Send)]51#[async_trait(?Send)]
@@ -23,7 +57,7 @@ impl DebugAdapter for LldbDebugAdapter { · 5 unmodified lines
23 async fn get_binary(57 async fn get_binary(
24 &self,58 &self,
25 delegate: &dyn DapDelegate,59 delegate: &dyn DapDelegate,
26 _: &DebugAdapterConfig,60 config: &DebugTaskDefinition,
27 user_installed_path: Option<PathBuf>,61 user_installed_path: Option<PathBuf>,
28 _: &mut AsyncApp,62 _: &mut AsyncApp,
29 ) -> Result<DebugAdapterBinary> {63 ) -> Result<DebugAdapterBinary> {
@@ -37,11 +71,13 @@ impl DebugAdapter for LldbDebugAdapter { · 7 unmodified lines
37 };71 };
38 72
39 Ok(DebugAdapterBinary {73 Ok(DebugAdapterBinary {
74 adapter_name: Self::ADAPTER_NAME.into(),
40 command: lldb_dap_path,75 command: lldb_dap_path,
41 arguments: None,76 arguments: None,
42 envs: None,77 envs: None,
43 cwd: None,78 cwd: None,
44 connection: None,79 connection: None,
80 request_args: self.request_args(config),
45 })81 })
46 }82 }
47 83
@@ -60,40 +96,10 @@ impl DebugAdapter for LldbDebugAdapter { · 12 unmodified lines
60 async fn get_installed_binary(96 async fn get_installed_binary(
61 &self,97 &self,
62 _: &dyn DapDelegate,98 _: &dyn DapDelegate,
63 _: &DebugAdapterConfig,99 _: &DebugTaskDefinition,
64 _: Option<PathBuf>,100 _: Option<PathBuf>,
65 _: &mut AsyncApp,101 _: &mut AsyncApp,
66 ) -> Result<DebugAdapterBinary> {102 ) -> Result<DebugAdapterBinary> {
67 unimplemented!("LLDB debug adapter cannot be installed by Zed (yet)")103 unimplemented!("LLDB debug adapter cannot be installed by Zed (yet)")
68 }104 }
69
70 fn request_args(&self, config: &DebugTaskDefinition) -> Value {
71 let mut args = json!({
72 "request": match config.request {
73 DebugRequestType::Launch(_) => "launch",
74 DebugRequestType::Attach(_) => "attach",
75 },
76 });
77 let map = args.as_object_mut().unwrap();
78 match &config.request {
79 DebugRequestType::Attach(attach) => {
80 map.insert("pid".into(), attach.process_id.into());
81 }
82 DebugRequestType::Launch(launch) => {
83 map.insert("program".into(), launch.program.clone().into());
84
85 if !launch.args.is_empty() {
86 map.insert("args".into(), launch.args.clone().into());
87 }
88
89 if let Some(stop_on_entry) = config.stop_on_entry {
90 map.insert("stopOnEntry".into(), stop_on_entry.into());
91 }
92 if let Some(cwd) = launch.cwd.as_ref() {
93 map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
94 }
95 }
96 }
97 args
98 }
99}105}
crates/dap_adapters/src/php.rs+25−18
@@ -13,6 +13,28 @@ impl PhpDebugAdapter { · 12 unmodified lines
13 const ADAPTER_NAME: &'static str = "PHP";13 const ADAPTER_NAME: &'static str = "PHP";
14 const ADAPTER_PACKAGE_NAME: &'static str = "vscode-php-debug";14 const ADAPTER_PACKAGE_NAME: &'static str = "vscode-php-debug";
15 const ADAPTER_PATH: &'static str = "extension/out/phpDebug.js";15 const ADAPTER_PATH: &'static str = "extension/out/phpDebug.js";
16
17 fn request_args(
18 &self,
19 config: &DebugTaskDefinition,
20 ) -> Result<dap::StartDebuggingRequestArguments> {
21 match &config.request {
22 dap::DebugRequestType::Attach(_) => {
23 anyhow::bail!("php adapter does not support attaching")
24 }
25 dap::DebugRequestType::Launch(launch_config) => {
26 Ok(dap::StartDebuggingRequestArguments {
27 configuration: json!({
28 "program": launch_config.program,
29 "cwd": launch_config.cwd,
30 "args": launch_config.args,
31 "stopOnEntry": config.stop_on_entry.unwrap_or_default(),
32 }),
33 request: config.request.to_dap(),
34 })
35 }
36 }
37 }
16}38}
17 39
18#[async_trait(?Send)]40#[async_trait(?Send)]
@@ -50,7 +72,7 @@ impl DebugAdapter for PhpDebugAdapter { · 31 unmodified lines
50 async fn get_installed_binary(72 async fn get_installed_binary(
51 &self,73 &self,
52 delegate: &dyn DapDelegate,74 delegate: &dyn DapDelegate,
53 config: &DebugAdapterConfig,75 config: &DebugTaskDefinition,
54 user_installed_path: Option<PathBuf>,76 user_installed_path: Option<PathBuf>,
55 _: &mut AsyncApp,77 _: &mut AsyncApp,
56 ) -> Result<DebugAdapterBinary> {78 ) -> Result<DebugAdapterBinary> {
@@ -72,6 +94,7 @@ impl DebugAdapter for PhpDebugAdapter { · 15 unmodified lines
72 let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;94 let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
73 95
74 Ok(DebugAdapterBinary {96 Ok(DebugAdapterBinary {
97 adapter_name: self.name(),
75 command: delegate98 command: delegate
76 .node_runtime()99 .node_runtime()
77 .binary_path()100 .binary_path()
@@ -89,6 +112,7 @@ impl DebugAdapter for PhpDebugAdapter { · 11 unmodified lines
89 }),112 }),
90 cwd: None,113 cwd: None,
91 envs: None,114 envs: None,
115 request_args: self.request_args(config)?,
92 })116 })
93 }117 }
94 118
@@ -107,21 +131,4 @@ impl DebugAdapter for PhpDebugAdapter { · 12 unmodified lines
107 131
108 Ok(())132 Ok(())
109 }133 }
110
111 fn request_args(&self, config: &DebugTaskDefinition) -> Value {
112 match &config.request {
113 dap::DebugRequestType::Attach(_) => {
114 // php adapter does not support attaching
115 json!({})
116 }
117 dap::DebugRequestType::Launch(launch_config) => {
118 json!({
119 "program": launch_config.program,
120 "cwd": launch_config.cwd,
121 "args": launch_config.args,
122 "stopOnEntry": config.stop_on_entry.unwrap_or_default(),
123 })
124 }
125 }
126 }
127}134}
crates/dap_adapters/src/python.rs+36−31
@@ -1,5 +1,5 @@
1use crate::*;1use crate::*;
2use dap::DebugRequestType;2use dap::{DebugRequestType, StartDebuggingRequestArguments};
3use gpui::AsyncApp;3use gpui::AsyncApp;
4use std::{ffi::OsStr, path::PathBuf};4use std::{ffi::OsStr, path::PathBuf};
5use task::DebugTaskDefinition;5use task::DebugTaskDefinition;
@@ -12,6 +12,38 @@ impl PythonDebugAdapter { · 6 unmodified lines
12 const ADAPTER_PACKAGE_NAME: &'static str = "debugpy";12 const ADAPTER_PACKAGE_NAME: &'static str = "debugpy";
13 const ADAPTER_PATH: &'static str = "src/debugpy/adapter";13 const ADAPTER_PATH: &'static str = "src/debugpy/adapter";
14 const LANGUAGE_NAME: &'static str = "Python";14 const LANGUAGE_NAME: &'static str = "Python";
15
16 fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
17 let mut args = json!({
18 "request": match config.request {
19 DebugRequestType::Launch(_) => "launch",
20 DebugRequestType::Attach(_) => "attach",
21 },
22 "subProcess": true,
23 "redirectOutput": true,
24 });
25 let map = args.as_object_mut().unwrap();
26 match &config.request {
27 DebugRequestType::Attach(attach) => {
28 map.insert("processId".into(), attach.process_id.into());
29 }
30 DebugRequestType::Launch(launch) => {
31 map.insert("program".into(), launch.program.clone().into());
32 map.insert("args".into(), launch.args.clone().into());
33
34 if let Some(stop_on_entry) = config.stop_on_entry {
35 map.insert("stopOnEntry".into(), stop_on_entry.into());
36 }
37 if let Some(cwd) = launch.cwd.as_ref() {
38 map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
39 }
40 }
41 }
42 StartDebuggingRequestArguments {
43 configuration: args,
44 request: config.request.to_dap(),
45 }
46 }
15}47}
16 48
17#[async_trait(?Send)]49#[async_trait(?Send)]
@@ -64,7 +96,7 @@ impl DebugAdapter for PythonDebugAdapter { · 46 unmodified lines
64 async fn get_installed_binary(96 async fn get_installed_binary(
65 &self,97 &self,
66 delegate: &dyn DapDelegate,98 delegate: &dyn DapDelegate,
67 config: &DebugAdapterConfig,99 config: &DebugTaskDefinition,
68 user_installed_path: Option<PathBuf>,100 user_installed_path: Option<PathBuf>,
69 cx: &mut AsyncApp,101 cx: &mut AsyncApp,
70 ) -> Result<DebugAdapterBinary> {102 ) -> Result<DebugAdapterBinary> {
@@ -109,6 +141,7 @@ impl DebugAdapter for PythonDebugAdapter { · 38 unmodified lines
109 };141 };
110 142
111 Ok(DebugAdapterBinary {143 Ok(DebugAdapterBinary {
144 adapter_name: self.name(),
112 command: python_path.ok_or(anyhow!("failed to find binary path for python"))?,145 command: python_path.ok_or(anyhow!("failed to find binary path for python"))?,
113 arguments: Some(vec![146 arguments: Some(vec![
114 debugpy_dir.join(Self::ADAPTER_PATH).into(),147 debugpy_dir.join(Self::ADAPTER_PATH).into(),
@@ -122,35 +155,7 @@ impl DebugAdapter for PythonDebugAdapter { · 7 unmodified lines
122 }),155 }),
123 cwd: None,156 cwd: None,
124 envs: None,157 envs: None,
158 request_args: self.request_args(config),
125 })159 })
126 }160 }
127
128 fn request_args(&self, config: &DebugTaskDefinition) -> Value {
129 let mut args = json!({
130 "request": match config.request {
131 DebugRequestType::Launch(_) => "launch",
132 DebugRequestType::Attach(_) => "attach",
133 },
134 "subProcess": true,
135 "redirectOutput": true,
136 });
137 let map = args.as_object_mut().unwrap();
138 match &config.request {
139 DebugRequestType::Attach(attach) => {
140 map.insert("processId".into(), attach.process_id.into());
141 }
142 DebugRequestType::Launch(launch) => {
143 map.insert("program".into(), launch.program.clone().into());
144 map.insert("args".into(), launch.args.clone().into());
145
146 if let Some(stop_on_entry) = config.stop_on_entry {
147 map.insert("stopOnEntry".into(), stop_on_entry.into());
148 }
149 if let Some(cwd) = launch.cwd.as_ref() {
150 map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
151 }
152 }
153 }
154 args
155 }
156}161}
crates/debugger_ui/src/attach_modal.rs+1−4
@@ -228,10 +228,7 @@ impl PickerDelegate for AttachModalDelegate { · 227 unmodified lines
228 let config = self.debug_config.clone();228 let config = self.debug_config.clone();
229 self.project229 self.project
230 .update(cx, |project, cx| {230 .update(cx, |project, cx| {
231 #[cfg(any(test, feature = "test-support"))]231 let ret = project.start_debug_session(config, cx);
232 let ret = project.fake_debug_session(config.request, None, false, cx);
233 #[cfg(not(any(test, feature = "test-support")))]
234 let ret = project.start_debug_session(config.into(), cx);
235 ret232 ret
236 })233 })
237 .detach_and_log_err(cx);234 .detach_and_log_err(cx);
crates/debugger_ui/src/new_session_modal.rs+1−1
@@ -146,7 +146,7 @@ impl NewSessionModal { · 145 unmodified lines
146 {146 {
147 this.start_debug_session(debug_config, cx)147 this.start_debug_session(debug_config, cx)
148 } else {148 } else {
149 this.start_debug_session(config.into(), cx)149 this.start_debug_session(config, cx)
150 }150 }
151 })?;151 })?;
152 let spawn_result = task.await;152 let spawn_result = task.await;
crates/debugger_ui/src/tests/attach_modal.rs+30−10
@@ -26,18 +26,30 @@ async fn test_direct_attach_to_process(executor: BackgroundExecutor, cx: &mut Te · 25 unmodified lines
26 let workspace = init_test_workspace(&project, cx).await;26 let workspace = init_test_workspace(&project, cx).await;
27 let cx = &mut VisualTestContext::from_window(*workspace, cx);27 let cx = &mut VisualTestContext::from_window(*workspace, cx);
28 28
29 let task = project.update(cx, |project, cx| {29 let session = debugger::test::start_debug_session_with(
30 project.fake_debug_session(30 &project,
31 dap::DebugRequestType::Attach(AttachConfig {31 cx,
32 DebugTaskDefinition {
33 adapter: "fake-adapter".to_string(),
34 request: dap::DebugRequestType::Attach(AttachConfig {
32 process_id: Some(10),35 process_id: Some(10),
33 }),36 }),
34 None,37 label: "label".to_string(),
35 false,38 initialize_args: None,
36 cx,39 tcp_connection: None,
37 )40 locator: None,
38 });41 stop_on_entry: None,
39 42 },
40 let session = task.await.unwrap();43 |client| {
44 client.on_request::<dap::requests::Attach, _>(move |_, args| {
45 assert_eq!(json!({"request": "attach", "process_id": 10}), args.raw);
46
47 Ok(())
48 });
49 },
50 )
51 .await
52 .unwrap();
41 53
42 cx.run_until_parked();54 cx.run_until_parked();
43 55
@@ -77,7 +89,15 @@ async fn test_show_attach_modal_and_select_process( · 33 unmodified lines
77 let project = Project::test(fs, ["/project".as_ref()], cx).await;89 let project = Project::test(fs, ["/project".as_ref()], cx).await;
78 let workspace = init_test_workspace(&project, cx).await;90 let workspace = init_test_workspace(&project, cx).await;
79 let cx = &mut VisualTestContext::from_window(*workspace, cx);91 let cx = &mut VisualTestContext::from_window(*workspace, cx);
92 // Set up handlers for sessions spawned via modal.
93 let _initialize_subscription =
94 project::debugger::test::intercept_debug_sessions(cx, |client| {
95 client.on_request::<dap::requests::Attach, _>(move |_, args| {
96 assert_eq!(json!({"request": "attach", "process_id": 1}), args.raw);
80 97
98 Ok(())
99 });
100 });
81 let attach_modal = workspace101 let attach_modal = workspace
82 .update(cx, |workspace, window, cx| {102 .update(cx, |workspace, window, cx| {
83 workspace.toggle_modal(window, cx, |window, cx| {103 workspace.toggle_modal(window, cx, |window, cx| {
crates/debugger_ui/src/tests/console.rs+8−18
@@ -3,7 +3,6 @@ use dap::requests::StackTrace; · 2 unmodified lines
3use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};3use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
4use project::{FakeFs, Project};4use project::{FakeFs, Project};
5use serde_json::json;5use serde_json::json;
6use task::LaunchConfig;
7use tests::{init_test, init_test_workspace};6use tests::{init_test, init_test_workspace};
8 7
9#[gpui::test]8#[gpui::test]
@@ -29,26 +28,17 @@ async fn test_handle_output_event(executor: BackgroundExecutor, cx: &mut TestApp · 19 unmodified lines
29 })28 })
30 .unwrap();29 .unwrap();
31 30
32 let task = project.update(cx, |project, cx| {31 let session = debugger::test::start_debug_session(&project, cx, |_| {})
33 project.fake_debug_session(32 .await
34 dap::DebugRequestType::Launch(LaunchConfig::default()),33 .unwrap();
35 None,
36 false,
37 cx,
38 )
39 });
40
41 let session = task.await.unwrap();
42 let client = session.update(cx, |session, _| session.adapter_client().unwrap());34 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
43 35
44 client36 client.on_request::<StackTrace, _>(move |_, _| {
45 .on_request::<StackTrace, _>(move |_, _| {37 Ok(dap::StackTraceResponse {
46 Ok(dap::StackTraceResponse {38 stack_frames: Vec::default(),
47 stack_frames: Vec::default(),39 total_frames: None,
48 total_frames: None,
49 })
50 })40 })
51 .await;41 });
52 42
53 client43 client
54 .fake_event(dap::messages::Events::Output(dap::OutputEvent {44 .fake_event(dap::messages::Events::Output(dap::OutputEvent {
crates/debugger_ui/src/tests/debugger_panel.rs+256−355
@@ -1,7 +1,7 @@
1use crate::*;1use crate::*;
2use dap::{2use dap::{
3 ErrorResponse, RunInTerminalRequestArguments, SourceBreakpoint, StartDebuggingRequestArguments,3 ErrorResponse, Message, RunInTerminalRequestArguments, SourceBreakpoint,
4 StartDebuggingRequestArgumentsRequest,4 StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest,
5 client::SessionId,5 client::SessionId,
6 requests::{6 requests::{
7 Continue, Disconnect, Launch, Next, RunInTerminal, SetBreakpoints, StackTrace,7 Continue, Disconnect, Launch, Next, RunInTerminal, SetBreakpoints, StackTrace,
@@ -25,7 +25,6 @@ use std::{ · 17 unmodified lines
25 atomic::{AtomicBool, Ordering},25 atomic::{AtomicBool, Ordering},
26 },26 },
27};27};
28use task::LaunchConfig;
29use terminal_view::{TerminalView, terminal_panel::TerminalPanel};28use terminal_view::{TerminalView, terminal_panel::TerminalPanel};
30use tests::{active_debug_session_panel, init_test, init_test_workspace};29use tests::{active_debug_session_panel, init_test, init_test_workspace};
31use util::path;30use util::path;
@@ -49,37 +48,26 @@ async fn test_basic_show_debug_panel(executor: BackgroundExecutor, cx: &mut Test · 17 unmodified lines
49 let workspace = init_test_workspace(&project, cx).await;48 let workspace = init_test_workspace(&project, cx).await;
50 let cx = &mut VisualTestContext::from_window(*workspace, cx);49 let cx = &mut VisualTestContext::from_window(*workspace, cx);
51 50
52 let task = project.update(cx, |project, cx| {51 let session = debugger::test::start_debug_session(&project, cx, |_| {})
53 project.fake_debug_session(52 .await
54 dap::DebugRequestType::Launch(LaunchConfig::default()),53 .unwrap();
55 None,
56 false,
57 cx,
58 )
59 });
60
61 let session = task.await.unwrap();
62 let client = session.update(cx, |session, _| session.adapter_client().unwrap());54 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
63 55
64 client56 client.on_request::<Threads, _>(move |_, _| {
65 .on_request::<Threads, _>(move |_, _| {57 Ok(dap::ThreadsResponse {
66 Ok(dap::ThreadsResponse {58 threads: vec![dap::Thread {
67 threads: vec![dap::Thread {59 id: 1,
68 id: 1,60 name: "Thread 1".into(),
69 name: "Thread 1".into(),61 }],
70 }],
71 })
72 })62 })
73 .await;63 });
74 64
75 client65 client.on_request::<StackTrace, _>(move |_, _| {
76 .on_request::<StackTrace, _>(move |_, _| {66 Ok(dap::StackTraceResponse {
77 Ok(dap::StackTraceResponse {67 stack_frames: Vec::default(),
78 stack_frames: Vec::default(),68 total_frames: None,
79 total_frames: None,
80 })
81 })69 })
82 .await;70 });
83 71
84 cx.run_until_parked();72 cx.run_until_parked();
85 73
@@ -199,37 +187,26 @@ async fn test_we_can_only_have_one_panel_per_debug_session( · 113 unmodified lines
199 let workspace = init_test_workspace(&project, cx).await;187 let workspace = init_test_workspace(&project, cx).await;
200 let cx = &mut VisualTestContext::from_window(*workspace, cx);188 let cx = &mut VisualTestContext::from_window(*workspace, cx);
201 189
202 let task = project.update(cx, |project, cx| {190 let session = debugger::test::start_debug_session(&project, cx, |_| {})
203 project.fake_debug_session(191 .await
204 dap::DebugRequestType::Launch(LaunchConfig::default()),192 .unwrap();
205 None,
206 false,
207 cx,
208 )
209 });
210
211 let session = task.await.unwrap();
212 let client = session.update(cx, |session, _| session.adapter_client().unwrap());193 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
213 194
214 client195 client.on_request::<Threads, _>(move |_, _| {
215 .on_request::<Threads, _>(move |_, _| {196 Ok(dap::ThreadsResponse {
216 Ok(dap::ThreadsResponse {197 threads: vec![dap::Thread {
217 threads: vec![dap::Thread {198 id: 1,
218 id: 1,199 name: "Thread 1".into(),
219 name: "Thread 1".into(),200 }],
220 }],
221 })
222 })201 })
223 .await;202 });
224 203
225 client204 client.on_request::<StackTrace, _>(move |_, _| {
226 .on_request::<StackTrace, _>(move |_, _| {205 Ok(dap::StackTraceResponse {
227 Ok(dap::StackTraceResponse {206 stack_frames: Vec::default(),
228 stack_frames: Vec::default(),207 total_frames: None,
229 total_frames: None,
230 })
231 })208 })
232 .await;209 });
233 210
234 cx.run_until_parked();211 cx.run_until_parked();
235 212
@@ -377,16 +354,9 @@ async fn test_handle_successful_run_in_terminal_reverse_request( · 141 unmodified lines
377 let workspace = init_test_workspace(&project, cx).await;354 let workspace = init_test_workspace(&project, cx).await;
378 let cx = &mut VisualTestContext::from_window(*workspace, cx);355 let cx = &mut VisualTestContext::from_window(*workspace, cx);
379 356
380 let task = project.update(cx, |project, cx| {357 let session = debugger::test::start_debug_session(&project, cx, |_| {})
381 project.fake_debug_session(358 .await
382 dap::DebugRequestType::Launch(LaunchConfig::default()),359 .unwrap();
383 None,
384 false,
385 cx,
386 )
387 });
388
389 let session = task.await.unwrap();
390 let client = session.update(cx, |session, _| session.adapter_client().unwrap());360 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
391 361
392 client362 client
@@ -474,16 +444,9 @@ async fn test_handle_error_run_in_terminal_reverse_request( · 81 unmodified lines
474 let workspace = init_test_workspace(&project, cx).await;444 let workspace = init_test_workspace(&project, cx).await;
475 let cx = &mut VisualTestContext::from_window(*workspace, cx);445 let cx = &mut VisualTestContext::from_window(*workspace, cx);
476 446
477 let task = project.update(cx, |project, cx| {447 let session = debugger::test::start_debug_session(&project, cx, |_| {})
478 project.fake_debug_session(448 .await
479 dap::DebugRequestType::Launch(LaunchConfig::default()),449 .unwrap();
480 None,
481 false,
482 cx,
483 )
484 });
485
486 let session = task.await.unwrap();
487 let client = session.update(cx, |session, _| session.adapter_client().unwrap());450 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
488 451
489 client452 client
@@ -559,28 +522,19 @@ async fn test_handle_start_debugging_reverse_request( · 69 unmodified lines
559 let workspace = init_test_workspace(&project, cx).await;522 let workspace = init_test_workspace(&project, cx).await;
560 let cx = &mut VisualTestContext::from_window(*workspace, cx);523 let cx = &mut VisualTestContext::from_window(*workspace, cx);
561 524
562 let task = project.update(cx, |project, cx| {525 let session = debugger::test::start_debug_session(&project, cx, |_| {})
563 project.fake_debug_session(526 .await
564 dap::DebugRequestType::Launch(LaunchConfig::default()),527 .unwrap();
565 None,
566 false,
567 cx,
568 )
569 });
570
571 let session = task.await.unwrap();
572 let client = session.update(cx, |session, _| session.adapter_client().unwrap());528 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
573 529
574 client530 client.on_request::<dap::requests::Threads, _>(move |_, _| {
575 .on_request::<dap::requests::Threads, _>(move |_, _| {531 Ok(dap::ThreadsResponse {
576 Ok(dap::ThreadsResponse {532 threads: vec![dap::Thread {
577 threads: vec![dap::Thread {533 id: 1,
578 id: 1,534 name: "Thread 1".into(),
579 name: "Thread 1".into(),535 }],
580 }],
581 })
582 })536 })
583 .await;537 });
584 538
585 client539 client
586 .on_response::<StartDebugging, _>({540 .on_response::<StartDebugging, _>({
@@ -593,7 +547,9 @@ async fn test_handle_start_debugging_reverse_request( · 6 unmodified lines
593 }547 }
594 })548 })
595 .await;549 .await;
596 550 // Set up handlers for sessions spawned with reverse request too.
551 let _reverse_request_subscription =
552 project::debugger::test::intercept_debug_sessions(cx, |_| {});
597 client553 client
598 .fake_reverse_request::<StartDebugging>(StartDebuggingRequestArguments {554 .fake_reverse_request::<StartDebugging>(StartDebuggingRequestArguments {
599 configuration: json!({}),555 configuration: json!({}),
@@ -612,20 +568,16 @@ async fn test_handle_start_debugging_reverse_request( · 12 unmodified lines
612 });568 });
613 let child_client = child_session.update(cx, |session, _| session.adapter_client().unwrap());569 let child_client = child_session.update(cx, |session, _| session.adapter_client().unwrap());
614 570
615 child_client571 child_client.on_request::<dap::requests::Threads, _>(move |_, _| {
616 .on_request::<dap::requests::Threads, _>(move |_, _| {572 Ok(dap::ThreadsResponse {
617 Ok(dap::ThreadsResponse {573 threads: vec![dap::Thread {
618 threads: vec![dap::Thread {574 id: 1,
619 id: 1,575 name: "Thread 1".into(),
620 name: "Thread 1".into(),576 }],
621 }],
622 })
623 })577 })
624 .await;578 });
625 579
626 child_client580 child_client.on_request::<Disconnect, _>(move |_, _| Ok(()));
627 .on_request::<Disconnect, _>(move |_, _| Ok(()))
628 .await;
629 581
630 child_client582 child_client
631 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {583 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -677,31 +629,24 @@ async fn test_shutdown_children_when_parent_session_shutdown( · 45 unmodified lines
677 let workspace = init_test_workspace(&project, cx).await;629 let workspace = init_test_workspace(&project, cx).await;
678 let cx = &mut VisualTestContext::from_window(*workspace, cx);630 let cx = &mut VisualTestContext::from_window(*workspace, cx);
679 631
680 let task = project.update(cx, |project, cx| {632 let parent_session = debugger::test::start_debug_session(&project, cx, |_| {})
681 project.fake_debug_session(633 .await
682 dap::DebugRequestType::Launch(LaunchConfig::default()),634 .unwrap();
683 None,
684 false,
685 cx,
686 )
687 });
688
689 let parent_session = task.await.unwrap();
690 let client = parent_session.update(cx, |session, _| session.adapter_client().unwrap());635 let client = parent_session.update(cx, |session, _| session.adapter_client().unwrap());
691 636
692 client637 client.on_request::<dap::requests::Threads, _>(move |_, _| {
693 .on_request::<dap::requests::Threads, _>(move |_, _| {638 Ok(dap::ThreadsResponse {
694 Ok(dap::ThreadsResponse {639 threads: vec![dap::Thread {
695 threads: vec![dap::Thread {640 id: 1,
696 id: 1,641 name: "Thread 1".into(),
697 name: "Thread 1".into(),642 }],
698 }],
699 })
700 })643 })
701 .await;644 });
702 645
703 client.on_response::<StartDebugging, _>(move |_| {}).await;646 client.on_response::<StartDebugging, _>(move |_| {}).await;
704 647 // Set up handlers for sessions spawned with reverse request too.
648 let _reverse_request_subscription =
649 project::debugger::test::intercept_debug_sessions(cx, |_| {});
705 // start first child session650 // start first child session
706 client651 client
707 .fake_reverse_request::<StartDebugging>(StartDebuggingRequestArguments {652 .fake_reverse_request::<StartDebugging>(StartDebuggingRequestArguments {
@@ -729,9 +674,7 @@ async fn test_shutdown_children_when_parent_session_shutdown( · 21 unmodified lines
729 let first_child_client =674 let first_child_client =
730 first_child_session.update(cx, |session, _| session.adapter_client().unwrap());675 first_child_session.update(cx, |session, _| session.adapter_client().unwrap());
731 676
732 first_child_client677 first_child_client.on_request::<Disconnect, _>(move |_, _| Ok(()));
733 .on_request::<Disconnect, _>(move |_, _| Ok(()))
734 .await;
735 678
736 // configure second child session679 // configure second child session
737 let second_child_session = dap_store.read_with(cx, |dap_store, _| {680 let second_child_session = dap_store.read_with(cx, |dap_store, _| {
@@ -740,9 +683,7 @@ async fn test_shutdown_children_when_parent_session_shutdown( · 2 unmodified lines
740 let second_child_client =683 let second_child_client =
741 second_child_session.update(cx, |session, _| session.adapter_client().unwrap());684 second_child_session.update(cx, |session, _| session.adapter_client().unwrap());
742 685
743 second_child_client686 second_child_client.on_request::<Disconnect, _>(move |_, _| Ok(()));
744 .on_request::<Disconnect, _>(move |_, _| Ok(()))
745 .await;
746 687
747 cx.run_until_parked();688 cx.run_until_parked();
748 689
@@ -796,20 +737,15 @@ async fn test_shutdown_parent_session_if_all_children_are_shutdown( · 47 unmodified lines
796 let workspace = init_test_workspace(&project, cx).await;737 let workspace = init_test_workspace(&project, cx).await;
797 let cx = &mut VisualTestContext::from_window(*workspace, cx);738 let cx = &mut VisualTestContext::from_window(*workspace, cx);
798 739
799 let task = project.update(cx, |project, cx| {740 let parent_session = debugger::test::start_debug_session(&project, cx, |_| {})
800 project.fake_debug_session(741 .await
801 dap::DebugRequestType::Launch(LaunchConfig::default()),742 .unwrap();
802 None,
803 false,
804 cx,
805 )
806 });
807
808 let parent_session = task.await.unwrap();
809 let client = parent_session.update(cx, |session, _| session.adapter_client().unwrap());743 let client = parent_session.update(cx, |session, _| session.adapter_client().unwrap());
810 744
811 client.on_response::<StartDebugging, _>(move |_| {}).await;745 client.on_response::<StartDebugging, _>(move |_| {}).await;
812 746 // Set up handlers for sessions spawned with reverse request too.
747 let _reverse_request_subscription =
748 project::debugger::test::intercept_debug_sessions(cx, |_| {});
813 // start first child session749 // start first child session
814 client750 client
815 .fake_reverse_request::<StartDebugging>(StartDebuggingRequestArguments {751 .fake_reverse_request::<StartDebugging>(StartDebuggingRequestArguments {
@@ -837,9 +773,7 @@ async fn test_shutdown_parent_session_if_all_children_are_shutdown( · 21 unmodified lines
837 let first_child_client =773 let first_child_client =
838 first_child_session.update(cx, |session, _| session.adapter_client().unwrap());774 first_child_session.update(cx, |session, _| session.adapter_client().unwrap());
839 775
840 first_child_client776 first_child_client.on_request::<Disconnect, _>(move |_, _| Ok(()));
841 .on_request::<Disconnect, _>(move |_, _| Ok(()))
842 .await;
843 777
844 // configure second child session778 // configure second child session
845 let second_child_session = dap_store.read_with(cx, |dap_store, _| {779 let second_child_session = dap_store.read_with(cx, |dap_store, _| {
@@ -848,9 +782,7 @@ async fn test_shutdown_parent_session_if_all_children_are_shutdown( · 2 unmodified lines
848 let second_child_client =782 let second_child_client =
849 second_child_session.update(cx, |session, _| session.adapter_client().unwrap());783 second_child_session.update(cx, |session, _| session.adapter_client().unwrap());
850 784
851 second_child_client785 second_child_client.on_request::<Disconnect, _>(move |_, _| Ok(()));
852 .on_request::<Disconnect, _>(move |_, _| Ok(()))
853 .await;
854 786
855 cx.run_until_parked();787 cx.run_until_parked();
856 788
@@ -926,123 +858,107 @@ async fn test_debug_panel_item_thread_status_reset_on_failure( · 69 unmodified lines
926 let workspace = init_test_workspace(&project, cx).await;858 let workspace = init_test_workspace(&project, cx).await;
927 let cx = &mut VisualTestContext::from_window(*workspace, cx);859 let cx = &mut VisualTestContext::from_window(*workspace, cx);
928 860
929 let task = project.update(cx, |project, cx| {861 let session = debugger::test::start_debug_session(&project, cx, |client| {
930 project.fake_debug_session(862 client.on_request::<dap::requests::Initialize, _>(move |_, _| {
931 dap::DebugRequestType::Launch(LaunchConfig::default()),863 Ok(dap::Capabilities {
932 Some(dap::Capabilities {
933 supports_step_back: Some(true),864 supports_step_back: Some(true),
934 ..Default::default()865 ..Default::default()
935 }),866 })
936 false,867 });
937 cx,868 })
938 )869 .await
939 });870 .unwrap();
940 871
941 let session = task.await.unwrap();
942 let client = session.update(cx, |session, _| session.adapter_client().unwrap());872 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
943 const THREAD_ID_NUM: u64 = 1;873 const THREAD_ID_NUM: u64 = 1;
944 874
945 client875 client.on_request::<dap::requests::Threads, _>(move |_, _| {
946 .on_request::<dap::requests::Threads, _>(move |_, _| {876 Ok(dap::ThreadsResponse {
947 Ok(dap::ThreadsResponse {877 threads: vec![dap::Thread {
948 threads: vec![dap::Thread {878 id: THREAD_ID_NUM,
949 id: THREAD_ID_NUM,879 name: "Thread 1".into(),
950 name: "Thread 1".into(),880 }],
951 }],
952 })
953 })881 })
954 .await;882 });
955 883
956 client.on_request::<Launch, _>(move |_, _| Ok(())).await;884 client.on_request::<Launch, _>(move |_, _| Ok(()));
957 885
958 client886 client.on_request::<StackTrace, _>(move |_, _| {
959 .on_request::<StackTrace, _>(move |_, _| {887 Ok(dap::StackTraceResponse {
960 Ok(dap::StackTraceResponse {888 stack_frames: Vec::default(),
961 stack_frames: Vec::default(),889 total_frames: None,
962 total_frames: None,
963 })
964 })890 })
965 .await;891 });
966 892
967 client893 client.on_request::<Next, _>(move |_, _| {
968 .on_request::<Next, _>(move |_, _| {894 Err(ErrorResponse {
969 Err(ErrorResponse {895 error: Some(dap::Message {
970 error: Some(dap::Message {896 id: 1,
971 id: 1,897 format: "error".into(),
972 format: "error".into(),898 variables: None,
973 variables: None,899 send_telemetry: None,
974 send_telemetry: None,900 show_user: None,
975 show_user: None,901 url: None,
976 url: None,902 url_label: None,
977 url_label: None,903 }),
978 }),
979 })
980 })904 })
981 .await;905 });
982 906
983 client907 client.on_request::<StepOut, _>(move |_, _| {
984 .on_request::<StepOut, _>(move |_, _| {908 Err(ErrorResponse {
985 Err(ErrorResponse {909 error: Some(dap::Message {
986 error: Some(dap::Message {910 id: 1,
987 id: 1,911 format: "error".into(),
988 format: "error".into(),912 variables: None,
989 variables: None,913 send_telemetry: None,
990 send_telemetry: None,914 show_user: None,
991 show_user: None,915 url: None,
992 url: None,916 url_label: None,
993 url_label: None,917 }),
994 }),
995 })
996 })918 })
997 .await;919 });
998 920
999 client921 client.on_request::<StepIn, _>(move |_, _| {
1000 .on_request::<StepIn, _>(move |_, _| {922 Err(ErrorResponse {
1001 Err(ErrorResponse {923 error: Some(dap::Message {
1002 error: Some(dap::Message {924 id: 1,
1003 id: 1,925 format: "error".into(),
1004 format: "error".into(),926 variables: None,
1005 variables: None,927 send_telemetry: None,
1006 send_telemetry: None,928 show_user: None,
1007 show_user: None,929 url: None,
1008 url: None,930 url_label: None,
1009 url_label: None,931 }),
1010 }),
1011 })
1012 })932 })
1013 .await;933 });
1014 934
1015 client935 client.on_request::<StepBack, _>(move |_, _| {
1016 .on_request::<StepBack, _>(move |_, _| {936 Err(ErrorResponse {
1017 Err(ErrorResponse {937 error: Some(dap::Message {
1018 error: Some(dap::Message {938 id: 1,
1019 id: 1,939 format: "error".into(),
1020 format: "error".into(),940 variables: None,
1021 variables: None,941 send_telemetry: None,
1022 send_telemetry: None,942 show_user: None,
1023 show_user: None,943 url: None,
1024 url: None,944 url_label: None,
1025 url_label: None,945 }),
1026 }),
1027 })
1028 })946 })
1029 .await;947 });
1030 948
1031 client949 client.on_request::<Continue, _>(move |_, _| {
1032 .on_request::<Continue, _>(move |_, _| {950 Err(ErrorResponse {
1033 Err(ErrorResponse {951 error: Some(dap::Message {
1034 error: Some(dap::Message {952 id: 1,
1035 id: 1,953 format: "error".into(),
1036 format: "error".into(),954 variables: None,
1037 variables: None,955 send_telemetry: None,
1038 send_telemetry: None,956 show_user: None,
1039 show_user: None,957 url: None,
1040 url: None,958 url_label: None,
1041 url_label: None,959 }),
1042 }),
1043 })
1044 })960 })
1045 .await;961 });
1046 962
1047 client963 client
1048 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {964 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -1157,16 +1073,9 @@ async fn test_send_breakpoints_when_editor_has_been_saved( · 108 unmodified lines
1157 .update(cx, |_, _, cx| worktree.read(cx).id())1073 .update(cx, |_, _, cx| worktree.read(cx).id())
1158 .unwrap();1074 .unwrap();
1159 1075
1160 let task = project.update(cx, |project, cx| {1076 let session = debugger::test::start_debug_session(&project, cx, |_| {})
1161 project.fake_debug_session(1077 .await
1162 dap::DebugRequestType::Launch(LaunchConfig::default()),1078 .unwrap();
1163 None,
1164 false,
1165 cx,
1166 )
1167 });
1168
1169 let session = task.await.unwrap();
1170 let client = session.update(cx, |session, _| session.adapter_client().unwrap());1079 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
1171 1080
1172 let buffer = project1081 let buffer = project
@@ -1186,16 +1095,14 @@ async fn test_send_breakpoints_when_editor_has_been_saved( · 13 unmodified lines
1186 )1095 )
1187 });1096 });
1188 1097
1189 client.on_request::<Launch, _>(move |_, _| Ok(())).await;1098 client.on_request::<Launch, _>(move |_, _| Ok(()));
1190 1099
1191 client1100 client.on_request::<StackTrace, _>(move |_, _| {
1192 .on_request::<StackTrace, _>(move |_, _| {1101 Ok(dap::StackTraceResponse {
1193 Ok(dap::StackTraceResponse {1102 stack_frames: Vec::default(),
1194 stack_frames: Vec::default(),1103 total_frames: None,
1195 total_frames: None,
1196 })
1197 })1104 })
1198 .await;1105 });
1199 1106
1200 client1107 client
1201 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {1108 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -1210,32 +1117,30 @@ async fn test_send_breakpoints_when_editor_has_been_saved( · 8 unmodified lines
1210 .await;1117 .await;
1211 1118
1212 let called_set_breakpoints = Arc::new(AtomicBool::new(false));1119 let called_set_breakpoints = Arc::new(AtomicBool::new(false));
1213 client1120 client.on_request::<SetBreakpoints, _>({
1214 .on_request::<SetBreakpoints, _>({1121 let called_set_breakpoints = called_set_breakpoints.clone();
1215 let called_set_breakpoints = called_set_breakpoints.clone();1122 move |_, args| {
1216 move |_, args| {1123 assert_eq!(path!("/project/main.rs"), args.source.path.unwrap());
1217 assert_eq!(path!("/project/main.rs"), args.source.path.unwrap());1124 assert_eq!(
1218 assert_eq!(1125 vec![SourceBreakpoint {
1219 vec![SourceBreakpoint {1126 line: 2,
1220 line: 2,1127 column: None,
1221 column: None,1128 condition: None,
1222 condition: None,1129 hit_condition: None,
1223 hit_condition: None,1130 log_message: None,
1224 log_message: None,1131 mode: None
1225 mode: None1132 }],
1226 }],1133 args.breakpoints.unwrap()
1227 args.breakpoints.unwrap()1134 );
1228 );1135 assert!(!args.source_modified.unwrap());
1229 assert!(!args.source_modified.unwrap());
1230 1136
1231 called_set_breakpoints.store(true, Ordering::SeqCst);1137 called_set_breakpoints.store(true, Ordering::SeqCst);
1232 1138
1233 Ok(dap::SetBreakpointsResponse {1139 Ok(dap::SetBreakpointsResponse {
1234 breakpoints: Vec::default(),1140 breakpoints: Vec::default(),
1235 })1141 })
1236 }1142 }
1237 })1143 });
1238 .await;
1239 1144
1240 editor.update_in(cx, |editor, window, cx| {1145 editor.update_in(cx, |editor, window, cx| {
1241 editor.move_down(&actions::MoveDown, window, cx);1146 editor.move_down(&actions::MoveDown, window, cx);
@@ -1250,32 +1155,30 @@ async fn test_send_breakpoints_when_editor_has_been_saved( · 8 unmodified lines
1250 );1155 );
1251 1156
1252 let called_set_breakpoints = Arc::new(AtomicBool::new(false));1157 let called_set_breakpoints = Arc::new(AtomicBool::new(false));
1253 client1158 client.on_request::<SetBreakpoints, _>({
1254 .on_request::<SetBreakpoints, _>({1159 let called_set_breakpoints = called_set_breakpoints.clone();
1255 let called_set_breakpoints = called_set_breakpoints.clone();1160 move |_, args| {
1256 move |_, args| {1161 assert_eq!(path!("/project/main.rs"), args.source.path.unwrap());
1257 assert_eq!(path!("/project/main.rs"), args.source.path.unwrap());1162 assert_eq!(
1258 assert_eq!(1163 vec![SourceBreakpoint {
1259 vec![SourceBreakpoint {1164 line: 3,
1260 line: 3,1165 column: None,
1261 column: None,1166 condition: None,
1262 condition: None,1167 hit_condition: None,
1263 hit_condition: None,1168 log_message: None,
1264 log_message: None,1169 mode: None
1265 mode: None1170 }],
1266 }],1171 args.breakpoints.unwrap()
1267 args.breakpoints.unwrap()1172 );
1268 );1173 assert!(args.source_modified.unwrap());
1269 assert!(args.source_modified.unwrap());
1270 1174
1271 called_set_breakpoints.store(true, Ordering::SeqCst);1175 called_set_breakpoints.store(true, Ordering::SeqCst);
1272 1176
1273 Ok(dap::SetBreakpointsResponse {1177 Ok(dap::SetBreakpointsResponse {
1274 breakpoints: Vec::default(),1178 breakpoints: Vec::default(),
1275 })1179 })
1276 }1180 }
1277 })1181 });
1278 .await;
1279 1182
1280 editor.update_in(cx, |editor, window, cx| {1183 editor.update_in(cx, |editor, window, cx| {
1281 editor.move_up(&actions::MoveUp, window, cx);1184 editor.move_up(&actions::MoveUp, window, cx);
@@ -1387,49 +1290,40 @@ async fn test_unsetting_breakpoints_on_clear_breakpoint_action( · 105 unmodified lines
1387 editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);1290 editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
1388 });1291 });
1389 1292
1390 let task = project.update(cx, |project, cx| {1293 let session = debugger::test::start_debug_session(&project, cx, |_| {})
1391 project.fake_debug_session(1294 .await
1392 dap::DebugRequestType::Launch(LaunchConfig::default()),1295 .unwrap();
1393 None,
1394 false,
1395 cx,
1396 )
1397 });
1398
1399 let session = task.await.unwrap();
1400 let client = session.update(cx, |session, _| session.adapter_client().unwrap());1296 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
1401 1297
1402 let called_set_breakpoints = Arc::new(AtomicBool::new(false));1298 let called_set_breakpoints = Arc::new(AtomicBool::new(false));
1403 1299
1404 client1300 client.on_request::<SetBreakpoints, _>({
1405 .on_request::<SetBreakpoints, _>({1301 let called_set_breakpoints = called_set_breakpoints.clone();
1406 let called_set_breakpoints = called_set_breakpoints.clone();1302 move |_, args| {
1407 move |_, args| {1303 assert!(
1408 assert!(1304 args.breakpoints.is_none_or(|bps| bps.is_empty()),
1409 args.breakpoints.is_none_or(|bps| bps.is_empty()),1305 "Send empty breakpoint sets to clear them from DAP servers"
1410 "Send empty breakpoint sets to clear them from DAP servers"1306 );
1411 );
1412 1307
1413 match args1308 match args
1414 .source1309 .source
1415 .path1310 .path
1416 .expect("We should always send a breakpoint's path")1311 .expect("We should always send a breakpoint's path")
1417 .as_str()1312 .as_str()
1418 {1313 {
1419 "/project/main.rs" | "/project/second.rs" => {}1314 "/project/main.rs" | "/project/second.rs" => {}
1420 _ => {1315 _ => {
1421 panic!("Unset breakpoints for path that doesn't have any")1316 panic!("Unset breakpoints for path that doesn't have any")
1422 }
1423 }1317 }
1318 }
1424 1319
1425 called_set_breakpoints.store(true, Ordering::SeqCst);1320 called_set_breakpoints.store(true, Ordering::SeqCst);
1426 1321
1427 Ok(dap::SetBreakpointsResponse {1322 Ok(dap::SetBreakpointsResponse {
1428 breakpoints: Vec::default(),1323 breakpoints: Vec::default(),
1429 })1324 })
1430 }1325 }
1431 })1326 });
1432 .await;
1433 1327
1434 cx.dispatch_action(crate::ClearAllBreakpoints);1328 cx.dispatch_action(crate::ClearAllBreakpoints);
1435 cx.run_until_parked();1329 cx.run_until_parked();
@@ -1464,13 +1358,20 @@ async fn test_debug_session_is_shutdown_when_attach_and_launch_request_fails( · 28 unmodified lines
1464 let workspace = init_test_workspace(&project, cx).await;1358 let workspace = init_test_workspace(&project, cx).await;
1465 let cx = &mut VisualTestContext::from_window(*workspace, cx);1359 let cx = &mut VisualTestContext::from_window(*workspace, cx);
1466 1360
1467 let task = project.update(cx, |project, cx| {1361 let task = project::debugger::test::start_debug_session(&project, cx, |client| {
1468 project.fake_debug_session(1362 client.on_request::<dap::requests::Initialize, _>(|_, _| {
1469 dap::DebugRequestType::Launch(LaunchConfig::default()),1363 Err(ErrorResponse {
1470 None,1364 error: Some(Message {
1471 true,1365 format: "failed to launch".to_string(),
1472 cx,1366 id: 1,
1473 )1367 variables: None,
1368 send_telemetry: None,
1369 show_user: None,
1370 url: None,
1371 url_label: None,
1372 }),
1373 })
1374 });
1474 });1375 });
1475 1376
1476 assert!(1377 assert!(
crates/debugger_ui/src/tests/module_list.rs+31−54
@@ -4,15 +4,17 @@ use crate::{ · 3 unmodified lines
4};4};
5use dap::{5use dap::{
6 StoppedEvent,6 StoppedEvent,
7 requests::{Modules, StackTrace, Threads},7 requests::{Initialize, Modules},
8};8};
9use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};9use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
10use project::{FakeFs, Project};10use project::{
11 FakeFs, Project,
12 debugger::{self},
13};
11use std::sync::{14use std::sync::{
12 Arc,15 Arc,
13 atomic::{AtomicBool, AtomicI32, Ordering},16 atomic::{AtomicBool, AtomicI32, Ordering},
14};17};
15use task::LaunchConfig;
16 18
17#[gpui::test]19#[gpui::test]
18async fn test_module_list(executor: BackgroundExecutor, cx: &mut TestAppContext) {20async fn test_module_list(executor: BackgroundExecutor, cx: &mut TestAppContext) {
@@ -29,31 +31,19 @@ async fn test_module_list(executor: BackgroundExecutor, cx: &mut TestAppContext) · 10 unmodified lines
29 .unwrap();31 .unwrap();
30 let cx = &mut VisualTestContext::from_window(*workspace, cx);32 let cx = &mut VisualTestContext::from_window(*workspace, cx);
31 33
32 let task = project.update(cx, |project, cx| {34 let session = debugger::test::start_debug_session(&project, cx, |client| {
33 project.fake_debug_session(35 client.on_request::<Initialize, _>(move |_, _| {
34 dap::DebugRequestType::Launch(LaunchConfig::default()),36 Ok(dap::Capabilities {
35 Some(dap::Capabilities {
36 supports_modules_request: Some(true),37 supports_modules_request: Some(true),
37 ..Default::default()38 ..Default::default()
38 }),39 })
39 false,40 });
40 cx,41 })
41 )42 .await
42 });43 .unwrap();
43 44
44 let session = task.await.unwrap();
45 let client = session.update(cx, |session, _| session.adapter_client().unwrap());45 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
46 46
47 client
48 .on_request::<StackTrace, _>(move |_, args| {
49 assert!(args.thread_id == 1);
50 Ok(dap::StackTraceResponse {
51 stack_frames: Vec::default(),
52 total_frames: None,
53 })
54 })
55 .await;
56
57 let called_modules = Arc::new(AtomicBool::new(false));47 let called_modules = Arc::new(AtomicBool::new(false));
58 let modules = vec![48 let modules = vec![
59 dap::Module {49 dap::Module {
@@ -82,38 +72,25 @@ async fn test_module_list(executor: BackgroundExecutor, cx: &mut TestAppContext) · 22 unmodified lines
82 },72 },
83 ];73 ];
84 74
85 client75 client.on_request::<Modules, _>({
86 .on_request::<Threads, _>(move |_, _| {76 let called_modules = called_modules.clone();
87 Ok(dap::ThreadsResponse {77 let modules_request_count = AtomicI32::new(0);
88 threads: vec![dap::Thread {78 let modules = modules.clone();
89 id: 1,79 move |_, _| {
90 name: "Thread 1".into(),80 modules_request_count.fetch_add(1, Ordering::SeqCst);
91 }],81 assert_eq!(
82 1,
83 modules_request_count.load(Ordering::SeqCst),
84 "This request should only be called once from the host"
85 );
86 called_modules.store(true, Ordering::SeqCst);
87
88 Ok(dap::ModulesResponse {
89 modules: modules.clone(),
90 total_modules: Some(2u64),
92 })91 })
93 })92 }
94 .await;93 });
95
96 client
97 .on_request::<Modules, _>({
98 let called_modules = called_modules.clone();
99 let modules_request_count = AtomicI32::new(0);
100 let modules = modules.clone();
101 move |_, _| {
102 modules_request_count.fetch_add(1, Ordering::SeqCst);
103 assert_eq!(
104 1,
105 modules_request_count.load(Ordering::SeqCst),
106 "This request should only be called once from the host"
107 );
108 called_modules.store(true, Ordering::SeqCst);
109
110 Ok(dap::ModulesResponse {
111 modules: modules.clone(),
112 total_modules: Some(2u64),
113 })
114 }
115 })
116 .await;
117 94
118 client95 client
119 .fake_event(dap::messages::Events::Stopped(StoppedEvent {96 .fake_event(dap::messages::Events::Stopped(StoppedEvent {
crates/debugger_ui/src/tests/stack_frame_list.rs+71−102
@@ -5,14 +5,13 @@ use crate::{ · 4 unmodified lines
5};5};
6use dap::{6use dap::{
7 StackFrame,7 StackFrame,
8 requests::{StackTrace, Threads},8 requests::{Scopes, StackTrace, Threads},
9};9};
10use editor::{Editor, ToPoint as _};10use editor::{Editor, ToPoint as _};
11use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};11use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
12use project::{FakeFs, Project};12use project::{FakeFs, Project, debugger};
13use serde_json::json;13use serde_json::json;
14use std::sync::Arc;14use std::sync::Arc;
15use task::LaunchConfig;
16use unindent::Unindent as _;15use unindent::Unindent as _;
17use util::path;16use util::path;
18 17
@@ -51,29 +50,20 @@ async fn test_fetch_initial_stack_frames_and_go_to_stack_frame( · 32 unmodified lines
51 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;50 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
52 let workspace = init_test_workspace(&project, cx).await;51 let workspace = init_test_workspace(&project, cx).await;
53 let cx = &mut VisualTestContext::from_window(*workspace, cx);52 let cx = &mut VisualTestContext::from_window(*workspace, cx);
54 53 let session = debugger::test::start_debug_session(&project, cx, |_| {})
55 let task = project.update(cx, |project, cx| {54 .await
56 project.fake_debug_session(55 .unwrap();
57 dap::DebugRequestType::Launch(LaunchConfig::default()),
58 None,
59 false,
60 cx,
61 )
62 });
63
64 let session = task.await.unwrap();
65 let client = session.update(cx, |session, _| session.adapter_client().unwrap());56 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
66 57 client.on_request::<Scopes, _>(move |_, _| Ok(dap::ScopesResponse { scopes: vec![] }));
67 client58
68 .on_request::<Threads, _>(move |_, _| {59 client.on_request::<Threads, _>(move |_, _| {
69 Ok(dap::ThreadsResponse {60 Ok(dap::ThreadsResponse {
70 threads: vec![dap::Thread {61 threads: vec![dap::Thread {
71 id: 1,62 id: 1,
72 name: "Thread 1".into(),63 name: "Thread 1".into(),
73 }],64 }],
74 })
75 })65 })
76 .await;66 });
77 67
78 let stack_frames = vec![68 let stack_frames = vec![
79 StackFrame {69 StackFrame {
@@ -122,19 +112,17 @@ async fn test_fetch_initial_stack_frames_and_go_to_stack_frame( · 42 unmodified lines
122 },112 },
123 ];113 ];
124 114
125 client115 client.on_request::<StackTrace, _>({
126 .on_request::<StackTrace, _>({116 let stack_frames = Arc::new(stack_frames.clone());
127 let stack_frames = Arc::new(stack_frames.clone());117 move |_, args| {
128 move |_, args| {118 assert_eq!(1, args.thread_id);
129 assert_eq!(1, args.thread_id);119
130 120 Ok(dap::StackTraceResponse {
131 Ok(dap::StackTraceResponse {121 stack_frames: (*stack_frames).clone(),
132 stack_frames: (*stack_frames).clone(),122 total_frames: None,
133 total_frames: None,123 })
134 })124 }
135 }125 });
136 })
137 .await;
138 126
139 client127 client
140 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {128 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -241,29 +229,21 @@ async fn test_select_stack_frame(executor: BackgroundExecutor, cx: &mut TestAppC · 100 unmodified lines
241 });229 });
242 230
243 let cx = &mut VisualTestContext::from_window(*workspace, cx);231 let cx = &mut VisualTestContext::from_window(*workspace, cx);
244 232 let session = debugger::test::start_debug_session(&project, cx, |_| {})
245 let task = project.update(cx, |project, cx| {233 .await
246 project.fake_debug_session(234 .unwrap();
247 dap::DebugRequestType::Launch(LaunchConfig::default()),
248 None,
249 false,
250 cx,
251 )
252 });
253
254 let session = task.await.unwrap();
255 let client = session.update(cx, |session, _| session.adapter_client().unwrap());235 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
256 236
257 client237 client.on_request::<Threads, _>(move |_, _| {
258 .on_request::<Threads, _>(move |_, _| {238 Ok(dap::ThreadsResponse {
259 Ok(dap::ThreadsResponse {239 threads: vec![dap::Thread {
260 threads: vec![dap::Thread {240 id: 1,
261 id: 1,241 name: "Thread 1".into(),
262 name: "Thread 1".into(),242 }],
263 }],
264 })
265 })243 })
266 .await;244 });
245
246 client.on_request::<Scopes, _>(move |_, _| Ok(dap::ScopesResponse { scopes: vec![] }));
267 247
268 let stack_frames = vec![248 let stack_frames = vec![
269 StackFrame {249 StackFrame {
@@ -312,19 +292,17 @@ async fn test_select_stack_frame(executor: BackgroundExecutor, cx: &mut TestAppC · 42 unmodified lines
312 },292 },
313 ];293 ];
314 294
315 client295 client.on_request::<StackTrace, _>({
316 .on_request::<StackTrace, _>({296 let stack_frames = Arc::new(stack_frames.clone());
317 let stack_frames = Arc::new(stack_frames.clone());297 move |_, args| {
318 move |_, args| {298 assert_eq!(1, args.thread_id);
319 assert_eq!(1, args.thread_id);299
320 300 Ok(dap::StackTraceResponse {
321 Ok(dap::StackTraceResponse {301 stack_frames: (*stack_frames).clone(),
322 stack_frames: (*stack_frames).clone(),302 total_frames: None,
323 total_frames: None,303 })
324 })304 }
325 }305 });
326 })
327 .await;
328 306
329 client307 client
330 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {308 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -517,28 +495,21 @@ async fn test_collapsed_entries(executor: BackgroundExecutor, cx: &mut TestAppCo · 186 unmodified lines
517 let workspace = init_test_workspace(&project, cx).await;495 let workspace = init_test_workspace(&project, cx).await;
518 let cx = &mut VisualTestContext::from_window(*workspace, cx);496 let cx = &mut VisualTestContext::from_window(*workspace, cx);
519 497
520 let task = project.update(cx, |project, cx| {498 let session = debugger::test::start_debug_session(&project, cx, |_| {})
521 project.fake_debug_session(499 .await
522 dap::DebugRequestType::Launch(LaunchConfig::default()),500 .unwrap();
523 None,
524 false,
525 cx,
526 )
527 });
528
529 let session = task.await.unwrap();
530 let client = session.update(cx, |session, _| session.adapter_client().unwrap());501 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
531 502
532 client503 client.on_request::<Threads, _>(move |_, _| {
533 .on_request::<Threads, _>(move |_, _| {504 Ok(dap::ThreadsResponse {
534 Ok(dap::ThreadsResponse {505 threads: vec![dap::Thread {
535 threads: vec![dap::Thread {506 id: 1,
536 id: 1,507 name: "Thread 1".into(),
537 name: "Thread 1".into(),508 }],
538 }],
539 })
540 })509 })
541 .await;510 });
511
512 client.on_request::<Scopes, _>(move |_, _| Ok(dap::ScopesResponse { scopes: vec![] }));
542 513
543 let stack_frames = vec![514 let stack_frames = vec![
544 StackFrame {515 StackFrame {
@@ -697,19 +668,17 @@ async fn test_collapsed_entries(executor: BackgroundExecutor, cx: &mut TestAppCo · 152 unmodified lines
697 },668 },
698 ];669 ];
699 670
700 client671 client.on_request::<StackTrace, _>({
701 .on_request::<StackTrace, _>({672 let stack_frames = Arc::new(stack_frames.clone());
702 let stack_frames = Arc::new(stack_frames.clone());673 move |_, args| {
703 move |_, args| {674 assert_eq!(1, args.thread_id);
704 assert_eq!(1, args.thread_id);675
705 676 Ok(dap::StackTraceResponse {
706 Ok(dap::StackTraceResponse {677 stack_frames: (*stack_frames).clone(),
707 stack_frames: (*stack_frames).clone(),678 total_frames: None,
708 total_frames: None,679 })
709 })680 }
710 }681 });
711 })
712 .await;
713 682
714 client683 client
715 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {684 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
crates/debugger_ui/src/tests/variable_list.rs+254−342
@@ -15,9 +15,8 @@ use dap::{ · 14 unmodified lines
15};15};
16use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};16use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
17use menu::{SelectFirst, SelectNext, SelectPrevious};17use menu::{SelectFirst, SelectNext, SelectPrevious};
18use project::{FakeFs, Project};18use project::{FakeFs, Project, debugger};
19use serde_json::json;19use serde_json::json;
20use task::LaunchConfig;
21use unindent::Unindent as _;20use unindent::Unindent as _;
22use util::path;21use util::path;
23 22
@@ -55,29 +54,19 @@ async fn test_basic_fetch_initial_scope_and_variables( · 31 unmodified lines
55 })54 })
56 .unwrap();55 .unwrap();
57 let cx = &mut VisualTestContext::from_window(*workspace, cx);56 let cx = &mut VisualTestContext::from_window(*workspace, cx);
58 57 let session = debugger::test::start_debug_session(&project, cx, |_| {})
59 let task = project.update(cx, |project, cx| {58 .await
60 project.fake_debug_session(59 .unwrap();
61 dap::DebugRequestType::Launch(LaunchConfig::default()),
62 None,
63 false,
64 cx,
65 )
66 });
67
68 let session = task.await.unwrap();
69 let client = session.update(cx, |session, _| session.adapter_client().unwrap());60 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
70 61
71 client62 client.on_request::<dap::requests::Threads, _>(move |_, _| {
72 .on_request::<dap::requests::Threads, _>(move |_, _| {63 Ok(dap::ThreadsResponse {
73 Ok(dap::ThreadsResponse {64 threads: vec![dap::Thread {
74 threads: vec![dap::Thread {65 id: 1,
75 id: 1,66 name: "Thread 1".into(),
76 name: "Thread 1".into(),67 }],
77 }],
78 })
79 })68 })
80 .await;69 });
81 70
82 let stack_frames = vec![StackFrame {71 let stack_frames = vec![StackFrame {
83 id: 1,72 id: 1,
@@ -102,19 +91,17 @@ async fn test_basic_fetch_initial_scope_and_variables( · 18 unmodified lines
102 presentation_hint: None,91 presentation_hint: None,
103 }];92 }];
104 93
105 client94 client.on_request::<StackTrace, _>({
106 .on_request::<StackTrace, _>({95 let stack_frames = Arc::new(stack_frames.clone());
107 let stack_frames = Arc::new(stack_frames.clone());96 move |_, args| {
108 move |_, args| {97 assert_eq!(1, args.thread_id);
109 assert_eq!(1, args.thread_id);98
110 99 Ok(dap::StackTraceResponse {
111 Ok(dap::StackTraceResponse {100 stack_frames: (*stack_frames).clone(),
112 stack_frames: (*stack_frames).clone(),101 total_frames: None,
113 total_frames: None,102 })
114 })103 }
115 }104 });
116 })
117 .await;
118 105
119 let scopes = vec![Scope {106 let scopes = vec![Scope {
120 name: "Scope 1".into(),107 name: "Scope 1".into(),
@@ -130,18 +117,16 @@ async fn test_basic_fetch_initial_scope_and_variables( · 9 unmodified lines
130 end_column: None,117 end_column: None,
131 }];118 }];
132 119
133 client120 client.on_request::<Scopes, _>({
134 .on_request::<Scopes, _>({121 let scopes = Arc::new(scopes.clone());
135 let scopes = Arc::new(scopes.clone());122 move |_, args| {
136 move |_, args| {123 assert_eq!(1, args.frame_id);
137 assert_eq!(1, args.frame_id);
138 124
139 Ok(dap::ScopesResponse {125 Ok(dap::ScopesResponse {
140 scopes: (*scopes).clone(),126 scopes: (*scopes).clone(),
141 })127 })
142 }128 }
143 })129 });
144 .await;
145 130
146 let variables = vec![131 let variables = vec![
147 Variable {132 Variable {
@@ -172,18 +157,16 @@ async fn test_basic_fetch_initial_scope_and_variables( · 24 unmodified lines
172 },157 },
173 ];158 ];
174 159
175 client160 client.on_request::<Variables, _>({
176 .on_request::<Variables, _>({161 let variables = Arc::new(variables.clone());
177 let variables = Arc::new(variables.clone());162 move |_, args| {
178 move |_, args| {163 assert_eq!(2, args.variables_reference);
179 assert_eq!(2, args.variables_reference);
180 164
181 Ok(dap::VariablesResponse {165 Ok(dap::VariablesResponse {
182 variables: (*variables).clone(),166 variables: (*variables).clone(),
183 })167 })
184 }168 }
185 })169 });
186 .await;
187 170
188 client171 client
189 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {172 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -283,39 +266,28 @@ async fn test_fetch_variables_for_multiple_scopes( · 93 unmodified lines
283 .unwrap();266 .unwrap();
284 let cx = &mut VisualTestContext::from_window(*workspace, cx);267 let cx = &mut VisualTestContext::from_window(*workspace, cx);
285 268
286 let task = project.update(cx, |project, cx| {269 let session = debugger::test::start_debug_session(&project, cx, |_| {})
287 project.fake_debug_session(270 .await
288 dap::DebugRequestType::Launch(LaunchConfig::default()),271 .unwrap();
289 None,
290 false,
291 cx,
292 )
293 });
294
295 let session = task.await.unwrap();
296 let client = session.update(cx, |session, _| session.adapter_client().unwrap());272 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
297 273
298 client274 client.on_request::<dap::requests::Threads, _>(move |_, _| {
299 .on_request::<dap::requests::Threads, _>(move |_, _| {275 Ok(dap::ThreadsResponse {
300 Ok(dap::ThreadsResponse {276 threads: vec![dap::Thread {
301 threads: vec![dap::Thread {277 id: 1,
302 id: 1,278 name: "Thread 1".into(),
303 name: "Thread 1".into(),279 }],
304 }],
305 })
306 })280 })
307 .await;281 });
308 282
309 client283 client.on_request::<Initialize, _>(move |_, _| {
310 .on_request::<Initialize, _>(move |_, _| {284 Ok(dap::Capabilities {
311 Ok(dap::Capabilities {285 supports_step_back: Some(false),
312 supports_step_back: Some(false),286 ..Default::default()
313 ..Default::default()
314 })
315 })287 })
316 .await;288 });
317 289
318 client.on_request::<Launch, _>(move |_, _| Ok(())).await;290 client.on_request::<Launch, _>(move |_, _| Ok(()));
319 291
320 let stack_frames = vec![StackFrame {292 let stack_frames = vec![StackFrame {
321 id: 1,293 id: 1,
@@ -340,19 +312,17 @@ async fn test_fetch_variables_for_multiple_scopes( · 18 unmodified lines
340 presentation_hint: None,312 presentation_hint: None,
341 }];313 }];
342 314
343 client315 client.on_request::<StackTrace, _>({
344 .on_request::<StackTrace, _>({316 let stack_frames = Arc::new(stack_frames.clone());
345 let stack_frames = Arc::new(stack_frames.clone());317 move |_, args| {
346 move |_, args| {318 assert_eq!(1, args.thread_id);
347 assert_eq!(1, args.thread_id);319
348 320 Ok(dap::StackTraceResponse {
349 Ok(dap::StackTraceResponse {321 stack_frames: (*stack_frames).clone(),
350 stack_frames: (*stack_frames).clone(),322 total_frames: None,
351 total_frames: None,323 })
352 })324 }
353 }325 });
354 })
355 .await;
356 326
357 let scopes = vec![327 let scopes = vec![
358 Scope {328 Scope {
@@ -383,18 +353,16 @@ async fn test_fetch_variables_for_multiple_scopes( · 24 unmodified lines
383 },353 },
384 ];354 ];
385 355
386 client356 client.on_request::<Scopes, _>({
387 .on_request::<Scopes, _>({357 let scopes = Arc::new(scopes.clone());
388 let scopes = Arc::new(scopes.clone());358 move |_, args| {
389 move |_, args| {359 assert_eq!(1, args.frame_id);
390 assert_eq!(1, args.frame_id);
391 360
392 Ok(dap::ScopesResponse {361 Ok(dap::ScopesResponse {
393 scopes: (*scopes).clone(),362 scopes: (*scopes).clone(),
394 })363 })
395 }364 }
396 })365 });
397 .await;
398 366
399 let mut variables = HashMap::default();367 let mut variables = HashMap::default();
400 variables.insert(368 variables.insert(
@@ -445,16 +413,14 @@ async fn test_fetch_variables_for_multiple_scopes( · 44 unmodified lines
445 }],413 }],
446 );414 );
447 415
448 client416 client.on_request::<Variables, _>({
449 .on_request::<Variables, _>({417 let variables = Arc::new(variables.clone());
450 let variables = Arc::new(variables.clone());418 move |_, args| {
451 move |_, args| {419 Ok(dap::VariablesResponse {
452 Ok(dap::VariablesResponse {420 variables: variables.get(&args.variables_reference).unwrap().clone(),
453 variables: variables.get(&args.variables_reference).unwrap().clone(),421 })
454 })422 }
455 }423 });
456 })
457 .await;
458 424
459 client425 client
460 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {426 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -562,40 +528,28 @@ async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestApp · 101 unmodified lines
562 })528 })
563 .unwrap();529 .unwrap();
564 let cx = &mut VisualTestContext::from_window(*workspace, cx);530 let cx = &mut VisualTestContext::from_window(*workspace, cx);
565 531 let session = debugger::test::start_debug_session(&project, cx, |_| {})
566 let task = project.update(cx, |project, cx| {532 .await
567 project.fake_debug_session(533 .unwrap();
568 dap::DebugRequestType::Launch(LaunchConfig::default()),
569 None,
570 false,
571 cx,
572 )
573 });
574
575 let session = task.await.unwrap();
576 let client = session.update(cx, |session, _| session.adapter_client().unwrap());534 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
577 535
578 client536 client.on_request::<dap::requests::Threads, _>(move |_, _| {
579 .on_request::<dap::requests::Threads, _>(move |_, _| {537 Ok(dap::ThreadsResponse {
580 Ok(dap::ThreadsResponse {538 threads: vec![dap::Thread {
581 threads: vec![dap::Thread {539 id: 1,
582 id: 1,540 name: "Thread 1".into(),
583 name: "Thread 1".into(),541 }],
584 }],
585 })
586 })542 })
587 .await;543 });
588 544
589 client545 client.on_request::<Initialize, _>(move |_, _| {
590 .on_request::<Initialize, _>(move |_, _| {546 Ok(dap::Capabilities {
591 Ok(dap::Capabilities {547 supports_step_back: Some(false),
592 supports_step_back: Some(false),548 ..Default::default()
593 ..Default::default()
594 })
595 })549 })
596 .await;550 });
597 551
598 client.on_request::<Launch, _>(move |_, _| Ok(())).await;552 client.on_request::<Launch, _>(move |_, _| Ok(()));
599 553
600 let stack_frames = vec![StackFrame {554 let stack_frames = vec![StackFrame {
601 id: 1,555 id: 1,
@@ -620,19 +574,17 @@ async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestApp · 18 unmodified lines
620 presentation_hint: None,574 presentation_hint: None,
621 }];575 }];
622 576
623 client577 client.on_request::<StackTrace, _>({
624 .on_request::<StackTrace, _>({578 let stack_frames = Arc::new(stack_frames.clone());
625 let stack_frames = Arc::new(stack_frames.clone());579 move |_, args| {
626 move |_, args| {580 assert_eq!(1, args.thread_id);
627 assert_eq!(1, args.thread_id);581
628 582 Ok(dap::StackTraceResponse {
629 Ok(dap::StackTraceResponse {583 stack_frames: (*stack_frames).clone(),
630 stack_frames: (*stack_frames).clone(),584 total_frames: None,
631 total_frames: None,585 })
632 })586 }
633 }587 });
634 })
635 .await;
636 588
637 let scopes = vec![589 let scopes = vec![
638 Scope {590 Scope {
@@ -663,18 +615,16 @@ async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestApp · 24 unmodified lines
663 },615 },
664 ];616 ];
665 617
666 client618 client.on_request::<Scopes, _>({
667 .on_request::<Scopes, _>({619 let scopes = Arc::new(scopes.clone());
668 let scopes = Arc::new(scopes.clone());620 move |_, args| {
669 move |_, args| {621 assert_eq!(1, args.frame_id);
670 assert_eq!(1, args.frame_id);
671 622
672 Ok(dap::ScopesResponse {623 Ok(dap::ScopesResponse {
673 scopes: (*scopes).clone(),624 scopes: (*scopes).clone(),
674 })625 })
675 }626 }
676 })627 });
677 .await;
678 628
679 let scope1_variables = vec![629 let scope1_variables = vec![
680 Variable {630 Variable {
@@ -748,25 +698,23 @@ async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestApp · 67 unmodified lines
748 value_location_reference: None,698 value_location_reference: None,
749 }];699 }];
750 700
751 client701 client.on_request::<Variables, _>({
752 .on_request::<Variables, _>({702 let scope1_variables = Arc::new(scope1_variables.clone());
753 let scope1_variables = Arc::new(scope1_variables.clone());703 let nested_variables = Arc::new(nested_variables.clone());
754 let nested_variables = Arc::new(nested_variables.clone());704 let scope2_variables = Arc::new(scope2_variables.clone());
755 let scope2_variables = Arc::new(scope2_variables.clone());705 move |_, args| match args.variables_reference {
756 move |_, args| match args.variables_reference {706 4 => Ok(dap::VariablesResponse {
757 4 => Ok(dap::VariablesResponse {707 variables: (*scope2_variables).clone(),
758 variables: (*scope2_variables).clone(),708 }),
759 }),709 3 => Ok(dap::VariablesResponse {
760 3 => Ok(dap::VariablesResponse {710 variables: (*nested_variables).clone(),
761 variables: (*nested_variables).clone(),711 }),
762 }),712 2 => Ok(dap::VariablesResponse {
763 2 => Ok(dap::VariablesResponse {713 variables: (*scope1_variables).clone(),
764 variables: (*scope1_variables).clone(),714 }),
765 }),715 id => unreachable!("unexpected variables reference {id}"),
766 id => unreachable!("unexpected variables reference {id}"),716 }
767 }717 });
768 })
769 .await;
770 718
771 client719 client
772 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {720 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -1365,39 +1313,28 @@ async fn test_variable_list_only_sends_requests_when_rendering( · 592 unmodified lines
1365 let workspace = init_test_workspace(&project, cx).await;1313 let workspace = init_test_workspace(&project, cx).await;
1366 let cx = &mut VisualTestContext::from_window(*workspace, cx);1314 let cx = &mut VisualTestContext::from_window(*workspace, cx);
1367 1315
1368 let task = project.update(cx, |project, cx| {1316 let session = debugger::test::start_debug_session(&project, cx, |_| {})
1369 project.fake_debug_session(1317 .await
1370 dap::DebugRequestType::Launch(LaunchConfig::default()),1318 .unwrap();
1371 None,
1372 false,
1373 cx,
1374 )
1375 });
1376
1377 let session = task.await.unwrap();
1378 let client = session.update(cx, |session, _| session.adapter_client().unwrap());1319 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
1379 1320
1380 client1321 client.on_request::<dap::requests::Threads, _>(move |_, _| {
1381 .on_request::<dap::requests::Threads, _>(move |_, _| {1322 Ok(dap::ThreadsResponse {
1382 Ok(dap::ThreadsResponse {1323 threads: vec![dap::Thread {
1383 threads: vec![dap::Thread {1324 id: 1,
1384 id: 1,1325 name: "Thread 1".into(),
1385 name: "Thread 1".into(),1326 }],
1386 }],
1387 })
1388 })1327 })
1389 .await;1328 });
1390 1329
1391 client1330 client.on_request::<Initialize, _>(move |_, _| {
1392 .on_request::<Initialize, _>(move |_, _| {1331 Ok(dap::Capabilities {
1393 Ok(dap::Capabilities {1332 supports_step_back: Some(false),
1394 supports_step_back: Some(false),1333 ..Default::default()
1395 ..Default::default()
1396 })
1397 })1334 })
1398 .await;1335 });
1399 1336
1400 client.on_request::<Launch, _>(move |_, _| Ok(())).await;1337 client.on_request::<Launch, _>(move |_, _| Ok(()));
1401 1338
1402 let stack_frames = vec![1339 let stack_frames = vec![
1403 StackFrame {1340 StackFrame {
@@ -1446,19 +1383,17 @@ async fn test_variable_list_only_sends_requests_when_rendering( · 42 unmodified lines
1446 },1383 },
1447 ];1384 ];
1448 1385
1449 client1386 client.on_request::<StackTrace, _>({
1450 .on_request::<StackTrace, _>({1387 let stack_frames = Arc::new(stack_frames.clone());
1451 let stack_frames = Arc::new(stack_frames.clone());1388 move |_, args| {
1452 move |_, args| {1389 assert_eq!(1, args.thread_id);
1453 assert_eq!(1, args.thread_id);1390
1454 1391 Ok(dap::StackTraceResponse {
1455 Ok(dap::StackTraceResponse {1392 stack_frames: (*stack_frames).clone(),
1456 stack_frames: (*stack_frames).clone(),1393 total_frames: None,
1457 total_frames: None,1394 })
1458 })1395 }
1459 }1396 });
1460 })
1461 .await;
1462 1397
1463 let frame_1_scopes = vec![Scope {1398 let frame_1_scopes = vec![Scope {
1464 name: "Frame 1 Scope 1".into(),1399 name: "Frame 1 Scope 1".into(),
@@ -1476,25 +1411,23 @@ async fn test_variable_list_only_sends_requests_when_rendering( · 11 unmodified lines
1476 1411
1477 let made_scopes_request = Arc::new(AtomicBool::new(false));1412 let made_scopes_request = Arc::new(AtomicBool::new(false));
1478 1413
1479 client1414 client.on_request::<Scopes, _>({
1480 .on_request::<Scopes, _>({1415 let frame_1_scopes = Arc::new(frame_1_scopes.clone());
1481 let frame_1_scopes = Arc::new(frame_1_scopes.clone());1416 let made_scopes_request = made_scopes_request.clone();
1482 let made_scopes_request = made_scopes_request.clone();1417 move |_, args| {
1483 move |_, args| {1418 assert_eq!(1, args.frame_id);
1484 assert_eq!(1, args.frame_id);1419 assert!(
1485 assert!(1420 !made_scopes_request.load(Ordering::SeqCst),
1486 !made_scopes_request.load(Ordering::SeqCst),1421 "We should be caching the scope request"
1487 "We should be caching the scope request"1422 );
1488 );
1489 1423
1490 made_scopes_request.store(true, Ordering::SeqCst);1424 made_scopes_request.store(true, Ordering::SeqCst);
1491 1425
1492 Ok(dap::ScopesResponse {1426 Ok(dap::ScopesResponse {
1493 scopes: (*frame_1_scopes).clone(),1427 scopes: (*frame_1_scopes).clone(),
1494 })1428 })
1495 }1429 }
1496 })1430 });
1497 .await;
1498 1431
1499 let frame_1_variables = vec![1432 let frame_1_variables = vec![
1500 Variable {1433 Variable {
@@ -1525,18 +1458,16 @@ async fn test_variable_list_only_sends_requests_when_rendering( · 24 unmodified lines
1525 },1458 },
1526 ];1459 ];
1527 1460
1528 client1461 client.on_request::<Variables, _>({
1529 .on_request::<Variables, _>({1462 let frame_1_variables = Arc::new(frame_1_variables.clone());
1530 let frame_1_variables = Arc::new(frame_1_variables.clone());1463 move |_, args| {
1531 move |_, args| {1464 assert_eq!(2, args.variables_reference);
1532 assert_eq!(2, args.variables_reference);
1533 1465
1534 Ok(dap::VariablesResponse {1466 Ok(dap::VariablesResponse {
1535 variables: (*frame_1_variables).clone(),1467 variables: (*frame_1_variables).clone(),
1536 })1468 })
1537 }1469 }
1538 })1470 });
1539 .await;
1540 1471
1541 cx.run_until_parked();1472 cx.run_until_parked();
1542 1473
@@ -1629,39 +1560,28 @@ async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame( · 86 unmodified lines
1629 .unwrap();1560 .unwrap();
1630 let cx = &mut VisualTestContext::from_window(*workspace, cx);1561 let cx = &mut VisualTestContext::from_window(*workspace, cx);
1631 1562
1632 let task = project.update(cx, |project, cx| {1563 let session = debugger::test::start_debug_session(&project, cx, |_| {})
1633 project.fake_debug_session(1564 .await
1634 dap::DebugRequestType::Launch(LaunchConfig::default()),1565 .unwrap();
1635 None,
1636 false,
1637 cx,
1638 )
1639 });
1640
1641 let session = task.await.unwrap();
1642 let client = session.update(cx, |session, _| session.adapter_client().unwrap());1566 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
1643 1567
1644 client1568 client.on_request::<dap::requests::Threads, _>(move |_, _| {
1645 .on_request::<dap::requests::Threads, _>(move |_, _| {1569 Ok(dap::ThreadsResponse {
1646 Ok(dap::ThreadsResponse {1570 threads: vec![dap::Thread {
1647 threads: vec![dap::Thread {1571 id: 1,
1648 id: 1,1572 name: "Thread 1".into(),
1649 name: "Thread 1".into(),1573 }],
1650 }],
1651 })
1652 })1574 })
1653 .await;1575 });
1654 1576
1655 client1577 client.on_request::<Initialize, _>(move |_, _| {
1656 .on_request::<Initialize, _>(move |_, _| {1578 Ok(dap::Capabilities {
1657 Ok(dap::Capabilities {1579 supports_step_back: Some(false),
1658 supports_step_back: Some(false),1580 ..Default::default()
1659 ..Default::default()
1660 })
1661 })1581 })
1662 .await;1582 });
1663 1583
1664 client.on_request::<Launch, _>(move |_, _| Ok(())).await;1584 client.on_request::<Launch, _>(move |_, _| Ok(()));
1665 1585
1666 let stack_frames = vec![1586 let stack_frames = vec![
1667 StackFrame {1587 StackFrame {
@@ -1710,19 +1630,17 @@ async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame( · 42 unmodified lines
1710 },1630 },
1711 ];1631 ];
1712 1632
1713 client1633 client.on_request::<StackTrace, _>({
1714 .on_request::<StackTrace, _>({1634 let stack_frames = Arc::new(stack_frames.clone());
1715 let stack_frames = Arc::new(stack_frames.clone());1635 move |_, args| {
1716 move |_, args| {1636 assert_eq!(1, args.thread_id);
1717 assert_eq!(1, args.thread_id);1637
1718 1638 Ok(dap::StackTraceResponse {
1719 Ok(dap::StackTraceResponse {1639 stack_frames: (*stack_frames).clone(),
1720 stack_frames: (*stack_frames).clone(),1640 total_frames: None,
1721 total_frames: None,1641 })
1722 })1642 }
1723 }1643 });
1724 })
1725 .await;
1726 1644
1727 let frame_1_scopes = vec![Scope {1645 let frame_1_scopes = vec![Scope {
1728 name: "Frame 1 Scope 1".into(),1646 name: "Frame 1 Scope 1".into(),
@@ -1757,30 +1675,28 @@ async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame( · 28 unmodified lines
1757 let called_second_stack_frame = Arc::new(AtomicBool::new(false));1675 let called_second_stack_frame = Arc::new(AtomicBool::new(false));
1758 let called_first_stack_frame = Arc::new(AtomicBool::new(false));1676 let called_first_stack_frame = Arc::new(AtomicBool::new(false));
1759 1677
1760 client1678 client.on_request::<Scopes, _>({
1761 .on_request::<Scopes, _>({1679 let frame_1_scopes = Arc::new(frame_1_scopes.clone());
1762 let frame_1_scopes = Arc::new(frame_1_scopes.clone());1680 let frame_2_scopes = Arc::new(frame_2_scopes.clone());
1763 let frame_2_scopes = Arc::new(frame_2_scopes.clone());1681 let called_first_stack_frame = called_first_stack_frame.clone();
1764 let called_first_stack_frame = called_first_stack_frame.clone();1682 let called_second_stack_frame = called_second_stack_frame.clone();
1765 let called_second_stack_frame = called_second_stack_frame.clone();1683 move |_, args| match args.frame_id {
1766 move |_, args| match args.frame_id {1684 1 => {
1767 1 => {1685 called_first_stack_frame.store(true, Ordering::SeqCst);
1768 called_first_stack_frame.store(true, Ordering::SeqCst);1686 Ok(dap::ScopesResponse {
1769 Ok(dap::ScopesResponse {1687 scopes: (*frame_1_scopes).clone(),
1770 scopes: (*frame_1_scopes).clone(),1688 })
1771 })
1772 }
1773 2 => {
1774 called_second_stack_frame.store(true, Ordering::SeqCst);
1775
1776 Ok(dap::ScopesResponse {
1777 scopes: (*frame_2_scopes).clone(),
1778 })
1779 }
1780 _ => panic!("Made a scopes request with an invalid frame id"),
1781 }1689 }
1782 })1690 2 => {
1783 .await;1691 called_second_stack_frame.store(true, Ordering::SeqCst);
1692
1693 Ok(dap::ScopesResponse {
1694 scopes: (*frame_2_scopes).clone(),
1695 })
1696 }
1697 _ => panic!("Made a scopes request with an invalid frame id"),
1698 }
1699 });
1784 1700
1785 let frame_1_variables = vec![1701 let frame_1_variables = vec![
1786 Variable {1702 Variable {
@@ -1840,18 +1756,16 @@ async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame( · 53 unmodified lines
1840 },1756 },
1841 ];1757 ];
1842 1758
1843 client1759 client.on_request::<Variables, _>({
1844 .on_request::<Variables, _>({1760 let frame_1_variables = Arc::new(frame_1_variables.clone());
1845 let frame_1_variables = Arc::new(frame_1_variables.clone());1761 move |_, args| {
1846 move |_, args| {1762 assert_eq!(2, args.variables_reference);
1847 assert_eq!(2, args.variables_reference);
1848 1763
1849 Ok(dap::VariablesResponse {1764 Ok(dap::VariablesResponse {
1850 variables: (*frame_1_variables).clone(),1765 variables: (*frame_1_variables).clone(),
1851 })1766 })
1852 }1767 }
1853 })1768 });
1854 .await;
1855 1769
1856 client1770 client
1857 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {1771 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -1907,18 +1821,16 @@ async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame( · 49 unmodified lines
1907 assert_eq!(frame_1_variables, variables);1821 assert_eq!(frame_1_variables, variables);
1908 });1822 });
1909 1823
1910 client1824 client.on_request::<Variables, _>({
1911 .on_request::<Variables, _>({1825 let frame_2_variables = Arc::new(frame_2_variables.clone());
1912 let frame_2_variables = Arc::new(frame_2_variables.clone());1826 move |_, args| {
1913 move |_, args| {1827 assert_eq!(3, args.variables_reference);
1914 assert_eq!(3, args.variables_reference);
1915 1828
1916 Ok(dap::VariablesResponse {1829 Ok(dap::VariablesResponse {
1917 variables: (*frame_2_variables).clone(),1830 variables: (*frame_2_variables).clone(),
1918 })1831 })
1919 }1832 }
1920 })1833 });
1921 .await;
1922 1834
1923 running_state1835 running_state
1924 .update_in(cx, |running_state, window, cx| {1836 .update_in(cx, |running_state, window, cx| {
crates/project/src/debugger.rs+3−0
@@ -16,3 +16,6 @@ pub mod dap_command; · 15 unmodified lines
16pub mod dap_store;16pub mod dap_store;
17mod locator_store;17mod locator_store;
18pub mod session;18pub mod session;
19
20#[cfg(any(feature = "test-support", test))]
21pub mod test;
crates/project/src/debugger/dap_store.rs+36−127
@@ -3,15 +3,15 @@ use super::{ · 2 unmodified lines
3 locator_store::LocatorStore,3 locator_store::LocatorStore,
4 session::{self, Session, SessionStateEvent},4 session::{self, Session, SessionStateEvent},
5};5};
6use crate::{ProjectEnvironment, debugger, worktree_store::WorktreeStore};6use crate::{ProjectEnvironment, debugger};
7use anyhow::{Result, anyhow};7use anyhow::{Result, anyhow};
8use async_trait::async_trait;8use async_trait::async_trait;
9use collections::HashMap;9use collections::HashMap;
10use dap::{10use dap::{
11 Capabilities, CompletionItem, CompletionsArguments, DapRegistry, ErrorResponse,11 Capabilities, CompletionItem, CompletionsArguments, ErrorResponse, EvaluateArguments,
12 EvaluateArguments, EvaluateArgumentsContext, EvaluateResponse, RunInTerminalRequestArguments,12 EvaluateArgumentsContext, EvaluateResponse, RunInTerminalRequestArguments, Source,
13 Source, StartDebuggingRequestArguments,13 StartDebuggingRequestArguments,
14 adapters::{DapStatus, DebugAdapterName},14 adapters::{DapStatus, DebugAdapterBinary, DebugAdapterName},
15 client::SessionId,15 client::SessionId,
16 messages::Message,16 messages::Message,
17 requests::{Completions, Evaluate, Request as _, RunInTerminal, StartDebugging},17 requests::{Completions, Evaluate, Request as _, RunInTerminal, StartDebugging},
@@ -42,7 +42,7 @@ use std::{ · 24 unmodified lines
42 sync::{Arc, atomic::Ordering::SeqCst},42 sync::{Arc, atomic::Ordering::SeqCst},
43};43};
44use std::{collections::VecDeque, sync::atomic::AtomicU32};44use std::{collections::VecDeque, sync::atomic::AtomicU32};
45use task::{DebugAdapterConfig, DebugRequestDisposition};45use task::DebugTaskDefinition;
46use util::ResultExt as _;46use util::ResultExt as _;
47use worktree::Worktree;47use worktree::Worktree;
48 48
@@ -78,10 +78,8 @@ pub struct LocalDapStore { · 29 unmodified lines
78 node_runtime: NodeRuntime,78 node_runtime: NodeRuntime,
79 next_session_id: AtomicU32,79 next_session_id: AtomicU32,
80 http_client: Arc<dyn HttpClient>,80 http_client: Arc<dyn HttpClient>,
81 worktree_store: Entity<WorktreeStore>,
82 environment: Entity<ProjectEnvironment>,81 environment: Entity<ProjectEnvironment>,
83 language_registry: Arc<LanguageRegistry>,82 language_registry: Arc<LanguageRegistry>,
84 debug_adapters: Arc<DapRegistry>,
85 toolchain_store: Arc<dyn LanguageToolchainStore>,83 toolchain_store: Arc<dyn LanguageToolchainStore>,
86 locator_store: Arc<LocatorStore>,84 locator_store: Arc<LocatorStore>,
87 start_debugging_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,85 start_debugging_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
@@ -132,11 +130,9 @@ impl DapStore { · 44 unmodified lines
132 node_runtime: NodeRuntime,130 node_runtime: NodeRuntime,
133 fs: Arc<dyn Fs>,131 fs: Arc<dyn Fs>,
134 language_registry: Arc<LanguageRegistry>,132 language_registry: Arc<LanguageRegistry>,
135 debug_adapters: Arc<DapRegistry>,
136 environment: Entity<ProjectEnvironment>,133 environment: Entity<ProjectEnvironment>,
137 toolchain_store: Arc<dyn LanguageToolchainStore>,134 toolchain_store: Arc<dyn LanguageToolchainStore>,
138 breakpoint_store: Entity<BreakpointStore>,135 breakpoint_store: Entity<BreakpointStore>,
139 worktree_store: Entity<WorktreeStore>,
140 cx: &mut Context<Self>,136 cx: &mut Context<Self>,
141 ) -> Self {137 ) -> Self {
142 cx.on_app_quit(Self::shutdown_sessions).detach();138 cx.on_app_quit(Self::shutdown_sessions).detach();
@@ -170,10 +166,8 @@ impl DapStore { · 27 unmodified lines
170 environment,166 environment,
171 http_client,167 http_client,
172 node_runtime,168 node_runtime,
173 worktree_store,
174 toolchain_store,169 toolchain_store,
175 language_registry,170 language_registry,
176 debug_adapters,
177 start_debugging_tx,171 start_debugging_tx,
178 _start_debugging_task,172 _start_debugging_task,
179 locator_store: Arc::from(LocatorStore::new()),173 locator_store: Arc::from(LocatorStore::new()),
@@ -320,18 +314,12 @@ impl DapStore { · 140 unmodified lines
320 Ok(())314 Ok(())
321 }315 }
322 316
323 pub fn new_session(317 pub fn delegate(&self, worktree: &Entity<Worktree>, cx: &mut App) -> DapAdapterDelegate {
324 &mut self,
325 mut config: DebugAdapterConfig,
326 worktree: &Entity<Worktree>,
327 parent_session: Option<Entity<Session>>,
328 cx: &mut Context<Self>,
329 ) -> (SessionId, Task<Result<Entity<Session>>>) {
330 let Some(local_store) = self.as_local() else {318 let Some(local_store) = self.as_local() else {
331 unimplemented!("Starting session on remote side");319 unimplemented!("Starting session on remote side");
332 };320 };
333 321
334 let delegate = DapAdapterDelegate::new(322 DapAdapterDelegate::new(
335 local_store.fs.clone(),323 local_store.fs.clone(),
336 worktree.read(cx).id(),324 worktree.read(cx).id(),
337 local_store.node_runtime.clone(),325 local_store.node_runtime.clone(),
@@ -341,7 +329,20 @@ impl DapStore { · 3 unmodified lines
341 local_store.environment.update(cx, |env, cx| {329 local_store.environment.update(cx, |env, cx| {
342 env.get_worktree_environment(worktree.clone(), cx)330 env.get_worktree_environment(worktree.clone(), cx)
343 }),331 }),
344 );332 )
333 }
334
335 pub fn new_session(
336 &mut self,
337 binary: DebugAdapterBinary,
338 mut config: DebugTaskDefinition,
339 parent_session: Option<Entity<Session>>,
340 cx: &mut Context<Self>,
341 ) -> (SessionId, Task<Result<Entity<Session>>>) {
342 let Some(local_store) = self.as_local() else {
343 unimplemented!("Starting session on remote side");
344 };
345
345 let session_id = local_store.next_session_id();346 let session_id = local_store.next_session_id();
346 347
347 if let Some(session) = &parent_session {348 if let Some(session) = &parent_session {
@@ -352,7 +353,6 @@ impl DapStore { · 4 unmodified lines
352 353
353 let (initialized_tx, initialized_rx) = oneshot::channel();354 let (initialized_tx, initialized_rx) = oneshot::channel();
354 let locator_store = local_store.locator_store.clone();355 let locator_store = local_store.locator_store.clone();
355 let debug_adapters = local_store.debug_adapters.clone();
356 356
357 let start_debugging_tx = local_store.start_debugging_tx.clone();357 let start_debugging_tx = local_store.start_debugging_tx.clone();
358 358
@@ -373,86 +373,31 @@ impl DapStore { · 14 unmodified lines
373 this.breakpoint_store.clone(),373 this.breakpoint_store.clone(),
374 session_id,374 session_id,
375 parent_session,375 parent_session,
376 delegate,376 binary,
377 config,377 config,
378 start_debugging_tx.clone(),378 start_debugging_tx.clone(),
379 initialized_tx,379 initialized_tx,
380 debug_adapters,
381 cx,380 cx,
382 )381 )
383 })?;382 })?;
384 383
385 this.update(cx, |_, cx| {384 let ret = this
386 create_new_session(session_id, initialized_rx, start_client_task, cx)385 .update(cx, |_, cx| {
387 })?386 create_new_session(session_id, initialized_rx, start_client_task, cx)
388 .await387 })?
388 .await;
389 ret
389 });390 });
390 391
391 (session_id, task)392 (session_id, task)
392 }393 }
393 394
394 #[cfg(any(test, feature = "test-support"))]
395 pub fn new_fake_session(
396 &mut self,
397 config: DebugAdapterConfig,
398 worktree: &Entity<Worktree>,
399 parent_session: Option<Entity<Session>>,
400 caps: Capabilities,
401 fails: bool,
402 cx: &mut Context<Self>,
403 ) -> (SessionId, Task<Result<Entity<Session>>>) {
404 let Some(local_store) = self.as_local() else {
405 unimplemented!("Starting session on remote side");
406 };
407
408 let delegate = DapAdapterDelegate::new(
409 local_store.fs.clone(),
410 worktree.read(cx).id(),
411 local_store.node_runtime.clone(),
412 local_store.http_client.clone(),
413 local_store.language_registry.clone(),
414 local_store.toolchain_store.clone(),
415 local_store.environment.update(cx, |env, cx| {
416 env.get_worktree_environment(worktree.clone(), cx)
417 }),
418 );
419 let session_id = local_store.next_session_id();
420
421 if let Some(session) = &parent_session {
422 session.update(cx, |session, _| {
423 session.add_child_session_id(session_id);
424 });
425 }
426
427 let (initialized_tx, initialized_rx) = oneshot::channel();
428
429 let start_client_task = Session::fake(
430 self.breakpoint_store.clone(),
431 session_id,
432 parent_session,
433 delegate,
434 config,
435 local_store.start_debugging_tx.clone(),
436 initialized_tx,
437 caps,
438 fails,
439 cx,
440 );
441
442 let task = create_new_session(session_id, initialized_rx, start_client_task, cx);
443 (session_id, task)
444 }
445
446 fn handle_start_debugging_request(395 fn handle_start_debugging_request(
447 &mut self,396 &mut self,
448 session_id: SessionId,397 session_id: SessionId,
449 request: dap::messages::Request,398 request: dap::messages::Request,
450 cx: &mut Context<Self>,399 cx: &mut Context<Self>,
451 ) -> Task<Result<()>> {400 ) -> Task<Result<()>> {
452 let Some(local_store) = self.as_local() else {
453 unreachable!("Cannot response for non-local session");
454 };
455
456 let Some(parent_session) = self.session_by_id(session_id) else {401 let Some(parent_session) = self.session_by_id(session_id) else {
457 return Task::ready(Err(anyhow!("Session not found")));402 return Task::ready(Err(anyhow!("Session not found")));
458 };403 };
@@ -461,41 +406,12 @@ impl DapStore { · 2 unmodified lines
461 request.arguments.unwrap_or_default(),406 request.arguments.unwrap_or_default(),
462 )407 )
463 .expect("To parse StartDebuggingRequestArguments");408 .expect("To parse StartDebuggingRequestArguments");
464 let worktree = local_store409 let mut binary = parent_session.read(cx).binary().clone();
465 .worktree_store410 let config = parent_session.read(cx).configuration().unwrap().clone();
466 .update(cx, |this, _| this.worktrees().next())411 binary.request_args = args;
467 .expect("worktree-less project");
468 412
469 let Some(config) = parent_session.read(cx).configuration() else {
470 unreachable!("there must be a config for local sessions");
471 };
472
473 let debug_config = DebugAdapterConfig {
474 label: config.label,
475 adapter: config.adapter,
476 request: DebugRequestDisposition::ReverseRequest(args),
477 initialize_args: config.initialize_args.clone(),
478 tcp_connection: config.tcp_connection.clone(),
479 locator: None,
480 stop_on_entry: config.stop_on_entry,
481 };
482
483 #[cfg(any(test, feature = "test-support"))]
484 let new_session_task = {
485 let caps = parent_session.read(cx).capabilities.clone();
486 self.new_fake_session(
487 debug_config,
488 &worktree,
489 Some(parent_session.clone()),
490 caps,
491 false,
492 cx,
493 )
494 .1
495 };
496 #[cfg(not(any(test, feature = "test-support")))]
497 let new_session_task = self413 let new_session_task = self
498 .new_session(debug_config, &worktree, Some(parent_session.clone()), cx)414 .new_session(binary, config, Some(parent_session.clone()), cx)
499 .1;415 .1;
500 416
501 let request_seq = request.seq;417 let request_seq = request.seq;
@@ -607,13 +523,7 @@ impl DapStore { · 105 unmodified lines
607 .map(Arc::from)523 .map(Arc::from)
608 .or_else(|| {524 .or_else(|| {
609 self.session_by_id(session_id)525 self.session_by_id(session_id)
610 .and_then(|session| {526 .and_then(|session| session.read(cx).binary().cwd.as_deref().map(Arc::from))
611 session
612 .read(cx)
613 .configuration()
614 .and_then(|config| config.request.cwd())
615 })
616 .map(Arc::from)
617 });527 });
618 cx.emit(DapStoreEvent::RunInTerminal {528 cx.emit(DapStoreEvent::RunInTerminal {
619 session_id,529 session_id,
@@ -852,7 +762,7 @@ fn create_new_session( · 232 unmodified lines
852 cx.emit(DapStoreEvent::DebugClientStarted(session_id));762 cx.emit(DapStoreEvent::DebugClientStarted(session_id));
853 cx.notify();763 cx.notify();
854 })?;764 })?;
855 let seq_result = {765 let seq_result = async || {
856 session766 session
857 .update(cx, |session, cx| session.request_initialize(cx))?767 .update(cx, |session, cx| session.request_initialize(cx))?
858 .await?;768 .await?;
@@ -863,12 +773,11 @@ fn create_new_session( · 4 unmodified lines
863 })?773 })?
864 .await774 .await
865 };775 };
866 match seq_result {776 match seq_result().await {
867 Ok(_) => {}777 Ok(_) => {}
868 Err(error) => {778 Err(error) => {
869 this.update(cx, |this, cx| {779 this.update(cx, |this, cx| {
870 cx.emit(DapStoreEvent::Notification(error.to_string()));780 cx.emit(DapStoreEvent::Notification(error.to_string()));
871
872 this.shutdown_session(session_id, cx)781 this.shutdown_session(session_id, cx)
873 })?782 })?
874 .await783 .await
crates/project/src/debugger/locator_store.rs+2−2
@@ -1,9 +1,9 @@
1use anyhow::{Result, anyhow};1use anyhow::{Result, anyhow};
2use cargo::CargoLocator;2use cargo::CargoLocator;
3use collections::HashMap;3use collections::HashMap;
4use dap::DebugAdapterConfig;
5use gpui::SharedString;4use gpui::SharedString;
6use locators::DapLocator;5use locators::DapLocator;
6use task::DebugTaskDefinition;
7 7
8mod cargo;8mod cargo;
9mod locators;9mod locators;
@@ -23,7 +23,7 @@ impl LocatorStore { · 13 unmodified lines
23 23
24 pub(super) async fn resolve_debug_config(24 pub(super) async fn resolve_debug_config(
25 &self,25 &self,
26 debug_config: &mut DebugAdapterConfig,26 debug_config: &mut DebugTaskDefinition,
27 ) -> Result<()> {27 ) -> Result<()> {
28 let Some(locator_name) = &debug_config.locator else {28 let Some(locator_name) = &debug_config.locator else {
29 log::debug!("Attempted to resolve debug config without a locator field");29 log::debug!("Attempted to resolve debug config without a locator field");
crates/project/src/debugger/locator_store/cargo.rs+3−5
@@ -1,12 +1,12 @@
1use super::DapLocator;1use super::DapLocator;
2use anyhow::{Result, anyhow};2use anyhow::{Result, anyhow};
3use async_trait::async_trait;3use async_trait::async_trait;
4use dap::DebugAdapterConfig;
5use serde_json::{Value, json};4use serde_json::{Value, json};
6use smol::{5use smol::{
7 io::AsyncReadExt,6 io::AsyncReadExt,
8 process::{Command, Stdio},7 process::{Command, Stdio},
9};8};
9use task::DebugTaskDefinition;
10use util::maybe;10use util::maybe;
11 11
12pub(super) struct CargoLocator;12pub(super) struct CargoLocator;
@@ -38,11 +38,9 @@ async fn find_best_executable(executables: &[String], test_name: &str) -> Option · 25 unmodified lines
38}38}
39#[async_trait]39#[async_trait]
40impl DapLocator for CargoLocator {40impl DapLocator for CargoLocator {
41 async fn run_locator(&self, debug_config: &mut DebugAdapterConfig) -> Result<()> {41 async fn run_locator(&self, debug_config: &mut DebugTaskDefinition) -> Result<()> {
42 let Some(launch_config) = (match &mut debug_config.request {42 let Some(launch_config) = (match &mut debug_config.request {
43 task::DebugRequestDisposition::UserConfigured(task::DebugRequestType::Launch(43 task::DebugRequestType::Launch(launch_config) => Some(launch_config),
44 launch_config,
45 )) => Some(launch_config),
46 _ => None,44 _ => None,
47 }) else {45 }) else {
48 return Err(anyhow!("Couldn't get launch config in locator"));46 return Err(anyhow!("Couldn't get launch config in locator"));
crates/project/src/debugger/locator_store/locators.rs+2−2
@@ -1,8 +1,8 @@
1use anyhow::Result;1use anyhow::Result;
2use async_trait::async_trait;2use async_trait::async_trait;
3use dap::DebugAdapterConfig;3use task::DebugTaskDefinition;
4 4
5#[async_trait]5#[async_trait]
6pub(super) trait DapLocator: Send + Sync {6pub(super) trait DapLocator: Send + Sync {
7 async fn run_locator(&self, debug_config: &mut DebugAdapterConfig) -> Result<()>;7 async fn run_locator(&self, debug_config: &mut DebugTaskDefinition) -> Result<()>;
8}8}
crates/project/src/debugger/session.rs+49−298
@@ -1,5 +1,3 @@
1use crate::project_settings::ProjectSettings;
2
3use super::breakpoint_store::{1use super::breakpoint_store::{
4 BreakpointStore, BreakpointStoreEvent, BreakpointUpdatedReason, SourceBreakpoint,2 BreakpointStore, BreakpointStoreEvent, BreakpointUpdatedReason, SourceBreakpoint,
5};3};
@@ -11,22 +9,17 @@ use super::dap_command::{ · 5 unmodified lines
11 StepBackCommand, StepCommand, StepInCommand, StepOutCommand, TerminateCommand,9 StepBackCommand, StepCommand, StepInCommand, StepOutCommand, TerminateCommand,
12 TerminateThreadsCommand, ThreadsCommand, VariablesCommand,10 TerminateThreadsCommand, ThreadsCommand, VariablesCommand,
13};11};
14use super::dap_store::DapAdapterDelegate;
15use anyhow::{Context as _, Result, anyhow};12use anyhow::{Context as _, Result, anyhow};
16use collections::{HashMap, HashSet, IndexMap, IndexSet};13use collections::{HashMap, HashSet, IndexMap, IndexSet};
17use dap::adapters::{DebugAdapter, DebugAdapterBinary};14use dap::adapters::DebugAdapterBinary;
18use dap::messages::Response;15use dap::messages::Response;
19use dap::{16use dap::{
20 Capabilities, ContinueArguments, EvaluateArgumentsContext, Module, Source, StackFrameId,17 Capabilities, ContinueArguments, EvaluateArgumentsContext, Module, Source, StackFrameId,
21 SteppingGranularity, StoppedEvent, VariableReference,18 SteppingGranularity, StoppedEvent, VariableReference,
22 adapters::{DapDelegate, DapStatus},
23 client::{DebugAdapterClient, SessionId},19 client::{DebugAdapterClient, SessionId},
24 messages::{Events, Message},20 messages::{Events, Message},
25};21};
26use dap::{22use dap::{ExceptionBreakpointsFilter, ExceptionFilterOptions, OutputEventCategory};
27 DapRegistry, DebugRequestType, ExceptionBreakpointsFilter, ExceptionFilterOptions,
28 OutputEventCategory,
29};
30use futures::channel::oneshot;23use futures::channel::oneshot;
31use futures::{FutureExt, future::Shared};24use futures::{FutureExt, future::Shared};
32use gpui::{25use gpui::{
@@ -35,11 +28,9 @@ use gpui::{ · 2 unmodified lines
35};28};
36use rpc::AnyProtoClient;29use rpc::AnyProtoClient;
37use serde_json::{Value, json};30use serde_json::{Value, json};
38use settings::Settings;
39use smol::stream::StreamExt;31use smol::stream::StreamExt;
40use std::any::TypeId;32use std::any::TypeId;
41use std::collections::BTreeMap;33use std::collections::BTreeMap;
42use std::path::PathBuf;
43use std::u64;34use std::u64;
44use std::{35use std::{
45 any::Any,36 any::Any,
@@ -48,7 +39,7 @@ use std::{ · 2 unmodified lines
48 path::Path,39 path::Path,
49 sync::Arc,40 sync::Arc,
50};41};
51use task::{DebugAdapterConfig, DebugTaskDefinition};42use task::DebugTaskDefinition;
52use text::{PointUtf16, ToPointUtf16};43use text::{PointUtf16, ToPointUtf16};
53use util::{ResultExt, merge_json_value_into};44use util::{ResultExt, merge_json_value_into};
54 45
@@ -168,9 +159,9 @@ enum Mode { · 113 unmodified lines
168#[derive(Clone)]159#[derive(Clone)]
169pub struct LocalMode {160pub struct LocalMode {
170 client: Arc<DebugAdapterClient>,161 client: Arc<DebugAdapterClient>,
171 config: DebugAdapterConfig,162 definition: DebugTaskDefinition,
172 adapter: Arc<dyn DebugAdapter>,163 binary: DebugAdapterBinary,
173 breakpoint_store: Entity<BreakpointStore>,164 pub(crate) breakpoint_store: Entity<BreakpointStore>,
174 tmp_breakpoint: Option<SourceBreakpoint>,165 tmp_breakpoint: Option<SourceBreakpoint>,
175}166}
176 167
@@ -191,185 +182,37 @@ fn client_source(abs_path: &Path) -> dap::Source { · 14 unmodified lines
191 182
192impl LocalMode {183impl LocalMode {
193 fn new(184 fn new(
194 debug_adapters: Arc<DapRegistry>,
195 session_id: SessionId,185 session_id: SessionId,
196 parent_session: Option<Entity<Session>>,186 parent_session: Option<Entity<Session>>,
197 breakpoint_store: Entity<BreakpointStore>,187 breakpoint_store: Entity<BreakpointStore>,
198 config: DebugAdapterConfig,188 config: DebugTaskDefinition,
199 delegate: DapAdapterDelegate,189 binary: DebugAdapterBinary,
200 messages_tx: futures::channel::mpsc::UnboundedSender<Message>,190 messages_tx: futures::channel::mpsc::UnboundedSender<Message>,
201 cx: AsyncApp,191 cx: AsyncApp,
202 ) -> Task<Result<Self>> {192 ) -> Task<Result<Self>> {
203 Self::new_inner(193 Self::new_inner(
204 debug_adapters,
205 session_id,194 session_id,
206 parent_session,195 parent_session,
207 breakpoint_store,196 breakpoint_store,
208 config,197 config,
209 delegate,198 binary,
210 messages_tx,199 messages_tx,
211 async |_, _| {},200 async |_, _| {},
212 cx,201 cx,
213 )202 )
214 }203 }
215 #[cfg(any(test, feature = "test-support"))]
216 fn new_fake(
217 session_id: SessionId,
218 parent_session: Option<Entity<Session>>,
219 breakpoint_store: Entity<BreakpointStore>,
220 config: DebugAdapterConfig,
221 delegate: DapAdapterDelegate,
222 messages_tx: futures::channel::mpsc::UnboundedSender<Message>,
223 caps: Capabilities,
224 fail: bool,
225 cx: AsyncApp,
226 ) -> Task<Result<Self>> {
227 use task::DebugRequestDisposition;
228
229 let request = match config.request.clone() {
230 DebugRequestDisposition::UserConfigured(request) => request,
231 DebugRequestDisposition::ReverseRequest(reverse_request_args) => {
232 match reverse_request_args.request {
233 dap::StartDebuggingRequestArgumentsRequest::Launch => {
234 DebugRequestType::Launch(task::LaunchConfig {
235 program: "".to_owned(),
236 cwd: None,
237 args: Default::default(),
238 })
239 }
240 dap::StartDebuggingRequestArgumentsRequest::Attach => {
241 DebugRequestType::Attach(task::AttachConfig {
242 process_id: Some(0),
243 })
244 }
245 }
246 }
247 };
248
249 let callback = async move |session: &mut LocalMode, cx: AsyncApp| {
250 session
251 .client
252 .on_request::<dap::requests::Initialize, _>(move |_, _| Ok(caps.clone()))
253 .await;
254
255 let paths = cx
256 .update(|cx| session.breakpoint_store.read(cx).breakpoint_paths())
257 .expect("Breakpoint store should exist in all tests that start debuggers");
258
259 session
260 .client
261 .on_request::<dap::requests::SetBreakpoints, _>(move |_, args| {
262 let p = Arc::from(Path::new(&args.source.path.unwrap()));
263 if !paths.contains(&p) {
264 panic!("Sent breakpoints for path without any")
265 }
266 204
267 Ok(dap::SetBreakpointsResponse {
268 breakpoints: Vec::default(),
269 })
270 })
271 .await;
272
273 match request {
274 dap::DebugRequestType::Launch(_) => {
275 if fail {
276 session
277 .client
278 .on_request::<dap::requests::Launch, _>(move |_, _| {
279 Err(dap::ErrorResponse {
280 error: Some(dap::Message {
281 id: 1,
282 format: "error".into(),
283 variables: None,
284 send_telemetry: None,
285 show_user: None,
286 url: None,
287 url_label: None,
288 }),
289 })
290 })
291 .await;
292 } else {
293 session
294 .client
295 .on_request::<dap::requests::Launch, _>(move |_, _| Ok(()))
296 .await;
297 }
298 }
299 dap::DebugRequestType::Attach(attach_config) => {
300 if fail {
301 session
302 .client
303 .on_request::<dap::requests::Attach, _>(move |_, _| {
304 Err(dap::ErrorResponse {
305 error: Some(dap::Message {
306 id: 1,
307 format: "error".into(),
308 variables: None,
309 send_telemetry: None,
310 show_user: None,
311 url: None,
312 url_label: None,
313 }),
314 })
315 })
316 .await;
317 } else {
318 session
319 .client
320 .on_request::<dap::requests::Attach, _>(move |_, args| {
321 assert_eq!(
322 json!({"request": "attach", "process_id": attach_config.process_id.unwrap()}),
323 args.raw
324 );
325
326 Ok(())
327 })
328 .await;
329 }
330 }
331 }
332
333 session
334 .client
335 .on_request::<dap::requests::SetExceptionBreakpoints, _>(move |_, _| {
336 Ok(dap::SetExceptionBreakpointsResponse { breakpoints: None })
337 })
338 .await;
339
340 session
341 .client
342 .on_request::<dap::requests::Disconnect, _>(move |_, _| Ok(()))
343 .await;
344 session.client.fake_event(Events::Initialized(None)).await;
345 };
346 Self::new_inner(
347 DapRegistry::fake().into(),
348 session_id,
349 parent_session,
350 breakpoint_store,
351 config,
352 delegate,
353 messages_tx,
354 callback,
355 cx,
356 )
357 }
358 fn new_inner(205 fn new_inner(
359 registry: Arc<DapRegistry>,
360 session_id: SessionId,206 session_id: SessionId,
361 parent_session: Option<Entity<Session>>,207 parent_session: Option<Entity<Session>>,
362 breakpoint_store: Entity<BreakpointStore>,208 breakpoint_store: Entity<BreakpointStore>,
363 config: DebugAdapterConfig,209 config: DebugTaskDefinition,
364 delegate: DapAdapterDelegate,210 binary: DebugAdapterBinary,
365 messages_tx: futures::channel::mpsc::UnboundedSender<Message>,211 messages_tx: futures::channel::mpsc::UnboundedSender<Message>,
366 on_initialized: impl AsyncFnOnce(&mut LocalMode, AsyncApp) + 'static,212 on_initialized: impl AsyncFnOnce(&mut LocalMode, AsyncApp) + 'static,
367 cx: AsyncApp,213 cx: AsyncApp,
368 ) -> Task<Result<Self>> {214 ) -> Task<Result<Self>> {
369 cx.spawn(async move |cx| {215 cx.spawn(async move |cx| {
370 let (adapter, binary) =
371 Self::get_adapter_binary(&registry, &config, &delegate, cx).await?;
372
373 let message_handler = Box::new(move |message| {216 let message_handler = Box::new(move |message| {
374 messages_tx.unbounded_send(message).ok();217 messages_tx.unbounded_send(message).ok();
375 });218 });
@@ -380,13 +223,12 @@ impl LocalMode { · 4 unmodified lines
380 .flatten()223 .flatten()
381 {224 {
382 client225 client
383 .reconnect(session_id, binary, message_handler, cx.clone())226 .reconnect(session_id, binary.clone(), message_handler, cx.clone())
384 .await?227 .await?
385 } else {228 } else {
386 DebugAdapterClient::start(229 DebugAdapterClient::start(
387 session_id,230 session_id,
388 adapter.name(),231 binary.clone(),
389 binary,
390 message_handler,232 message_handler,
391 cx.clone(),233 cx.clone(),
392 )234 )
@@ -397,10 +239,10 @@ impl LocalMode { · 4 unmodified lines
397 239
398 let mut session = Self {240 let mut session = Self {
399 client,241 client,
400 adapter,
401 breakpoint_store,242 breakpoint_store,
402 tmp_breakpoint: None,243 tmp_breakpoint: None,
403 config: config.clone(),244 definition: config,
245 binary,
404 };246 };
405 247
406 on_initialized(&mut session, cx.clone()).await;248 on_initialized(&mut session, cx.clone()).await;
@@ -533,55 +375,12 @@ impl LocalMode { · 126 unmodified lines
533 })375 })
534 }376 }
535 377
536 async fn get_adapter_binary(
537 registry: &Arc<DapRegistry>,
538 config: &DebugAdapterConfig,
539 delegate: &DapAdapterDelegate,
540 cx: &mut AsyncApp,
541 ) -> Result<(Arc<dyn DebugAdapter>, DebugAdapterBinary)> {
542 let adapter = registry
543 .adapter(&config.adapter)
544 .ok_or_else(|| anyhow!("Debug adapter with name `{}` was not found", config.adapter))?;
545
546 let binary = cx.update(|cx| {
547 ProjectSettings::get_global(cx)
548 .dap
549 .get(&adapter.name())
550 .and_then(|s| s.binary.as_ref().map(PathBuf::from))
551 })?;
552
553 let binary = match adapter.get_binary(delegate, &config, binary, cx).await {
554 Err(error) => {
555 delegate.update_status(
556 adapter.name(),
557 DapStatus::Failed {
558 error: error.to_string(),
559 },
560 );
561
562 return Err(error);
563 }
564 Ok(mut binary) => {
565 delegate.update_status(adapter.name(), DapStatus::None);
566
567 let shell_env = delegate.shell_env().await;
568 let mut envs = binary.envs.unwrap_or_default();
569 envs.extend(shell_env);
570 binary.envs = Some(envs);
571
572 binary
573 }
574 };
575
576 Ok((adapter, binary))
577 }
578
579 pub fn label(&self) -> String {378 pub fn label(&self) -> String {
580 self.config.label.clone()379 self.definition.label.clone()
581 }380 }
582 381
583 fn request_initialization(&self, cx: &App) -> Task<Result<Capabilities>> {382 fn request_initialization(&self, cx: &App) -> Task<Result<Capabilities>> {
584 let adapter_id = self.adapter.name().to_string();383 let adapter_id = self.binary.adapter_name.to_string();
585 384
586 self.request(Initialize { adapter_id }, cx.background_executor().clone())385 self.request(Initialize { adapter_id }, cx.background_executor().clone())
587 }386 }
@@ -592,36 +391,26 @@ impl LocalMode { · 4 unmodified lines
592 initialized_rx: oneshot::Receiver<()>,391 initialized_rx: oneshot::Receiver<()>,
593 cx: &App,392 cx: &App,
594 ) -> Task<Result<()>> {393 ) -> Task<Result<()>> {
595 let (mut raw, is_launch) = match &self.config.request {394 let mut raw = self.binary.request_args.clone();
596 task::DebugRequestDisposition::UserConfigured(_) => {
597 let Ok(raw) = DebugTaskDefinition::try_from(self.config.clone()) else {
598 debug_assert!(false, "This part of code should be unreachable in practice");
599 return Task::ready(Err(anyhow!(
600 "Expected debug config conversion to succeed"
601 )));
602 };
603 let is_launch = matches!(raw.request, DebugRequestType::Launch(_));
604 let raw = self.adapter.request_args(&raw);
605 (raw, is_launch)
606 }
607 task::DebugRequestDisposition::ReverseRequest(start_debugging_request_arguments) => (
608 start_debugging_request_arguments.configuration.clone(),
609 matches!(
610 start_debugging_request_arguments.request,
611 dap::StartDebuggingRequestArgumentsRequest::Launch
612 ),
613 ),
614 };
615 395
616 merge_json_value_into(396 merge_json_value_into(
617 self.config.initialize_args.clone().unwrap_or(json!({})),397 self.definition.initialize_args.clone().unwrap_or(json!({})),
618 &mut raw,398 &mut raw.configuration,
619 );399 );
620 // Of relevance: https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522400 // Of relevance: https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522
621 let launch = if is_launch {401 let launch = match raw.request {
622 self.request(Launch { raw }, cx.background_executor().clone())402 dap::StartDebuggingRequestArgumentsRequest::Launch => self.request(
623 } else {403 Launch {
624 self.request(Attach { raw }, cx.background_executor().clone())404 raw: raw.configuration,
405 },
406 cx.background_executor().clone(),
407 ),
408 dap::StartDebuggingRequestArgumentsRequest::Attach => self.request(
409 Attach {
410 raw: raw.configuration,
411 },
412 cx.background_executor().clone(),
413 ),
625 };414 };
626 415
627 let configuration_done_supported = ConfigurationDone::is_supported(capabilities);416 let configuration_done_supported = ConfigurationDone::is_supported(capabilities);
@@ -656,12 +445,13 @@ impl LocalMode { · 28 unmodified lines
656 })?445 })?
657 .await446 .await
658 .ok();447 .ok();
659 if configuration_done_supported {448 let ret = if configuration_done_supported {
660 this.request(ConfigurationDone {}, cx.background_executor().clone())449 this.request(ConfigurationDone {}, cx.background_executor().clone())
661 } else {450 } else {
662 Task::ready(Ok(()))451 Task::ready(Ok(()))
663 }452 }
664 .await453 .await;
454 ret
665 }455 }
666 });456 });
667 457
@@ -909,68 +699,22 @@ impl Session { · 241 unmodified lines
909 breakpoint_store: Entity<BreakpointStore>,699 breakpoint_store: Entity<BreakpointStore>,
910 session_id: SessionId,700 session_id: SessionId,
911 parent_session: Option<Entity<Session>>,701 parent_session: Option<Entity<Session>>,
912 delegate: DapAdapterDelegate,702 binary: DebugAdapterBinary,
913 config: DebugAdapterConfig,703 config: DebugTaskDefinition,
914 start_debugging_requests_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,704 start_debugging_requests_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
915 initialized_tx: oneshot::Sender<()>,705 initialized_tx: oneshot::Sender<()>,
916 debug_adapters: Arc<DapRegistry>,
917 cx: &mut App,706 cx: &mut App,
918 ) -> Task<Result<Entity<Self>>> {707 ) -> Task<Result<Entity<Self>>> {
919 let (message_tx, message_rx) = futures::channel::mpsc::unbounded();708 let (message_tx, message_rx) = futures::channel::mpsc::unbounded();
920 709
921 cx.spawn(async move |cx| {710 cx.spawn(async move |cx| {
922 let mode = LocalMode::new(711 let mode = LocalMode::new(
923 debug_adapters,
924 session_id,
925 parent_session.clone(),
926 breakpoint_store.clone(),
927 config.clone(),
928 delegate,
929 message_tx,
930 cx.clone(),
931 )
932 .await?;
933
934 cx.new(|cx| {
935 create_local_session(
936 breakpoint_store,
937 session_id,
938 parent_session,
939 start_debugging_requests_tx,
940 initialized_tx,
941 message_rx,
942 mode,
943 cx,
944 )
945 })
946 })
947 }
948
949 #[cfg(any(test, feature = "test-support"))]
950 pub(crate) fn fake(
951 breakpoint_store: Entity<BreakpointStore>,
952 session_id: SessionId,
953 parent_session: Option<Entity<Session>>,
954 delegate: DapAdapterDelegate,
955 config: DebugAdapterConfig,
956 start_debugging_requests_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
957 initialized_tx: oneshot::Sender<()>,
958 caps: Capabilities,
959 fails: bool,
960 cx: &mut App,
961 ) -> Task<Result<Entity<Session>>> {
962 let (message_tx, message_rx) = futures::channel::mpsc::unbounded();
963
964 cx.spawn(async move |cx| {
965 let mode = LocalMode::new_fake(
966 session_id,712 session_id,
967 parent_session.clone(),713 parent_session.clone(),
968 breakpoint_store.clone(),714 breakpoint_store.clone(),
969 config.clone(),715 config.clone(),
970 delegate,716 binary,
971 message_tx,717 message_tx,
972 caps,
973 fails,
974 cx.clone(),718 cx.clone(),
975 )719 )
976 .await?;720 .await?;
@@ -1047,16 +791,23 @@ impl Session { · 70 unmodified lines
1047 &self.capabilities791 &self.capabilities
1048 }792 }
1049 793
794 pub fn binary(&self) -> &DebugAdapterBinary {
795 let Mode::Local(local_mode) = &self.mode else {
796 panic!("Session is not local");
797 };
798 &local_mode.binary
799 }
800
1050 pub fn adapter_name(&self) -> SharedString {801 pub fn adapter_name(&self) -> SharedString {
1051 match &self.mode {802 match &self.mode {
1052 Mode::Local(local_mode) => local_mode.adapter.name().into(),803 Mode::Local(local_mode) => local_mode.definition.adapter.clone().into(),
1053 Mode::Remote(remote_mode) => remote_mode._adapter_name.clone(),804 Mode::Remote(remote_mode) => remote_mode._adapter_name.clone(),
1054 }805 }
1055 }806 }
1056 807
1057 pub fn configuration(&self) -> Option<DebugAdapterConfig> {808 pub fn configuration(&self) -> Option<DebugTaskDefinition> {
1058 if let Mode::Local(local_mode) = &self.mode {809 if let Mode::Local(local_mode) = &self.mode {
1059 Some(local_mode.config.clone())810 Some(local_mode.definition.clone())
1060 } else {811 } else {
1061 None812 None
1062 }813 }
crates/project/src/debugger/test.rs+98−0
@@ -0,0 +1,98 @@
1use std::{path::Path, sync::Arc};
2
3use anyhow::Result;
4use dap::{DebugRequestType, client::DebugAdapterClient};
5use gpui::{App, AppContext, Entity, Subscription, Task};
6use task::DebugTaskDefinition;
7
8use crate::Project;
9
10use super::session::Session;
11
12pub fn intercept_debug_sessions<T: Fn(&Arc<DebugAdapterClient>) + 'static>(
13 cx: &mut gpui::TestAppContext,
14 configure: T,
15) -> Subscription {
16 cx.update(|cx| {
17 cx.observe_new::<Session>(move |session, _, cx| {
18 let client = session.adapter_client().unwrap();
19 register_default_handlers(session, &client, cx);
20 configure(&client);
21 cx.background_spawn(async move {
22 client
23 .fake_event(dap::messages::Events::Initialized(Some(Default::default())))
24 .await
25 })
26 .detach();
27 })
28 })
29}
30
31pub fn start_debug_session_with<T: Fn(&Arc<DebugAdapterClient>) + 'static>(
32 project: &Entity<Project>,
33 cx: &mut gpui::TestAppContext,
34 config: DebugTaskDefinition,
35 configure: T,
36) -> Task<Result<Entity<Session>>> {
37 let subscription = intercept_debug_sessions(cx, configure);
38 let task = project.update(cx, |project, cx| project.start_debug_session(config, cx));
39 cx.spawn(async move |_| {
40 let result = task.await;
41 drop(subscription);
42 result
43 })
44}
45
46pub fn start_debug_session<T: Fn(&Arc<DebugAdapterClient>) + 'static>(
47 project: &Entity<Project>,
48 cx: &mut gpui::TestAppContext,
49 configure: T,
50) -> Task<Result<Entity<Session>>> {
51 start_debug_session_with(
52 project,
53 cx,
54 DebugTaskDefinition {
55 adapter: "fake-adapter".to_string(),
56 request: DebugRequestType::Launch(Default::default()),
57 label: "test".to_string(),
58 initialize_args: None,
59 tcp_connection: None,
60 locator: None,
61 stop_on_entry: None,
62 },
63 configure,
64 )
65}
66
67fn register_default_handlers(session: &Session, client: &Arc<DebugAdapterClient>, cx: &mut App) {
68 client.on_request::<dap::requests::Initialize, _>(move |_, _| Ok(Default::default()));
69 let paths = session
70 .as_local()
71 .unwrap()
72 .breakpoint_store
73 .read(cx)
74 .breakpoint_paths();
75
76 client.on_request::<dap::requests::SetBreakpoints, _>(move |_, args| {
77 let p = Arc::from(Path::new(&args.source.path.unwrap()));
78 if !paths.contains(&p) {
79 panic!("Sent breakpoints for path without any")
80 }
81
82 Ok(dap::SetBreakpointsResponse {
83 breakpoints: Vec::default(),
84 })
85 });
86
87 client.on_request::<dap::requests::Launch, _>(move |_, _| Ok(()));
88
89 client.on_request::<dap::requests::SetExceptionBreakpoints, _>(move |_, _| {
90 Ok(dap::SetExceptionBreakpointsResponse { breakpoints: None })
91 });
92
93 client.on_request::<dap::requests::Disconnect, _>(move |_, _| Ok(()));
94
95 client.on_request::<dap::requests::Threads, _>(move |_, _| {
96 Ok(dap::ThreadsResponse { threads: vec![] })
97 });
98}
crates/project/src/project.rs+32−51
@@ -25,6 +25,7 @@ mod environment; · 24 unmodified lines
25use buffer_diff::BufferDiff;25use buffer_diff::BufferDiff;
26pub use environment::{EnvironmentErrorMessage, ProjectEnvironmentEvent};26pub use environment::{EnvironmentErrorMessage, ProjectEnvironmentEvent};
27use git_store::{Repository, RepositoryId};27use git_store::{Repository, RepositoryId};
28use task::DebugTaskDefinition;
28pub mod search_history;29pub mod search_history;
29mod yarn;30mod yarn;
30 31
@@ -38,7 +39,7 @@ use client::{ · 7 unmodified lines
38};39};
39use clock::ReplicaId;40use clock::ReplicaId;
40 41
41use dap::{DapRegistry, DebugAdapterConfig, client::DebugAdapterClient};42use dap::{DapRegistry, client::DebugAdapterClient};
42 43
43use collections::{BTreeSet, HashMap, HashSet};44use collections::{BTreeSet, HashMap, HashSet};
44use debounced_delay::DebouncedDelay;45use debounced_delay::DebouncedDelay;
@@ -106,7 +107,7 @@ use terminals::Terminals; · 61 unmodified lines
106use text::{Anchor, BufferId};107use text::{Anchor, BufferId};
107use toolchain_store::EmptyToolchainStore;108use toolchain_store::EmptyToolchainStore;
108use util::{109use util::{
109 ResultExt as _, maybe,110 ResultExt as _,
110 paths::{SanitizedPath, compare_paths},111 paths::{SanitizedPath, compare_paths},
111};112};
112use worktree::{CreatedEntry, Snapshot, Traversal};113use worktree::{CreatedEntry, Snapshot, Traversal};
@@ -870,11 +871,9 @@ impl Project { · 757 unmodified lines
870 node.clone(),871 node.clone(),
871 fs.clone(),872 fs.clone(),
872 languages.clone(),873 languages.clone(),
873 debug_adapters.clone(),
874 environment.clone(),874 environment.clone(),
875 toolchain_store.read(cx).as_language_toolchain_store(),875 toolchain_store.read(cx).as_language_toolchain_store(),
876 breakpoint_store.clone(),876 breakpoint_store.clone(),
877 worktree_store.clone(),
878 cx,877 cx,
879 )878 )
880 });879 });
@@ -1458,64 +1457,46 @@ impl Project { · 577 unmodified lines
1458 }1457 }
1459 }1458 }
1460 1459
1461 pub fn queue_debug_session(&mut self, config: DebugAdapterConfig, cx: &mut Context<Self>) {
1462 if config.locator.is_none() {
1463 self.start_debug_session(config, cx).detach_and_log_err(cx);
1464 }
1465 }
1466
1467 pub fn start_debug_session(1460 pub fn start_debug_session(
1468 &mut self,1461 &mut self,
1469 config: DebugAdapterConfig,1462 config: DebugTaskDefinition,
1470 cx: &mut Context<Self>,1463 cx: &mut Context<Self>,
1471 ) -> Task<Result<Entity<Session>>> {1464 ) -> Task<Result<Entity<Session>>> {
1472 let worktree = maybe!({ self.worktrees(cx).next() });1465 let Some(worktree) = self.worktrees(cx).next() else {
1473
1474 let Some(worktree) = &worktree else {
1475 return Task::ready(Err(anyhow!("Failed to find a worktree")));1466 return Task::ready(Err(anyhow!("Failed to find a worktree")));
1476 };1467 };
1477 1468
1478 self.dap_store1469 let Some(adapter) = self.debug_adapters.adapter(&config.adapter) else {
1479 .update(cx, |dap_store, cx| {1470 return Task::ready(Err(anyhow!("Failed to find a debug adapter")));
1480 dap_store.new_session(config, worktree, None, cx)1471 };
1481 })
1482 .1
1483 }
1484 1472
1485 #[cfg(any(test, feature = "test-support"))]1473 let user_installed_path = ProjectSettings::get_global(cx)
1486 pub fn fake_debug_session(1474 .dap
1487 &mut self,1475 .get(&adapter.name())
1488 request: task::DebugRequestType,1476 .and_then(|s| s.binary.as_ref().map(PathBuf::from));
1489 caps: Option<dap::Capabilities>,
1490 fails: bool,
1491 cx: &mut Context<Self>,
1492 ) -> Task<Result<Entity<Session>>> {
1493 use dap::{Capabilities, FakeAdapter};
1494 use task::DebugRequestDisposition;
1495 1477
1496 let worktree = maybe!({ self.worktrees(cx).next() });1478 let result = cx.spawn(async move |this, cx| {
1479 let delegate = this.update(cx, |project, cx| {
1480 project
1481 .dap_store
1482 .update(cx, |dap_store, cx| dap_store.delegate(&worktree, cx))
1483 })?;
1497 1484
1498 let Some(worktree) = &worktree else {1485 let binary = adapter
1499 return Task::ready(Err(anyhow!("Failed to find a worktree")));1486 .get_binary(&delegate, &config, user_installed_path, cx)
1500 };1487 .await?;
1501 let config = DebugAdapterConfig {1488
1502 label: "test config".into(),1489 let ret = this
1503 adapter: FakeAdapter::ADAPTER_NAME.into(),1490 .update(cx, |project, cx| {
1504 request: DebugRequestDisposition::UserConfigured(request),1491 project.dap_store.update(cx, |dap_store, cx| {
1505 initialize_args: None,1492 dap_store.new_session(binary, config, None, cx)
1506 tcp_connection: None,1493 })
1507 locator: None,1494 })?
1508 stop_on_entry: None,1495 .1
1509 };1496 .await;
1510 let caps = caps.unwrap_or(Capabilities {1497 ret
1511 supports_step_back: Some(false),
1512 ..Default::default()
1513 });1498 });
1514 self.dap_store1499 result
1515 .update(cx, |dap_store, cx| {
1516 dap_store.new_fake_session(config, worktree, None, caps, fails, cx)
1517 })
1518 .1
1519 }1500 }
1520 1501
1521 #[cfg(any(test, feature = "test-support"))]1502 #[cfg(any(test, feature = "test-support"))]
crates/project/src/project_tests.rs+1−1
@@ -39,7 +39,7 @@ use std::{env, mem, num::NonZeroU32, ops::Range, str::FromStr, sync::OnceLock, t · 38 unmodified lines
39use task::{ResolvedTask, TaskContext};39use task::{ResolvedTask, TaskContext};
40use unindent::Unindent as _;40use unindent::Unindent as _;
41use util::{41use util::{
42 TryFutureExt as _, assert_set_eq, path,42 TryFutureExt as _, assert_set_eq, maybe, path,
43 paths::PathMatcher,43 paths::PathMatcher,
44 separator,44 separator,
45 test::{TempTree, marked_text_offsets},45 test::{TempTree, marked_text_offsets},
crates/remote_server/src/headless_project.rs+1−3
@@ -72,7 +72,7 @@ impl HeadlessProject { · 71 unmodified lines
72 http_client,72 http_client,
73 node_runtime,73 node_runtime,
74 languages,74 languages,
75 debug_adapters,75 debug_adapters: _debug_adapters,
76 extension_host_proxy: proxy,76 extension_host_proxy: proxy,
77 }: HeadlessAppState,77 }: HeadlessAppState,
78 cx: &mut Context<Self>,78 cx: &mut Context<Self>,
@@ -112,11 +112,9 @@ impl HeadlessProject { · 33 unmodified lines
112 node_runtime.clone(),112 node_runtime.clone(),
113 fs.clone(),113 fs.clone(),
114 languages.clone(),114 languages.clone(),
115 debug_adapters.clone(),
116 environment.clone(),115 environment.clone(),
117 toolchain_store.read(cx).as_language_toolchain_store(),116 toolchain_store.read(cx).as_language_toolchain_store(),
118 breakpoint_store.clone(),117 breakpoint_store.clone(),
119 worktree_store.clone(),
120 cx,118 cx,
121 )119 )
122 });120 });
crates/task/src/lib.rs+4−4
@@ -104,7 +104,7 @@ impl ResolvedTask { · 103 unmodified lines
104 }104 }
105 105
106 /// Get the configuration for the debug adapter that should be used for this task.106 /// Get the configuration for the debug adapter that should be used for this task.
107 pub fn resolved_debug_adapter_config(&self) -> Option<DebugAdapterConfig> {107 pub fn resolved_debug_adapter_config(&self) -> Option<DebugTaskDefinition> {
108 match self.original_task.task_type.clone() {108 match self.original_task.task_type.clone() {
109 TaskType::Debug(debug_args) if self.resolved.is_some() => {109 TaskType::Debug(debug_args) if self.resolved.is_some() => {
110 let resolved = self110 let resolved = self
@@ -127,10 +127,10 @@ impl ResolvedTask { · 16 unmodified lines
127 })127 })
128 .collect();128 .collect();
129 129
130 Some(DebugAdapterConfig {130 Some(DebugTaskDefinition {
131 label: resolved.label.clone(),131 label: resolved.label.clone(),
132 adapter: debug_args.adapter.clone(),132 adapter: debug_args.adapter.clone(),
133 request: DebugRequestDisposition::UserConfigured(match debug_args.request {133 request: match debug_args.request {
134 crate::task_template::DebugArgsRequest::Launch => {134 crate::task_template::DebugArgsRequest::Launch => {
135 DebugRequestType::Launch(LaunchConfig {135 DebugRequestType::Launch(LaunchConfig {
136 program: resolved.command.clone(),136 program: resolved.command.clone(),
@@ -141,7 +141,7 @@ impl ResolvedTask { · 4 unmodified lines
141 crate::task_template::DebugArgsRequest::Attach(attach_config) => {141 crate::task_template::DebugArgsRequest::Attach(attach_config) => {
142 DebugRequestType::Attach(attach_config)142 DebugRequestType::Attach(attach_config)
143 }143 }
144 }),144 },
145 initialize_args: debug_args.initialize_args,145 initialize_args: debug_args.initialize_args,
146 tcp_connection: debug_args.tcp_connection,146 tcp_connection: debug_args.tcp_connection,
147 locator: debug_args.locator.clone(),147 locator: debug_args.locator.clone(),
crates/tasks_ui/src/modal.rs+3−4
@@ -361,9 +361,8 @@ impl PickerDelegate for TasksModalDelegate { · 360 unmodified lines
361 361
362 match task.task_type() {362 match task.task_type() {
363 TaskType::Debug(config) if config.locator.is_none() => {363 TaskType::Debug(config) if config.locator.is_none() => {
364 let Some(config): Option<DebugTaskDefinition> = task364 let Some(config): Option<DebugTaskDefinition> =
365 .resolved_debug_adapter_config()365 task.resolved_debug_adapter_config()
366 .and_then(|config| config.try_into().ok())
367 else {366 else {
368 return;367 return;
369 };368 };
@@ -382,7 +381,7 @@ impl PickerDelegate for TasksModalDelegate { · 12 unmodified lines
382 .update(cx, |workspace, cx| {381 .update(cx, |workspace, cx| {
383 workspace.project().update(cx, |project, cx| {382 workspace.project().update(cx, |project, cx| {
384 project383 project
385 .start_debug_session(config.into(), cx)384 .start_debug_session(config, cx)
386 .detach_and_log_err(cx);385 .detach_and_log_err(cx);
387 });386 });
388 })387 })
crates/workspace/src/workspace.rs+2−2
@@ -96,7 +96,7 @@ use std::{ · 95 unmodified lines
96 sync::{Arc, LazyLock, Weak, atomic::AtomicUsize},96 sync::{Arc, LazyLock, Weak, atomic::AtomicUsize},
97 time::Duration,97 time::Duration,
98};98};
99use task::{DebugAdapterConfig, SpawnInTerminal, TaskId};99use task::{DebugTaskDefinition, SpawnInTerminal, TaskId};
100use theme::{ActiveTheme, SystemAppearance, ThemeSettings};100use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
101pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};101pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
102pub use ui;102pub use ui;
@@ -858,7 +858,7 @@ pub struct Workspace { · 755 unmodified lines
858 serialized_ssh_project: Option<SerializedSshProject>,858 serialized_ssh_project: Option<SerializedSshProject>,
859 _items_serializer: Task<Result<()>>,859 _items_serializer: Task<Result<()>>,
860 session_id: Option<String>,860 session_id: Option<String>,
861 debug_task_queue: HashMap<task::TaskId, DebugAdapterConfig>,861 debug_task_queue: HashMap<task::TaskId, DebugTaskDefinition>,
862}862}
863 863
864impl EventEmitter<Event> for Workspace {}864impl EventEmitter<Event> for Workspace {}