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, 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}