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