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