diff --git a/crates/dap/src/transport.rs b/crates/dap/src/transport.rs index 224b08d62b..336367a7a8 100644 --- a/crates/dap/src/transport.rs +++ b/crates/dap/src/transport.rs @@ -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, diff --git a/crates/dap_adapters/src/dap_adapters.rs b/crates/dap_adapters/src/dap_adapters.rs index 4c1ed025ad..d4e47a5cdc 100644 --- a/crates/dap_adapters/src/dap_adapters.rs +++ b/crates/dap_adapters/src/dap_adapters.rs @@ -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)); diff --git a/crates/dap_adapters/src/ruby.rs b/crates/dap_adapters/src/ruby.rs new file mode 100644 index 0000000000..b5767b4363 --- /dev/null +++ b/crates/dap_adapters/src/ruby.rs @@ -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, + _cx: &mut AsyncApp, + ) -> Result { + 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(), + }, + }) + } +} diff --git a/crates/debugger_ui/src/session/running/console.rs b/crates/debugger_ui/src/session/running/console.rs index 41da8364de..33cbd2d997 100644 --- a/crates/debugger_ui/src/session/running/console.rs +++ b/crates/debugger_ui/src/session/running/console.rs @@ -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, diff --git a/crates/project/src/debugger/session.rs b/crates/project/src/debugger/session.rs index 946073333c..6a98e9a5f3 100644 --- a/crates/project/src/debugger/session.rs +++ b/crates/project/src/debugger/session.rs @@ -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? }, );