Detailed changes
@@ -580,21 +580,31 @@ impl TcpTransport {
.unwrap_or(2000u64)
});
- let (rx, tx) = select! {
+ let (mut process, (rx, tx)) = select! {
_ = cx.background_executor().timer(Duration::from_millis(timeout)).fuse() => {
return Err(anyhow!(format!("Connection to TCP DAP timeout {}:{}", host, port)))
},
result = cx.spawn(async move |cx| {
loop {
match TcpStream::connect(address).await {
- Ok(stream) => return stream.split(),
+ Ok(stream) => return Ok((process, stream.split())),
Err(_) => {
+ if let Ok(Some(_)) = process.try_status() {
+ let output = process.output().await?;
+ let output = if output.stderr.is_empty() {
+ String::from_utf8_lossy(&output.stdout).to_string()
+ } else {
+ String::from_utf8_lossy(&output.stderr).to_string()
+ };
+ return Err(anyhow!("{}\nerror: process exited before debugger attached.", output));
+ }
cx.background_executor().timer(Duration::from_millis(100)).await;
}
}
}
- }).fuse() => result
+ }).fuse() => result?
};
+
log::info!(
"Debug adapter has connected to TCP server {}:{}",
host,
@@ -4,6 +4,7 @@ mod go;
mod javascript;
mod php;
mod python;
+mod ruby;
use std::{net::Ipv4Addr, sync::Arc};
@@ -24,6 +25,7 @@ use gpui::{App, BorrowAppContext};
use javascript::JsDebugAdapter;
use php::PhpDebugAdapter;
use python::PythonDebugAdapter;
+use ruby::RubyDebugAdapter;
use serde_json::{Value, json};
use task::TcpArgumentsTemplate;
@@ -33,6 +35,7 @@ pub fn init(cx: &mut App) {
registry.add_adapter(Arc::from(PythonDebugAdapter::default()));
registry.add_adapter(Arc::from(PhpDebugAdapter::default()));
registry.add_adapter(Arc::from(JsDebugAdapter::default()));
+ registry.add_adapter(Arc::from(RubyDebugAdapter));
registry.add_adapter(Arc::from(GoDebugAdapter));
registry.add_adapter(Arc::from(GdbDebugAdapter));
@@ -0,0 +1,102 @@
+use anyhow::{Result, anyhow};
+use async_trait::async_trait;
+use dap::{
+ DebugRequest, StartDebuggingRequestArguments,
+ adapters::{
+ self, DapDelegate, DebugAdapter, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition,
+ },
+};
+use gpui::AsyncApp;
+use std::path::PathBuf;
+use util::command::new_smol_command;
+
+use crate::ToDap;
+
+#[derive(Default)]
+pub(crate) struct RubyDebugAdapter;
+
+impl RubyDebugAdapter {
+ const ADAPTER_NAME: &'static str = "Ruby";
+}
+
+#[async_trait(?Send)]
+impl DebugAdapter for RubyDebugAdapter {
+ fn name(&self) -> DebugAdapterName {
+ DebugAdapterName(Self::ADAPTER_NAME.into())
+ }
+
+ async fn get_binary(
+ &self,
+ delegate: &dyn DapDelegate,
+ definition: &DebugTaskDefinition,
+ _user_installed_path: Option<PathBuf>,
+ _cx: &mut AsyncApp,
+ ) -> Result<DebugAdapterBinary> {
+ let adapter_path = paths::debug_adapters_dir().join(self.name().as_ref());
+ let mut rdbg_path = adapter_path.join("rdbg");
+ if !delegate.fs().is_file(&rdbg_path).await {
+ match delegate.which("rdbg".as_ref()) {
+ Some(path) => rdbg_path = path,
+ None => {
+ delegate.output_to_console(
+ "rdbg not found on path, trying `gem install debug`".to_string(),
+ );
+ let output = new_smol_command("gem")
+ .arg("install")
+ .arg("--no-document")
+ .arg("--bindir")
+ .arg(adapter_path)
+ .arg("debug")
+ .output()
+ .await?;
+ if !output.status.success() {
+ return Err(anyhow!(
+ "Failed to install rdbg:\n{}",
+ String::from_utf8_lossy(&output.stderr).to_string()
+ ));
+ }
+ }
+ }
+ }
+
+ let tcp_connection = definition.tcp_connection.clone().unwrap_or_default();
+ let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
+
+ let DebugRequest::Launch(mut launch) = definition.request.clone() else {
+ anyhow::bail!("rdbg does not yet support attaching");
+ };
+
+ let mut arguments = vec![
+ "--open".to_string(),
+ format!("--port={}", port),
+ format!("--host={}", host),
+ ];
+ if launch.args.is_empty() {
+ let program = launch.program.clone();
+ let mut split = program.split(" ");
+ launch.program = split.next().unwrap().to_string();
+ launch.args = split.map(|s| s.to_string()).collect();
+ }
+ if delegate.which(launch.program.as_ref()).is_some() {
+ arguments.push("--command".to_string())
+ }
+ arguments.push(launch.program);
+ arguments.extend(launch.args);
+
+ Ok(DebugAdapterBinary {
+ command: rdbg_path.to_string_lossy().to_string(),
+ arguments,
+ connection: Some(adapters::TcpArguments {
+ host,
+ port,
+ timeout,
+ }),
+ cwd: launch.cwd,
+ envs: launch.env.into_iter().collect(),
+ request_args: StartDebuggingRequestArguments {
+ configuration: serde_json::Value::Object(Default::default()),
+ request: definition.request.to_dap(),
+ },
+ })
+ }
+}
@@ -152,7 +152,7 @@ impl Console {
session
.evaluate(
expression,
- Some(dap::EvaluateArgumentsContext::Variables),
+ Some(dap::EvaluateArgumentsContext::Repl),
self.stack_frame_list.read(cx).selected_stack_frame_id(),
None,
cx,
@@ -10,7 +10,7 @@ use super::dap_command::{
TerminateThreadsCommand, ThreadsCommand, VariablesCommand,
};
use super::dap_store::DapStore;
-use anyhow::{Context as _, Result, anyhow};
+use anyhow::{Result, anyhow};
use collections::{HashMap, HashSet, IndexMap, IndexSet};
use dap::adapters::{DebugAdapterBinary, DebugAdapterName};
use dap::messages::Response;
@@ -169,8 +169,7 @@ impl LocalMode {
.await?
} else {
DebugAdapterClient::start(session_id, binary.clone(), message_handler, cx.clone())
- .await
- .with_context(|| format!("Failed to start {:?}", &binary.command))?
+ .await?
},
);