codelldb.rs

  1use std::{path::PathBuf, sync::OnceLock};
  2
  3use anyhow::{Result, bail};
  4use async_trait::async_trait;
  5use dap::adapters::latest_github_release;
  6use gpui::AsyncApp;
  7use task::{DebugAdapterConfig, DebugRequestType, DebugTaskDefinition};
  8
  9use crate::*;
 10
 11#[derive(Default)]
 12pub(crate) struct CodeLldbDebugAdapter {
 13    last_known_version: OnceLock<String>,
 14}
 15
 16impl CodeLldbDebugAdapter {
 17    const ADAPTER_NAME: &'static str = "CodeLLDB";
 18}
 19
 20#[async_trait(?Send)]
 21impl DebugAdapter for CodeLldbDebugAdapter {
 22    fn name(&self) -> DebugAdapterName {
 23        DebugAdapterName(Self::ADAPTER_NAME.into())
 24    }
 25
 26    async fn install_binary(
 27        &self,
 28        version: AdapterVersion,
 29        delegate: &dyn DapDelegate,
 30    ) -> Result<()> {
 31        adapters::download_adapter_from_github(
 32            self.name(),
 33            version,
 34            adapters::DownloadedFileType::Vsix,
 35            delegate,
 36        )
 37        .await?;
 38
 39        Ok(())
 40    }
 41
 42    async fn fetch_latest_adapter_version(
 43        &self,
 44        delegate: &dyn DapDelegate,
 45    ) -> Result<AdapterVersion> {
 46        let release =
 47            latest_github_release("vadimcn/codelldb", true, false, delegate.http_client()).await?;
 48
 49        let arch = match std::env::consts::ARCH {
 50            "aarch64" => "arm64",
 51            "x86_64" => "x64",
 52            _ => {
 53                return Err(anyhow!(
 54                    "unsupported architecture {}",
 55                    std::env::consts::ARCH
 56                ));
 57            }
 58        };
 59        let platform = match std::env::consts::OS {
 60            "macos" => "darwin",
 61            "linux" => "linux",
 62            "windows" => "win32",
 63            _ => {
 64                return Err(anyhow!(
 65                    "unsupported operating system {}",
 66                    std::env::consts::OS
 67                ));
 68            }
 69        };
 70        let asset_name = format!("codelldb-{platform}-{arch}.vsix");
 71        let _ = self.last_known_version.set(release.tag_name.clone());
 72        let ret = AdapterVersion {
 73            tag_name: release.tag_name,
 74            url: release
 75                .assets
 76                .iter()
 77                .find(|asset| asset.name == asset_name)
 78                .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?
 79                .browser_download_url
 80                .clone(),
 81        };
 82
 83        Ok(ret)
 84    }
 85
 86    async fn get_installed_binary(
 87        &self,
 88        _: &dyn DapDelegate,
 89        _: &DebugAdapterConfig,
 90        _: Option<PathBuf>,
 91        _: &mut AsyncApp,
 92    ) -> Result<DebugAdapterBinary> {
 93        let Some(version) = self.last_known_version.get() else {
 94            bail!("Could not determine latest CodeLLDB version");
 95        };
 96        let adapter_path = paths::debug_adapters_dir().join(&Self::ADAPTER_NAME);
 97        let version_path = adapter_path.join(format!("{}_{}", Self::ADAPTER_NAME, version));
 98
 99        let adapter_dir = version_path.join("extension").join("adapter");
100        let command = adapter_dir.join("codelldb");
101        let command = command
102            .to_str()
103            .map(ToOwned::to_owned)
104            .ok_or_else(|| anyhow!("Adapter path is expected to be valid UTF-8"))?;
105        Ok(DebugAdapterBinary {
106            command,
107            cwd: Some(adapter_dir),
108            ..Default::default()
109        })
110    }
111
112    fn request_args(&self, config: &DebugTaskDefinition) -> Value {
113        let mut args = json!({
114            "request": match config.request {
115                DebugRequestType::Launch(_) => "launch",
116                DebugRequestType::Attach(_) => "attach",
117            },
118        });
119        let map = args.as_object_mut().unwrap();
120        match &config.request {
121            DebugRequestType::Attach(attach) => {
122                map.insert("pid".into(), attach.process_id.into());
123            }
124            DebugRequestType::Launch(launch) => {
125                map.insert("program".into(), launch.program.clone().into());
126
127                if !launch.args.is_empty() {
128                    map.insert("args".into(), launch.args.clone().into());
129                }
130
131                if let Some(stop_on_entry) = config.stop_on_entry {
132                    map.insert("stopOnEntry".into(), stop_on_entry.into());
133                }
134                if let Some(cwd) = launch.cwd.as_ref() {
135                    map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into());
136                }
137            }
138        }
139        args
140    }
141}