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}