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