extension_lsp_adapter.rs

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