Detailed changes
@@ -3,13 +3,13 @@ use anyhow::{Context as _, Ok, Result, anyhow};
use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive;
use async_trait::async_trait;
+use dap_types::StartDebuggingRequestArguments;
use futures::io::BufReader;
use gpui::{AsyncApp, SharedString};
pub use http_client::{HttpClient, github::latest_github_release};
use language::LanguageToolchainStore;
use node_runtime::NodeRuntime;
use serde::{Deserialize, Serialize};
-use serde_json::Value;
use settings::WorktreeId;
use smol::{self, fs::File, lock::Mutex};
use std::{
@@ -22,7 +22,7 @@ use std::{
path::PathBuf,
sync::Arc,
};
-use task::{DebugAdapterConfig, DebugTaskDefinition};
+use task::DebugTaskDefinition;
use util::ResultExt;
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -93,13 +93,15 @@ pub struct TcpArguments {
pub port: u16,
pub timeout: Option<u64>,
}
-#[derive(Default, Debug, Clone)]
+#[derive(Debug, Clone)]
pub struct DebugAdapterBinary {
+ pub adapter_name: DebugAdapterName,
pub command: String,
pub arguments: Option<Vec<OsString>>,
pub envs: Option<HashMap<String, String>>,
pub cwd: Option<PathBuf>,
pub connection: Option<TcpArguments>,
+ pub request_args: StartDebuggingRequestArguments,
}
#[derive(Debug)]
@@ -220,7 +222,7 @@ pub trait DebugAdapter: 'static + Send + Sync {
async fn get_binary(
&self,
delegate: &dyn DapDelegate,
- config: &DebugAdapterConfig,
+ config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
cx: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
@@ -284,13 +286,10 @@ pub trait DebugAdapter: 'static + Send + Sync {
async fn get_installed_binary(
&self,
delegate: &dyn DapDelegate,
- config: &DebugAdapterConfig,
+ config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
cx: &mut AsyncApp,
) -> Result<DebugAdapterBinary>;
-
- /// Should return base configuration to make the debug adapter work
- fn request_args(&self, config: &DebugTaskDefinition) -> Value;
}
#[cfg(any(test, feature = "test-support"))]
pub struct FakeAdapter {}
@@ -302,6 +301,31 @@ impl FakeAdapter {
pub fn new() -> Self {
Self {}
}
+
+ fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
+ use serde_json::json;
+ use task::DebugRequestType;
+
+ let value = json!({
+ "request": match config.request {
+ DebugRequestType::Launch(_) => "launch",
+ DebugRequestType::Attach(_) => "attach",
+ },
+ "process_id": if let DebugRequestType::Attach(attach_config) = &config.request {
+ attach_config.process_id
+ } else {
+ None
+ },
+ });
+ let request = match config.request {
+ DebugRequestType::Launch(_) => dap_types::StartDebuggingRequestArgumentsRequest::Launch,
+ DebugRequestType::Attach(_) => dap_types::StartDebuggingRequestArgumentsRequest::Attach,
+ };
+ StartDebuggingRequestArguments {
+ configuration: value,
+ request,
+ }
+ }
}
#[cfg(any(test, feature = "test-support"))]
@@ -314,16 +338,18 @@ impl DebugAdapter for FakeAdapter {
async fn get_binary(
&self,
_: &dyn DapDelegate,
- _: &DebugAdapterConfig,
+ config: &DebugTaskDefinition,
_: Option<PathBuf>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
Ok(DebugAdapterBinary {
+ adapter_name: Self::ADAPTER_NAME.into(),
command: "command".into(),
arguments: None,
connection: None,
envs: None,
cwd: None,
+ request_args: self.request_args(config),
})
}
@@ -345,27 +371,10 @@ impl DebugAdapter for FakeAdapter {
async fn get_installed_binary(
&self,
_: &dyn DapDelegate,
- _: &DebugAdapterConfig,
+ _: &DebugTaskDefinition,
_: Option<PathBuf>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
unimplemented!("get installed binary");
}
-
- fn request_args(&self, config: &DebugTaskDefinition) -> Value {
- use serde_json::json;
- use task::DebugRequestType;
-
- json!({
- "request": match config.request {
- DebugRequestType::Launch(_) => "launch",
- DebugRequestType::Attach(_) => "attach",
- },
- "process_id": if let DebugRequestType::Attach(attach_config) = &config.request {
- attach_config.process_id
- } else {
- None
- },
- })
- }
}
@@ -39,7 +39,6 @@ impl SessionId {
/// Represents a connection to the debug adapter process, either via stdout/stdin or a socket.
pub struct DebugAdapterClient {
id: SessionId,
- name: DebugAdapterName,
sequence_count: AtomicU64,
binary: DebugAdapterBinary,
executor: BackgroundExecutor,
@@ -51,7 +50,6 @@ pub type DapMessageHandler = Box<dyn FnMut(Message) + 'static + Send + Sync>;
impl DebugAdapterClient {
pub async fn start(
id: SessionId,
- name: DebugAdapterName,
binary: DebugAdapterBinary,
message_handler: DapMessageHandler,
cx: AsyncApp,
@@ -60,7 +58,6 @@ impl DebugAdapterClient {
TransportDelegate::start(&binary, cx.clone()).await?;
let this = Self {
id,
- name,
binary,
transport_delegate,
sequence_count: AtomicU64::new(1),
@@ -91,6 +88,7 @@ impl DebugAdapterClient {
) -> Result<Self> {
let binary = match self.transport_delegate.transport() {
crate::transport::Transport::Tcp(tcp_transport) => DebugAdapterBinary {
+ adapter_name: binary.adapter_name,
command: binary.command,
arguments: binary.arguments,
envs: binary.envs,
@@ -100,11 +98,12 @@ impl DebugAdapterClient {
port: tcp_transport.port,
timeout: Some(tcp_transport.timeout),
}),
+ request_args: binary.request_args,
},
_ => self.binary.clone(),
};
- Self::start(session_id, self.name(), binary, message_handler, cx).await
+ Self::start(session_id, binary, message_handler, cx).await
}
async fn handle_receive_messages(
@@ -189,7 +188,17 @@ impl DebugAdapterClient {
let response = response??;
match response.success {
- true => Ok(serde_json::from_value(response.body.unwrap_or_default())?),
+ true => {
+ if let Some(json) = response.body {
+ Ok(serde_json::from_value(json)?)
+ // Note: dap types configure themselves to return `None` when an empty object is received,
+ // which then fails here...
+ } else if let Ok(result) = serde_json::from_value(serde_json::Value::Object(Default::default())) {
+ Ok(result)
+ } else {
+ Ok(serde_json::from_value(Default::default())?)
+ }
+ }
false => Err(anyhow!("Request failed: {}", response.message.unwrap_or_default())),
}
}
@@ -211,7 +220,7 @@ impl DebugAdapterClient {
}
pub fn name(&self) -> DebugAdapterName {
- self.name.clone()
+ self.binary.adapter_name.clone()
}
pub fn binary(&self) -> &DebugAdapterBinary {
&self.binary
@@ -238,14 +247,14 @@ impl DebugAdapterClient {
}
#[cfg(any(test, feature = "test-support"))]
- pub async fn on_request<R: dap_types::requests::Request, F>(&self, handler: F)
+ pub fn on_request<R: dap_types::requests::Request, F>(&self, handler: F)
where
F: 'static
+ Send
+ FnMut(u64, R::Arguments) -> Result<R::Response, dap_types::ErrorResponse>,
{
let transport = self.transport_delegate.transport().as_fake();
- transport.on_request::<R, F>(handler).await;
+ transport.on_request::<R, F>(handler);
}
#[cfg(any(test, feature = "test-support"))]
@@ -282,7 +291,7 @@ mod tests {
use crate::{client::DebugAdapterClient, debugger_settings::DebuggerSettings};
use dap_types::{
Capabilities, InitializeRequestArguments, InitializeRequestArgumentsPathFormat,
- RunInTerminalRequestArguments,
+ RunInTerminalRequestArguments, StartDebuggingRequestArguments,
messages::Events,
requests::{Initialize, Request, RunInTerminal},
};
@@ -312,13 +321,17 @@ mod tests {
let client = DebugAdapterClient::start(
crate::client::SessionId(1),
- DebugAdapterName("adapter".into()),
DebugAdapterBinary {
+ adapter_name: "adapter".into(),
command: "command".into(),
arguments: Default::default(),
envs: Default::default(),
connection: None,
cwd: None,
+ request_args: StartDebuggingRequestArguments {
+ configuration: serde_json::Value::Null,
+ request: dap_types::StartDebuggingRequestArgumentsRequest::Launch,
+ },
},
Box::new(|_| panic!("Did not expect to hit this code path")),
cx.to_async(),
@@ -326,14 +339,12 @@ mod tests {
.await
.unwrap();
- client
- .on_request::<Initialize, _>(move |_, _| {
- Ok(dap_types::Capabilities {
- supports_configuration_done_request: Some(true),
- ..Default::default()
- })
+ client.on_request::<Initialize, _>(move |_, _| {
+ Ok(dap_types::Capabilities {
+ supports_configuration_done_request: Some(true),
+ ..Default::default()
})
- .await;
+ });
cx.run_until_parked();
@@ -381,13 +392,17 @@ mod tests {
let client = DebugAdapterClient::start(
crate::client::SessionId(1),
- DebugAdapterName("adapter".into()),
DebugAdapterBinary {
+ adapter_name: "adapter".into(),
command: "command".into(),
arguments: Default::default(),
envs: Default::default(),
connection: None,
cwd: None,
+ request_args: StartDebuggingRequestArguments {
+ configuration: serde_json::Value::Null,
+ request: dap_types::StartDebuggingRequestArgumentsRequest::Launch,
+ },
},
Box::new({
let called_event_handler = called_event_handler.clone();
@@ -431,13 +446,17 @@ mod tests {
let client = DebugAdapterClient::start(
crate::client::SessionId(1),
- DebugAdapterName("test-adapter".into()),
DebugAdapterBinary {
+ adapter_name: "test-adapter".into(),
command: "command".into(),
arguments: Default::default(),
envs: Default::default(),
connection: None,
cwd: None,
+ request_args: dap_types::StartDebuggingRequestArguments {
+ configuration: serde_json::Value::Null,
+ request: dap_types::StartDebuggingRequestArgumentsRequest::Launch,
+ },
},
Box::new({
let called_event_handler = called_event_handler.clone();
@@ -699,14 +699,8 @@ impl StdioTransport {
}
#[cfg(any(test, feature = "test-support"))]
-type RequestHandler = Box<
- dyn Send
- + FnMut(
- u64,
- serde_json::Value,
- Arc<Mutex<async_pipe::PipeWriter>>,
- ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>>,
->;
+type RequestHandler =
+ Box<dyn Send + FnMut(u64, serde_json::Value) -> dap_types::messages::Response>;
#[cfg(any(test, feature = "test-support"))]
type ResponseHandler = Box<dyn Send + Fn(Response)>;
@@ -714,45 +708,41 @@ type ResponseHandler = Box<dyn Send + Fn(Response)>;
#[cfg(any(test, feature = "test-support"))]
pub struct FakeTransport {
// for sending fake response back from adapter side
- request_handlers: Arc<Mutex<HashMap<&'static str, RequestHandler>>>,
+ request_handlers: Arc<parking_lot::Mutex<HashMap<&'static str, RequestHandler>>>,
// for reverse request responses
- response_handlers: Arc<Mutex<HashMap<&'static str, ResponseHandler>>>,
+ response_handlers: Arc<parking_lot::Mutex<HashMap<&'static str, ResponseHandler>>>,
}
#[cfg(any(test, feature = "test-support"))]
impl FakeTransport {
- pub async fn on_request<R: dap_types::requests::Request, F>(&self, mut handler: F)
+ pub fn on_request<R: dap_types::requests::Request, F>(&self, mut handler: F)
where
F: 'static + Send + FnMut(u64, R::Arguments) -> Result<R::Response, ErrorResponse>,
{
- self.request_handlers.lock().await.insert(
+ self.request_handlers.lock().insert(
R::COMMAND,
- Box::new(
- move |seq, args, writer: Arc<Mutex<async_pipe::PipeWriter>>| {
- let response = handler(seq, serde_json::from_value(args).unwrap());
-
- let message = serde_json::to_string(&Message::Response(Response {
+ Box::new(move |seq, args| {
+ let result = handler(seq, serde_json::from_value(args).unwrap());
+ let response = match result {
+ Ok(response) => Response {
seq: seq + 1,
request_seq: seq,
- success: response.as_ref().is_ok(),
+ success: true,
command: R::COMMAND.into(),
- body: util::maybe!({ serde_json::to_value(response.ok()?).ok() }),
+ body: Some(serde_json::to_value(response).unwrap()),
message: None,
- }))
- .unwrap();
-
- let writer = writer.clone();
-
- Box::pin(async move {
- let mut writer = writer.lock().await;
- writer
- .write_all(TransportDelegate::build_rpc_message(message).as_bytes())
- .await
- .unwrap();
- writer.flush().await.unwrap();
- })
- },
- ),
+ },
+ Err(response) => Response {
+ seq: seq + 1,
+ request_seq: seq,
+ success: false,
+ command: R::COMMAND.into(),
+ body: Some(serde_json::to_value(response).unwrap()),
+ message: None,
+ },
+ };
+ response
+ }),
);
}
@@ -762,14 +752,13 @@ impl FakeTransport {
{
self.response_handlers
.lock()
- .await
.insert(R::COMMAND, Box::new(handler));
}
async fn start(cx: AsyncApp) -> Result<(TransportPipe, Self)> {
let this = Self {
- request_handlers: Arc::new(Mutex::new(HashMap::default())),
- response_handlers: Arc::new(Mutex::new(HashMap::default())),
+ request_handlers: Arc::new(parking_lot::Mutex::new(HashMap::default())),
+ response_handlers: Arc::new(parking_lot::Mutex::new(HashMap::default())),
};
use dap_types::requests::{Request, RunInTerminal, StartDebugging};
use serde_json::json;
@@ -816,23 +805,31 @@ impl FakeTransport {
.unwrap();
writer.flush().await.unwrap();
} else {
- if let Some(handle) = request_handlers
+ let response = if let Some(handle) = request_handlers
.lock()
- .await
.get_mut(request.command.as_str())
{
handle(
request.seq,
request.arguments.unwrap_or(json!({})),
- stdout_writer.clone(),
)
- .await;
} else {
- log::error!(
- "No request handler for {}",
- request.command
- );
- }
+ panic!("No request handler for {}", request.command);
+ };
+ let message =
+ serde_json::to_string(&Message::Response(response))
+ .unwrap();
+
+ let mut writer = stdout_writer.lock().await;
+
+ writer
+ .write_all(
+ TransportDelegate::build_rpc_message(message)
+ .as_bytes(),
+ )
+ .await
+ .unwrap();
+ writer.flush().await.unwrap();
}
}
Message::Event(event) => {
@@ -850,10 +847,8 @@ impl FakeTransport {
writer.flush().await.unwrap();
}
Message::Response(response) => {
- if let Some(handle) = response_handlers
- .lock()
- .await
- .get(response.command.as_str())
+ if let Some(handle) =
+ response_handlers.lock().get(response.command.as_str())
{
handle(response);
} else {
@@ -4,7 +4,7 @@ use anyhow::{Result, bail};
use async_trait::async_trait;
use dap::adapters::latest_github_release;
use gpui::AsyncApp;
-use task::{DebugAdapterConfig, DebugRequestType, DebugTaskDefinition};
+use task::{DebugRequestType, DebugTaskDefinition};
use crate::*;
@@ -15,6 +15,42 @@ pub(crate) struct CodeLldbDebugAdapter {
impl CodeLldbDebugAdapter {
const ADAPTER_NAME: &'static str = "CodeLLDB";
+
+ fn request_args(&self, config: &DebugTaskDefinition) -> dap::StartDebuggingRequestArguments {
+ let mut configuration = json!({
+ "request": match config.request {
+ DebugRequestType::Launch(_) => "launch",
+ DebugRequestType::Attach(_) => "attach",
+ },
+ });
+ let map = configuration.as_object_mut().unwrap();
+ // CodeLLDB uses `name` for a terminal label.
+ map.insert("name".into(), Value::String(config.label.clone()));
+ let request = config.request.to_dap();
+ match &config.request {
+ DebugRequestType::Attach(attach) => {
+ map.insert("pid".into(), attach.process_id.into());
+ }
+ DebugRequestType::Launch(launch) => {
+ map.insert("program".into(), launch.program.clone().into());
+
+ if !launch.args.is_empty() {
+ map.insert("args".into(), launch.args.clone().into());
+ }
+
+ if let Some(stop_on_entry) = config.stop_on_entry {
+ map.insert("stopOnEntry".into(), stop_on_entry.into());
+ }
+ if let Some(cwd) = launch.cwd.as_ref() {
+ map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
+ }
+ }
+ }
+ dap::StartDebuggingRequestArguments {
+ request,
+ configuration,
+ }
+ }
}
#[async_trait(?Send)]
@@ -86,7 +122,7 @@ impl DebugAdapter for CodeLldbDebugAdapter {
async fn get_installed_binary(
&self,
_: &dyn DapDelegate,
- _: &DebugAdapterConfig,
+ config: &DebugTaskDefinition,
_: Option<PathBuf>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
@@ -111,39 +147,10 @@ impl DebugAdapter for CodeLldbDebugAdapter {
.to_string()
.into(),
]),
- ..Default::default()
+ request_args: self.request_args(config),
+ adapter_name: "test".into(),
+ envs: None,
+ connection: None,
})
}
-
- fn request_args(&self, config: &DebugTaskDefinition) -> Value {
- let mut args = json!({
- "request": match config.request {
- DebugRequestType::Launch(_) => "launch",
- DebugRequestType::Attach(_) => "attach",
- },
- });
- let map = args.as_object_mut().unwrap();
- // CodeLLDB uses `name` for a terminal label.
- map.insert("name".into(), Value::String(config.label.clone()));
- match &config.request {
- DebugRequestType::Attach(attach) => {
- map.insert("pid".into(), attach.process_id.into());
- }
- DebugRequestType::Launch(launch) => {
- map.insert("program".into(), launch.program.clone().into());
-
- if !launch.args.is_empty() {
- map.insert("args".into(), launch.args.clone().into());
- }
-
- if let Some(stop_on_entry) = config.stop_on_entry {
- map.insert("stopOnEntry".into(), stop_on_entry.into());
- }
- if let Some(cwd) = launch.cwd.as_ref() {
- map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
- }
- }
- }
- args
- }
}
@@ -12,7 +12,7 @@ use anyhow::{Result, anyhow};
use async_trait::async_trait;
use codelldb::CodeLldbDebugAdapter;
use dap::{
- DapRegistry,
+ DapRegistry, DebugRequestType,
adapters::{
self, AdapterVersion, DapDelegate, DebugAdapter, DebugAdapterBinary, DebugAdapterName,
GithubRepo,
@@ -25,7 +25,7 @@ use lldb::LldbDebugAdapter;
use php::PhpDebugAdapter;
use python::PythonDebugAdapter;
use serde_json::{Value, json};
-use task::{DebugAdapterConfig, TCPHost};
+use task::TCPHost;
pub fn init(registry: Arc<DapRegistry>) {
registry.add_adapter(Arc::from(CodeLldbDebugAdapter::default()));
@@ -51,3 +51,16 @@ pub(crate) async fn configure_tcp_connection(
Ok((host, port, timeout))
}
+
+trait ToDap {
+ fn to_dap(&self) -> dap::StartDebuggingRequestArgumentsRequest;
+}
+
+impl ToDap for DebugRequestType {
+ fn to_dap(&self) -> dap::StartDebuggingRequestArgumentsRequest {
+ match self {
+ Self::Launch(_) => dap::StartDebuggingRequestArgumentsRequest::Launch,
+ Self::Attach(_) => dap::StartDebuggingRequestArgumentsRequest::Attach,
+ }
+ }
+}
@@ -2,8 +2,9 @@ use std::ffi::OsStr;
use anyhow::{Result, bail};
use async_trait::async_trait;
+use dap::StartDebuggingRequestArguments;
use gpui::AsyncApp;
-use task::{DebugAdapterConfig, DebugRequestType, DebugTaskDefinition};
+use task::{DebugRequestType, DebugTaskDefinition};
use crate::*;
@@ -12,6 +13,44 @@ pub(crate) struct GdbDebugAdapter;
impl GdbDebugAdapter {
const ADAPTER_NAME: &'static str = "GDB";
+
+ fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
+ let mut args = json!({
+ "request": match config.request {
+ DebugRequestType::Launch(_) => "launch",
+ DebugRequestType::Attach(_) => "attach",
+ },
+ });
+
+ let map = args.as_object_mut().unwrap();
+ match &config.request {
+ DebugRequestType::Attach(attach) => {
+ map.insert("pid".into(), attach.process_id.into());
+ }
+
+ DebugRequestType::Launch(launch) => {
+ map.insert("program".into(), launch.program.clone().into());
+
+ if !launch.args.is_empty() {
+ map.insert("args".into(), launch.args.clone().into());
+ }
+
+ if let Some(stop_on_entry) = config.stop_on_entry {
+ map.insert(
+ "stopAtBeginningOfMainSubprogram".into(),
+ stop_on_entry.into(),
+ );
+ }
+ if let Some(cwd) = launch.cwd.as_ref() {
+ map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
+ }
+ }
+ }
+ StartDebuggingRequestArguments {
+ configuration: args,
+ request: config.request.to_dap(),
+ }
+ }
}
#[async_trait(?Send)]
@@ -23,7 +62,7 @@ impl DebugAdapter for GdbDebugAdapter {
async fn get_binary(
&self,
delegate: &dyn DapDelegate,
- _: &DebugAdapterConfig,
+ config: &DebugTaskDefinition,
user_installed_path: Option<std::path::PathBuf>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
@@ -43,11 +82,13 @@ impl DebugAdapter for GdbDebugAdapter {
let gdb_path = user_setting_path.unwrap_or(gdb_path?);
Ok(DebugAdapterBinary {
+ adapter_name: Self::ADAPTER_NAME.into(),
command: gdb_path,
arguments: Some(vec!["-i=dap".into()]),
envs: None,
cwd: None,
connection: None,
+ request_args: self.request_args(config),
})
}
@@ -66,45 +107,10 @@ impl DebugAdapter for GdbDebugAdapter {
async fn get_installed_binary(
&self,
_: &dyn DapDelegate,
- _: &DebugAdapterConfig,
+ _: &DebugTaskDefinition,
_: Option<std::path::PathBuf>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
unimplemented!("GDB cannot be installed by Zed (yet)")
}
-
- fn request_args(&self, config: &DebugTaskDefinition) -> Value {
- let mut args = json!({
- "request": match config.request {
- DebugRequestType::Launch(_) => "launch",
- DebugRequestType::Attach(_) => "attach",
- },
- });
-
- let map = args.as_object_mut().unwrap();
- match &config.request {
- DebugRequestType::Attach(attach) => {
- map.insert("pid".into(), attach.process_id.into());
- }
-
- DebugRequestType::Launch(launch) => {
- map.insert("program".into(), launch.program.clone().into());
-
- if !launch.args.is_empty() {
- map.insert("args".into(), launch.args.clone().into());
- }
-
- if let Some(stop_on_entry) = config.stop_on_entry {
- map.insert(
- "stopAtBeginningOfMainSubprogram".into(),
- stop_on_entry.into(),
- );
- }
- if let Some(cwd) = launch.cwd.as_ref() {
- map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
- }
- }
- }
- args
- }
}
@@ -1,3 +1,4 @@
+use dap::StartDebuggingRequestArguments;
use gpui::AsyncApp;
use std::{ffi::OsStr, path::PathBuf};
use task::DebugTaskDefinition;
@@ -9,6 +10,31 @@ pub(crate) struct GoDebugAdapter;
impl GoDebugAdapter {
const ADAPTER_NAME: &'static str = "Delve";
+ fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
+ let mut args = match &config.request {
+ dap::DebugRequestType::Attach(attach_config) => {
+ json!({
+ "processId": attach_config.process_id,
+ })
+ }
+ dap::DebugRequestType::Launch(launch_config) => json!({
+ "program": launch_config.program,
+ "cwd": launch_config.cwd,
+ "args": launch_config.args
+ }),
+ };
+
+ let map = args.as_object_mut().unwrap();
+
+ if let Some(stop_on_entry) = config.stop_on_entry {
+ map.insert("stopOnEntry".into(), stop_on_entry.into());
+ }
+
+ StartDebuggingRequestArguments {
+ configuration: args,
+ request: config.request.to_dap(),
+ }
+ }
}
#[async_trait(?Send)]
@@ -20,7 +46,7 @@ impl DebugAdapter for GoDebugAdapter {
async fn get_binary(
&self,
delegate: &dyn DapDelegate,
- config: &DebugAdapterConfig,
+ config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
cx: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
@@ -53,7 +79,7 @@ impl DebugAdapter for GoDebugAdapter {
async fn get_installed_binary(
&self,
delegate: &dyn DapDelegate,
- config: &DebugAdapterConfig,
+ config: &DebugTaskDefinition,
_: Option<PathBuf>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
@@ -66,6 +92,7 @@ impl DebugAdapter for GoDebugAdapter {
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
Ok(DebugAdapterBinary {
+ adapter_name: self.name(),
command: delve_path,
arguments: Some(vec![
"dap".into(),
@@ -79,29 +106,7 @@ impl DebugAdapter for GoDebugAdapter {
port,
timeout,
}),
+ request_args: self.request_args(config),
})
}
-
- fn request_args(&self, config: &DebugTaskDefinition) -> Value {
- let mut args = match &config.request {
- dap::DebugRequestType::Attach(attach_config) => {
- json!({
- "processId": attach_config.process_id,
- })
- }
- dap::DebugRequestType::Launch(launch_config) => json!({
- "program": launch_config.program,
- "cwd": launch_config.cwd,
- "args": launch_config.args
- }),
- };
-
- let map = args.as_object_mut().unwrap();
-
- if let Some(stop_on_entry) = config.stop_on_entry {
- map.insert("stopOnEntry".into(), stop_on_entry.into());
- }
-
- args
- }
}
@@ -1,4 +1,5 @@
use adapters::latest_github_release;
+use dap::StartDebuggingRequestArguments;
use gpui::AsyncApp;
use std::path::PathBuf;
use task::{DebugRequestType, DebugTaskDefinition};
@@ -12,6 +13,40 @@ impl JsDebugAdapter {
const ADAPTER_NAME: &'static str = "JavaScript";
const ADAPTER_NPM_NAME: &'static str = "vscode-js-debug";
const ADAPTER_PATH: &'static str = "js-debug/src/dapDebugServer.js";
+
+ fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
+ let mut args = json!({
+ "type": "pwa-node",
+ "request": match config.request {
+ DebugRequestType::Launch(_) => "launch",
+ DebugRequestType::Attach(_) => "attach",
+ },
+ });
+ let map = args.as_object_mut().unwrap();
+ match &config.request {
+ DebugRequestType::Attach(attach) => {
+ map.insert("processId".into(), attach.process_id.into());
+ }
+ DebugRequestType::Launch(launch) => {
+ map.insert("program".into(), launch.program.clone().into());
+
+ if !launch.args.is_empty() {
+ map.insert("args".into(), launch.args.clone().into());
+ }
+
+ if let Some(stop_on_entry) = config.stop_on_entry {
+ map.insert("stopOnEntry".into(), stop_on_entry.into());
+ }
+ if let Some(cwd) = launch.cwd.as_ref() {
+ map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
+ }
+ }
+ }
+ StartDebuggingRequestArguments {
+ configuration: args,
+ request: config.request.to_dap(),
+ }
+ }
}
#[async_trait(?Send)]
@@ -49,7 +84,7 @@ impl DebugAdapter for JsDebugAdapter {
async fn get_installed_binary(
&self,
delegate: &dyn DapDelegate,
- config: &DebugAdapterConfig,
+ config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
@@ -71,6 +106,7 @@ impl DebugAdapter for JsDebugAdapter {
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
Ok(DebugAdapterBinary {
+ adapter_name: self.name(),
command: delegate
.node_runtime()
.binary_path()
@@ -89,6 +125,7 @@ impl DebugAdapter for JsDebugAdapter {
port,
timeout,
}),
+ request_args: self.request_args(config),
})
}
@@ -107,35 +144,4 @@ impl DebugAdapter for JsDebugAdapter {
return Ok(());
}
-
- fn request_args(&self, config: &DebugTaskDefinition) -> Value {
- let mut args = json!({
- "type": "pwa-node",
- "request": match config.request {
- DebugRequestType::Launch(_) => "launch",
- DebugRequestType::Attach(_) => "attach",
- },
- });
- let map = args.as_object_mut().unwrap();
- match &config.request {
- DebugRequestType::Attach(attach) => {
- map.insert("processId".into(), attach.process_id.into());
- }
- DebugRequestType::Launch(launch) => {
- map.insert("program".into(), launch.program.clone().into());
-
- if !launch.args.is_empty() {
- map.insert("args".into(), launch.args.clone().into());
- }
-
- if let Some(stop_on_entry) = config.stop_on_entry {
- map.insert("stopOnEntry".into(), stop_on_entry.into());
- }
- if let Some(cwd) = launch.cwd.as_ref() {
- map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
- }
- }
- }
- args
- }
}
@@ -3,7 +3,7 @@ use std::{ffi::OsStr, path::PathBuf};
use anyhow::Result;
use async_trait::async_trait;
use gpui::AsyncApp;
-use task::{DebugAdapterConfig, DebugRequestType, DebugTaskDefinition};
+use task::{DebugRequestType, DebugTaskDefinition};
use crate::*;
@@ -12,6 +12,40 @@ pub(crate) struct LldbDebugAdapter;
impl LldbDebugAdapter {
const ADAPTER_NAME: &'static str = "LLDB";
+
+ fn request_args(&self, config: &DebugTaskDefinition) -> dap::StartDebuggingRequestArguments {
+ let request = config.request.to_dap();
+ let mut args = json!({
+ "request": match config.request {
+ DebugRequestType::Launch(_) => "launch",
+ DebugRequestType::Attach(_) => "attach",
+ },
+ });
+ let map = args.as_object_mut().unwrap();
+ match &config.request {
+ DebugRequestType::Attach(attach) => {
+ map.insert("pid".into(), attach.process_id.into());
+ }
+ DebugRequestType::Launch(launch) => {
+ map.insert("program".into(), launch.program.clone().into());
+
+ if !launch.args.is_empty() {
+ map.insert("args".into(), launch.args.clone().into());
+ }
+
+ if let Some(stop_on_entry) = config.stop_on_entry {
+ map.insert("stopOnEntry".into(), stop_on_entry.into());
+ }
+ if let Some(cwd) = launch.cwd.as_ref() {
+ map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
+ }
+ }
+ }
+ dap::StartDebuggingRequestArguments {
+ request,
+ configuration: args,
+ }
+ }
}
#[async_trait(?Send)]
@@ -23,7 +57,7 @@ impl DebugAdapter for LldbDebugAdapter {
async fn get_binary(
&self,
delegate: &dyn DapDelegate,
- _: &DebugAdapterConfig,
+ config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
@@ -37,11 +71,13 @@ impl DebugAdapter for LldbDebugAdapter {
};
Ok(DebugAdapterBinary {
+ adapter_name: Self::ADAPTER_NAME.into(),
command: lldb_dap_path,
arguments: None,
envs: None,
cwd: None,
connection: None,
+ request_args: self.request_args(config),
})
}
@@ -60,40 +96,10 @@ impl DebugAdapter for LldbDebugAdapter {
async fn get_installed_binary(
&self,
_: &dyn DapDelegate,
- _: &DebugAdapterConfig,
+ _: &DebugTaskDefinition,
_: Option<PathBuf>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
unimplemented!("LLDB debug adapter cannot be installed by Zed (yet)")
}
-
- fn request_args(&self, config: &DebugTaskDefinition) -> Value {
- let mut args = json!({
- "request": match config.request {
- DebugRequestType::Launch(_) => "launch",
- DebugRequestType::Attach(_) => "attach",
- },
- });
- let map = args.as_object_mut().unwrap();
- match &config.request {
- DebugRequestType::Attach(attach) => {
- map.insert("pid".into(), attach.process_id.into());
- }
- DebugRequestType::Launch(launch) => {
- map.insert("program".into(), launch.program.clone().into());
-
- if !launch.args.is_empty() {
- map.insert("args".into(), launch.args.clone().into());
- }
-
- if let Some(stop_on_entry) = config.stop_on_entry {
- map.insert("stopOnEntry".into(), stop_on_entry.into());
- }
- if let Some(cwd) = launch.cwd.as_ref() {
- map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
- }
- }
- }
- args
- }
}
@@ -13,6 +13,28 @@ impl PhpDebugAdapter {
const ADAPTER_NAME: &'static str = "PHP";
const ADAPTER_PACKAGE_NAME: &'static str = "vscode-php-debug";
const ADAPTER_PATH: &'static str = "extension/out/phpDebug.js";
+
+ fn request_args(
+ &self,
+ config: &DebugTaskDefinition,
+ ) -> Result<dap::StartDebuggingRequestArguments> {
+ match &config.request {
+ dap::DebugRequestType::Attach(_) => {
+ anyhow::bail!("php adapter does not support attaching")
+ }
+ dap::DebugRequestType::Launch(launch_config) => {
+ Ok(dap::StartDebuggingRequestArguments {
+ configuration: json!({
+ "program": launch_config.program,
+ "cwd": launch_config.cwd,
+ "args": launch_config.args,
+ "stopOnEntry": config.stop_on_entry.unwrap_or_default(),
+ }),
+ request: config.request.to_dap(),
+ })
+ }
+ }
+ }
}
#[async_trait(?Send)]
@@ -50,7 +72,7 @@ impl DebugAdapter for PhpDebugAdapter {
async fn get_installed_binary(
&self,
delegate: &dyn DapDelegate,
- config: &DebugAdapterConfig,
+ config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
@@ -72,6 +94,7 @@ impl DebugAdapter for PhpDebugAdapter {
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
Ok(DebugAdapterBinary {
+ adapter_name: self.name(),
command: delegate
.node_runtime()
.binary_path()
@@ -89,6 +112,7 @@ impl DebugAdapter for PhpDebugAdapter {
}),
cwd: None,
envs: None,
+ request_args: self.request_args(config)?,
})
}
@@ -107,21 +131,4 @@ impl DebugAdapter for PhpDebugAdapter {
Ok(())
}
-
- fn request_args(&self, config: &DebugTaskDefinition) -> Value {
- match &config.request {
- dap::DebugRequestType::Attach(_) => {
- // php adapter does not support attaching
- json!({})
- }
- dap::DebugRequestType::Launch(launch_config) => {
- json!({
- "program": launch_config.program,
- "cwd": launch_config.cwd,
- "args": launch_config.args,
- "stopOnEntry": config.stop_on_entry.unwrap_or_default(),
- })
- }
- }
- }
}
@@ -1,5 +1,5 @@
use crate::*;
-use dap::DebugRequestType;
+use dap::{DebugRequestType, StartDebuggingRequestArguments};
use gpui::AsyncApp;
use std::{ffi::OsStr, path::PathBuf};
use task::DebugTaskDefinition;
@@ -12,6 +12,38 @@ impl PythonDebugAdapter {
const ADAPTER_PACKAGE_NAME: &'static str = "debugpy";
const ADAPTER_PATH: &'static str = "src/debugpy/adapter";
const LANGUAGE_NAME: &'static str = "Python";
+
+ fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
+ let mut args = json!({
+ "request": match config.request {
+ DebugRequestType::Launch(_) => "launch",
+ DebugRequestType::Attach(_) => "attach",
+ },
+ "subProcess": true,
+ "redirectOutput": true,
+ });
+ let map = args.as_object_mut().unwrap();
+ match &config.request {
+ DebugRequestType::Attach(attach) => {
+ map.insert("processId".into(), attach.process_id.into());
+ }
+ DebugRequestType::Launch(launch) => {
+ map.insert("program".into(), launch.program.clone().into());
+ map.insert("args".into(), launch.args.clone().into());
+
+ if let Some(stop_on_entry) = config.stop_on_entry {
+ map.insert("stopOnEntry".into(), stop_on_entry.into());
+ }
+ if let Some(cwd) = launch.cwd.as_ref() {
+ map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
+ }
+ }
+ }
+ StartDebuggingRequestArguments {
+ configuration: args,
+ request: config.request.to_dap(),
+ }
+ }
}
#[async_trait(?Send)]
@@ -64,7 +96,7 @@ impl DebugAdapter for PythonDebugAdapter {
async fn get_installed_binary(
&self,
delegate: &dyn DapDelegate,
- config: &DebugAdapterConfig,
+ config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
cx: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
@@ -109,6 +141,7 @@ impl DebugAdapter for PythonDebugAdapter {
};
Ok(DebugAdapterBinary {
+ adapter_name: self.name(),
command: python_path.ok_or(anyhow!("failed to find binary path for python"))?,
arguments: Some(vec![
debugpy_dir.join(Self::ADAPTER_PATH).into(),
@@ -122,35 +155,7 @@ impl DebugAdapter for PythonDebugAdapter {
}),
cwd: None,
envs: None,
+ request_args: self.request_args(config),
})
}
-
- fn request_args(&self, config: &DebugTaskDefinition) -> Value {
- let mut args = json!({
- "request": match config.request {
- DebugRequestType::Launch(_) => "launch",
- DebugRequestType::Attach(_) => "attach",
- },
- "subProcess": true,
- "redirectOutput": true,
- });
- let map = args.as_object_mut().unwrap();
- match &config.request {
- DebugRequestType::Attach(attach) => {
- map.insert("processId".into(), attach.process_id.into());
- }
- DebugRequestType::Launch(launch) => {
- map.insert("program".into(), launch.program.clone().into());
- map.insert("args".into(), launch.args.clone().into());
-
- if let Some(stop_on_entry) = config.stop_on_entry {
- map.insert("stopOnEntry".into(), stop_on_entry.into());
- }
- if let Some(cwd) = launch.cwd.as_ref() {
- map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
- }
- }
- }
- args
- }
}
@@ -228,10 +228,7 @@ impl PickerDelegate for AttachModalDelegate {
let config = self.debug_config.clone();
self.project
.update(cx, |project, cx| {
- #[cfg(any(test, feature = "test-support"))]
- let ret = project.fake_debug_session(config.request, None, false, cx);
- #[cfg(not(any(test, feature = "test-support")))]
- let ret = project.start_debug_session(config.into(), cx);
+ let ret = project.start_debug_session(config, cx);
ret
})
.detach_and_log_err(cx);
@@ -146,7 +146,7 @@ impl NewSessionModal {
{
this.start_debug_session(debug_config, cx)
} else {
- this.start_debug_session(config.into(), cx)
+ this.start_debug_session(config, cx)
}
})?;
let spawn_result = task.await;
@@ -26,18 +26,30 @@ async fn test_direct_attach_to_process(executor: BackgroundExecutor, cx: &mut Te
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Attach(AttachConfig {
+ let session = debugger::test::start_debug_session_with(
+ &project,
+ cx,
+ DebugTaskDefinition {
+ adapter: "fake-adapter".to_string(),
+ request: dap::DebugRequestType::Attach(AttachConfig {
process_id: Some(10),
}),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ label: "label".to_string(),
+ initialize_args: None,
+ tcp_connection: None,
+ locator: None,
+ stop_on_entry: None,
+ },
+ |client| {
+ client.on_request::<dap::requests::Attach, _>(move |_, args| {
+ assert_eq!(json!({"request": "attach", "process_id": 10}), args.raw);
+
+ Ok(())
+ });
+ },
+ )
+ .await
+ .unwrap();
cx.run_until_parked();
@@ -77,7 +89,15 @@ async fn test_show_attach_modal_and_select_process(
let project = Project::test(fs, ["/project".as_ref()], cx).await;
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
+ // Set up handlers for sessions spawned via modal.
+ let _initialize_subscription =
+ project::debugger::test::intercept_debug_sessions(cx, |client| {
+ client.on_request::<dap::requests::Attach, _>(move |_, args| {
+ assert_eq!(json!({"request": "attach", "process_id": 1}), args.raw);
+ Ok(())
+ });
+ });
let attach_modal = workspace
.update(cx, |workspace, window, cx| {
workspace.toggle_modal(window, cx, |window, cx| {
@@ -3,7 +3,6 @@ use dap::requests::StackTrace;
use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
use project::{FakeFs, Project};
use serde_json::json;
-use task::LaunchConfig;
use tests::{init_test, init_test_workspace};
#[gpui::test]
@@ -29,26 +28,17 @@ async fn test_handle_output_event(executor: BackgroundExecutor, cx: &mut TestApp
})
.unwrap();
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<StackTrace, _>(move |_, _| {
- Ok(dap::StackTraceResponse {
- stack_frames: Vec::default(),
- total_frames: None,
- })
+ client.on_request::<StackTrace, _>(move |_, _| {
+ Ok(dap::StackTraceResponse {
+ stack_frames: Vec::default(),
+ total_frames: None,
})
- .await;
+ });
client
.fake_event(dap::messages::Events::Output(dap::OutputEvent {
@@ -1,7 +1,7 @@
use crate::*;
use dap::{
- ErrorResponse, RunInTerminalRequestArguments, SourceBreakpoint, StartDebuggingRequestArguments,
- StartDebuggingRequestArgumentsRequest,
+ ErrorResponse, Message, RunInTerminalRequestArguments, SourceBreakpoint,
+ StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest,
client::SessionId,
requests::{
Continue, Disconnect, Launch, Next, RunInTerminal, SetBreakpoints, StackTrace,
@@ -25,7 +25,6 @@ use std::{
atomic::{AtomicBool, Ordering},
},
};
-use task::LaunchConfig;
use terminal_view::{TerminalView, terminal_panel::TerminalPanel};
use tests::{active_debug_session_panel, init_test, init_test_workspace};
use util::path;
@@ -49,37 +48,26 @@ async fn test_basic_show_debug_panel(executor: BackgroundExecutor, cx: &mut Test
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
- client
- .on_request::<StackTrace, _>(move |_, _| {
- Ok(dap::StackTraceResponse {
- stack_frames: Vec::default(),
- total_frames: None,
- })
+ client.on_request::<StackTrace, _>(move |_, _| {
+ Ok(dap::StackTraceResponse {
+ stack_frames: Vec::default(),
+ total_frames: None,
})
- .await;
+ });
cx.run_until_parked();
@@ -199,37 +187,26 @@ async fn test_we_can_only_have_one_panel_per_debug_session(
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
- client
- .on_request::<StackTrace, _>(move |_, _| {
- Ok(dap::StackTraceResponse {
- stack_frames: Vec::default(),
- total_frames: None,
- })
+ client.on_request::<StackTrace, _>(move |_, _| {
+ Ok(dap::StackTraceResponse {
+ stack_frames: Vec::default(),
+ total_frames: None,
})
- .await;
+ });
cx.run_until_parked();
@@ -377,16 +354,9 @@ async fn test_handle_successful_run_in_terminal_reverse_request(
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
client
@@ -474,16 +444,9 @@ async fn test_handle_error_run_in_terminal_reverse_request(
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
client
@@ -559,28 +522,19 @@ async fn test_handle_start_debugging_reverse_request(
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<dap::requests::Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<dap::requests::Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
client
.on_response::<StartDebugging, _>({
@@ -593,7 +547,9 @@ async fn test_handle_start_debugging_reverse_request(
}
})
.await;
-
+ // Set up handlers for sessions spawned with reverse request too.
+ let _reverse_request_subscription =
+ project::debugger::test::intercept_debug_sessions(cx, |_| {});
client
.fake_reverse_request::<StartDebugging>(StartDebuggingRequestArguments {
configuration: json!({}),
@@ -612,20 +568,16 @@ async fn test_handle_start_debugging_reverse_request(
});
let child_client = child_session.update(cx, |session, _| session.adapter_client().unwrap());
- child_client
- .on_request::<dap::requests::Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ child_client.on_request::<dap::requests::Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
- child_client
- .on_request::<Disconnect, _>(move |_, _| Ok(()))
- .await;
+ child_client.on_request::<Disconnect, _>(move |_, _| Ok(()));
child_client
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -677,31 +629,24 @@ async fn test_shutdown_children_when_parent_session_shutdown(
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let parent_session = task.await.unwrap();
+ let parent_session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = parent_session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<dap::requests::Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<dap::requests::Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
client.on_response::<StartDebugging, _>(move |_| {}).await;
-
+ // Set up handlers for sessions spawned with reverse request too.
+ let _reverse_request_subscription =
+ project::debugger::test::intercept_debug_sessions(cx, |_| {});
// start first child session
client
.fake_reverse_request::<StartDebugging>(StartDebuggingRequestArguments {
@@ -729,9 +674,7 @@ async fn test_shutdown_children_when_parent_session_shutdown(
let first_child_client =
first_child_session.update(cx, |session, _| session.adapter_client().unwrap());
- first_child_client
- .on_request::<Disconnect, _>(move |_, _| Ok(()))
- .await;
+ first_child_client.on_request::<Disconnect, _>(move |_, _| Ok(()));
// configure second child session
let second_child_session = dap_store.read_with(cx, |dap_store, _| {
@@ -740,9 +683,7 @@ async fn test_shutdown_children_when_parent_session_shutdown(
let second_child_client =
second_child_session.update(cx, |session, _| session.adapter_client().unwrap());
- second_child_client
- .on_request::<Disconnect, _>(move |_, _| Ok(()))
- .await;
+ second_child_client.on_request::<Disconnect, _>(move |_, _| Ok(()));
cx.run_until_parked();
@@ -796,20 +737,15 @@ async fn test_shutdown_parent_session_if_all_children_are_shutdown(
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let parent_session = task.await.unwrap();
+ let parent_session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = parent_session.update(cx, |session, _| session.adapter_client().unwrap());
client.on_response::<StartDebugging, _>(move |_| {}).await;
-
+ // Set up handlers for sessions spawned with reverse request too.
+ let _reverse_request_subscription =
+ project::debugger::test::intercept_debug_sessions(cx, |_| {});
// start first child session
client
.fake_reverse_request::<StartDebugging>(StartDebuggingRequestArguments {
@@ -837,9 +773,7 @@ async fn test_shutdown_parent_session_if_all_children_are_shutdown(
let first_child_client =
first_child_session.update(cx, |session, _| session.adapter_client().unwrap());
- first_child_client
- .on_request::<Disconnect, _>(move |_, _| Ok(()))
- .await;
+ first_child_client.on_request::<Disconnect, _>(move |_, _| Ok(()));
// configure second child session
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(
let second_child_client =
second_child_session.update(cx, |session, _| session.adapter_client().unwrap());
- second_child_client
- .on_request::<Disconnect, _>(move |_, _| Ok(()))
- .await;
+ second_child_client.on_request::<Disconnect, _>(move |_, _| Ok(()));
cx.run_until_parked();
@@ -926,123 +858,107 @@ async fn test_debug_panel_item_thread_status_reset_on_failure(
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- Some(dap::Capabilities {
+ let session = debugger::test::start_debug_session(&project, cx, |client| {
+ client.on_request::<dap::requests::Initialize, _>(move |_, _| {
+ Ok(dap::Capabilities {
supports_step_back: Some(true),
..Default::default()
- }),
- false,
- cx,
- )
- });
+ })
+ });
+ })
+ .await
+ .unwrap();
- let session = task.await.unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
const THREAD_ID_NUM: u64 = 1;
- client
- .on_request::<dap::requests::Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: THREAD_ID_NUM,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<dap::requests::Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: THREAD_ID_NUM,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
- client.on_request::<Launch, _>(move |_, _| Ok(())).await;
+ client.on_request::<Launch, _>(move |_, _| Ok(()));
- client
- .on_request::<StackTrace, _>(move |_, _| {
- Ok(dap::StackTraceResponse {
- stack_frames: Vec::default(),
- total_frames: None,
- })
+ client.on_request::<StackTrace, _>(move |_, _| {
+ Ok(dap::StackTraceResponse {
+ stack_frames: Vec::default(),
+ total_frames: None,
})
- .await;
+ });
- client
- .on_request::<Next, _>(move |_, _| {
- Err(ErrorResponse {
- error: Some(dap::Message {
- id: 1,
- format: "error".into(),
- variables: None,
- send_telemetry: None,
- show_user: None,
- url: None,
- url_label: None,
- }),
- })
+ client.on_request::<Next, _>(move |_, _| {
+ Err(ErrorResponse {
+ error: Some(dap::Message {
+ id: 1,
+ format: "error".into(),
+ variables: None,
+ send_telemetry: None,
+ show_user: None,
+ url: None,
+ url_label: None,
+ }),
})
- .await;
+ });
- client
- .on_request::<StepOut, _>(move |_, _| {
- Err(ErrorResponse {
- error: Some(dap::Message {
- id: 1,
- format: "error".into(),
- variables: None,
- send_telemetry: None,
- show_user: None,
- url: None,
- url_label: None,
- }),
- })
+ client.on_request::<StepOut, _>(move |_, _| {
+ Err(ErrorResponse {
+ error: Some(dap::Message {
+ id: 1,
+ format: "error".into(),
+ variables: None,
+ send_telemetry: None,
+ show_user: None,
+ url: None,
+ url_label: None,
+ }),
})
- .await;
+ });
- client
- .on_request::<StepIn, _>(move |_, _| {
- Err(ErrorResponse {
- error: Some(dap::Message {
- id: 1,
- format: "error".into(),
- variables: None,
- send_telemetry: None,
- show_user: None,
- url: None,
- url_label: None,
- }),
- })
+ client.on_request::<StepIn, _>(move |_, _| {
+ Err(ErrorResponse {
+ error: Some(dap::Message {
+ id: 1,
+ format: "error".into(),
+ variables: None,
+ send_telemetry: None,
+ show_user: None,
+ url: None,
+ url_label: None,
+ }),
})
- .await;
+ });
- client
- .on_request::<StepBack, _>(move |_, _| {
- Err(ErrorResponse {
- error: Some(dap::Message {
- id: 1,
- format: "error".into(),
- variables: None,
- send_telemetry: None,
- show_user: None,
- url: None,
- url_label: None,
- }),
- })
+ client.on_request::<StepBack, _>(move |_, _| {
+ Err(ErrorResponse {
+ error: Some(dap::Message {
+ id: 1,
+ format: "error".into(),
+ variables: None,
+ send_telemetry: None,
+ show_user: None,
+ url: None,
+ url_label: None,
+ }),
})
- .await;
+ });
- client
- .on_request::<Continue, _>(move |_, _| {
- Err(ErrorResponse {
- error: Some(dap::Message {
- id: 1,
- format: "error".into(),
- variables: None,
- send_telemetry: None,
- show_user: None,
- url: None,
- url_label: None,
- }),
- })
+ client.on_request::<Continue, _>(move |_, _| {
+ Err(ErrorResponse {
+ error: Some(dap::Message {
+ id: 1,
+ format: "error".into(),
+ variables: None,
+ send_telemetry: None,
+ show_user: None,
+ url: None,
+ url_label: None,
+ }),
})
- .await;
+ });
client
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -1157,16 +1073,9 @@ async fn test_send_breakpoints_when_editor_has_been_saved(
.update(cx, |_, _, cx| worktree.read(cx).id())
.unwrap();
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
let buffer = project
@@ -1186,16 +1095,14 @@ async fn test_send_breakpoints_when_editor_has_been_saved(
)
});
- client.on_request::<Launch, _>(move |_, _| Ok(())).await;
+ client.on_request::<Launch, _>(move |_, _| Ok(()));
- client
- .on_request::<StackTrace, _>(move |_, _| {
- Ok(dap::StackTraceResponse {
- stack_frames: Vec::default(),
- total_frames: None,
- })
+ client.on_request::<StackTrace, _>(move |_, _| {
+ Ok(dap::StackTraceResponse {
+ stack_frames: Vec::default(),
+ total_frames: None,
})
- .await;
+ });
client
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -1210,32 +1117,30 @@ async fn test_send_breakpoints_when_editor_has_been_saved(
.await;
let called_set_breakpoints = Arc::new(AtomicBool::new(false));
- client
- .on_request::<SetBreakpoints, _>({
- let called_set_breakpoints = called_set_breakpoints.clone();
- move |_, args| {
- assert_eq!(path!("/project/main.rs"), args.source.path.unwrap());
- assert_eq!(
- vec![SourceBreakpoint {
- line: 2,
- column: None,
- condition: None,
- hit_condition: None,
- log_message: None,
- mode: None
- }],
- args.breakpoints.unwrap()
- );
- assert!(!args.source_modified.unwrap());
+ client.on_request::<SetBreakpoints, _>({
+ let called_set_breakpoints = called_set_breakpoints.clone();
+ move |_, args| {
+ assert_eq!(path!("/project/main.rs"), args.source.path.unwrap());
+ assert_eq!(
+ vec![SourceBreakpoint {
+ line: 2,
+ column: None,
+ condition: None,
+ hit_condition: None,
+ log_message: None,
+ mode: None
+ }],
+ args.breakpoints.unwrap()
+ );
+ assert!(!args.source_modified.unwrap());
- called_set_breakpoints.store(true, Ordering::SeqCst);
+ called_set_breakpoints.store(true, Ordering::SeqCst);
- Ok(dap::SetBreakpointsResponse {
- breakpoints: Vec::default(),
- })
- }
- })
- .await;
+ Ok(dap::SetBreakpointsResponse {
+ breakpoints: Vec::default(),
+ })
+ }
+ });
editor.update_in(cx, |editor, window, cx| {
editor.move_down(&actions::MoveDown, window, cx);
@@ -1250,32 +1155,30 @@ async fn test_send_breakpoints_when_editor_has_been_saved(
);
let called_set_breakpoints = Arc::new(AtomicBool::new(false));
- client
- .on_request::<SetBreakpoints, _>({
- let called_set_breakpoints = called_set_breakpoints.clone();
- move |_, args| {
- assert_eq!(path!("/project/main.rs"), args.source.path.unwrap());
- assert_eq!(
- vec![SourceBreakpoint {
- line: 3,
- column: None,
- condition: None,
- hit_condition: None,
- log_message: None,
- mode: None
- }],
- args.breakpoints.unwrap()
- );
- assert!(args.source_modified.unwrap());
+ client.on_request::<SetBreakpoints, _>({
+ let called_set_breakpoints = called_set_breakpoints.clone();
+ move |_, args| {
+ assert_eq!(path!("/project/main.rs"), args.source.path.unwrap());
+ assert_eq!(
+ vec![SourceBreakpoint {
+ line: 3,
+ column: None,
+ condition: None,
+ hit_condition: None,
+ log_message: None,
+ mode: None
+ }],
+ args.breakpoints.unwrap()
+ );
+ assert!(args.source_modified.unwrap());
- called_set_breakpoints.store(true, Ordering::SeqCst);
+ called_set_breakpoints.store(true, Ordering::SeqCst);
- Ok(dap::SetBreakpointsResponse {
- breakpoints: Vec::default(),
- })
- }
- })
- .await;
+ Ok(dap::SetBreakpointsResponse {
+ breakpoints: Vec::default(),
+ })
+ }
+ });
editor.update_in(cx, |editor, window, cx| {
editor.move_up(&actions::MoveUp, window, cx);
@@ -1387,49 +1290,40 @@ async fn test_unsetting_breakpoints_on_clear_breakpoint_action(
editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
});
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
let called_set_breakpoints = Arc::new(AtomicBool::new(false));
- client
- .on_request::<SetBreakpoints, _>({
- let called_set_breakpoints = called_set_breakpoints.clone();
- move |_, args| {
- assert!(
- args.breakpoints.is_none_or(|bps| bps.is_empty()),
- "Send empty breakpoint sets to clear them from DAP servers"
- );
+ client.on_request::<SetBreakpoints, _>({
+ let called_set_breakpoints = called_set_breakpoints.clone();
+ move |_, args| {
+ assert!(
+ args.breakpoints.is_none_or(|bps| bps.is_empty()),
+ "Send empty breakpoint sets to clear them from DAP servers"
+ );
- match args
- .source
- .path
- .expect("We should always send a breakpoint's path")
- .as_str()
- {
- "/project/main.rs" | "/project/second.rs" => {}
- _ => {
- panic!("Unset breakpoints for path that doesn't have any")
- }
+ match args
+ .source
+ .path
+ .expect("We should always send a breakpoint's path")
+ .as_str()
+ {
+ "/project/main.rs" | "/project/second.rs" => {}
+ _ => {
+ panic!("Unset breakpoints for path that doesn't have any")
}
+ }
- called_set_breakpoints.store(true, Ordering::SeqCst);
+ called_set_breakpoints.store(true, Ordering::SeqCst);
- Ok(dap::SetBreakpointsResponse {
- breakpoints: Vec::default(),
- })
- }
- })
- .await;
+ Ok(dap::SetBreakpointsResponse {
+ breakpoints: Vec::default(),
+ })
+ }
+ });
cx.dispatch_action(crate::ClearAllBreakpoints);
cx.run_until_parked();
@@ -1464,13 +1358,20 @@ async fn test_debug_session_is_shutdown_when_attach_and_launch_request_fails(
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- true,
- cx,
- )
+ let task = project::debugger::test::start_debug_session(&project, cx, |client| {
+ client.on_request::<dap::requests::Initialize, _>(|_, _| {
+ Err(ErrorResponse {
+ error: Some(Message {
+ format: "failed to launch".to_string(),
+ id: 1,
+ variables: None,
+ send_telemetry: None,
+ show_user: None,
+ url: None,
+ url_label: None,
+ }),
+ })
+ });
});
assert!(
@@ -4,15 +4,17 @@ use crate::{
};
use dap::{
StoppedEvent,
- requests::{Modules, StackTrace, Threads},
+ requests::{Initialize, Modules},
};
use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
-use project::{FakeFs, Project};
+use project::{
+ FakeFs, Project,
+ debugger::{self},
+};
use std::sync::{
Arc,
atomic::{AtomicBool, AtomicI32, Ordering},
};
-use task::LaunchConfig;
#[gpui::test]
async fn test_module_list(executor: BackgroundExecutor, cx: &mut TestAppContext) {
@@ -29,31 +31,19 @@ async fn test_module_list(executor: BackgroundExecutor, cx: &mut TestAppContext)
.unwrap();
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- Some(dap::Capabilities {
+ let session = debugger::test::start_debug_session(&project, cx, |client| {
+ client.on_request::<Initialize, _>(move |_, _| {
+ Ok(dap::Capabilities {
supports_modules_request: Some(true),
..Default::default()
- }),
- false,
- cx,
- )
- });
+ })
+ });
+ })
+ .await
+ .unwrap();
- let session = task.await.unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<StackTrace, _>(move |_, args| {
- assert!(args.thread_id == 1);
- Ok(dap::StackTraceResponse {
- stack_frames: Vec::default(),
- total_frames: None,
- })
- })
- .await;
-
let called_modules = Arc::new(AtomicBool::new(false));
let modules = vec![
dap::Module {
@@ -82,38 +72,25 @@ async fn test_module_list(executor: BackgroundExecutor, cx: &mut TestAppContext)
},
];
- client
- .on_request::<Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
+ client.on_request::<Modules, _>({
+ let called_modules = called_modules.clone();
+ let modules_request_count = AtomicI32::new(0);
+ let modules = modules.clone();
+ move |_, _| {
+ modules_request_count.fetch_add(1, Ordering::SeqCst);
+ assert_eq!(
+ 1,
+ modules_request_count.load(Ordering::SeqCst),
+ "This request should only be called once from the host"
+ );
+ called_modules.store(true, Ordering::SeqCst);
+
+ Ok(dap::ModulesResponse {
+ modules: modules.clone(),
+ total_modules: Some(2u64),
})
- })
- .await;
-
- client
- .on_request::<Modules, _>({
- let called_modules = called_modules.clone();
- let modules_request_count = AtomicI32::new(0);
- let modules = modules.clone();
- move |_, _| {
- modules_request_count.fetch_add(1, Ordering::SeqCst);
- assert_eq!(
- 1,
- modules_request_count.load(Ordering::SeqCst),
- "This request should only be called once from the host"
- );
- called_modules.store(true, Ordering::SeqCst);
-
- Ok(dap::ModulesResponse {
- modules: modules.clone(),
- total_modules: Some(2u64),
- })
- }
- })
- .await;
+ }
+ });
client
.fake_event(dap::messages::Events::Stopped(StoppedEvent {
@@ -5,14 +5,13 @@ use crate::{
};
use dap::{
StackFrame,
- requests::{StackTrace, Threads},
+ requests::{Scopes, StackTrace, Threads},
};
use editor::{Editor, ToPoint as _};
use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
-use project::{FakeFs, Project};
+use project::{FakeFs, Project, debugger};
use serde_json::json;
use std::sync::Arc;
-use task::LaunchConfig;
use unindent::Unindent as _;
use util::path;
@@ -51,29 +50,20 @@ async fn test_fetch_initial_stack_frames_and_go_to_stack_frame(
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
-
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
-
- client
- .on_request::<Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<Scopes, _>(move |_, _| Ok(dap::ScopesResponse { scopes: vec![] }));
+
+ client.on_request::<Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
let stack_frames = vec![
StackFrame {
@@ -122,19 +112,17 @@ async fn test_fetch_initial_stack_frames_and_go_to_stack_frame(
},
];
- client
- .on_request::<StackTrace, _>({
- let stack_frames = Arc::new(stack_frames.clone());
- move |_, args| {
- assert_eq!(1, args.thread_id);
-
- Ok(dap::StackTraceResponse {
- stack_frames: (*stack_frames).clone(),
- total_frames: None,
- })
- }
- })
- .await;
+ client.on_request::<StackTrace, _>({
+ let stack_frames = Arc::new(stack_frames.clone());
+ move |_, args| {
+ assert_eq!(1, args.thread_id);
+
+ Ok(dap::StackTraceResponse {
+ stack_frames: (*stack_frames).clone(),
+ total_frames: None,
+ })
+ }
+ });
client
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -241,29 +229,21 @@ async fn test_select_stack_frame(executor: BackgroundExecutor, cx: &mut TestAppC
});
let cx = &mut VisualTestContext::from_window(*workspace, cx);
-
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
+
+ client.on_request::<Scopes, _>(move |_, _| Ok(dap::ScopesResponse { scopes: vec![] }));
let stack_frames = vec![
StackFrame {
@@ -312,19 +292,17 @@ async fn test_select_stack_frame(executor: BackgroundExecutor, cx: &mut TestAppC
},
];
- client
- .on_request::<StackTrace, _>({
- let stack_frames = Arc::new(stack_frames.clone());
- move |_, args| {
- assert_eq!(1, args.thread_id);
-
- Ok(dap::StackTraceResponse {
- stack_frames: (*stack_frames).clone(),
- total_frames: None,
- })
- }
- })
- .await;
+ client.on_request::<StackTrace, _>({
+ let stack_frames = Arc::new(stack_frames.clone());
+ move |_, args| {
+ assert_eq!(1, args.thread_id);
+
+ Ok(dap::StackTraceResponse {
+ stack_frames: (*stack_frames).clone(),
+ total_frames: None,
+ })
+ }
+ });
client
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -517,28 +495,21 @@ async fn test_collapsed_entries(executor: BackgroundExecutor, cx: &mut TestAppCo
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
+
+ client.on_request::<Scopes, _>(move |_, _| Ok(dap::ScopesResponse { scopes: vec![] }));
let stack_frames = vec![
StackFrame {
@@ -697,19 +668,17 @@ async fn test_collapsed_entries(executor: BackgroundExecutor, cx: &mut TestAppCo
},
];
- client
- .on_request::<StackTrace, _>({
- let stack_frames = Arc::new(stack_frames.clone());
- move |_, args| {
- assert_eq!(1, args.thread_id);
-
- Ok(dap::StackTraceResponse {
- stack_frames: (*stack_frames).clone(),
- total_frames: None,
- })
- }
- })
- .await;
+ client.on_request::<StackTrace, _>({
+ let stack_frames = Arc::new(stack_frames.clone());
+ move |_, args| {
+ assert_eq!(1, args.thread_id);
+
+ Ok(dap::StackTraceResponse {
+ stack_frames: (*stack_frames).clone(),
+ total_frames: None,
+ })
+ }
+ });
client
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -15,9 +15,8 @@ use dap::{
};
use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
use menu::{SelectFirst, SelectNext, SelectPrevious};
-use project::{FakeFs, Project};
+use project::{FakeFs, Project, debugger};
use serde_json::json;
-use task::LaunchConfig;
use unindent::Unindent as _;
use util::path;
@@ -55,29 +54,19 @@ async fn test_basic_fetch_initial_scope_and_variables(
})
.unwrap();
let cx = &mut VisualTestContext::from_window(*workspace, cx);
-
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<dap::requests::Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<dap::requests::Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
let stack_frames = vec![StackFrame {
id: 1,
@@ -102,19 +91,17 @@ async fn test_basic_fetch_initial_scope_and_variables(
presentation_hint: None,
}];
- client
- .on_request::<StackTrace, _>({
- let stack_frames = Arc::new(stack_frames.clone());
- move |_, args| {
- assert_eq!(1, args.thread_id);
-
- Ok(dap::StackTraceResponse {
- stack_frames: (*stack_frames).clone(),
- total_frames: None,
- })
- }
- })
- .await;
+ client.on_request::<StackTrace, _>({
+ let stack_frames = Arc::new(stack_frames.clone());
+ move |_, args| {
+ assert_eq!(1, args.thread_id);
+
+ Ok(dap::StackTraceResponse {
+ stack_frames: (*stack_frames).clone(),
+ total_frames: None,
+ })
+ }
+ });
let scopes = vec![Scope {
name: "Scope 1".into(),
@@ -130,18 +117,16 @@ async fn test_basic_fetch_initial_scope_and_variables(
end_column: None,
}];
- client
- .on_request::<Scopes, _>({
- let scopes = Arc::new(scopes.clone());
- move |_, args| {
- assert_eq!(1, args.frame_id);
+ client.on_request::<Scopes, _>({
+ let scopes = Arc::new(scopes.clone());
+ move |_, args| {
+ assert_eq!(1, args.frame_id);
- Ok(dap::ScopesResponse {
- scopes: (*scopes).clone(),
- })
- }
- })
- .await;
+ Ok(dap::ScopesResponse {
+ scopes: (*scopes).clone(),
+ })
+ }
+ });
let variables = vec![
Variable {
@@ -172,18 +157,16 @@ async fn test_basic_fetch_initial_scope_and_variables(
},
];
- client
- .on_request::<Variables, _>({
- let variables = Arc::new(variables.clone());
- move |_, args| {
- assert_eq!(2, args.variables_reference);
+ client.on_request::<Variables, _>({
+ let variables = Arc::new(variables.clone());
+ move |_, args| {
+ assert_eq!(2, args.variables_reference);
- Ok(dap::VariablesResponse {
- variables: (*variables).clone(),
- })
- }
- })
- .await;
+ Ok(dap::VariablesResponse {
+ variables: (*variables).clone(),
+ })
+ }
+ });
client
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -283,39 +266,28 @@ async fn test_fetch_variables_for_multiple_scopes(
.unwrap();
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<dap::requests::Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<dap::requests::Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
- client
- .on_request::<Initialize, _>(move |_, _| {
- Ok(dap::Capabilities {
- supports_step_back: Some(false),
- ..Default::default()
- })
+ client.on_request::<Initialize, _>(move |_, _| {
+ Ok(dap::Capabilities {
+ supports_step_back: Some(false),
+ ..Default::default()
})
- .await;
+ });
- client.on_request::<Launch, _>(move |_, _| Ok(())).await;
+ client.on_request::<Launch, _>(move |_, _| Ok(()));
let stack_frames = vec![StackFrame {
id: 1,
@@ -340,19 +312,17 @@ async fn test_fetch_variables_for_multiple_scopes(
presentation_hint: None,
}];
- client
- .on_request::<StackTrace, _>({
- let stack_frames = Arc::new(stack_frames.clone());
- move |_, args| {
- assert_eq!(1, args.thread_id);
-
- Ok(dap::StackTraceResponse {
- stack_frames: (*stack_frames).clone(),
- total_frames: None,
- })
- }
- })
- .await;
+ client.on_request::<StackTrace, _>({
+ let stack_frames = Arc::new(stack_frames.clone());
+ move |_, args| {
+ assert_eq!(1, args.thread_id);
+
+ Ok(dap::StackTraceResponse {
+ stack_frames: (*stack_frames).clone(),
+ total_frames: None,
+ })
+ }
+ });
let scopes = vec![
Scope {
@@ -383,18 +353,16 @@ async fn test_fetch_variables_for_multiple_scopes(
},
];
- client
- .on_request::<Scopes, _>({
- let scopes = Arc::new(scopes.clone());
- move |_, args| {
- assert_eq!(1, args.frame_id);
+ client.on_request::<Scopes, _>({
+ let scopes = Arc::new(scopes.clone());
+ move |_, args| {
+ assert_eq!(1, args.frame_id);
- Ok(dap::ScopesResponse {
- scopes: (*scopes).clone(),
- })
- }
- })
- .await;
+ Ok(dap::ScopesResponse {
+ scopes: (*scopes).clone(),
+ })
+ }
+ });
let mut variables = HashMap::default();
variables.insert(
@@ -445,16 +413,14 @@ async fn test_fetch_variables_for_multiple_scopes(
}],
);
- client
- .on_request::<Variables, _>({
- let variables = Arc::new(variables.clone());
- move |_, args| {
- Ok(dap::VariablesResponse {
- variables: variables.get(&args.variables_reference).unwrap().clone(),
- })
- }
- })
- .await;
+ client.on_request::<Variables, _>({
+ let variables = Arc::new(variables.clone());
+ move |_, args| {
+ Ok(dap::VariablesResponse {
+ variables: variables.get(&args.variables_reference).unwrap().clone(),
+ })
+ }
+ });
client
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -562,40 +528,28 @@ async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestApp
})
.unwrap();
let cx = &mut VisualTestContext::from_window(*workspace, cx);
-
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<dap::requests::Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<dap::requests::Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
- client
- .on_request::<Initialize, _>(move |_, _| {
- Ok(dap::Capabilities {
- supports_step_back: Some(false),
- ..Default::default()
- })
+ client.on_request::<Initialize, _>(move |_, _| {
+ Ok(dap::Capabilities {
+ supports_step_back: Some(false),
+ ..Default::default()
})
- .await;
+ });
- client.on_request::<Launch, _>(move |_, _| Ok(())).await;
+ client.on_request::<Launch, _>(move |_, _| Ok(()));
let stack_frames = vec![StackFrame {
id: 1,
@@ -620,19 +574,17 @@ async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestApp
presentation_hint: None,
}];
- client
- .on_request::<StackTrace, _>({
- let stack_frames = Arc::new(stack_frames.clone());
- move |_, args| {
- assert_eq!(1, args.thread_id);
-
- Ok(dap::StackTraceResponse {
- stack_frames: (*stack_frames).clone(),
- total_frames: None,
- })
- }
- })
- .await;
+ client.on_request::<StackTrace, _>({
+ let stack_frames = Arc::new(stack_frames.clone());
+ move |_, args| {
+ assert_eq!(1, args.thread_id);
+
+ Ok(dap::StackTraceResponse {
+ stack_frames: (*stack_frames).clone(),
+ total_frames: None,
+ })
+ }
+ });
let scopes = vec![
Scope {
@@ -663,18 +615,16 @@ async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestApp
},
];
- client
- .on_request::<Scopes, _>({
- let scopes = Arc::new(scopes.clone());
- move |_, args| {
- assert_eq!(1, args.frame_id);
+ client.on_request::<Scopes, _>({
+ let scopes = Arc::new(scopes.clone());
+ move |_, args| {
+ assert_eq!(1, args.frame_id);
- Ok(dap::ScopesResponse {
- scopes: (*scopes).clone(),
- })
- }
- })
- .await;
+ Ok(dap::ScopesResponse {
+ scopes: (*scopes).clone(),
+ })
+ }
+ });
let scope1_variables = vec![
Variable {
@@ -748,25 +698,23 @@ async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestApp
value_location_reference: None,
}];
- client
- .on_request::<Variables, _>({
- let scope1_variables = Arc::new(scope1_variables.clone());
- let nested_variables = Arc::new(nested_variables.clone());
- let scope2_variables = Arc::new(scope2_variables.clone());
- move |_, args| match args.variables_reference {
- 4 => Ok(dap::VariablesResponse {
- variables: (*scope2_variables).clone(),
- }),
- 3 => Ok(dap::VariablesResponse {
- variables: (*nested_variables).clone(),
- }),
- 2 => Ok(dap::VariablesResponse {
- variables: (*scope1_variables).clone(),
- }),
- id => unreachable!("unexpected variables reference {id}"),
- }
- })
- .await;
+ client.on_request::<Variables, _>({
+ let scope1_variables = Arc::new(scope1_variables.clone());
+ let nested_variables = Arc::new(nested_variables.clone());
+ let scope2_variables = Arc::new(scope2_variables.clone());
+ move |_, args| match args.variables_reference {
+ 4 => Ok(dap::VariablesResponse {
+ variables: (*scope2_variables).clone(),
+ }),
+ 3 => Ok(dap::VariablesResponse {
+ variables: (*nested_variables).clone(),
+ }),
+ 2 => Ok(dap::VariablesResponse {
+ variables: (*scope1_variables).clone(),
+ }),
+ id => unreachable!("unexpected variables reference {id}"),
+ }
+ });
client
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
@@ -1365,39 +1313,28 @@ async fn test_variable_list_only_sends_requests_when_rendering(
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<dap::requests::Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<dap::requests::Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
- client
- .on_request::<Initialize, _>(move |_, _| {
- Ok(dap::Capabilities {
- supports_step_back: Some(false),
- ..Default::default()
- })
+ client.on_request::<Initialize, _>(move |_, _| {
+ Ok(dap::Capabilities {
+ supports_step_back: Some(false),
+ ..Default::default()
})
- .await;
+ });
- client.on_request::<Launch, _>(move |_, _| Ok(())).await;
+ client.on_request::<Launch, _>(move |_, _| Ok(()));
let stack_frames = vec![
StackFrame {
@@ -1446,19 +1383,17 @@ async fn test_variable_list_only_sends_requests_when_rendering(
},
];
- client
- .on_request::<StackTrace, _>({
- let stack_frames = Arc::new(stack_frames.clone());
- move |_, args| {
- assert_eq!(1, args.thread_id);
-
- Ok(dap::StackTraceResponse {
- stack_frames: (*stack_frames).clone(),
- total_frames: None,
- })
- }
- })
- .await;
+ client.on_request::<StackTrace, _>({
+ let stack_frames = Arc::new(stack_frames.clone());
+ move |_, args| {
+ assert_eq!(1, args.thread_id);
+
+ Ok(dap::StackTraceResponse {
+ stack_frames: (*stack_frames).clone(),
+ total_frames: None,
+ })
+ }
+ });
let frame_1_scopes = vec![Scope {
name: "Frame 1 Scope 1".into(),
@@ -1476,25 +1411,23 @@ async fn test_variable_list_only_sends_requests_when_rendering(
let made_scopes_request = Arc::new(AtomicBool::new(false));
- client
- .on_request::<Scopes, _>({
- let frame_1_scopes = Arc::new(frame_1_scopes.clone());
- let made_scopes_request = made_scopes_request.clone();
- move |_, args| {
- assert_eq!(1, args.frame_id);
- assert!(
- !made_scopes_request.load(Ordering::SeqCst),
- "We should be caching the scope request"
- );
+ client.on_request::<Scopes, _>({
+ let frame_1_scopes = Arc::new(frame_1_scopes.clone());
+ let made_scopes_request = made_scopes_request.clone();
+ move |_, args| {
+ assert_eq!(1, args.frame_id);
+ assert!(
+ !made_scopes_request.load(Ordering::SeqCst),
+ "We should be caching the scope request"
+ );
- made_scopes_request.store(true, Ordering::SeqCst);
+ made_scopes_request.store(true, Ordering::SeqCst);
- Ok(dap::ScopesResponse {
- scopes: (*frame_1_scopes).clone(),
- })
- }
- })
- .await;
+ Ok(dap::ScopesResponse {
+ scopes: (*frame_1_scopes).clone(),
+ })
+ }
+ });
let frame_1_variables = vec![
Variable {
@@ -1525,18 +1458,16 @@ async fn test_variable_list_only_sends_requests_when_rendering(
},
];
- client
- .on_request::<Variables, _>({
- let frame_1_variables = Arc::new(frame_1_variables.clone());
- move |_, args| {
- assert_eq!(2, args.variables_reference);
+ client.on_request::<Variables, _>({
+ let frame_1_variables = Arc::new(frame_1_variables.clone());
+ move |_, args| {
+ assert_eq!(2, args.variables_reference);
- Ok(dap::VariablesResponse {
- variables: (*frame_1_variables).clone(),
- })
- }
- })
- .await;
+ Ok(dap::VariablesResponse {
+ variables: (*frame_1_variables).clone(),
+ })
+ }
+ });
cx.run_until_parked();
@@ -1629,39 +1560,28 @@ async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame(
.unwrap();
let cx = &mut VisualTestContext::from_window(*workspace, cx);
- let task = project.update(cx, |project, cx| {
- project.fake_debug_session(
- dap::DebugRequestType::Launch(LaunchConfig::default()),
- None,
- false,
- cx,
- )
- });
-
- let session = task.await.unwrap();
+ let session = debugger::test::start_debug_session(&project, cx, |_| {})
+ .await
+ .unwrap();
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
- client
- .on_request::<dap::requests::Threads, _>(move |_, _| {
- Ok(dap::ThreadsResponse {
- threads: vec![dap::Thread {
- id: 1,
- name: "Thread 1".into(),
- }],
- })
+ client.on_request::<dap::requests::Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse {
+ threads: vec![dap::Thread {
+ id: 1,
+ name: "Thread 1".into(),
+ }],
})
- .await;
+ });
- client
- .on_request::<Initialize, _>(move |_, _| {
- Ok(dap::Capabilities {
- supports_step_back: Some(false),
- ..Default::default()
- })
+ client.on_request::<Initialize, _>(move |_, _| {
+ Ok(dap::Capabilities {
+ supports_step_back: Some(false),
+ ..Default::default()
})
- .await;
+ });
- client.on_request::<Launch, _>(move |_, _| Ok(())).await;
+ client.on_request::<Launch, _>(move |_, _| Ok(()));
let stack_frames = vec![
StackFrame {
@@ -1710,19 +1630,17 @@ async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame(
},
];
- client
- .on_request::<StackTrace, _>({
- let stack_frames = Arc::new(stack_frames.clone());
- move |_, args| {
- assert_eq!(1, args.thread_id);
-
- Ok(dap::StackTraceResponse {
- stack_frames: (*stack_frames).clone(),
- total_frames: None,
- })
- }
- })
- .await;
+ client.on_request::<StackTrace, _>({
+ let stack_frames = Arc::new(stack_frames.clone());
+ move |_, args| {
+ assert_eq!(1, args.thread_id);
+
+ Ok(dap::StackTraceResponse {
+ stack_frames: (*stack_frames).clone(),
+ total_frames: None,
+ })
+ }
+ });
let frame_1_scopes = vec![Scope {
name: "Frame 1 Scope 1".into(),
@@ -1757,30 +1675,28 @@ async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame(
let called_second_stack_frame = Arc::new(AtomicBool::new(false));
let called_first_stack_frame = Arc::new(AtomicBool::new(false));
- client
- .on_request::<Scopes, _>({
- let frame_1_scopes = Arc::new(frame_1_scopes.clone());
- let frame_2_scopes = Arc::new(frame_2_scopes.clone());
- let called_first_stack_frame = called_first_stack_frame.clone();
- let called_second_stack_frame = called_second_stack_frame.clone();
- move |_, args| match args.frame_id {
- 1 => {
- called_first_stack_frame.store(true, Ordering::SeqCst);
- Ok(dap::ScopesResponse {
- scopes: (*frame_1_scopes).clone(),
- })
- }
- 2 => {
- called_second_stack_frame.store(true, Ordering::SeqCst);
-
- Ok(dap::ScopesResponse {
- scopes: (*frame_2_scopes).clone(),
- })
- }
- _ => panic!("Made a scopes request with an invalid frame id"),
+ client.on_request::<Scopes, _>({
+ let frame_1_scopes = Arc::new(frame_1_scopes.clone());
+ let frame_2_scopes = Arc::new(frame_2_scopes.clone());
+ let called_first_stack_frame = called_first_stack_frame.clone();
+ let called_second_stack_frame = called_second_stack_frame.clone();
+ move |_, args| match args.frame_id {
+ 1 => {
+ called_first_stack_frame.store(true, Ordering::SeqCst);
+ Ok(dap::ScopesResponse {
+ scopes: (*frame_1_scopes).clone(),
+ })
}
- })
- .await;
+ 2 => {
+ called_second_stack_frame.store(true, Ordering::SeqCst);
+
+ Ok(dap::ScopesResponse {
+ scopes: (*frame_2_scopes).clone(),
+ })
+ }
+ _ => panic!("Made a scopes request with an invalid frame id"),
+ }
+ });
let frame_1_variables = vec![
Variable {
@@ -1840,18 +1756,16 @@ async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame(
},
];
- client
- .on_request::<Variables, _>({
- let frame_1_variables = Arc::new(frame_1_variables.clone());
- move |_, args| {
- assert_eq!(2, args.variables_reference);
+ client.on_request::<Variables, _>({
+ let frame_1_variables = Arc::new(frame_1_variables.clone());
+ move |_, args| {
+ assert_eq!(2, args.variables_reference);
- Ok(dap::VariablesResponse {
- variables: (*frame_1_variables).clone(),
- })
- }
- })
- .await;
+ Ok(dap::VariablesResponse {
+ variables: (*frame_1_variables).clone(),
+ })
+ }
+ });
client
.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(
assert_eq!(frame_1_variables, variables);
});
- client
- .on_request::<Variables, _>({
- let frame_2_variables = Arc::new(frame_2_variables.clone());
- move |_, args| {
- assert_eq!(3, args.variables_reference);
+ client.on_request::<Variables, _>({
+ let frame_2_variables = Arc::new(frame_2_variables.clone());
+ move |_, args| {
+ assert_eq!(3, args.variables_reference);
- Ok(dap::VariablesResponse {
- variables: (*frame_2_variables).clone(),
- })
- }
- })
- .await;
+ Ok(dap::VariablesResponse {
+ variables: (*frame_2_variables).clone(),
+ })
+ }
+ });
running_state
.update_in(cx, |running_state, window, cx| {
@@ -16,3 +16,6 @@ pub mod dap_command;
pub mod dap_store;
mod locator_store;
pub mod session;
+
+#[cfg(any(feature = "test-support", test))]
+pub mod test;
@@ -3,15 +3,15 @@ use super::{
locator_store::LocatorStore,
session::{self, Session, SessionStateEvent},
};
-use crate::{ProjectEnvironment, debugger, worktree_store::WorktreeStore};
+use crate::{ProjectEnvironment, debugger};
use anyhow::{Result, anyhow};
use async_trait::async_trait;
use collections::HashMap;
use dap::{
- Capabilities, CompletionItem, CompletionsArguments, DapRegistry, ErrorResponse,
- EvaluateArguments, EvaluateArgumentsContext, EvaluateResponse, RunInTerminalRequestArguments,
- Source, StartDebuggingRequestArguments,
- adapters::{DapStatus, DebugAdapterName},
+ Capabilities, CompletionItem, CompletionsArguments, ErrorResponse, EvaluateArguments,
+ EvaluateArgumentsContext, EvaluateResponse, RunInTerminalRequestArguments, Source,
+ StartDebuggingRequestArguments,
+ adapters::{DapStatus, DebugAdapterBinary, DebugAdapterName},
client::SessionId,
messages::Message,
requests::{Completions, Evaluate, Request as _, RunInTerminal, StartDebugging},
@@ -42,7 +42,7 @@ use std::{
sync::{Arc, atomic::Ordering::SeqCst},
};
use std::{collections::VecDeque, sync::atomic::AtomicU32};
-use task::{DebugAdapterConfig, DebugRequestDisposition};
+use task::DebugTaskDefinition;
use util::ResultExt as _;
use worktree::Worktree;
@@ -78,10 +78,8 @@ pub struct LocalDapStore {
node_runtime: NodeRuntime,
next_session_id: AtomicU32,
http_client: Arc<dyn HttpClient>,
- worktree_store: Entity<WorktreeStore>,
environment: Entity<ProjectEnvironment>,
language_registry: Arc<LanguageRegistry>,
- debug_adapters: Arc<DapRegistry>,
toolchain_store: Arc<dyn LanguageToolchainStore>,
locator_store: Arc<LocatorStore>,
start_debugging_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
@@ -132,11 +130,9 @@ impl DapStore {
node_runtime: NodeRuntime,
fs: Arc<dyn Fs>,
language_registry: Arc<LanguageRegistry>,
- debug_adapters: Arc<DapRegistry>,
environment: Entity<ProjectEnvironment>,
toolchain_store: Arc<dyn LanguageToolchainStore>,
breakpoint_store: Entity<BreakpointStore>,
- worktree_store: Entity<WorktreeStore>,
cx: &mut Context<Self>,
) -> Self {
cx.on_app_quit(Self::shutdown_sessions).detach();
@@ -170,10 +166,8 @@ impl DapStore {
environment,
http_client,
node_runtime,
- worktree_store,
toolchain_store,
language_registry,
- debug_adapters,
start_debugging_tx,
_start_debugging_task,
locator_store: Arc::from(LocatorStore::new()),
@@ -320,18 +314,12 @@ impl DapStore {
Ok(())
}
- pub fn new_session(
- &mut self,
- mut config: DebugAdapterConfig,
- worktree: &Entity<Worktree>,
- parent_session: Option<Entity<Session>>,
- cx: &mut Context<Self>,
- ) -> (SessionId, Task<Result<Entity<Session>>>) {
+ pub fn delegate(&self, worktree: &Entity<Worktree>, cx: &mut App) -> DapAdapterDelegate {
let Some(local_store) = self.as_local() else {
unimplemented!("Starting session on remote side");
};
- let delegate = DapAdapterDelegate::new(
+ DapAdapterDelegate::new(
local_store.fs.clone(),
worktree.read(cx).id(),
local_store.node_runtime.clone(),
@@ -341,7 +329,20 @@ impl DapStore {
local_store.environment.update(cx, |env, cx| {
env.get_worktree_environment(worktree.clone(), cx)
}),
- );
+ )
+ }
+
+ pub fn new_session(
+ &mut self,
+ binary: DebugAdapterBinary,
+ mut config: DebugTaskDefinition,
+ parent_session: Option<Entity<Session>>,
+ cx: &mut Context<Self>,
+ ) -> (SessionId, Task<Result<Entity<Session>>>) {
+ let Some(local_store) = self.as_local() else {
+ unimplemented!("Starting session on remote side");
+ };
+
let session_id = local_store.next_session_id();
if let Some(session) = &parent_session {
@@ -352,7 +353,6 @@ impl DapStore {
let (initialized_tx, initialized_rx) = oneshot::channel();
let locator_store = local_store.locator_store.clone();
- let debug_adapters = local_store.debug_adapters.clone();
let start_debugging_tx = local_store.start_debugging_tx.clone();
@@ -373,86 +373,31 @@ impl DapStore {
this.breakpoint_store.clone(),
session_id,
parent_session,
- delegate,
+ binary,
config,
start_debugging_tx.clone(),
initialized_tx,
- debug_adapters,
cx,
)
})?;
- this.update(cx, |_, cx| {
- create_new_session(session_id, initialized_rx, start_client_task, cx)
- })?
- .await
+ let ret = this
+ .update(cx, |_, cx| {
+ create_new_session(session_id, initialized_rx, start_client_task, cx)
+ })?
+ .await;
+ ret
});
(session_id, task)
}
- #[cfg(any(test, feature = "test-support"))]
- pub fn new_fake_session(
- &mut self,
- config: DebugAdapterConfig,
- worktree: &Entity<Worktree>,
- parent_session: Option<Entity<Session>>,
- caps: Capabilities,
- fails: bool,
- cx: &mut Context<Self>,
- ) -> (SessionId, Task<Result<Entity<Session>>>) {
- let Some(local_store) = self.as_local() else {
- unimplemented!("Starting session on remote side");
- };
-
- let delegate = DapAdapterDelegate::new(
- local_store.fs.clone(),
- worktree.read(cx).id(),
- local_store.node_runtime.clone(),
- local_store.http_client.clone(),
- local_store.language_registry.clone(),
- local_store.toolchain_store.clone(),
- local_store.environment.update(cx, |env, cx| {
- env.get_worktree_environment(worktree.clone(), cx)
- }),
- );
- let session_id = local_store.next_session_id();
-
- if let Some(session) = &parent_session {
- session.update(cx, |session, _| {
- session.add_child_session_id(session_id);
- });
- }
-
- let (initialized_tx, initialized_rx) = oneshot::channel();
-
- let start_client_task = Session::fake(
- self.breakpoint_store.clone(),
- session_id,
- parent_session,
- delegate,
- config,
- local_store.start_debugging_tx.clone(),
- initialized_tx,
- caps,
- fails,
- cx,
- );
-
- let task = create_new_session(session_id, initialized_rx, start_client_task, cx);
- (session_id, task)
- }
-
fn handle_start_debugging_request(
&mut self,
session_id: SessionId,
request: dap::messages::Request,
cx: &mut Context<Self>,
) -> Task<Result<()>> {
- let Some(local_store) = self.as_local() else {
- unreachable!("Cannot response for non-local session");
- };
-
let Some(parent_session) = self.session_by_id(session_id) else {
return Task::ready(Err(anyhow!("Session not found")));
};
@@ -461,41 +406,12 @@ impl DapStore {
request.arguments.unwrap_or_default(),
)
.expect("To parse StartDebuggingRequestArguments");
- let worktree = local_store
- .worktree_store
- .update(cx, |this, _| this.worktrees().next())
- .expect("worktree-less project");
+ let mut binary = parent_session.read(cx).binary().clone();
+ let config = parent_session.read(cx).configuration().unwrap().clone();
+ binary.request_args = args;
- let Some(config) = parent_session.read(cx).configuration() else {
- unreachable!("there must be a config for local sessions");
- };
-
- let debug_config = DebugAdapterConfig {
- label: config.label,
- adapter: config.adapter,
- request: DebugRequestDisposition::ReverseRequest(args),
- initialize_args: config.initialize_args.clone(),
- tcp_connection: config.tcp_connection.clone(),
- locator: None,
- stop_on_entry: config.stop_on_entry,
- };
-
- #[cfg(any(test, feature = "test-support"))]
- let new_session_task = {
- let caps = parent_session.read(cx).capabilities.clone();
- self.new_fake_session(
- debug_config,
- &worktree,
- Some(parent_session.clone()),
- caps,
- false,
- cx,
- )
- .1
- };
- #[cfg(not(any(test, feature = "test-support")))]
let new_session_task = self
- .new_session(debug_config, &worktree, Some(parent_session.clone()), cx)
+ .new_session(binary, config, Some(parent_session.clone()), cx)
.1;
let request_seq = request.seq;
@@ -607,13 +523,7 @@ impl DapStore {
.map(Arc::from)
.or_else(|| {
self.session_by_id(session_id)
- .and_then(|session| {
- session
- .read(cx)
- .configuration()
- .and_then(|config| config.request.cwd())
- })
- .map(Arc::from)
+ .and_then(|session| session.read(cx).binary().cwd.as_deref().map(Arc::from))
});
cx.emit(DapStoreEvent::RunInTerminal {
session_id,
@@ -852,7 +762,7 @@ fn create_new_session(
cx.emit(DapStoreEvent::DebugClientStarted(session_id));
cx.notify();
})?;
- let seq_result = {
+ let seq_result = async || {
session
.update(cx, |session, cx| session.request_initialize(cx))?
.await?;
@@ -863,12 +773,11 @@ fn create_new_session(
})?
.await
};
- match seq_result {
+ match seq_result().await {
Ok(_) => {}
Err(error) => {
this.update(cx, |this, cx| {
cx.emit(DapStoreEvent::Notification(error.to_string()));
-
this.shutdown_session(session_id, cx)
})?
.await
@@ -1,9 +1,9 @@
use anyhow::{Result, anyhow};
use cargo::CargoLocator;
use collections::HashMap;
-use dap::DebugAdapterConfig;
use gpui::SharedString;
use locators::DapLocator;
+use task::DebugTaskDefinition;
mod cargo;
mod locators;
@@ -23,7 +23,7 @@ impl LocatorStore {
pub(super) async fn resolve_debug_config(
&self,
- debug_config: &mut DebugAdapterConfig,
+ debug_config: &mut DebugTaskDefinition,
) -> Result<()> {
let Some(locator_name) = &debug_config.locator else {
log::debug!("Attempted to resolve debug config without a locator field");
@@ -1,12 +1,12 @@
use super::DapLocator;
use anyhow::{Result, anyhow};
use async_trait::async_trait;
-use dap::DebugAdapterConfig;
use serde_json::{Value, json};
use smol::{
io::AsyncReadExt,
process::{Command, Stdio},
};
+use task::DebugTaskDefinition;
use util::maybe;
pub(super) struct CargoLocator;
@@ -38,11 +38,9 @@ async fn find_best_executable(executables: &[String], test_name: &str) -> Option
}
#[async_trait]
impl DapLocator for CargoLocator {
- async fn run_locator(&self, debug_config: &mut DebugAdapterConfig) -> Result<()> {
+ async fn run_locator(&self, debug_config: &mut DebugTaskDefinition) -> Result<()> {
let Some(launch_config) = (match &mut debug_config.request {
- task::DebugRequestDisposition::UserConfigured(task::DebugRequestType::Launch(
- launch_config,
- )) => Some(launch_config),
+ task::DebugRequestType::Launch(launch_config) => Some(launch_config),
_ => None,
}) else {
return Err(anyhow!("Couldn't get launch config in locator"));
@@ -1,8 +1,8 @@
use anyhow::Result;
use async_trait::async_trait;
-use dap::DebugAdapterConfig;
+use task::DebugTaskDefinition;
#[async_trait]
pub(super) trait DapLocator: Send + Sync {
- async fn run_locator(&self, debug_config: &mut DebugAdapterConfig) -> Result<()>;
+ async fn run_locator(&self, debug_config: &mut DebugTaskDefinition) -> Result<()>;
}
@@ -1,5 +1,3 @@
-use crate::project_settings::ProjectSettings;
-
use super::breakpoint_store::{
BreakpointStore, BreakpointStoreEvent, BreakpointUpdatedReason, SourceBreakpoint,
};
@@ -11,22 +9,17 @@ use super::dap_command::{
StepBackCommand, StepCommand, StepInCommand, StepOutCommand, TerminateCommand,
TerminateThreadsCommand, ThreadsCommand, VariablesCommand,
};
-use super::dap_store::DapAdapterDelegate;
use anyhow::{Context as _, Result, anyhow};
use collections::{HashMap, HashSet, IndexMap, IndexSet};
-use dap::adapters::{DebugAdapter, DebugAdapterBinary};
+use dap::adapters::DebugAdapterBinary;
use dap::messages::Response;
use dap::{
Capabilities, ContinueArguments, EvaluateArgumentsContext, Module, Source, StackFrameId,
SteppingGranularity, StoppedEvent, VariableReference,
- adapters::{DapDelegate, DapStatus},
client::{DebugAdapterClient, SessionId},
messages::{Events, Message},
};
-use dap::{
- DapRegistry, DebugRequestType, ExceptionBreakpointsFilter, ExceptionFilterOptions,
- OutputEventCategory,
-};
+use dap::{ExceptionBreakpointsFilter, ExceptionFilterOptions, OutputEventCategory};
use futures::channel::oneshot;
use futures::{FutureExt, future::Shared};
use gpui::{
@@ -35,11 +28,9 @@ use gpui::{
};
use rpc::AnyProtoClient;
use serde_json::{Value, json};
-use settings::Settings;
use smol::stream::StreamExt;
use std::any::TypeId;
use std::collections::BTreeMap;
-use std::path::PathBuf;
use std::u64;
use std::{
any::Any,
@@ -48,7 +39,7 @@ use std::{
path::Path,
sync::Arc,
};
-use task::{DebugAdapterConfig, DebugTaskDefinition};
+use task::DebugTaskDefinition;
use text::{PointUtf16, ToPointUtf16};
use util::{ResultExt, merge_json_value_into};
@@ -168,9 +159,9 @@ enum Mode {
#[derive(Clone)]
pub struct LocalMode {
client: Arc<DebugAdapterClient>,
- config: DebugAdapterConfig,
- adapter: Arc<dyn DebugAdapter>,
- breakpoint_store: Entity<BreakpointStore>,
+ definition: DebugTaskDefinition,
+ binary: DebugAdapterBinary,
+ pub(crate) breakpoint_store: Entity<BreakpointStore>,
tmp_breakpoint: Option<SourceBreakpoint>,
}
@@ -191,185 +182,37 @@ fn client_source(abs_path: &Path) -> dap::Source {
impl LocalMode {
fn new(
- debug_adapters: Arc<DapRegistry>,
session_id: SessionId,
parent_session: Option<Entity<Session>>,
breakpoint_store: Entity<BreakpointStore>,
- config: DebugAdapterConfig,
- delegate: DapAdapterDelegate,
+ config: DebugTaskDefinition,
+ binary: DebugAdapterBinary,
messages_tx: futures::channel::mpsc::UnboundedSender<Message>,
cx: AsyncApp,
) -> Task<Result<Self>> {
Self::new_inner(
- debug_adapters,
session_id,
parent_session,
breakpoint_store,
config,
- delegate,
+ binary,
messages_tx,
async |_, _| {},
cx,
)
}
- #[cfg(any(test, feature = "test-support"))]
- fn new_fake(
- session_id: SessionId,
- parent_session: Option<Entity<Session>>,
- breakpoint_store: Entity<BreakpointStore>,
- config: DebugAdapterConfig,
- delegate: DapAdapterDelegate,
- messages_tx: futures::channel::mpsc::UnboundedSender<Message>,
- caps: Capabilities,
- fail: bool,
- cx: AsyncApp,
- ) -> Task<Result<Self>> {
- use task::DebugRequestDisposition;
-
- let request = match config.request.clone() {
- DebugRequestDisposition::UserConfigured(request) => request,
- DebugRequestDisposition::ReverseRequest(reverse_request_args) => {
- match reverse_request_args.request {
- dap::StartDebuggingRequestArgumentsRequest::Launch => {
- DebugRequestType::Launch(task::LaunchConfig {
- program: "".to_owned(),
- cwd: None,
- args: Default::default(),
- })
- }
- dap::StartDebuggingRequestArgumentsRequest::Attach => {
- DebugRequestType::Attach(task::AttachConfig {
- process_id: Some(0),
- })
- }
- }
- }
- };
-
- let callback = async move |session: &mut LocalMode, cx: AsyncApp| {
- session
- .client
- .on_request::<dap::requests::Initialize, _>(move |_, _| Ok(caps.clone()))
- .await;
-
- let paths = cx
- .update(|cx| session.breakpoint_store.read(cx).breakpoint_paths())
- .expect("Breakpoint store should exist in all tests that start debuggers");
-
- session
- .client
- .on_request::<dap::requests::SetBreakpoints, _>(move |_, args| {
- let p = Arc::from(Path::new(&args.source.path.unwrap()));
- if !paths.contains(&p) {
- panic!("Sent breakpoints for path without any")
- }
- Ok(dap::SetBreakpointsResponse {
- breakpoints: Vec::default(),
- })
- })
- .await;
-
- match request {
- dap::DebugRequestType::Launch(_) => {
- if fail {
- session
- .client
- .on_request::<dap::requests::Launch, _>(move |_, _| {
- Err(dap::ErrorResponse {
- error: Some(dap::Message {
- id: 1,
- format: "error".into(),
- variables: None,
- send_telemetry: None,
- show_user: None,
- url: None,
- url_label: None,
- }),
- })
- })
- .await;
- } else {
- session
- .client
- .on_request::<dap::requests::Launch, _>(move |_, _| Ok(()))
- .await;
- }
- }
- dap::DebugRequestType::Attach(attach_config) => {
- if fail {
- session
- .client
- .on_request::<dap::requests::Attach, _>(move |_, _| {
- Err(dap::ErrorResponse {
- error: Some(dap::Message {
- id: 1,
- format: "error".into(),
- variables: None,
- send_telemetry: None,
- show_user: None,
- url: None,
- url_label: None,
- }),
- })
- })
- .await;
- } else {
- session
- .client
- .on_request::<dap::requests::Attach, _>(move |_, args| {
- assert_eq!(
- json!({"request": "attach", "process_id": attach_config.process_id.unwrap()}),
- args.raw
- );
-
- Ok(())
- })
- .await;
- }
- }
- }
-
- session
- .client
- .on_request::<dap::requests::SetExceptionBreakpoints, _>(move |_, _| {
- Ok(dap::SetExceptionBreakpointsResponse { breakpoints: None })
- })
- .await;
-
- session
- .client
- .on_request::<dap::requests::Disconnect, _>(move |_, _| Ok(()))
- .await;
- session.client.fake_event(Events::Initialized(None)).await;
- };
- Self::new_inner(
- DapRegistry::fake().into(),
- session_id,
- parent_session,
- breakpoint_store,
- config,
- delegate,
- messages_tx,
- callback,
- cx,
- )
- }
fn new_inner(
- registry: Arc<DapRegistry>,
session_id: SessionId,
parent_session: Option<Entity<Session>>,
breakpoint_store: Entity<BreakpointStore>,
- config: DebugAdapterConfig,
- delegate: DapAdapterDelegate,
+ config: DebugTaskDefinition,
+ binary: DebugAdapterBinary,
messages_tx: futures::channel::mpsc::UnboundedSender<Message>,
on_initialized: impl AsyncFnOnce(&mut LocalMode, AsyncApp) + 'static,
cx: AsyncApp,
) -> Task<Result<Self>> {
cx.spawn(async move |cx| {
- let (adapter, binary) =
- Self::get_adapter_binary(®istry, &config, &delegate, cx).await?;
-
let message_handler = Box::new(move |message| {
messages_tx.unbounded_send(message).ok();
});
@@ -380,13 +223,12 @@ impl LocalMode {
.flatten()
{
client
- .reconnect(session_id, binary, message_handler, cx.clone())
+ .reconnect(session_id, binary.clone(), message_handler, cx.clone())
.await?
} else {
DebugAdapterClient::start(
session_id,
- adapter.name(),
- binary,
+ binary.clone(),
message_handler,
cx.clone(),
)
@@ -397,10 +239,10 @@ impl LocalMode {
let mut session = Self {
client,
- adapter,
breakpoint_store,
tmp_breakpoint: None,
- config: config.clone(),
+ definition: config,
+ binary,
};
on_initialized(&mut session, cx.clone()).await;
@@ -533,55 +375,12 @@ impl LocalMode {
})
}
- async fn get_adapter_binary(
- registry: &Arc<DapRegistry>,
- config: &DebugAdapterConfig,
- delegate: &DapAdapterDelegate,
- cx: &mut AsyncApp,
- ) -> Result<(Arc<dyn DebugAdapter>, DebugAdapterBinary)> {
- let adapter = registry
- .adapter(&config.adapter)
- .ok_or_else(|| anyhow!("Debug adapter with name `{}` was not found", config.adapter))?;
-
- let binary = cx.update(|cx| {
- ProjectSettings::get_global(cx)
- .dap
- .get(&adapter.name())
- .and_then(|s| s.binary.as_ref().map(PathBuf::from))
- })?;
-
- let binary = match adapter.get_binary(delegate, &config, binary, cx).await {
- Err(error) => {
- delegate.update_status(
- adapter.name(),
- DapStatus::Failed {
- error: error.to_string(),
- },
- );
-
- return Err(error);
- }
- Ok(mut binary) => {
- delegate.update_status(adapter.name(), DapStatus::None);
-
- let shell_env = delegate.shell_env().await;
- let mut envs = binary.envs.unwrap_or_default();
- envs.extend(shell_env);
- binary.envs = Some(envs);
-
- binary
- }
- };
-
- Ok((adapter, binary))
- }
-
pub fn label(&self) -> String {
- self.config.label.clone()
+ self.definition.label.clone()
}
fn request_initialization(&self, cx: &App) -> Task<Result<Capabilities>> {
- let adapter_id = self.adapter.name().to_string();
+ let adapter_id = self.binary.adapter_name.to_string();
self.request(Initialize { adapter_id }, cx.background_executor().clone())
}
@@ -592,36 +391,26 @@ impl LocalMode {
initialized_rx: oneshot::Receiver<()>,
cx: &App,
) -> Task<Result<()>> {
- let (mut raw, is_launch) = match &self.config.request {
- task::DebugRequestDisposition::UserConfigured(_) => {
- let Ok(raw) = DebugTaskDefinition::try_from(self.config.clone()) else {
- debug_assert!(false, "This part of code should be unreachable in practice");
- return Task::ready(Err(anyhow!(
- "Expected debug config conversion to succeed"
- )));
- };
- let is_launch = matches!(raw.request, DebugRequestType::Launch(_));
- let raw = self.adapter.request_args(&raw);
- (raw, is_launch)
- }
- task::DebugRequestDisposition::ReverseRequest(start_debugging_request_arguments) => (
- start_debugging_request_arguments.configuration.clone(),
- matches!(
- start_debugging_request_arguments.request,
- dap::StartDebuggingRequestArgumentsRequest::Launch
- ),
- ),
- };
+ let mut raw = self.binary.request_args.clone();
merge_json_value_into(
- self.config.initialize_args.clone().unwrap_or(json!({})),
- &mut raw,
+ self.definition.initialize_args.clone().unwrap_or(json!({})),
+ &mut raw.configuration,
);
// Of relevance: https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522
- let launch = if is_launch {
- self.request(Launch { raw }, cx.background_executor().clone())
- } else {
- self.request(Attach { raw }, cx.background_executor().clone())
+ let launch = match raw.request {
+ dap::StartDebuggingRequestArgumentsRequest::Launch => self.request(
+ Launch {
+ raw: raw.configuration,
+ },
+ cx.background_executor().clone(),
+ ),
+ dap::StartDebuggingRequestArgumentsRequest::Attach => self.request(
+ Attach {
+ raw: raw.configuration,
+ },
+ cx.background_executor().clone(),
+ ),
};
let configuration_done_supported = ConfigurationDone::is_supported(capabilities);
@@ -656,12 +445,13 @@ impl LocalMode {
})?
.await
.ok();
- if configuration_done_supported {
+ let ret = if configuration_done_supported {
this.request(ConfigurationDone {}, cx.background_executor().clone())
} else {
Task::ready(Ok(()))
}
- .await
+ .await;
+ ret
}
});
@@ -909,68 +699,22 @@ impl Session {
breakpoint_store: Entity<BreakpointStore>,
session_id: SessionId,
parent_session: Option<Entity<Session>>,
- delegate: DapAdapterDelegate,
- config: DebugAdapterConfig,
+ binary: DebugAdapterBinary,
+ config: DebugTaskDefinition,
start_debugging_requests_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
initialized_tx: oneshot::Sender<()>,
- debug_adapters: Arc<DapRegistry>,
cx: &mut App,
) -> Task<Result<Entity<Self>>> {
let (message_tx, message_rx) = futures::channel::mpsc::unbounded();
cx.spawn(async move |cx| {
let mode = LocalMode::new(
- debug_adapters,
- session_id,
- parent_session.clone(),
- breakpoint_store.clone(),
- config.clone(),
- delegate,
- message_tx,
- cx.clone(),
- )
- .await?;
-
- cx.new(|cx| {
- create_local_session(
- breakpoint_store,
- session_id,
- parent_session,
- start_debugging_requests_tx,
- initialized_tx,
- message_rx,
- mode,
- cx,
- )
- })
- })
- }
-
- #[cfg(any(test, feature = "test-support"))]
- pub(crate) fn fake(
- breakpoint_store: Entity<BreakpointStore>,
- session_id: SessionId,
- parent_session: Option<Entity<Session>>,
- delegate: DapAdapterDelegate,
- config: DebugAdapterConfig,
- start_debugging_requests_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
- initialized_tx: oneshot::Sender<()>,
- caps: Capabilities,
- fails: bool,
- cx: &mut App,
- ) -> Task<Result<Entity<Session>>> {
- let (message_tx, message_rx) = futures::channel::mpsc::unbounded();
-
- cx.spawn(async move |cx| {
- let mode = LocalMode::new_fake(
session_id,
parent_session.clone(),
breakpoint_store.clone(),
config.clone(),
- delegate,
+ binary,
message_tx,
- caps,
- fails,
cx.clone(),
)
.await?;
@@ -1047,16 +791,23 @@ impl Session {
&self.capabilities
}
+ pub fn binary(&self) -> &DebugAdapterBinary {
+ let Mode::Local(local_mode) = &self.mode else {
+ panic!("Session is not local");
+ };
+ &local_mode.binary
+ }
+
pub fn adapter_name(&self) -> SharedString {
match &self.mode {
- Mode::Local(local_mode) => local_mode.adapter.name().into(),
+ Mode::Local(local_mode) => local_mode.definition.adapter.clone().into(),
Mode::Remote(remote_mode) => remote_mode._adapter_name.clone(),
}
}
- pub fn configuration(&self) -> Option<DebugAdapterConfig> {
+ pub fn configuration(&self) -> Option<DebugTaskDefinition> {
if let Mode::Local(local_mode) = &self.mode {
- Some(local_mode.config.clone())
+ Some(local_mode.definition.clone())
} else {
None
}
@@ -0,0 +1,98 @@
+use std::{path::Path, sync::Arc};
+
+use anyhow::Result;
+use dap::{DebugRequestType, client::DebugAdapterClient};
+use gpui::{App, AppContext, Entity, Subscription, Task};
+use task::DebugTaskDefinition;
+
+use crate::Project;
+
+use super::session::Session;
+
+pub fn intercept_debug_sessions<T: Fn(&Arc<DebugAdapterClient>) + 'static>(
+ cx: &mut gpui::TestAppContext,
+ configure: T,
+) -> Subscription {
+ cx.update(|cx| {
+ cx.observe_new::<Session>(move |session, _, cx| {
+ let client = session.adapter_client().unwrap();
+ register_default_handlers(session, &client, cx);
+ configure(&client);
+ cx.background_spawn(async move {
+ client
+ .fake_event(dap::messages::Events::Initialized(Some(Default::default())))
+ .await
+ })
+ .detach();
+ })
+ })
+}
+
+pub fn start_debug_session_with<T: Fn(&Arc<DebugAdapterClient>) + 'static>(
+ project: &Entity<Project>,
+ cx: &mut gpui::TestAppContext,
+ config: DebugTaskDefinition,
+ configure: T,
+) -> Task<Result<Entity<Session>>> {
+ let subscription = intercept_debug_sessions(cx, configure);
+ let task = project.update(cx, |project, cx| project.start_debug_session(config, cx));
+ cx.spawn(async move |_| {
+ let result = task.await;
+ drop(subscription);
+ result
+ })
+}
+
+pub fn start_debug_session<T: Fn(&Arc<DebugAdapterClient>) + 'static>(
+ project: &Entity<Project>,
+ cx: &mut gpui::TestAppContext,
+ configure: T,
+) -> Task<Result<Entity<Session>>> {
+ start_debug_session_with(
+ project,
+ cx,
+ DebugTaskDefinition {
+ adapter: "fake-adapter".to_string(),
+ request: DebugRequestType::Launch(Default::default()),
+ label: "test".to_string(),
+ initialize_args: None,
+ tcp_connection: None,
+ locator: None,
+ stop_on_entry: None,
+ },
+ configure,
+ )
+}
+
+fn register_default_handlers(session: &Session, client: &Arc<DebugAdapterClient>, cx: &mut App) {
+ client.on_request::<dap::requests::Initialize, _>(move |_, _| Ok(Default::default()));
+ let paths = session
+ .as_local()
+ .unwrap()
+ .breakpoint_store
+ .read(cx)
+ .breakpoint_paths();
+
+ client.on_request::<dap::requests::SetBreakpoints, _>(move |_, args| {
+ let p = Arc::from(Path::new(&args.source.path.unwrap()));
+ if !paths.contains(&p) {
+ panic!("Sent breakpoints for path without any")
+ }
+
+ Ok(dap::SetBreakpointsResponse {
+ breakpoints: Vec::default(),
+ })
+ });
+
+ client.on_request::<dap::requests::Launch, _>(move |_, _| Ok(()));
+
+ client.on_request::<dap::requests::SetExceptionBreakpoints, _>(move |_, _| {
+ Ok(dap::SetExceptionBreakpointsResponse { breakpoints: None })
+ });
+
+ client.on_request::<dap::requests::Disconnect, _>(move |_, _| Ok(()));
+
+ client.on_request::<dap::requests::Threads, _>(move |_, _| {
+ Ok(dap::ThreadsResponse { threads: vec![] })
+ });
+}
@@ -25,6 +25,7 @@ mod environment;
use buffer_diff::BufferDiff;
pub use environment::{EnvironmentErrorMessage, ProjectEnvironmentEvent};
use git_store::{Repository, RepositoryId};
+use task::DebugTaskDefinition;
pub mod search_history;
mod yarn;
@@ -38,7 +39,7 @@ use client::{
};
use clock::ReplicaId;
-use dap::{DapRegistry, DebugAdapterConfig, client::DebugAdapterClient};
+use dap::{DapRegistry, client::DebugAdapterClient};
use collections::{BTreeSet, HashMap, HashSet};
use debounced_delay::DebouncedDelay;
@@ -106,7 +107,7 @@ use terminals::Terminals;
use text::{Anchor, BufferId};
use toolchain_store::EmptyToolchainStore;
use util::{
- ResultExt as _, maybe,
+ ResultExt as _,
paths::{SanitizedPath, compare_paths},
};
use worktree::{CreatedEntry, Snapshot, Traversal};
@@ -870,11 +871,9 @@ impl Project {
node.clone(),
fs.clone(),
languages.clone(),
- debug_adapters.clone(),
environment.clone(),
toolchain_store.read(cx).as_language_toolchain_store(),
breakpoint_store.clone(),
- worktree_store.clone(),
cx,
)
});
@@ -1458,64 +1457,46 @@ impl Project {
}
}
- pub fn queue_debug_session(&mut self, config: DebugAdapterConfig, cx: &mut Context<Self>) {
- if config.locator.is_none() {
- self.start_debug_session(config, cx).detach_and_log_err(cx);
- }
- }
-
pub fn start_debug_session(
&mut self,
- config: DebugAdapterConfig,
+ config: DebugTaskDefinition,
cx: &mut Context<Self>,
) -> Task<Result<Entity<Session>>> {
- let worktree = maybe!({ self.worktrees(cx).next() });
-
- let Some(worktree) = &worktree else {
+ let Some(worktree) = self.worktrees(cx).next() else {
return Task::ready(Err(anyhow!("Failed to find a worktree")));
};
- self.dap_store
- .update(cx, |dap_store, cx| {
- dap_store.new_session(config, worktree, None, cx)
- })
- .1
- }
+ let Some(adapter) = self.debug_adapters.adapter(&config.adapter) else {
+ return Task::ready(Err(anyhow!("Failed to find a debug adapter")));
+ };
- #[cfg(any(test, feature = "test-support"))]
- pub fn fake_debug_session(
- &mut self,
- request: task::DebugRequestType,
- caps: Option<dap::Capabilities>,
- fails: bool,
- cx: &mut Context<Self>,
- ) -> Task<Result<Entity<Session>>> {
- use dap::{Capabilities, FakeAdapter};
- use task::DebugRequestDisposition;
+ let user_installed_path = ProjectSettings::get_global(cx)
+ .dap
+ .get(&adapter.name())
+ .and_then(|s| s.binary.as_ref().map(PathBuf::from));
- let worktree = maybe!({ self.worktrees(cx).next() });
+ let result = cx.spawn(async move |this, cx| {
+ let delegate = this.update(cx, |project, cx| {
+ project
+ .dap_store
+ .update(cx, |dap_store, cx| dap_store.delegate(&worktree, cx))
+ })?;
- let Some(worktree) = &worktree else {
- return Task::ready(Err(anyhow!("Failed to find a worktree")));
- };
- let config = DebugAdapterConfig {
- label: "test config".into(),
- adapter: FakeAdapter::ADAPTER_NAME.into(),
- request: DebugRequestDisposition::UserConfigured(request),
- initialize_args: None,
- tcp_connection: None,
- locator: None,
- stop_on_entry: None,
- };
- let caps = caps.unwrap_or(Capabilities {
- supports_step_back: Some(false),
- ..Default::default()
+ let binary = adapter
+ .get_binary(&delegate, &config, user_installed_path, cx)
+ .await?;
+
+ let ret = this
+ .update(cx, |project, cx| {
+ project.dap_store.update(cx, |dap_store, cx| {
+ dap_store.new_session(binary, config, None, cx)
+ })
+ })?
+ .1
+ .await;
+ ret
});
- self.dap_store
- .update(cx, |dap_store, cx| {
- dap_store.new_fake_session(config, worktree, None, caps, fails, cx)
- })
- .1
+ result
}
#[cfg(any(test, feature = "test-support"))]
@@ -39,7 +39,7 @@ use std::{env, mem, num::NonZeroU32, ops::Range, str::FromStr, sync::OnceLock, t
use task::{ResolvedTask, TaskContext};
use unindent::Unindent as _;
use util::{
- TryFutureExt as _, assert_set_eq, path,
+ TryFutureExt as _, assert_set_eq, maybe, path,
paths::PathMatcher,
separator,
test::{TempTree, marked_text_offsets},
@@ -72,7 +72,7 @@ impl HeadlessProject {
http_client,
node_runtime,
languages,
- debug_adapters,
+ debug_adapters: _debug_adapters,
extension_host_proxy: proxy,
}: HeadlessAppState,
cx: &mut Context<Self>,
@@ -112,11 +112,9 @@ impl HeadlessProject {
node_runtime.clone(),
fs.clone(),
languages.clone(),
- debug_adapters.clone(),
environment.clone(),
toolchain_store.read(cx).as_language_toolchain_store(),
breakpoint_store.clone(),
- worktree_store.clone(),
cx,
)
});
@@ -104,7 +104,7 @@ impl ResolvedTask {
}
/// Get the configuration for the debug adapter that should be used for this task.
- pub fn resolved_debug_adapter_config(&self) -> Option<DebugAdapterConfig> {
+ pub fn resolved_debug_adapter_config(&self) -> Option<DebugTaskDefinition> {
match self.original_task.task_type.clone() {
TaskType::Debug(debug_args) if self.resolved.is_some() => {
let resolved = self
@@ -127,10 +127,10 @@ impl ResolvedTask {
})
.collect();
- Some(DebugAdapterConfig {
+ Some(DebugTaskDefinition {
label: resolved.label.clone(),
adapter: debug_args.adapter.clone(),
- request: DebugRequestDisposition::UserConfigured(match debug_args.request {
+ request: match debug_args.request {
crate::task_template::DebugArgsRequest::Launch => {
DebugRequestType::Launch(LaunchConfig {
program: resolved.command.clone(),
@@ -141,7 +141,7 @@ impl ResolvedTask {
crate::task_template::DebugArgsRequest::Attach(attach_config) => {
DebugRequestType::Attach(attach_config)
}
- }),
+ },
initialize_args: debug_args.initialize_args,
tcp_connection: debug_args.tcp_connection,
locator: debug_args.locator.clone(),
@@ -361,9 +361,8 @@ impl PickerDelegate for TasksModalDelegate {
match task.task_type() {
TaskType::Debug(config) if config.locator.is_none() => {
- let Some(config): Option<DebugTaskDefinition> = task
- .resolved_debug_adapter_config()
- .and_then(|config| config.try_into().ok())
+ let Some(config): Option<DebugTaskDefinition> =
+ task.resolved_debug_adapter_config()
else {
return;
};
@@ -382,7 +381,7 @@ impl PickerDelegate for TasksModalDelegate {
.update(cx, |workspace, cx| {
workspace.project().update(cx, |project, cx| {
project
- .start_debug_session(config.into(), cx)
+ .start_debug_session(config, cx)
.detach_and_log_err(cx);
});
})
@@ -96,7 +96,7 @@ use std::{
sync::{Arc, LazyLock, Weak, atomic::AtomicUsize},
time::Duration,
};
-use task::{DebugAdapterConfig, SpawnInTerminal, TaskId};
+use task::{DebugTaskDefinition, SpawnInTerminal, TaskId};
use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
pub use ui;
@@ -858,7 +858,7 @@ pub struct Workspace {
serialized_ssh_project: Option<SerializedSshProject>,
_items_serializer: Task<Result<()>>,
session_id: Option<String>,
- debug_task_queue: HashMap<task::TaskId, DebugAdapterConfig>,
+ debug_task_queue: HashMap<task::TaskId, DebugTaskDefinition>,
}
impl EventEmitter<Event> for Workspace {}