Debug adapters log to console (#29957)
Closes #ISSUE Release Notes: - N/A
This commit is contained in:
parent
de554589a8
commit
68793c0ac2
19 changed files with 268 additions and 330 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4100,6 +4100,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"async-trait",
|
||||
"dap",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"language",
|
||||
"lsp-types",
|
||||
|
|
|
@ -84,19 +84,6 @@ impl ActivityIndicator {
|
|||
})
|
||||
.detach();
|
||||
|
||||
let mut status_events = languages.dap_server_binary_statuses();
|
||||
cx.spawn(async move |this, cx| {
|
||||
while let Some((name, status)) = status_events.next().await {
|
||||
this.update(cx, |this, cx| {
|
||||
this.statuses.retain(|s| s.name != name);
|
||||
this.statuses.push(ServerStatus { name, status });
|
||||
cx.notify();
|
||||
})?;
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.subscribe(
|
||||
&project.read(cx).lsp_store(),
|
||||
|_, _, event, cx| match event {
|
||||
|
|
|
@ -12,10 +12,9 @@ use language::LanguageToolchainStore;
|
|||
use node_runtime::NodeRuntime;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::WorktreeId;
|
||||
use smol::{self, fs::File, lock::Mutex};
|
||||
use smol::{self, fs::File};
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::HashSet,
|
||||
ffi::OsStr,
|
||||
fmt::Debug,
|
||||
net::Ipv4Addr,
|
||||
|
@ -24,7 +23,6 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
use task::{AttachRequest, DebugRequest, DebugScenario, LaunchRequest, TcpArgumentsTemplate};
|
||||
use util::ResultExt;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum DapStatus {
|
||||
|
@ -41,8 +39,7 @@ pub trait DapDelegate {
|
|||
fn node_runtime(&self) -> NodeRuntime;
|
||||
fn toolchain_store(&self) -> Arc<dyn LanguageToolchainStore>;
|
||||
fn fs(&self) -> Arc<dyn Fs>;
|
||||
fn updated_adapters(&self) -> Arc<Mutex<HashSet<DebugAdapterName>>>;
|
||||
fn update_status(&self, dap_name: DebugAdapterName, status: DapStatus);
|
||||
fn output_to_console(&self, msg: String);
|
||||
fn which(&self, command: &OsStr) -> Option<PathBuf>;
|
||||
async fn shell_env(&self) -> collections::HashMap<String, String>;
|
||||
}
|
||||
|
@ -293,7 +290,7 @@ impl DebugAdapterBinary {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AdapterVersion {
|
||||
pub tag_name: String,
|
||||
pub url: String,
|
||||
|
@ -335,6 +332,7 @@ pub async fn download_adapter_from_github(
|
|||
adapter_name,
|
||||
&github_version.url,
|
||||
);
|
||||
delegate.output_to_console(format!("Downloading from {}...", github_version.url));
|
||||
|
||||
let mut response = delegate
|
||||
.http_client()
|
||||
|
@ -418,81 +416,6 @@ pub trait DebugAdapter: 'static + Send + Sync {
|
|||
config: &DebugTaskDefinition,
|
||||
user_installed_path: Option<PathBuf>,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<DebugAdapterBinary> {
|
||||
if delegate
|
||||
.updated_adapters()
|
||||
.lock()
|
||||
.await
|
||||
.contains(&self.name())
|
||||
{
|
||||
log::info!("Using cached debug adapter binary {}", self.name());
|
||||
|
||||
if let Some(binary) = self
|
||||
.get_installed_binary(delegate, &config, user_installed_path.clone(), cx)
|
||||
.await
|
||||
.log_err()
|
||||
{
|
||||
return Ok(binary);
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"Cached binary {} is corrupt falling back to install",
|
||||
self.name()
|
||||
);
|
||||
}
|
||||
|
||||
log::info!("Getting latest version of debug adapter {}", self.name());
|
||||
delegate.update_status(self.name(), DapStatus::CheckingForUpdate);
|
||||
if let Some(version) = self.fetch_latest_adapter_version(delegate).await.log_err() {
|
||||
log::info!("Installing latest version of debug adapter {}", self.name());
|
||||
delegate.update_status(self.name(), DapStatus::Downloading);
|
||||
match self.install_binary(version, delegate).await {
|
||||
Ok(_) => {
|
||||
delegate.update_status(self.name(), DapStatus::None);
|
||||
}
|
||||
Err(error) => {
|
||||
delegate.update_status(
|
||||
self.name(),
|
||||
DapStatus::Failed {
|
||||
error: error.to_string(),
|
||||
},
|
||||
);
|
||||
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
|
||||
delegate
|
||||
.updated_adapters()
|
||||
.lock_arc()
|
||||
.await
|
||||
.insert(self.name());
|
||||
}
|
||||
|
||||
self.get_installed_binary(delegate, &config, user_installed_path, cx)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn fetch_latest_adapter_version(
|
||||
&self,
|
||||
delegate: &dyn DapDelegate,
|
||||
) -> Result<AdapterVersion>;
|
||||
|
||||
/// Installs the binary for the debug adapter.
|
||||
/// This method is called when the adapter binary is not found or needs to be updated.
|
||||
/// It should download and install the necessary files for the debug adapter to function.
|
||||
async fn install_binary(
|
||||
&self,
|
||||
version: AdapterVersion,
|
||||
delegate: &dyn DapDelegate,
|
||||
) -> Result<()>;
|
||||
|
||||
async fn get_installed_binary(
|
||||
&self,
|
||||
delegate: &dyn DapDelegate,
|
||||
config: &DebugTaskDefinition,
|
||||
user_installed_path: Option<PathBuf>,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<DebugAdapterBinary>;
|
||||
|
||||
fn inline_value_provider(&self) -> Option<Box<dyn InlineValueProvider>> {
|
||||
|
@ -561,29 +484,4 @@ impl DebugAdapter for FakeAdapter {
|
|||
request_args: self.request_args(config),
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_latest_adapter_version(
|
||||
&self,
|
||||
_delegate: &dyn DapDelegate,
|
||||
) -> Result<AdapterVersion> {
|
||||
unimplemented!("fetch latest adapter version");
|
||||
}
|
||||
|
||||
async fn install_binary(
|
||||
&self,
|
||||
_version: AdapterVersion,
|
||||
_delegate: &dyn DapDelegate,
|
||||
) -> Result<()> {
|
||||
unimplemented!("install binary");
|
||||
}
|
||||
|
||||
async fn get_installed_binary(
|
||||
&self,
|
||||
_: &dyn DapDelegate,
|
||||
_: &DebugTaskDefinition,
|
||||
_: Option<PathBuf>,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<DebugAdapterBinary> {
|
||||
unimplemented!("get installed binary");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ doctest = false
|
|||
anyhow.workspace = true
|
||||
async-trait.workspace = true
|
||||
dap.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
language.workspace = true
|
||||
lsp-types.workspace = true
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
use std::{collections::HashMap, path::PathBuf, sync::OnceLock};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use dap::adapters::{DebugTaskDefinition, InlineValueProvider, latest_github_release};
|
||||
use futures::StreamExt;
|
||||
use gpui::AsyncApp;
|
||||
use task::DebugRequest;
|
||||
use util::fs::remove_matching;
|
||||
|
||||
use crate::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct CodeLldbDebugAdapter {
|
||||
last_known_version: OnceLock<String>,
|
||||
path_to_codelldb: OnceLock<String>,
|
||||
}
|
||||
|
||||
impl CodeLldbDebugAdapter {
|
||||
|
@ -54,29 +56,6 @@ impl CodeLldbDebugAdapter {
|
|||
configuration,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl DebugAdapter for CodeLldbDebugAdapter {
|
||||
fn name(&self) -> DebugAdapterName {
|
||||
DebugAdapterName(Self::ADAPTER_NAME.into())
|
||||
}
|
||||
|
||||
async fn install_binary(
|
||||
&self,
|
||||
version: AdapterVersion,
|
||||
delegate: &dyn DapDelegate,
|
||||
) -> Result<()> {
|
||||
adapters::download_adapter_from_github(
|
||||
self.name(),
|
||||
version,
|
||||
adapters::DownloadedFileType::Vsix,
|
||||
delegate,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn fetch_latest_adapter_version(
|
||||
&self,
|
||||
|
@ -107,7 +86,6 @@ impl DebugAdapter for CodeLldbDebugAdapter {
|
|||
}
|
||||
};
|
||||
let asset_name = format!("codelldb-{platform}-{arch}.vsix");
|
||||
let _ = self.last_known_version.set(release.tag_name.clone());
|
||||
let ret = AdapterVersion {
|
||||
tag_name: release.tag_name,
|
||||
url: release
|
||||
|
@ -121,28 +99,56 @@ impl DebugAdapter for CodeLldbDebugAdapter {
|
|||
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_installed_binary(
|
||||
#[async_trait(?Send)]
|
||||
impl DebugAdapter for CodeLldbDebugAdapter {
|
||||
fn name(&self) -> DebugAdapterName {
|
||||
DebugAdapterName(Self::ADAPTER_NAME.into())
|
||||
}
|
||||
|
||||
async fn get_binary(
|
||||
&self,
|
||||
_: &dyn DapDelegate,
|
||||
delegate: &dyn DapDelegate,
|
||||
config: &DebugTaskDefinition,
|
||||
_: Option<PathBuf>,
|
||||
user_installed_path: Option<PathBuf>,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<DebugAdapterBinary> {
|
||||
let Some(version) = self.last_known_version.get() else {
|
||||
bail!("Could not determine latest CodeLLDB version");
|
||||
};
|
||||
let adapter_path = paths::debug_adapters_dir().join(&Self::ADAPTER_NAME);
|
||||
let version_path = adapter_path.join(format!("{}_{}", Self::ADAPTER_NAME, version));
|
||||
let mut command = user_installed_path
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.or(self.path_to_codelldb.get().cloned());
|
||||
|
||||
if command.is_none() {
|
||||
delegate.output_to_console(format!("Checking latest version of {}...", self.name()));
|
||||
let adapter_path = paths::debug_adapters_dir().join(&Self::ADAPTER_NAME);
|
||||
let version_path =
|
||||
if let Ok(version) = self.fetch_latest_adapter_version(delegate).await {
|
||||
adapters::download_adapter_from_github(
|
||||
self.name(),
|
||||
version.clone(),
|
||||
adapters::DownloadedFileType::Vsix,
|
||||
delegate,
|
||||
)
|
||||
.await?;
|
||||
let version_path =
|
||||
adapter_path.join(format!("{}_{}", Self::ADAPTER_NAME, version.tag_name));
|
||||
remove_matching(&adapter_path, |entry| entry != version_path).await;
|
||||
version_path
|
||||
} else {
|
||||
let mut paths = delegate.fs().read_dir(&adapter_path).await?;
|
||||
paths
|
||||
.next()
|
||||
.await
|
||||
.ok_or_else(|| anyhow!("No adapter found"))??
|
||||
};
|
||||
let adapter_dir = version_path.join("extension").join("adapter");
|
||||
let path = adapter_dir.join("codelldb").to_string_lossy().to_string();
|
||||
self.path_to_codelldb.set(path.clone()).ok();
|
||||
command = Some(path);
|
||||
};
|
||||
|
||||
let adapter_dir = version_path.join("extension").join("adapter");
|
||||
let command = adapter_dir.join("codelldb");
|
||||
let command = command
|
||||
.to_str()
|
||||
.map(ToOwned::to_owned)
|
||||
.ok_or_else(|| anyhow!("Adapter path is expected to be valid UTF-8"))?;
|
||||
Ok(DebugAdapterBinary {
|
||||
command,
|
||||
command: command.unwrap(),
|
||||
cwd: None,
|
||||
arguments: vec![
|
||||
"--settings".into(),
|
||||
|
|
|
@ -29,9 +29,9 @@ use task::TcpArgumentsTemplate;
|
|||
pub fn init(cx: &mut App) {
|
||||
cx.update_default_global(|registry: &mut DapRegistry, _cx| {
|
||||
registry.add_adapter(Arc::from(CodeLldbDebugAdapter::default()));
|
||||
registry.add_adapter(Arc::from(PythonDebugAdapter));
|
||||
registry.add_adapter(Arc::from(PhpDebugAdapter));
|
||||
registry.add_adapter(Arc::from(JsDebugAdapter));
|
||||
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(GoDebugAdapter));
|
||||
registry.add_adapter(Arc::from(GdbDebugAdapter));
|
||||
})
|
||||
|
|
|
@ -90,26 +90,4 @@ impl DebugAdapter for GdbDebugAdapter {
|
|||
request_args: self.request_args(config),
|
||||
})
|
||||
}
|
||||
|
||||
async fn install_binary(
|
||||
&self,
|
||||
_version: AdapterVersion,
|
||||
_delegate: &dyn DapDelegate,
|
||||
) -> Result<()> {
|
||||
unimplemented!("GDB debug adapter cannot be installed by Zed (yet)")
|
||||
}
|
||||
|
||||
async fn fetch_latest_adapter_version(&self, _: &dyn DapDelegate) -> Result<AdapterVersion> {
|
||||
unimplemented!("Fetch latest GDB version not implemented (yet)")
|
||||
}
|
||||
|
||||
async fn get_installed_binary(
|
||||
&self,
|
||||
_: &dyn DapDelegate,
|
||||
_: &DebugTaskDefinition,
|
||||
_: Option<std::path::PathBuf>,
|
||||
_: &mut AsyncApp,
|
||||
) -> Result<DebugAdapterBinary> {
|
||||
unimplemented!("GDB cannot be installed by Zed (yet)")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,41 +46,8 @@ impl DebugAdapter for GoDebugAdapter {
|
|||
&self,
|
||||
delegate: &dyn DapDelegate,
|
||||
config: &DebugTaskDefinition,
|
||||
user_installed_path: Option<PathBuf>,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<DebugAdapterBinary> {
|
||||
self.get_installed_binary(delegate, config, user_installed_path, cx)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn fetch_latest_adapter_version(
|
||||
&self,
|
||||
_delegate: &dyn DapDelegate,
|
||||
) -> Result<AdapterVersion> {
|
||||
unimplemented!("This adapter is used from path for now");
|
||||
}
|
||||
|
||||
async fn install_binary(
|
||||
&self,
|
||||
version: AdapterVersion,
|
||||
delegate: &dyn DapDelegate,
|
||||
) -> Result<()> {
|
||||
adapters::download_adapter_from_github(
|
||||
self.name(),
|
||||
version,
|
||||
adapters::DownloadedFileType::Zip,
|
||||
delegate,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_installed_binary(
|
||||
&self,
|
||||
delegate: &dyn DapDelegate,
|
||||
config: &DebugTaskDefinition,
|
||||
_: Option<PathBuf>,
|
||||
_: &mut AsyncApp,
|
||||
_user_installed_path: Option<PathBuf>,
|
||||
_cx: &mut AsyncApp,
|
||||
) -> Result<DebugAdapterBinary> {
|
||||
let delve_path = delegate
|
||||
.which(OsStr::new("dlv"))
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
use adapters::latest_github_release;
|
||||
use dap::{StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
|
||||
use gpui::AsyncApp;
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
use std::{collections::HashMap, path::PathBuf, sync::OnceLock};
|
||||
use task::DebugRequest;
|
||||
use util::ResultExt;
|
||||
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct JsDebugAdapter;
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct JsDebugAdapter {
|
||||
checked: OnceLock<()>,
|
||||
}
|
||||
|
||||
impl JsDebugAdapter {
|
||||
const ADAPTER_NAME: &'static str = "JavaScript";
|
||||
|
@ -47,13 +50,6 @@ impl JsDebugAdapter {
|
|||
request: config.request.to_dap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl DebugAdapter for JsDebugAdapter {
|
||||
fn name(&self) -> DebugAdapterName {
|
||||
DebugAdapterName(Self::ADAPTER_NAME.into())
|
||||
}
|
||||
|
||||
async fn fetch_latest_adapter_version(
|
||||
&self,
|
||||
|
@ -130,20 +126,35 @@ impl DebugAdapter for JsDebugAdapter {
|
|||
request_args: self.request_args(config),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn install_binary(
|
||||
#[async_trait(?Send)]
|
||||
impl DebugAdapter for JsDebugAdapter {
|
||||
fn name(&self) -> DebugAdapterName {
|
||||
DebugAdapterName(Self::ADAPTER_NAME.into())
|
||||
}
|
||||
|
||||
async fn get_binary(
|
||||
&self,
|
||||
version: AdapterVersion,
|
||||
delegate: &dyn DapDelegate,
|
||||
) -> Result<()> {
|
||||
adapters::download_adapter_from_github(
|
||||
self.name(),
|
||||
version,
|
||||
adapters::DownloadedFileType::GzipTar,
|
||||
delegate,
|
||||
)
|
||||
.await?;
|
||||
config: &DebugTaskDefinition,
|
||||
user_installed_path: Option<PathBuf>,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<DebugAdapterBinary> {
|
||||
if self.checked.set(()).is_ok() {
|
||||
delegate.output_to_console(format!("Checking latest version of {}...", self.name()));
|
||||
if let Some(version) = self.fetch_latest_adapter_version(delegate).await.log_err() {
|
||||
adapters::download_adapter_from_github(
|
||||
self.name(),
|
||||
version,
|
||||
adapters::DownloadedFileType::GzipTar,
|
||||
delegate,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
self.get_installed_binary(delegate, &config, user_installed_path, cx)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
use adapters::latest_github_release;
|
||||
use dap::adapters::{DebugTaskDefinition, TcpArguments};
|
||||
use gpui::AsyncApp;
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
use std::{collections::HashMap, path::PathBuf, sync::OnceLock};
|
||||
use util::ResultExt;
|
||||
|
||||
use crate::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct PhpDebugAdapter;
|
||||
pub(crate) struct PhpDebugAdapter {
|
||||
checked: OnceLock<()>,
|
||||
}
|
||||
|
||||
impl PhpDebugAdapter {
|
||||
const ADAPTER_NAME: &'static str = "PHP";
|
||||
|
@ -32,13 +35,6 @@ impl PhpDebugAdapter {
|
|||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl DebugAdapter for PhpDebugAdapter {
|
||||
fn name(&self) -> DebugAdapterName {
|
||||
DebugAdapterName(Self::ADAPTER_NAME.into())
|
||||
}
|
||||
|
||||
async fn fetch_latest_adapter_version(
|
||||
&self,
|
||||
|
@ -114,20 +110,35 @@ impl DebugAdapter for PhpDebugAdapter {
|
|||
request_args: self.request_args(config)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn install_binary(
|
||||
#[async_trait(?Send)]
|
||||
impl DebugAdapter for PhpDebugAdapter {
|
||||
fn name(&self) -> DebugAdapterName {
|
||||
DebugAdapterName(Self::ADAPTER_NAME.into())
|
||||
}
|
||||
|
||||
async fn get_binary(
|
||||
&self,
|
||||
version: AdapterVersion,
|
||||
delegate: &dyn DapDelegate,
|
||||
) -> Result<()> {
|
||||
adapters::download_adapter_from_github(
|
||||
self.name(),
|
||||
version,
|
||||
adapters::DownloadedFileType::Vsix,
|
||||
delegate,
|
||||
)
|
||||
.await?;
|
||||
config: &DebugTaskDefinition,
|
||||
user_installed_path: Option<PathBuf>,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<DebugAdapterBinary> {
|
||||
if self.checked.set(()).is_ok() {
|
||||
delegate.output_to_console(format!("Checking latest version of {}...", self.name()));
|
||||
if let Some(version) = self.fetch_latest_adapter_version(delegate).await.log_err() {
|
||||
adapters::download_adapter_from_github(
|
||||
self.name(),
|
||||
version,
|
||||
adapters::DownloadedFileType::Vsix,
|
||||
delegate,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
self.get_installed_binary(delegate, &config, user_installed_path, cx)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,13 @@ use dap::{
|
|||
adapters::InlineValueProvider,
|
||||
};
|
||||
use gpui::AsyncApp;
|
||||
use std::{collections::HashMap, ffi::OsStr, path::PathBuf};
|
||||
use std::{collections::HashMap, ffi::OsStr, path::PathBuf, sync::OnceLock};
|
||||
use util::ResultExt;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct PythonDebugAdapter;
|
||||
pub(crate) struct PythonDebugAdapter {
|
||||
checked: OnceLock<()>,
|
||||
}
|
||||
|
||||
impl PythonDebugAdapter {
|
||||
const ADAPTER_NAME: &'static str = "Debugpy";
|
||||
|
@ -46,14 +49,6 @@ impl PythonDebugAdapter {
|
|||
request: config.request.to_dap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl DebugAdapter for PythonDebugAdapter {
|
||||
fn name(&self) -> DebugAdapterName {
|
||||
DebugAdapterName(Self::ADAPTER_NAME.into())
|
||||
}
|
||||
|
||||
async fn fetch_latest_adapter_version(
|
||||
&self,
|
||||
delegate: &dyn DapDelegate,
|
||||
|
@ -162,6 +157,31 @@ impl DebugAdapter for PythonDebugAdapter {
|
|||
request_args: self.request_args(config),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl DebugAdapter for PythonDebugAdapter {
|
||||
fn name(&self) -> DebugAdapterName {
|
||||
DebugAdapterName(Self::ADAPTER_NAME.into())
|
||||
}
|
||||
|
||||
async fn get_binary(
|
||||
&self,
|
||||
delegate: &dyn DapDelegate,
|
||||
config: &DebugTaskDefinition,
|
||||
user_installed_path: Option<PathBuf>,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<DebugAdapterBinary> {
|
||||
if self.checked.set(()).is_ok() {
|
||||
delegate.output_to_console(format!("Checking latest version of {}...", self.name()));
|
||||
if let Some(version) = self.fetch_latest_adapter_version(delegate).await.log_err() {
|
||||
self.install_binary(version, delegate).await?;
|
||||
}
|
||||
}
|
||||
|
||||
self.get_installed_binary(delegate, &config, user_installed_path, cx)
|
||||
.await
|
||||
}
|
||||
|
||||
fn inline_value_provider(&self) -> Option<Box<dyn InlineValueProvider>> {
|
||||
Some(Box::new(PythonInlineValueProvider))
|
||||
|
|
|
@ -104,7 +104,6 @@ pub struct LanguageRegistry {
|
|||
language_server_download_dir: Option<Arc<Path>>,
|
||||
executor: BackgroundExecutor,
|
||||
lsp_binary_status_tx: BinaryStatusSender,
|
||||
dap_binary_status_tx: BinaryStatusSender,
|
||||
}
|
||||
|
||||
struct LanguageRegistryState {
|
||||
|
@ -269,7 +268,6 @@ impl LanguageRegistry {
|
|||
}),
|
||||
language_server_download_dir: None,
|
||||
lsp_binary_status_tx: Default::default(),
|
||||
dap_binary_status_tx: Default::default(),
|
||||
executor,
|
||||
};
|
||||
this.add(PLAIN_TEXT.clone());
|
||||
|
@ -986,10 +984,6 @@ impl LanguageRegistry {
|
|||
self.lsp_binary_status_tx.send(server_name.0, status);
|
||||
}
|
||||
|
||||
pub fn update_dap_status(&self, server_name: LanguageServerName, status: BinaryStatus) {
|
||||
self.dap_binary_status_tx.send(server_name.0, status);
|
||||
}
|
||||
|
||||
pub fn next_language_server_id(&self) -> LanguageServerId {
|
||||
self.state.write().next_language_server_id()
|
||||
}
|
||||
|
@ -1046,12 +1040,6 @@ impl LanguageRegistry {
|
|||
self.lsp_binary_status_tx.subscribe()
|
||||
}
|
||||
|
||||
pub fn dap_server_binary_statuses(
|
||||
&self,
|
||||
) -> mpsc::UnboundedReceiver<(SharedString, BinaryStatus)> {
|
||||
self.dap_binary_status_tx.subscribe()
|
||||
}
|
||||
|
||||
pub async fn delete_server_container(&self, name: LanguageServerName) {
|
||||
log::info!("deleting server container");
|
||||
let Some(dir) = self.language_server_download_dir(&name) else {
|
||||
|
|
|
@ -16,22 +16,20 @@ use collections::HashMap;
|
|||
use dap::{
|
||||
Capabilities, CompletionItem, CompletionsArguments, DapRegistry, DebugRequest,
|
||||
EvaluateArguments, EvaluateArgumentsContext, EvaluateResponse, Source, StackFrameId,
|
||||
adapters::{
|
||||
DapStatus, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition, TcpArguments,
|
||||
},
|
||||
adapters::{DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition, TcpArguments},
|
||||
client::SessionId,
|
||||
messages::Message,
|
||||
requests::{Completions, Evaluate},
|
||||
};
|
||||
use fs::Fs;
|
||||
use futures::future::{Shared, join_all};
|
||||
use futures::{
|
||||
StreamExt,
|
||||
channel::mpsc::{self, UnboundedSender},
|
||||
future::{Shared, join_all},
|
||||
};
|
||||
use gpui::{App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task};
|
||||
use http_client::HttpClient;
|
||||
use language::{
|
||||
BinaryStatus, Buffer, LanguageRegistry, LanguageToolchainStore,
|
||||
language_settings::InlayHintKind, range_from_lsp,
|
||||
};
|
||||
use lsp::LanguageServerName;
|
||||
use language::{Buffer, LanguageToolchainStore, language_settings::InlayHintKind, range_from_lsp};
|
||||
use node_runtime::NodeRuntime;
|
||||
|
||||
use remote::SshRemoteClient;
|
||||
|
@ -40,10 +38,9 @@ use rpc::{
|
|||
proto::{self},
|
||||
};
|
||||
use settings::{Settings, WorktreeId};
|
||||
use smol::lock::Mutex;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::{BTreeMap, HashSet},
|
||||
collections::BTreeMap,
|
||||
ffi::OsStr,
|
||||
net::Ipv4Addr,
|
||||
path::{Path, PathBuf},
|
||||
|
@ -78,7 +75,6 @@ pub struct LocalDapStore {
|
|||
node_runtime: NodeRuntime,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
environment: Entity<ProjectEnvironment>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
toolchain_store: Arc<dyn LanguageToolchainStore>,
|
||||
}
|
||||
|
||||
|
@ -102,12 +98,13 @@ impl EventEmitter<DapStoreEvent> for DapStore {}
|
|||
impl DapStore {
|
||||
pub fn init(client: &AnyProtoClient, cx: &mut App) {
|
||||
static ADD_LOCATORS: Once = Once::new();
|
||||
client.add_entity_request_handler(Self::handle_run_debug_locator);
|
||||
client.add_entity_request_handler(Self::handle_get_debug_adapter_binary);
|
||||
ADD_LOCATORS.call_once(|| {
|
||||
DapRegistry::global(cx)
|
||||
.add_locator("cargo".into(), Arc::new(locators::cargo::CargoLocator {}))
|
||||
});
|
||||
client.add_entity_request_handler(Self::handle_run_debug_locator);
|
||||
client.add_entity_request_handler(Self::handle_get_debug_adapter_binary);
|
||||
client.add_entity_message_handler(Self::handle_log_to_debug_console);
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
|
@ -115,7 +112,6 @@ impl DapStore {
|
|||
http_client: Arc<dyn HttpClient>,
|
||||
node_runtime: NodeRuntime,
|
||||
fs: Arc<dyn Fs>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
environment: Entity<ProjectEnvironment>,
|
||||
toolchain_store: Arc<dyn LanguageToolchainStore>,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
|
@ -128,7 +124,6 @@ impl DapStore {
|
|||
http_client,
|
||||
node_runtime,
|
||||
toolchain_store,
|
||||
language_registry,
|
||||
});
|
||||
|
||||
Self::new(mode, breakpoint_store, worktree_store, cx)
|
||||
|
@ -179,6 +174,8 @@ impl DapStore {
|
|||
pub fn get_debug_adapter_binary(
|
||||
&mut self,
|
||||
definition: DebugTaskDefinition,
|
||||
session_id: SessionId,
|
||||
console: UnboundedSender<String>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<DebugAdapterBinary>> {
|
||||
match &self.mode {
|
||||
|
@ -196,7 +193,7 @@ impl DapStore {
|
|||
.get(&adapter.name())
|
||||
.and_then(|s| s.binary.as_ref().map(PathBuf::from));
|
||||
|
||||
let delegate = self.delegate(&worktree, cx);
|
||||
let delegate = self.delegate(&worktree, console, cx);
|
||||
let cwd: Arc<Path> = definition
|
||||
.cwd()
|
||||
.unwrap_or(worktree.read(cx).abs_path().as_ref())
|
||||
|
@ -228,6 +225,7 @@ impl DapStore {
|
|||
}
|
||||
DapStoreMode::Ssh(ssh) => {
|
||||
let request = ssh.upstream_client.request(proto::GetDebugAdapterBinary {
|
||||
session_id: session_id.to_proto(),
|
||||
project_id: ssh.upstream_project_id,
|
||||
definition: Some(definition.to_proto()),
|
||||
});
|
||||
|
@ -445,13 +443,15 @@ impl DapStore {
|
|||
};
|
||||
|
||||
let dap_store = cx.weak_entity();
|
||||
let console = session.update(cx, |session, cx| session.console_output(cx));
|
||||
let session_id = session.read(cx).session_id();
|
||||
|
||||
cx.spawn({
|
||||
let session = session.clone();
|
||||
async move |this, cx| {
|
||||
let mut binary = this
|
||||
.update(cx, |this, cx| {
|
||||
this.get_debug_adapter_binary(definition.clone(), cx)
|
||||
this.get_debug_adapter_binary(definition.clone(), session_id, console, cx)
|
||||
})?
|
||||
.await?;
|
||||
|
||||
|
@ -522,7 +522,12 @@ impl DapStore {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn delegate(&self, worktree: &Entity<Worktree>, cx: &mut App) -> DapAdapterDelegate {
|
||||
fn delegate(
|
||||
&self,
|
||||
worktree: &Entity<Worktree>,
|
||||
console: UnboundedSender<String>,
|
||||
cx: &mut App,
|
||||
) -> DapAdapterDelegate {
|
||||
let Some(local_store) = self.as_local() else {
|
||||
unimplemented!("Starting session on remote side");
|
||||
};
|
||||
|
@ -530,9 +535,9 @@ impl DapStore {
|
|||
DapAdapterDelegate::new(
|
||||
local_store.fs.clone(),
|
||||
worktree.read(cx).id(),
|
||||
console,
|
||||
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)
|
||||
|
@ -802,24 +807,65 @@ impl DapStore {
|
|||
.definition
|
||||
.ok_or_else(|| anyhow!("missing definition"))?,
|
||||
)?;
|
||||
let (tx, mut rx) = mpsc::unbounded();
|
||||
let session_id = envelope.payload.session_id;
|
||||
cx.spawn({
|
||||
let this = this.clone();
|
||||
async move |cx| {
|
||||
while let Some(message) = rx.next().await {
|
||||
this.update(cx, |this, _| {
|
||||
if let Some((downstream, project_id)) = this.downstream_client.clone() {
|
||||
downstream
|
||||
.send(proto::LogToDebugConsole {
|
||||
project_id,
|
||||
session_id,
|
||||
message,
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let binary = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.get_debug_adapter_binary(definition, cx)
|
||||
this.get_debug_adapter_binary(definition, SessionId::from_proto(session_id), tx, cx)
|
||||
})?
|
||||
.await?;
|
||||
Ok(binary.to_proto())
|
||||
}
|
||||
|
||||
async fn handle_log_to_debug_console(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::LogToDebugConsole>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<()> {
|
||||
let session_id = SessionId::from_proto(envelope.payload.session_id);
|
||||
this.update(&mut cx, |this, cx| {
|
||||
let Some(session) = this.sessions.get(&session_id) else {
|
||||
return;
|
||||
};
|
||||
session.update(cx, |session, cx| {
|
||||
session
|
||||
.console_output(cx)
|
||||
.unbounded_send(envelope.payload.message)
|
||||
.ok();
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DapAdapterDelegate {
|
||||
fs: Arc<dyn Fs>,
|
||||
console: mpsc::UnboundedSender<String>,
|
||||
worktree_id: WorktreeId,
|
||||
node_runtime: NodeRuntime,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
toolchain_store: Arc<dyn LanguageToolchainStore>,
|
||||
updated_adapters: Arc<Mutex<HashSet<DebugAdapterName>>>,
|
||||
load_shell_env_task: Shared<Task<Option<HashMap<String, String>>>>,
|
||||
}
|
||||
|
||||
|
@ -827,21 +873,20 @@ impl DapAdapterDelegate {
|
|||
pub fn new(
|
||||
fs: Arc<dyn Fs>,
|
||||
worktree_id: WorktreeId,
|
||||
status: mpsc::UnboundedSender<String>,
|
||||
node_runtime: NodeRuntime,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
toolchain_store: Arc<dyn LanguageToolchainStore>,
|
||||
load_shell_env_task: Shared<Task<Option<HashMap<String, String>>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
fs,
|
||||
console: status,
|
||||
worktree_id,
|
||||
http_client,
|
||||
node_runtime,
|
||||
toolchain_store,
|
||||
language_registry,
|
||||
load_shell_env_task,
|
||||
updated_adapters: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -864,21 +909,8 @@ impl dap::adapters::DapDelegate for DapAdapterDelegate {
|
|||
self.fs.clone()
|
||||
}
|
||||
|
||||
fn updated_adapters(&self) -> Arc<Mutex<HashSet<DebugAdapterName>>> {
|
||||
self.updated_adapters.clone()
|
||||
}
|
||||
|
||||
fn update_status(&self, dap_name: DebugAdapterName, status: dap::adapters::DapStatus) {
|
||||
let name = SharedString::from(dap_name.to_string());
|
||||
let status = match status {
|
||||
DapStatus::None => BinaryStatus::None,
|
||||
DapStatus::Downloading => BinaryStatus::Downloading,
|
||||
DapStatus::Failed { error } => BinaryStatus::Failed { error },
|
||||
DapStatus::CheckingForUpdate => BinaryStatus::CheckingForUpdate,
|
||||
};
|
||||
|
||||
self.language_registry
|
||||
.update_dap_status(LanguageServerName(name), status);
|
||||
fn output_to_console(&self, msg: String) {
|
||||
self.console.unbounded_send(msg).ok();
|
||||
}
|
||||
|
||||
fn which(&self, command: &OsStr) -> Option<PathBuf> {
|
||||
|
|
|
@ -170,7 +170,7 @@ impl LocalMode {
|
|||
} else {
|
||||
DebugAdapterClient::start(session_id, binary.clone(), message_handler, cx.clone())
|
||||
.await
|
||||
.with_context(|| "Failed to start communication with debug adapter")?
|
||||
.with_context(|| format!("Failed to start {:?}", &binary.command))?
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -815,6 +815,33 @@ impl Session {
|
|||
self.is_session_terminated
|
||||
}
|
||||
|
||||
pub fn console_output(&mut self, cx: &mut Context<Self>) -> mpsc::UnboundedSender<String> {
|
||||
let (tx, mut rx) = mpsc::unbounded();
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
while let Some(output) = rx.next().await {
|
||||
this.update(cx, |this, _| {
|
||||
this.output_token.0 += 1;
|
||||
this.output.push_back(dap::OutputEvent {
|
||||
category: None,
|
||||
output,
|
||||
group: None,
|
||||
variables_reference: None,
|
||||
source: None,
|
||||
line: None,
|
||||
column: None,
|
||||
data: None,
|
||||
location_reference: None,
|
||||
});
|
||||
})?;
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach();
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
pub fn is_local(&self) -> bool {
|
||||
matches!(self.mode, Mode::Running(_))
|
||||
}
|
||||
|
|
|
@ -894,7 +894,6 @@ impl Project {
|
|||
client.http_client(),
|
||||
node.clone(),
|
||||
fs.clone(),
|
||||
languages.clone(),
|
||||
environment.clone(),
|
||||
toolchain_store.read(cx).as_language_toolchain_store(),
|
||||
worktree_store.clone(),
|
||||
|
|
|
@ -559,6 +559,7 @@ message DapModuleId {
|
|||
|
||||
message GetDebugAdapterBinary {
|
||||
uint64 project_id = 1;
|
||||
uint64 session_id = 3;
|
||||
DebugTaskDefinition definition = 2;
|
||||
}
|
||||
|
||||
|
@ -605,3 +606,9 @@ message SpawnInTerminal {
|
|||
map<string, string> env = 4;
|
||||
optional string cwd = 5;
|
||||
}
|
||||
|
||||
message LogToDebugConsole {
|
||||
uint64 project_id = 1;
|
||||
uint64 session_id = 2;
|
||||
string message = 3;
|
||||
}
|
||||
|
|
|
@ -384,7 +384,9 @@ message Envelope {
|
|||
LspExtGoToParentModuleResponse lsp_ext_go_to_parent_module_response = 344;
|
||||
LspExtCancelFlycheck lsp_ext_cancel_flycheck = 345;
|
||||
LspExtRunFlycheck lsp_ext_run_flycheck = 346;
|
||||
LspExtClearFlycheck lsp_ext_clear_flycheck = 347; // current max
|
||||
LspExtClearFlycheck lsp_ext_clear_flycheck = 347;
|
||||
|
||||
LogToDebugConsole log_to_debug_console = 348; // current max
|
||||
}
|
||||
|
||||
reserved 87 to 88;
|
||||
|
|
|
@ -305,6 +305,7 @@ messages!(
|
|||
(DebugAdapterBinary, Background),
|
||||
(RunDebugLocators, Background),
|
||||
(DebugRequest, Background),
|
||||
(LogToDebugConsole, Background),
|
||||
);
|
||||
|
||||
request_messages!(
|
||||
|
@ -591,6 +592,7 @@ entity_messages!(
|
|||
ToggleBreakpoint,
|
||||
RunDebugLocators,
|
||||
GetDebugAdapterBinary,
|
||||
LogToDebugConsole,
|
||||
);
|
||||
|
||||
entity_messages!(
|
||||
|
|
|
@ -106,17 +106,18 @@ impl HeadlessProject {
|
|||
cx.new(|_| BreakpointStore::local(worktree_store.clone(), buffer_store.clone()));
|
||||
|
||||
let dap_store = cx.new(|cx| {
|
||||
DapStore::new_local(
|
||||
let mut dap_store = DapStore::new_local(
|
||||
http_client.clone(),
|
||||
node_runtime.clone(),
|
||||
fs.clone(),
|
||||
languages.clone(),
|
||||
environment.clone(),
|
||||
toolchain_store.read(cx).as_language_toolchain_store(),
|
||||
worktree_store.clone(),
|
||||
breakpoint_store.clone(),
|
||||
cx,
|
||||
)
|
||||
);
|
||||
dap_store.shared(SSH_PROJECT_ID, session.clone().into(), cx);
|
||||
dap_store
|
||||
});
|
||||
|
||||
let git_store = cx.new(|cx| {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue