extension_lsp_adapter.rs

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