extension_lsp_adapter.rs

  1use crate::wasm_host::{wit::LanguageServerConfig, WasmExtension, WasmHost};
  2use anyhow::{anyhow, Context, Result};
  3use async_trait::async_trait;
  4use futures::{Future, FutureExt};
  5use gpui::AsyncAppContext;
  6use language::{Language, LanguageServerName, LspAdapter, LspAdapterDelegate};
  7use lsp::LanguageServerBinary;
  8use std::{
  9    any::Any,
 10    path::{Path, PathBuf},
 11    pin::Pin,
 12    sync::Arc,
 13};
 14use wasmtime_wasi::WasiView as _;
 15
 16pub struct ExtensionLspAdapter {
 17    pub(crate) extension: WasmExtension,
 18    pub(crate) config: LanguageServerConfig,
 19    pub(crate) host: Arc<WasmHost>,
 20}
 21
 22#[async_trait(?Send)]
 23impl LspAdapter for ExtensionLspAdapter {
 24    fn name(&self) -> LanguageServerName {
 25        LanguageServerName(self.config.name.clone().into())
 26    }
 27
 28    fn get_language_server_command<'a>(
 29        self: Arc<Self>,
 30        _: Arc<Language>,
 31        _: Arc<Path>,
 32        delegate: Arc<dyn LspAdapterDelegate>,
 33        _: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
 34        _: &'a mut AsyncAppContext,
 35    ) -> Pin<Box<dyn 'a + Future<Output = Result<LanguageServerBinary>>>> {
 36        async move {
 37            let command = self
 38                .extension
 39                .call({
 40                    let this = self.clone();
 41                    |extension, store| {
 42                        async move {
 43                            let resource = store.data_mut().table().push(delegate)?;
 44                            let command = extension
 45                                .call_language_server_command(store, &this.config, resource)
 46                                .await?
 47                                .map_err(|e| anyhow!("{}", e))?;
 48                            anyhow::Ok(command)
 49                        }
 50                        .boxed()
 51                    }
 52                })
 53                .await?;
 54
 55            let path = self
 56                .host
 57                .path_from_extension(&self.extension.manifest.id, command.command.as_ref());
 58
 59            // TODO: Eventually we'll want to expose an extension API for doing this, but for
 60            // now we just manually set the file permissions for extensions that we know need it.
 61            if self.extension.manifest.id.as_ref() == "zig" {
 62                #[cfg(not(windows))]
 63                {
 64                    use std::fs::{self, Permissions};
 65                    use std::os::unix::fs::PermissionsExt;
 66
 67                    fs::set_permissions(&path, Permissions::from_mode(0o755))
 68                        .context("failed to set file permissions")?;
 69                }
 70            }
 71
 72            Ok(LanguageServerBinary {
 73                path,
 74                arguments: command.args.into_iter().map(|arg| arg.into()).collect(),
 75                env: Some(command.env.into_iter().collect()),
 76            })
 77        }
 78        .boxed_local()
 79    }
 80
 81    async fn fetch_latest_server_version(
 82        &self,
 83        _: &dyn LspAdapterDelegate,
 84    ) -> Result<Box<dyn 'static + Send + Any>> {
 85        unreachable!("get_language_server_command is overridden")
 86    }
 87
 88    async fn fetch_server_binary(
 89        &self,
 90        _: Box<dyn 'static + Send + Any>,
 91        _: PathBuf,
 92        _: &dyn LspAdapterDelegate,
 93    ) -> Result<LanguageServerBinary> {
 94        unreachable!("get_language_server_command is overridden")
 95    }
 96
 97    async fn cached_server_binary(
 98        &self,
 99        _: PathBuf,
100        _: &dyn LspAdapterDelegate,
101    ) -> Option<LanguageServerBinary> {
102        unreachable!("get_language_server_command is overridden")
103    }
104
105    async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
106        None
107    }
108
109    async fn initialization_options(
110        self: Arc<Self>,
111        delegate: &Arc<dyn LspAdapterDelegate>,
112    ) -> Result<Option<serde_json::Value>> {
113        let delegate = delegate.clone();
114        let json_options = self
115            .extension
116            .call({
117                let this = self.clone();
118                |extension, store| {
119                    async move {
120                        let resource = store.data_mut().table().push(delegate)?;
121                        let options = extension
122                            .call_language_server_initialization_options(
123                                store,
124                                &this.config,
125                                resource,
126                            )
127                            .await?
128                            .map_err(|e| anyhow!("{}", e))?;
129                        anyhow::Ok(options)
130                    }
131                    .boxed()
132                }
133            })
134            .await?;
135        Ok(if let Some(json_options) = json_options {
136            serde_json::from_str(&json_options).with_context(|| {
137                format!("failed to parse initialization_options from extension: {json_options}")
138            })?
139        } else {
140            None
141        })
142    }
143}