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