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