1use crate::wasm_host::{
2 wit::{self, LanguageServerConfig},
3 WasmExtension, WasmHost,
4};
5use anyhow::{anyhow, Context, Result};
6use async_trait::async_trait;
7use collections::HashMap;
8use futures::{Future, FutureExt};
9use gpui::AsyncAppContext;
10use language::{
11 CodeLabel, HighlightId, Language, LanguageServerName, LspAdapter, LspAdapterDelegate,
12};
13use lsp::LanguageServerBinary;
14use serde::Serialize;
15use serde_json::Value;
16use std::ops::Range;
17use std::{
18 any::Any,
19 path::{Path, PathBuf},
20 pin::Pin,
21 sync::Arc,
22};
23use util::{maybe, ResultExt};
24use wasmtime_wasi::WasiView as _;
25
26pub struct ExtensionLspAdapter {
27 pub(crate) extension: WasmExtension,
28 pub(crate) language_server_id: LanguageServerName,
29 pub(crate) config: LanguageServerConfig,
30 pub(crate) host: Arc<WasmHost>,
31}
32
33#[async_trait(?Send)]
34impl LspAdapter for ExtensionLspAdapter {
35 fn name(&self) -> LanguageServerName {
36 LanguageServerName(self.config.name.clone().into())
37 }
38
39 fn get_language_server_command<'a>(
40 self: Arc<Self>,
41 _: Arc<Language>,
42 _: Arc<Path>,
43 delegate: Arc<dyn LspAdapterDelegate>,
44 _: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
45 _: &'a mut AsyncAppContext,
46 ) -> Pin<Box<dyn 'a + Future<Output = Result<LanguageServerBinary>>>> {
47 async move {
48 let command = self
49 .extension
50 .call({
51 let this = self.clone();
52 |extension, store| {
53 async move {
54 let resource = store.data_mut().table().push(delegate)?;
55 let command = extension
56 .call_language_server_command(
57 store,
58 &this.language_server_id,
59 &this.config,
60 resource,
61 )
62 .await?
63 .map_err(|e| anyhow!("{}", e))?;
64 anyhow::Ok(command)
65 }
66 .boxed()
67 }
68 })
69 .await?;
70
71 let path = self
72 .host
73 .path_from_extension(&self.extension.manifest.id, command.command.as_ref());
74
75 // TODO: This should now be done via the `zed::make_file_executable` function in
76 // Zed extension API, but we're leaving these existing usages in place temporarily
77 // to avoid any compatibility issues between Zed and the extension versions.
78 //
79 // We can remove once the following extension versions no longer see any use:
80 // - toml@0.0.2
81 // - zig@0.0.1
82 if ["toml", "zig"].contains(&self.extension.manifest.id.as_ref())
83 && path.starts_with(&self.host.work_dir)
84 {
85 #[cfg(not(windows))]
86 {
87 use std::fs::{self, Permissions};
88 use std::os::unix::fs::PermissionsExt;
89
90 fs::set_permissions(&path, Permissions::from_mode(0o755))
91 .context("failed to set file permissions")?;
92 }
93 }
94
95 Ok(LanguageServerBinary {
96 path,
97 arguments: command.args.into_iter().map(|arg| arg.into()).collect(),
98 env: Some(command.env.into_iter().collect()),
99 })
100 }
101 .boxed_local()
102 }
103
104 async fn fetch_latest_server_version(
105 &self,
106 _: &dyn LspAdapterDelegate,
107 ) -> Result<Box<dyn 'static + Send + Any>> {
108 unreachable!("get_language_server_command is overridden")
109 }
110
111 async fn fetch_server_binary(
112 &self,
113 _: Box<dyn 'static + Send + Any>,
114 _: PathBuf,
115 _: &dyn LspAdapterDelegate,
116 ) -> Result<LanguageServerBinary> {
117 unreachable!("get_language_server_command is overridden")
118 }
119
120 async fn cached_server_binary(
121 &self,
122 _: PathBuf,
123 _: &dyn LspAdapterDelegate,
124 ) -> Option<LanguageServerBinary> {
125 unreachable!("get_language_server_command is overridden")
126 }
127
128 async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
129 None
130 }
131
132 fn language_ids(&self) -> HashMap<String, String> {
133 // TODO: The language IDs can be provided via the language server options
134 // in `extension.toml now but we're leaving these existing usages in place temporarily
135 // to avoid any compatibility issues between Zed and the extension versions.
136 //
137 // We can remove once the following extension versions no longer see any use:
138 // - php@0.0.1
139 if self.extension.manifest.id.as_ref() == "php" {
140 return HashMap::from_iter([("PHP".into(), "php".into())]);
141 }
142
143 self.extension
144 .manifest
145 .language_servers
146 .get(&LanguageServerName(self.config.name.clone().into()))
147 .map(|server| server.language_ids.clone())
148 .unwrap_or_default()
149 }
150
151 async fn initialization_options(
152 self: Arc<Self>,
153 delegate: &Arc<dyn LspAdapterDelegate>,
154 ) -> Result<Option<serde_json::Value>> {
155 let delegate = delegate.clone();
156 let json_options = self
157 .extension
158 .call({
159 let this = self.clone();
160 |extension, store| {
161 async move {
162 let resource = store.data_mut().table().push(delegate)?;
163 let options = extension
164 .call_language_server_initialization_options(
165 store,
166 &this.language_server_id,
167 &this.config,
168 resource,
169 )
170 .await?
171 .map_err(|e| anyhow!("{}", e))?;
172 anyhow::Ok(options)
173 }
174 .boxed()
175 }
176 })
177 .await?;
178 Ok(if let Some(json_options) = json_options {
179 serde_json::from_str(&json_options).with_context(|| {
180 format!("failed to parse initialization_options from extension: {json_options}")
181 })?
182 } else {
183 None
184 })
185 }
186
187 async fn workspace_configuration(
188 self: Arc<Self>,
189 delegate: &Arc<dyn LspAdapterDelegate>,
190 _cx: &mut AsyncAppContext,
191 ) -> Result<Value> {
192 let delegate = delegate.clone();
193 let json_options: Option<String> = self
194 .extension
195 .call({
196 let this = self.clone();
197 |extension, store| {
198 async move {
199 let resource = store.data_mut().table().push(delegate)?;
200 let options = extension
201 .call_language_server_workspace_configuration(
202 store,
203 &this.language_server_id,
204 resource,
205 )
206 .await?
207 .map_err(|e| anyhow!("{}", e))?;
208 anyhow::Ok(options)
209 }
210 .boxed()
211 }
212 })
213 .await?;
214 Ok(if let Some(json_options) = json_options {
215 serde_json::from_str(&json_options).with_context(|| {
216 format!("failed to parse initialization_options from extension: {json_options}")
217 })?
218 } else {
219 serde_json::json!({})
220 })
221 }
222
223 async fn labels_for_completions(
224 self: Arc<Self>,
225 completions: &[lsp::CompletionItem],
226 language: &Arc<Language>,
227 ) -> Result<Vec<Option<CodeLabel>>> {
228 let completions = completions
229 .into_iter()
230 .map(|completion| wit::Completion::from(completion.clone()))
231 .collect::<Vec<_>>();
232
233 let labels = self
234 .extension
235 .call({
236 let this = self.clone();
237 |extension, store| {
238 async move {
239 extension
240 .call_labels_for_completions(
241 store,
242 &this.language_server_id,
243 completions,
244 )
245 .await?
246 .map_err(|e| anyhow!("{}", e))
247 }
248 .boxed()
249 }
250 })
251 .await?;
252
253 Ok(labels_from_wit(labels, language))
254 }
255
256 async fn labels_for_symbols(
257 self: Arc<Self>,
258 symbols: &[(String, lsp::SymbolKind)],
259 language: &Arc<Language>,
260 ) -> Result<Vec<Option<CodeLabel>>> {
261 let symbols = symbols
262 .into_iter()
263 .cloned()
264 .map(|(name, kind)| wit::Symbol {
265 name,
266 kind: kind.into(),
267 })
268 .collect::<Vec<_>>();
269
270 let labels = self
271 .extension
272 .call({
273 let this = self.clone();
274 |extension, store| {
275 async move {
276 extension
277 .call_labels_for_symbols(store, &this.language_server_id, symbols)
278 .await?
279 .map_err(|e| anyhow!("{}", e))
280 }
281 .boxed()
282 }
283 })
284 .await?;
285
286 Ok(labels_from_wit(labels, language))
287 }
288}
289
290fn labels_from_wit(
291 labels: Vec<Option<wit::CodeLabel>>,
292 language: &Arc<Language>,
293) -> Vec<Option<CodeLabel>> {
294 labels
295 .into_iter()
296 .map(|label| {
297 let label = label?;
298 let runs = if !label.code.is_empty() {
299 language.highlight_text(&label.code.as_str().into(), 0..label.code.len())
300 } else {
301 Vec::new()
302 };
303 build_code_label(&label, &runs, &language)
304 })
305 .collect()
306}
307
308fn build_code_label(
309 label: &wit::CodeLabel,
310 parsed_runs: &[(Range<usize>, HighlightId)],
311 language: &Arc<Language>,
312) -> Option<CodeLabel> {
313 let mut text = String::new();
314 let mut runs = vec![];
315
316 for span in &label.spans {
317 match span {
318 wit::CodeLabelSpan::CodeRange(range) => {
319 let range = Range::from(*range);
320 let code_span = &label.code.get(range.clone())?;
321 let mut input_ix = range.start;
322 let mut output_ix = text.len();
323 for (run_range, id) in parsed_runs {
324 if run_range.start >= range.end {
325 break;
326 }
327 if run_range.end <= input_ix {
328 continue;
329 }
330
331 if run_range.start > input_ix {
332 let len = run_range.start - input_ix;
333 output_ix += len;
334 input_ix += len;
335 }
336
337 let len = range.end.min(run_range.end) - input_ix;
338 runs.push((output_ix..output_ix + len, *id));
339 output_ix += len;
340 input_ix += len;
341 }
342
343 text.push_str(code_span);
344 }
345 wit::CodeLabelSpan::Literal(span) => {
346 let highlight_id = language
347 .grammar()
348 .zip(span.highlight_name.as_ref())
349 .and_then(|(grammar, highlight_name)| {
350 grammar.highlight_id_for_name(&highlight_name)
351 })
352 .unwrap_or_default();
353 let ix = text.len();
354 runs.push((ix..ix + span.text.len(), highlight_id));
355 text.push_str(&span.text);
356 }
357 }
358 }
359
360 let filter_range = Range::from(label.filter_range);
361 text.get(filter_range.clone())?;
362 Some(CodeLabel {
363 text,
364 runs,
365 filter_range,
366 })
367}
368
369impl From<wit::Range> for Range<usize> {
370 fn from(range: wit::Range) -> Self {
371 let start = range.start as usize;
372 let end = range.end as usize;
373 start..end
374 }
375}
376
377impl From<lsp::CompletionItem> for wit::Completion {
378 fn from(value: lsp::CompletionItem) -> Self {
379 Self {
380 label: value.label,
381 detail: value.detail,
382 kind: value.kind.map(Into::into),
383 insert_text_format: value.insert_text_format.map(Into::into),
384 }
385 }
386}
387
388impl From<lsp::CompletionItemKind> for wit::CompletionKind {
389 fn from(value: lsp::CompletionItemKind) -> Self {
390 match value {
391 lsp::CompletionItemKind::TEXT => Self::Text,
392 lsp::CompletionItemKind::METHOD => Self::Method,
393 lsp::CompletionItemKind::FUNCTION => Self::Function,
394 lsp::CompletionItemKind::CONSTRUCTOR => Self::Constructor,
395 lsp::CompletionItemKind::FIELD => Self::Field,
396 lsp::CompletionItemKind::VARIABLE => Self::Variable,
397 lsp::CompletionItemKind::CLASS => Self::Class,
398 lsp::CompletionItemKind::INTERFACE => Self::Interface,
399 lsp::CompletionItemKind::MODULE => Self::Module,
400 lsp::CompletionItemKind::PROPERTY => Self::Property,
401 lsp::CompletionItemKind::UNIT => Self::Unit,
402 lsp::CompletionItemKind::VALUE => Self::Value,
403 lsp::CompletionItemKind::ENUM => Self::Enum,
404 lsp::CompletionItemKind::KEYWORD => Self::Keyword,
405 lsp::CompletionItemKind::SNIPPET => Self::Snippet,
406 lsp::CompletionItemKind::COLOR => Self::Color,
407 lsp::CompletionItemKind::FILE => Self::File,
408 lsp::CompletionItemKind::REFERENCE => Self::Reference,
409 lsp::CompletionItemKind::FOLDER => Self::Folder,
410 lsp::CompletionItemKind::ENUM_MEMBER => Self::EnumMember,
411 lsp::CompletionItemKind::CONSTANT => Self::Constant,
412 lsp::CompletionItemKind::STRUCT => Self::Struct,
413 lsp::CompletionItemKind::EVENT => Self::Event,
414 lsp::CompletionItemKind::OPERATOR => Self::Operator,
415 lsp::CompletionItemKind::TYPE_PARAMETER => Self::TypeParameter,
416 _ => Self::Other(extract_int(value)),
417 }
418 }
419}
420
421impl From<lsp::InsertTextFormat> for wit::InsertTextFormat {
422 fn from(value: lsp::InsertTextFormat) -> Self {
423 match value {
424 lsp::InsertTextFormat::PLAIN_TEXT => Self::PlainText,
425 lsp::InsertTextFormat::SNIPPET => Self::Snippet,
426 _ => Self::Other(extract_int(value)),
427 }
428 }
429}
430
431impl From<lsp::SymbolKind> for wit::SymbolKind {
432 fn from(value: lsp::SymbolKind) -> Self {
433 match value {
434 lsp::SymbolKind::FILE => Self::File,
435 lsp::SymbolKind::MODULE => Self::Module,
436 lsp::SymbolKind::NAMESPACE => Self::Namespace,
437 lsp::SymbolKind::PACKAGE => Self::Package,
438 lsp::SymbolKind::CLASS => Self::Class,
439 lsp::SymbolKind::METHOD => Self::Method,
440 lsp::SymbolKind::PROPERTY => Self::Property,
441 lsp::SymbolKind::FIELD => Self::Field,
442 lsp::SymbolKind::CONSTRUCTOR => Self::Constructor,
443 lsp::SymbolKind::ENUM => Self::Enum,
444 lsp::SymbolKind::INTERFACE => Self::Interface,
445 lsp::SymbolKind::FUNCTION => Self::Function,
446 lsp::SymbolKind::VARIABLE => Self::Variable,
447 lsp::SymbolKind::CONSTANT => Self::Constant,
448 lsp::SymbolKind::STRING => Self::String,
449 lsp::SymbolKind::NUMBER => Self::Number,
450 lsp::SymbolKind::BOOLEAN => Self::Boolean,
451 lsp::SymbolKind::ARRAY => Self::Array,
452 lsp::SymbolKind::OBJECT => Self::Object,
453 lsp::SymbolKind::KEY => Self::Key,
454 lsp::SymbolKind::NULL => Self::Null,
455 lsp::SymbolKind::ENUM_MEMBER => Self::EnumMember,
456 lsp::SymbolKind::STRUCT => Self::Struct,
457 lsp::SymbolKind::EVENT => Self::Event,
458 lsp::SymbolKind::OPERATOR => Self::Operator,
459 lsp::SymbolKind::TYPE_PARAMETER => Self::TypeParameter,
460 _ => Self::Other(extract_int(value)),
461 }
462 }
463}
464
465fn extract_int<T: Serialize>(value: T) -> i32 {
466 maybe!({
467 let kind = serde_json::to_value(&value)?;
468 serde_json::from_value(kind)
469 })
470 .log_err()
471 .unwrap_or(-1)
472}
473
474#[test]
475fn test_build_code_label() {
476 use util::test::marked_text_ranges;
477
478 let (code, code_ranges) = marked_text_ranges(
479 "«const» «a»: «fn»(«Bcd»(«Efgh»)) -> «Ijklm» = pqrs.tuv",
480 false,
481 );
482 let code_runs = code_ranges
483 .into_iter()
484 .map(|range| (range, HighlightId(0)))
485 .collect::<Vec<_>>();
486
487 let label = build_code_label(
488 &wit::CodeLabel {
489 spans: vec![
490 wit::CodeLabelSpan::CodeRange(wit::Range {
491 start: code.find("pqrs").unwrap() as u32,
492 end: code.len() as u32,
493 }),
494 wit::CodeLabelSpan::CodeRange(wit::Range {
495 start: code.find(": fn").unwrap() as u32,
496 end: code.find(" = ").unwrap() as u32,
497 }),
498 ],
499 filter_range: wit::Range {
500 start: 0,
501 end: "pqrs.tuv".len() as u32,
502 },
503 code,
504 },
505 &code_runs,
506 &language::PLAIN_TEXT,
507 )
508 .unwrap();
509
510 let (label_text, label_ranges) =
511 marked_text_ranges("pqrs.tuv: «fn»(«Bcd»(«Efgh»)) -> «Ijklm»", false);
512 let label_runs = label_ranges
513 .into_iter()
514 .map(|range| (range, HighlightId(0)))
515 .collect::<Vec<_>>();
516
517 assert_eq!(
518 label,
519 CodeLabel {
520 text: label_text,
521 runs: label_runs,
522 filter_range: label.filter_range.clone()
523 }
524 )
525}
526
527#[test]
528fn test_build_code_label_with_invalid_ranges() {
529 use util::test::marked_text_ranges;
530
531 let (code, code_ranges) = marked_text_ranges("const «a»: «B» = '🏀'", false);
532 let code_runs = code_ranges
533 .into_iter()
534 .map(|range| (range, HighlightId(0)))
535 .collect::<Vec<_>>();
536
537 // A span uses a code range that is invalid because it starts inside of
538 // a multi-byte character.
539 let label = build_code_label(
540 &wit::CodeLabel {
541 spans: vec![
542 wit::CodeLabelSpan::CodeRange(wit::Range {
543 start: code.find('B').unwrap() as u32,
544 end: code.find(" = ").unwrap() as u32,
545 }),
546 wit::CodeLabelSpan::CodeRange(wit::Range {
547 start: code.find('🏀').unwrap() as u32 + 1,
548 end: code.len() as u32,
549 }),
550 ],
551 filter_range: wit::Range {
552 start: 0,
553 end: "B".len() as u32,
554 },
555 code,
556 },
557 &code_runs,
558 &language::PLAIN_TEXT,
559 );
560 assert!(label.is_none());
561
562 // Filter range extends beyond actual text
563 let label = build_code_label(
564 &wit::CodeLabel {
565 spans: vec![wit::CodeLabelSpan::Literal(wit::CodeLabelSpanLiteral {
566 text: "abc".into(),
567 highlight_name: Some("type".into()),
568 })],
569 filter_range: wit::Range { start: 0, end: 5 },
570 code: String::new(),
571 },
572 &code_runs,
573 &language::PLAIN_TEXT,
574 );
575 assert!(label.is_none());
576}