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::{CodeActionKind, 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 code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
133 let code_action_kinds = self
134 .extension
135 .manifest
136 .language_servers
137 .get(&self.language_server_id)
138 .and_then(|server| server.code_action_kinds.clone());
139
140 code_action_kinds.or(Some(vec![
141 CodeActionKind::EMPTY,
142 CodeActionKind::QUICKFIX,
143 CodeActionKind::REFACTOR,
144 CodeActionKind::REFACTOR_EXTRACT,
145 CodeActionKind::SOURCE,
146 ]))
147 }
148
149 fn language_ids(&self) -> HashMap<String, String> {
150 // TODO: The language IDs can be provided via the language server options
151 // in `extension.toml now but we're leaving these existing usages in place temporarily
152 // to avoid any compatibility issues between Zed and the extension versions.
153 //
154 // We can remove once the following extension versions no longer see any use:
155 // - php@0.0.1
156 if self.extension.manifest.id.as_ref() == "php" {
157 return HashMap::from_iter([("PHP".into(), "php".into())]);
158 }
159
160 self.extension
161 .manifest
162 .language_servers
163 .get(&LanguageServerName(self.config.name.clone().into()))
164 .map(|server| server.language_ids.clone())
165 .unwrap_or_default()
166 }
167
168 async fn initialization_options(
169 self: Arc<Self>,
170 delegate: &Arc<dyn LspAdapterDelegate>,
171 ) -> Result<Option<serde_json::Value>> {
172 let delegate = delegate.clone();
173 let json_options = self
174 .extension
175 .call({
176 let this = self.clone();
177 |extension, store| {
178 async move {
179 let resource = store.data_mut().table().push(delegate)?;
180 let options = extension
181 .call_language_server_initialization_options(
182 store,
183 &this.language_server_id,
184 &this.config,
185 resource,
186 )
187 .await?
188 .map_err(|e| anyhow!("{}", e))?;
189 anyhow::Ok(options)
190 }
191 .boxed()
192 }
193 })
194 .await?;
195 Ok(if let Some(json_options) = json_options {
196 serde_json::from_str(&json_options).with_context(|| {
197 format!("failed to parse initialization_options from extension: {json_options}")
198 })?
199 } else {
200 None
201 })
202 }
203
204 async fn workspace_configuration(
205 self: Arc<Self>,
206 delegate: &Arc<dyn LspAdapterDelegate>,
207 _cx: &mut AsyncAppContext,
208 ) -> Result<Value> {
209 let delegate = delegate.clone();
210 let json_options: Option<String> = self
211 .extension
212 .call({
213 let this = self.clone();
214 |extension, store| {
215 async move {
216 let resource = store.data_mut().table().push(delegate)?;
217 let options = extension
218 .call_language_server_workspace_configuration(
219 store,
220 &this.language_server_id,
221 resource,
222 )
223 .await?
224 .map_err(|e| anyhow!("{}", e))?;
225 anyhow::Ok(options)
226 }
227 .boxed()
228 }
229 })
230 .await?;
231 Ok(if let Some(json_options) = json_options {
232 serde_json::from_str(&json_options).with_context(|| {
233 format!("failed to parse initialization_options from extension: {json_options}")
234 })?
235 } else {
236 serde_json::json!({})
237 })
238 }
239
240 async fn labels_for_completions(
241 self: Arc<Self>,
242 completions: &[lsp::CompletionItem],
243 language: &Arc<Language>,
244 ) -> Result<Vec<Option<CodeLabel>>> {
245 let completions = completions
246 .into_iter()
247 .map(|completion| wit::Completion::from(completion.clone()))
248 .collect::<Vec<_>>();
249
250 let labels = self
251 .extension
252 .call({
253 let this = self.clone();
254 |extension, store| {
255 async move {
256 extension
257 .call_labels_for_completions(
258 store,
259 &this.language_server_id,
260 completions,
261 )
262 .await?
263 .map_err(|e| anyhow!("{}", e))
264 }
265 .boxed()
266 }
267 })
268 .await?;
269
270 Ok(labels_from_wit(labels, language))
271 }
272
273 async fn labels_for_symbols(
274 self: Arc<Self>,
275 symbols: &[(String, lsp::SymbolKind)],
276 language: &Arc<Language>,
277 ) -> Result<Vec<Option<CodeLabel>>> {
278 let symbols = symbols
279 .into_iter()
280 .cloned()
281 .map(|(name, kind)| wit::Symbol {
282 name,
283 kind: kind.into(),
284 })
285 .collect::<Vec<_>>();
286
287 let labels = self
288 .extension
289 .call({
290 let this = self.clone();
291 |extension, store| {
292 async move {
293 extension
294 .call_labels_for_symbols(store, &this.language_server_id, symbols)
295 .await?
296 .map_err(|e| anyhow!("{}", e))
297 }
298 .boxed()
299 }
300 })
301 .await?;
302
303 Ok(labels_from_wit(labels, language))
304 }
305}
306
307fn labels_from_wit(
308 labels: Vec<Option<wit::CodeLabel>>,
309 language: &Arc<Language>,
310) -> Vec<Option<CodeLabel>> {
311 labels
312 .into_iter()
313 .map(|label| {
314 let label = label?;
315 let runs = if label.code.is_empty() {
316 Vec::new()
317 } else {
318 language.highlight_text(&label.code.as_str().into(), 0..label.code.len())
319 };
320 build_code_label(&label, &runs, &language)
321 })
322 .collect()
323}
324
325fn build_code_label(
326 label: &wit::CodeLabel,
327 parsed_runs: &[(Range<usize>, HighlightId)],
328 language: &Arc<Language>,
329) -> Option<CodeLabel> {
330 let mut text = String::new();
331 let mut runs = vec![];
332
333 for span in &label.spans {
334 match span {
335 wit::CodeLabelSpan::CodeRange(range) => {
336 let range = Range::from(*range);
337 let code_span = &label.code.get(range.clone())?;
338 let mut input_ix = range.start;
339 let mut output_ix = text.len();
340 for (run_range, id) in parsed_runs {
341 if run_range.start >= range.end {
342 break;
343 }
344 if run_range.end <= input_ix {
345 continue;
346 }
347
348 if run_range.start > input_ix {
349 let len = run_range.start - input_ix;
350 output_ix += len;
351 input_ix += len;
352 }
353
354 let len = range.end.min(run_range.end) - input_ix;
355 runs.push((output_ix..output_ix + len, *id));
356 output_ix += len;
357 input_ix += len;
358 }
359
360 text.push_str(code_span);
361 }
362 wit::CodeLabelSpan::Literal(span) => {
363 let highlight_id = language
364 .grammar()
365 .zip(span.highlight_name.as_ref())
366 .and_then(|(grammar, highlight_name)| {
367 grammar.highlight_id_for_name(&highlight_name)
368 })
369 .unwrap_or_default();
370 let ix = text.len();
371 runs.push((ix..ix + span.text.len(), highlight_id));
372 text.push_str(&span.text);
373 }
374 }
375 }
376
377 let filter_range = Range::from(label.filter_range);
378 text.get(filter_range.clone())?;
379 Some(CodeLabel {
380 text,
381 runs,
382 filter_range,
383 })
384}
385
386impl From<wit::Range> for Range<usize> {
387 fn from(range: wit::Range) -> Self {
388 let start = range.start as usize;
389 let end = range.end as usize;
390 start..end
391 }
392}
393
394impl From<lsp::CompletionItem> for wit::Completion {
395 fn from(value: lsp::CompletionItem) -> Self {
396 Self {
397 label: value.label,
398 detail: value.detail,
399 kind: value.kind.map(Into::into),
400 insert_text_format: value.insert_text_format.map(Into::into),
401 }
402 }
403}
404
405impl From<lsp::CompletionItemKind> for wit::CompletionKind {
406 fn from(value: lsp::CompletionItemKind) -> Self {
407 match value {
408 lsp::CompletionItemKind::TEXT => Self::Text,
409 lsp::CompletionItemKind::METHOD => Self::Method,
410 lsp::CompletionItemKind::FUNCTION => Self::Function,
411 lsp::CompletionItemKind::CONSTRUCTOR => Self::Constructor,
412 lsp::CompletionItemKind::FIELD => Self::Field,
413 lsp::CompletionItemKind::VARIABLE => Self::Variable,
414 lsp::CompletionItemKind::CLASS => Self::Class,
415 lsp::CompletionItemKind::INTERFACE => Self::Interface,
416 lsp::CompletionItemKind::MODULE => Self::Module,
417 lsp::CompletionItemKind::PROPERTY => Self::Property,
418 lsp::CompletionItemKind::UNIT => Self::Unit,
419 lsp::CompletionItemKind::VALUE => Self::Value,
420 lsp::CompletionItemKind::ENUM => Self::Enum,
421 lsp::CompletionItemKind::KEYWORD => Self::Keyword,
422 lsp::CompletionItemKind::SNIPPET => Self::Snippet,
423 lsp::CompletionItemKind::COLOR => Self::Color,
424 lsp::CompletionItemKind::FILE => Self::File,
425 lsp::CompletionItemKind::REFERENCE => Self::Reference,
426 lsp::CompletionItemKind::FOLDER => Self::Folder,
427 lsp::CompletionItemKind::ENUM_MEMBER => Self::EnumMember,
428 lsp::CompletionItemKind::CONSTANT => Self::Constant,
429 lsp::CompletionItemKind::STRUCT => Self::Struct,
430 lsp::CompletionItemKind::EVENT => Self::Event,
431 lsp::CompletionItemKind::OPERATOR => Self::Operator,
432 lsp::CompletionItemKind::TYPE_PARAMETER => Self::TypeParameter,
433 _ => Self::Other(extract_int(value)),
434 }
435 }
436}
437
438impl From<lsp::InsertTextFormat> for wit::InsertTextFormat {
439 fn from(value: lsp::InsertTextFormat) -> Self {
440 match value {
441 lsp::InsertTextFormat::PLAIN_TEXT => Self::PlainText,
442 lsp::InsertTextFormat::SNIPPET => Self::Snippet,
443 _ => Self::Other(extract_int(value)),
444 }
445 }
446}
447
448impl From<lsp::SymbolKind> for wit::SymbolKind {
449 fn from(value: lsp::SymbolKind) -> Self {
450 match value {
451 lsp::SymbolKind::FILE => Self::File,
452 lsp::SymbolKind::MODULE => Self::Module,
453 lsp::SymbolKind::NAMESPACE => Self::Namespace,
454 lsp::SymbolKind::PACKAGE => Self::Package,
455 lsp::SymbolKind::CLASS => Self::Class,
456 lsp::SymbolKind::METHOD => Self::Method,
457 lsp::SymbolKind::PROPERTY => Self::Property,
458 lsp::SymbolKind::FIELD => Self::Field,
459 lsp::SymbolKind::CONSTRUCTOR => Self::Constructor,
460 lsp::SymbolKind::ENUM => Self::Enum,
461 lsp::SymbolKind::INTERFACE => Self::Interface,
462 lsp::SymbolKind::FUNCTION => Self::Function,
463 lsp::SymbolKind::VARIABLE => Self::Variable,
464 lsp::SymbolKind::CONSTANT => Self::Constant,
465 lsp::SymbolKind::STRING => Self::String,
466 lsp::SymbolKind::NUMBER => Self::Number,
467 lsp::SymbolKind::BOOLEAN => Self::Boolean,
468 lsp::SymbolKind::ARRAY => Self::Array,
469 lsp::SymbolKind::OBJECT => Self::Object,
470 lsp::SymbolKind::KEY => Self::Key,
471 lsp::SymbolKind::NULL => Self::Null,
472 lsp::SymbolKind::ENUM_MEMBER => Self::EnumMember,
473 lsp::SymbolKind::STRUCT => Self::Struct,
474 lsp::SymbolKind::EVENT => Self::Event,
475 lsp::SymbolKind::OPERATOR => Self::Operator,
476 lsp::SymbolKind::TYPE_PARAMETER => Self::TypeParameter,
477 _ => Self::Other(extract_int(value)),
478 }
479 }
480}
481
482fn extract_int<T: Serialize>(value: T) -> i32 {
483 maybe!({
484 let kind = serde_json::to_value(&value)?;
485 serde_json::from_value(kind)
486 })
487 .log_err()
488 .unwrap_or(-1)
489}
490
491#[test]
492fn test_build_code_label() {
493 use util::test::marked_text_ranges;
494
495 let (code, code_ranges) = marked_text_ranges(
496 "«const» «a»: «fn»(«Bcd»(«Efgh»)) -> «Ijklm» = pqrs.tuv",
497 false,
498 );
499 let code_runs = code_ranges
500 .into_iter()
501 .map(|range| (range, HighlightId(0)))
502 .collect::<Vec<_>>();
503
504 let label = build_code_label(
505 &wit::CodeLabel {
506 spans: vec![
507 wit::CodeLabelSpan::CodeRange(wit::Range {
508 start: code.find("pqrs").unwrap() as u32,
509 end: code.len() as u32,
510 }),
511 wit::CodeLabelSpan::CodeRange(wit::Range {
512 start: code.find(": fn").unwrap() as u32,
513 end: code.find(" = ").unwrap() as u32,
514 }),
515 ],
516 filter_range: wit::Range {
517 start: 0,
518 end: "pqrs.tuv".len() as u32,
519 },
520 code,
521 },
522 &code_runs,
523 &language::PLAIN_TEXT,
524 )
525 .unwrap();
526
527 let (label_text, label_ranges) =
528 marked_text_ranges("pqrs.tuv: «fn»(«Bcd»(«Efgh»)) -> «Ijklm»", false);
529 let label_runs = label_ranges
530 .into_iter()
531 .map(|range| (range, HighlightId(0)))
532 .collect::<Vec<_>>();
533
534 assert_eq!(
535 label,
536 CodeLabel {
537 text: label_text,
538 runs: label_runs,
539 filter_range: label.filter_range.clone()
540 }
541 )
542}
543
544#[test]
545fn test_build_code_label_with_invalid_ranges() {
546 use util::test::marked_text_ranges;
547
548 let (code, code_ranges) = marked_text_ranges("const «a»: «B» = '🏀'", false);
549 let code_runs = code_ranges
550 .into_iter()
551 .map(|range| (range, HighlightId(0)))
552 .collect::<Vec<_>>();
553
554 // A span uses a code range that is invalid because it starts inside of
555 // a multi-byte character.
556 let label = build_code_label(
557 &wit::CodeLabel {
558 spans: vec![
559 wit::CodeLabelSpan::CodeRange(wit::Range {
560 start: code.find('B').unwrap() as u32,
561 end: code.find(" = ").unwrap() as u32,
562 }),
563 wit::CodeLabelSpan::CodeRange(wit::Range {
564 start: code.find('🏀').unwrap() as u32 + 1,
565 end: code.len() as u32,
566 }),
567 ],
568 filter_range: wit::Range {
569 start: 0,
570 end: "B".len() as u32,
571 },
572 code,
573 },
574 &code_runs,
575 &language::PLAIN_TEXT,
576 );
577 assert!(label.is_none());
578
579 // Filter range extends beyond actual text
580 let label = build_code_label(
581 &wit::CodeLabel {
582 spans: vec![wit::CodeLabelSpan::Literal(wit::CodeLabelSpanLiteral {
583 text: "abc".into(),
584 highlight_name: Some("type".into()),
585 })],
586 filter_range: wit::Range { start: 0, end: 5 },
587 code: String::new(),
588 },
589 &code_runs,
590 &language::PLAIN_TEXT,
591 );
592 assert!(label.is_none());
593}