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