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