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