1pub mod signature_help;
2
3use crate::{
4 CodeAction, CompletionSource, CoreCompletion, CoreCompletionResponse, DocumentColor,
5 DocumentHighlight, DocumentSymbol, Hover, HoverBlock, HoverBlockKind, InlayHint,
6 InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location,
7 LocationLink, LspAction, LspPullDiagnostics, MarkupContent, PrepareRenameResponse,
8 ProjectTransaction, PulledDiagnostics, ResolveState,
9 lsp_store::{LocalLspStore, LspFoldingRange, LspStore},
10 project_settings::ProjectSettings,
11};
12use anyhow::{Context as _, Result};
13use async_trait::async_trait;
14use client::proto::{self, PeerId};
15use clock::Global;
16use collections::HashMap;
17use futures::future;
18use gpui::{App, AsyncApp, Entity, SharedString, Task, prelude::FluentBuilder};
19use language::{
20 Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind, CharScopeContext,
21 OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped,
22 language_settings::{InlayHintKind, LanguageSettings},
23 point_from_lsp, point_to_lsp,
24 proto::{
25 deserialize_anchor, deserialize_anchor_range, deserialize_version, serialize_anchor,
26 serialize_anchor_range, serialize_version,
27 },
28 range_from_lsp, range_to_lsp,
29};
30use lsp::{
31 AdapterServerCapabilities, CodeActionKind, CodeActionOptions, CodeDescription,
32 CompletionContext, CompletionListItemDefaultsEditRange, CompletionTriggerKind,
33 DocumentHighlightKind, LanguageServer, LanguageServerId, LinkedEditingRangeServerCapabilities,
34 OneOf, RenameOptions, ServerCapabilities,
35};
36use serde_json::Value;
37use settings::Settings as _;
38use signature_help::{lsp_to_proto_signature, proto_to_lsp_signature};
39use std::{
40 cmp::Reverse, collections::hash_map, mem, ops::Range, path::Path, str::FromStr, sync::Arc,
41};
42use text::{BufferId, LineEnding};
43use util::{ResultExt as _, debug_panic};
44
45pub use signature_help::SignatureHelp;
46
47fn code_action_kind_matches(requested: &lsp::CodeActionKind, actual: &lsp::CodeActionKind) -> bool {
48 let requested_str = requested.as_str();
49 let actual_str = actual.as_str();
50
51 // Exact match or hierarchical match
52 actual_str == requested_str
53 || actual_str
54 .strip_prefix(requested_str)
55 .is_some_and(|suffix| suffix.starts_with('.'))
56}
57
58pub fn lsp_formatting_options(settings: &LanguageSettings) -> lsp::FormattingOptions {
59 lsp::FormattingOptions {
60 tab_size: settings.tab_size.into(),
61 insert_spaces: !settings.hard_tabs,
62 trim_trailing_whitespace: Some(settings.remove_trailing_whitespace_on_save),
63 trim_final_newlines: Some(settings.ensure_final_newline_on_save),
64 insert_final_newline: Some(settings.ensure_final_newline_on_save),
65 ..lsp::FormattingOptions::default()
66 }
67}
68
69pub fn file_path_to_lsp_url(path: &Path) -> Result<lsp::Uri> {
70 match lsp::Uri::from_file_path(path) {
71 Ok(url) => Ok(url),
72 Err(()) => anyhow::bail!("Invalid file path provided to LSP request: {path:?}"),
73 }
74}
75
76pub(crate) fn make_text_document_identifier(path: &Path) -> Result<lsp::TextDocumentIdentifier> {
77 Ok(lsp::TextDocumentIdentifier {
78 uri: file_path_to_lsp_url(path)?,
79 })
80}
81
82pub(crate) fn make_lsp_text_document_position(
83 path: &Path,
84 position: PointUtf16,
85) -> Result<lsp::TextDocumentPositionParams> {
86 Ok(lsp::TextDocumentPositionParams {
87 text_document: make_text_document_identifier(path)?,
88 position: point_to_lsp(position),
89 })
90}
91
92#[async_trait(?Send)]
93pub trait LspCommand: 'static + Sized + Send + std::fmt::Debug {
94 type Response: 'static + Default + Send + std::fmt::Debug;
95 type LspRequest: 'static + Send + lsp::request::Request;
96 type ProtoRequest: 'static + Send + proto::RequestMessage;
97
98 fn display_name(&self) -> &str;
99
100 fn status(&self) -> Option<String> {
101 None
102 }
103
104 fn to_lsp_params_or_response(
105 &self,
106 path: &Path,
107 buffer: &Buffer,
108 language_server: &Arc<LanguageServer>,
109 cx: &App,
110 ) -> Result<
111 LspParamsOrResponse<<Self::LspRequest as lsp::request::Request>::Params, Self::Response>,
112 > {
113 if self.check_capabilities(language_server.adapter_server_capabilities()) {
114 Ok(LspParamsOrResponse::Params(self.to_lsp(
115 path,
116 buffer,
117 language_server,
118 cx,
119 )?))
120 } else {
121 Ok(LspParamsOrResponse::Response(Default::default()))
122 }
123 }
124
125 /// When false, `to_lsp_params_or_response` default implementation will return the default response.
126 fn check_capabilities(&self, _: AdapterServerCapabilities) -> bool;
127
128 fn to_lsp(
129 &self,
130 path: &Path,
131 buffer: &Buffer,
132 language_server: &Arc<LanguageServer>,
133 cx: &App,
134 ) -> Result<<Self::LspRequest as lsp::request::Request>::Params>;
135
136 async fn response_from_lsp(
137 self,
138 message: <Self::LspRequest as lsp::request::Request>::Result,
139 lsp_store: Entity<LspStore>,
140 buffer: Entity<Buffer>,
141 server_id: LanguageServerId,
142 cx: AsyncApp,
143 ) -> Result<Self::Response>;
144
145 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
146
147 async fn from_proto(
148 message: Self::ProtoRequest,
149 lsp_store: Entity<LspStore>,
150 buffer: Entity<Buffer>,
151 cx: AsyncApp,
152 ) -> Result<Self>;
153
154 fn response_to_proto(
155 response: Self::Response,
156 lsp_store: &mut LspStore,
157 peer_id: PeerId,
158 buffer_version: &clock::Global,
159 cx: &mut App,
160 ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
161
162 async fn response_from_proto(
163 self,
164 message: <Self::ProtoRequest as proto::RequestMessage>::Response,
165 lsp_store: Entity<LspStore>,
166 buffer: Entity<Buffer>,
167 cx: AsyncApp,
168 ) -> Result<Self::Response>;
169
170 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId>;
171}
172
173pub enum LspParamsOrResponse<P, R> {
174 Params(P),
175 Response(R),
176}
177
178#[derive(Debug)]
179pub(crate) struct PrepareRename {
180 pub position: PointUtf16,
181}
182
183#[derive(Debug)]
184pub(crate) struct PerformRename {
185 pub position: PointUtf16,
186 pub new_name: String,
187 pub push_to_history: bool,
188}
189
190#[derive(Debug, Clone, Copy)]
191pub struct GetDefinitions {
192 pub position: PointUtf16,
193}
194
195#[derive(Debug, Clone, Copy)]
196pub(crate) struct GetDeclarations {
197 pub position: PointUtf16,
198}
199
200#[derive(Debug, Clone, Copy)]
201pub(crate) struct GetTypeDefinitions {
202 pub position: PointUtf16,
203}
204
205#[derive(Debug, Clone, Copy)]
206pub(crate) struct GetImplementations {
207 pub position: PointUtf16,
208}
209
210#[derive(Debug, Clone, Copy)]
211pub(crate) struct GetReferences {
212 pub position: PointUtf16,
213}
214
215#[derive(Debug)]
216pub(crate) struct GetDocumentHighlights {
217 pub position: PointUtf16,
218}
219
220#[derive(Debug, Copy, Clone)]
221pub(crate) struct GetDocumentSymbols;
222
223#[derive(Clone, Debug)]
224pub(crate) struct GetSignatureHelp {
225 pub position: PointUtf16,
226}
227
228#[derive(Clone, Debug)]
229pub(crate) struct GetHover {
230 pub position: PointUtf16,
231}
232
233#[derive(Debug)]
234pub(crate) struct GetCompletions {
235 pub position: PointUtf16,
236 pub context: CompletionContext,
237 pub server_id: Option<lsp::LanguageServerId>,
238}
239
240#[derive(Clone, Debug)]
241pub(crate) struct GetCodeActions {
242 pub range: Range<Anchor>,
243 pub kinds: Option<Vec<lsp::CodeActionKind>>,
244}
245
246#[derive(Debug)]
247pub(crate) struct OnTypeFormatting {
248 pub position: PointUtf16,
249 pub trigger: String,
250 pub options: lsp::FormattingOptions,
251 pub push_to_history: bool,
252}
253
254#[derive(Clone, Debug)]
255pub(crate) struct InlayHints {
256 pub range: Range<Anchor>,
257}
258
259#[derive(Debug, Clone, Copy)]
260pub(crate) struct SemanticTokensFull {
261 pub for_server: Option<LanguageServerId>,
262}
263
264#[derive(Debug, Clone)]
265pub(crate) struct SemanticTokensDelta {
266 pub previous_result_id: SharedString,
267}
268
269#[derive(Debug)]
270pub(crate) enum SemanticTokensResponse {
271 Full {
272 data: Vec<u32>,
273 result_id: Option<SharedString>,
274 },
275 Delta {
276 edits: Vec<SemanticTokensEdit>,
277 result_id: Option<SharedString>,
278 },
279}
280
281impl Default for SemanticTokensResponse {
282 fn default() -> Self {
283 Self::Delta {
284 edits: Vec::new(),
285 result_id: None,
286 }
287 }
288}
289
290#[derive(Debug)]
291pub(crate) struct SemanticTokensEdit {
292 pub start: u32,
293 pub delete_count: u32,
294 pub data: Vec<u32>,
295}
296
297#[derive(Debug, Copy, Clone)]
298pub(crate) struct GetCodeLens;
299
300#[derive(Debug, Copy, Clone)]
301pub(crate) struct GetDocumentColor;
302
303#[derive(Debug, Copy, Clone)]
304pub(crate) struct GetFoldingRanges;
305
306impl GetCodeLens {
307 pub(crate) fn can_resolve_lens(capabilities: &ServerCapabilities) -> bool {
308 capabilities
309 .code_lens_provider
310 .as_ref()
311 .and_then(|code_lens_options| code_lens_options.resolve_provider)
312 .unwrap_or(false)
313 }
314
315 /// After resolving code lenses, some may still have `command = None` (e.g. vtsls returns
316 /// no command for "0 implementations"). Synthesize a decorative title for these by
317 /// extracting the type word from resolved lenses that share the same `data.id`.
318 fn fill_unresolved_lens_titles(code_lenses: &mut [lsp::CodeLens]) {
319 let mut type_names_by_id: HashMap<i64, String> = HashMap::default();
320 for lens in code_lenses.iter() {
321 if let (Some(cmd), Some(data)) = (&lens.command, &lens.data) {
322 if let Some(id) = data.get("id").and_then(|v| v.as_i64()) {
323 type_names_by_id.entry(id).or_insert_with(|| {
324 Self::extract_type_word(&cmd.title)
325 .unwrap_or_default()
326 .to_string()
327 });
328 }
329 }
330 }
331
332 for lens in code_lenses.iter_mut() {
333 if lens.command.is_some() {
334 continue;
335 }
336 let Some(data) = &lens.data else {
337 continue;
338 };
339 let Some(id) = data.get("id").and_then(|v| v.as_i64()) else {
340 continue;
341 };
342 let type_name = type_names_by_id
343 .get(&id)
344 .map(|s| s.as_str())
345 .filter(|s| !s.is_empty())
346 .or_else(|| Self::well_known_lens_type(id));
347 let Some(word) = type_name else {
348 continue;
349 };
350 let title = format!("0 {word}");
351 lens.command = Some(lsp::Command {
352 title,
353 command: String::new(),
354 arguments: None,
355 });
356 }
357 }
358
359 /// Well-known code lens type ids used by TypeScript language servers (vtsls/tsserver).
360 fn well_known_lens_type(id: i64) -> Option<&'static str> {
361 match id {
362 1 => Some("references"),
363 2 => Some("implementations"),
364 _ => None,
365 }
366 }
367
368 /// Extract the type word from a title like "3 references" → "references".
369 fn extract_type_word(title: &str) -> Option<&str> {
370 let title = title.trim();
371 let space_idx = title.find(' ')?;
372 let (count_part, rest) = title.split_at(space_idx);
373 if count_part.parse::<u64>().is_ok() {
374 Some(rest.trim())
375 } else {
376 None
377 }
378 }
379}
380
381#[derive(Debug)]
382pub(crate) struct LinkedEditingRange {
383 pub position: Anchor,
384}
385
386#[derive(Clone, Debug)]
387pub struct GetDocumentDiagnostics {
388 /// We cannot blindly rely on server's capabilities.diagnostic_provider, as they're a singular field, whereas
389 /// a server can register multiple diagnostic providers post-mortem.
390 pub registration_id: Option<SharedString>,
391 pub identifier: Option<SharedString>,
392 pub previous_result_id: Option<SharedString>,
393}
394
395#[async_trait(?Send)]
396impl LspCommand for PrepareRename {
397 type Response = PrepareRenameResponse;
398 type LspRequest = lsp::request::PrepareRenameRequest;
399 type ProtoRequest = proto::PrepareRename;
400
401 fn display_name(&self) -> &str {
402 "Prepare rename"
403 }
404
405 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
406 capabilities
407 .server_capabilities
408 .rename_provider
409 .is_some_and(|capability| match capability {
410 OneOf::Left(enabled) => enabled,
411 OneOf::Right(options) => options.prepare_provider.unwrap_or(false),
412 })
413 }
414
415 fn to_lsp_params_or_response(
416 &self,
417 path: &Path,
418 buffer: &Buffer,
419 language_server: &Arc<LanguageServer>,
420 cx: &App,
421 ) -> Result<LspParamsOrResponse<lsp::TextDocumentPositionParams, PrepareRenameResponse>> {
422 let rename_provider = language_server
423 .adapter_server_capabilities()
424 .server_capabilities
425 .rename_provider;
426 match rename_provider {
427 Some(lsp::OneOf::Right(RenameOptions {
428 prepare_provider: Some(true),
429 ..
430 })) => Ok(LspParamsOrResponse::Params(self.to_lsp(
431 path,
432 buffer,
433 language_server,
434 cx,
435 )?)),
436 Some(lsp::OneOf::Right(_)) => Ok(LspParamsOrResponse::Response(
437 PrepareRenameResponse::OnlyUnpreparedRenameSupported,
438 )),
439 Some(lsp::OneOf::Left(true)) => Ok(LspParamsOrResponse::Response(
440 PrepareRenameResponse::OnlyUnpreparedRenameSupported,
441 )),
442 _ => anyhow::bail!("Rename not supported"),
443 }
444 }
445
446 fn to_lsp(
447 &self,
448 path: &Path,
449 _: &Buffer,
450 _: &Arc<LanguageServer>,
451 _: &App,
452 ) -> Result<lsp::TextDocumentPositionParams> {
453 make_lsp_text_document_position(path, self.position)
454 }
455
456 async fn response_from_lsp(
457 self,
458 message: Option<lsp::PrepareRenameResponse>,
459 _: Entity<LspStore>,
460 buffer: Entity<Buffer>,
461 _: LanguageServerId,
462 cx: AsyncApp,
463 ) -> Result<PrepareRenameResponse> {
464 buffer.read_with(&cx, |buffer, _| match message {
465 Some(lsp::PrepareRenameResponse::Range(range))
466 | Some(lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. }) => {
467 let Range { start, end } = range_from_lsp(range);
468 if buffer.clip_point_utf16(start, Bias::Left) == start.0
469 && buffer.clip_point_utf16(end, Bias::Left) == end.0
470 {
471 Ok(PrepareRenameResponse::Success(
472 buffer.anchor_after(start)..buffer.anchor_before(end),
473 ))
474 } else {
475 Ok(PrepareRenameResponse::InvalidPosition)
476 }
477 }
478 Some(lsp::PrepareRenameResponse::DefaultBehavior { .. }) => {
479 let snapshot = buffer.snapshot();
480 let (range, _) = snapshot.surrounding_word(self.position, None);
481 let range = snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end);
482 Ok(PrepareRenameResponse::Success(range))
483 }
484 None => Ok(PrepareRenameResponse::InvalidPosition),
485 })
486 }
487
488 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
489 proto::PrepareRename {
490 project_id,
491 buffer_id: buffer.remote_id().into(),
492 position: Some(language::proto::serialize_anchor(
493 &buffer.anchor_before(self.position),
494 )),
495 version: serialize_version(&buffer.version()),
496 }
497 }
498
499 async fn from_proto(
500 message: proto::PrepareRename,
501 _: Entity<LspStore>,
502 buffer: Entity<Buffer>,
503 mut cx: AsyncApp,
504 ) -> Result<Self> {
505 let position = message
506 .position
507 .and_then(deserialize_anchor)
508 .context("invalid position")?;
509 buffer
510 .update(&mut cx, |buffer, _| {
511 buffer.wait_for_version(deserialize_version(&message.version))
512 })
513 .await?;
514
515 Ok(Self {
516 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
517 })
518 }
519
520 fn response_to_proto(
521 response: PrepareRenameResponse,
522 _: &mut LspStore,
523 _: PeerId,
524 buffer_version: &clock::Global,
525 _: &mut App,
526 ) -> proto::PrepareRenameResponse {
527 match response {
528 PrepareRenameResponse::Success(range) => proto::PrepareRenameResponse {
529 can_rename: true,
530 only_unprepared_rename_supported: false,
531 start: Some(language::proto::serialize_anchor(&range.start)),
532 end: Some(language::proto::serialize_anchor(&range.end)),
533 version: serialize_version(buffer_version),
534 },
535 PrepareRenameResponse::OnlyUnpreparedRenameSupported => proto::PrepareRenameResponse {
536 can_rename: false,
537 only_unprepared_rename_supported: true,
538 start: None,
539 end: None,
540 version: vec![],
541 },
542 PrepareRenameResponse::InvalidPosition => proto::PrepareRenameResponse {
543 can_rename: false,
544 only_unprepared_rename_supported: false,
545 start: None,
546 end: None,
547 version: vec![],
548 },
549 }
550 }
551
552 async fn response_from_proto(
553 self,
554 message: proto::PrepareRenameResponse,
555 _: Entity<LspStore>,
556 buffer: Entity<Buffer>,
557 mut cx: AsyncApp,
558 ) -> Result<PrepareRenameResponse> {
559 if message.can_rename {
560 buffer
561 .update(&mut cx, |buffer, _| {
562 buffer.wait_for_version(deserialize_version(&message.version))
563 })
564 .await?;
565 if let (Some(start), Some(end)) = (
566 message.start.and_then(deserialize_anchor),
567 message.end.and_then(deserialize_anchor),
568 ) {
569 Ok(PrepareRenameResponse::Success(start..end))
570 } else {
571 anyhow::bail!(
572 "Missing start or end position in remote project PrepareRenameResponse"
573 );
574 }
575 } else if message.only_unprepared_rename_supported {
576 Ok(PrepareRenameResponse::OnlyUnpreparedRenameSupported)
577 } else {
578 Ok(PrepareRenameResponse::InvalidPosition)
579 }
580 }
581
582 fn buffer_id_from_proto(message: &proto::PrepareRename) -> Result<BufferId> {
583 BufferId::new(message.buffer_id)
584 }
585}
586
587#[async_trait(?Send)]
588impl LspCommand for PerformRename {
589 type Response = ProjectTransaction;
590 type LspRequest = lsp::request::Rename;
591 type ProtoRequest = proto::PerformRename;
592
593 fn display_name(&self) -> &str {
594 "Rename"
595 }
596
597 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
598 capabilities
599 .server_capabilities
600 .rename_provider
601 .is_some_and(|capability| match capability {
602 OneOf::Left(enabled) => enabled,
603 OneOf::Right(_) => true,
604 })
605 }
606
607 fn to_lsp(
608 &self,
609 path: &Path,
610 _: &Buffer,
611 _: &Arc<LanguageServer>,
612 _: &App,
613 ) -> Result<lsp::RenameParams> {
614 Ok(lsp::RenameParams {
615 text_document_position: make_lsp_text_document_position(path, self.position)?,
616 new_name: self.new_name.clone(),
617 work_done_progress_params: Default::default(),
618 })
619 }
620
621 async fn response_from_lsp(
622 self,
623 message: Option<lsp::WorkspaceEdit>,
624 lsp_store: Entity<LspStore>,
625 buffer: Entity<Buffer>,
626 server_id: LanguageServerId,
627 mut cx: AsyncApp,
628 ) -> Result<ProjectTransaction> {
629 if let Some(edit) = message {
630 let (_, lsp_server) =
631 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
632 LocalLspStore::deserialize_workspace_edit(
633 lsp_store,
634 edit,
635 self.push_to_history,
636 lsp_server,
637 &mut cx,
638 )
639 .await
640 } else {
641 Ok(ProjectTransaction::default())
642 }
643 }
644
645 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
646 proto::PerformRename {
647 project_id,
648 buffer_id: buffer.remote_id().into(),
649 position: Some(language::proto::serialize_anchor(
650 &buffer.anchor_before(self.position),
651 )),
652 new_name: self.new_name.clone(),
653 version: serialize_version(&buffer.version()),
654 }
655 }
656
657 async fn from_proto(
658 message: proto::PerformRename,
659 _: Entity<LspStore>,
660 buffer: Entity<Buffer>,
661 mut cx: AsyncApp,
662 ) -> Result<Self> {
663 let position = message
664 .position
665 .and_then(deserialize_anchor)
666 .context("invalid position")?;
667 buffer
668 .update(&mut cx, |buffer, _| {
669 buffer.wait_for_version(deserialize_version(&message.version))
670 })
671 .await?;
672 Ok(Self {
673 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
674 new_name: message.new_name,
675 push_to_history: false,
676 })
677 }
678
679 fn response_to_proto(
680 response: ProjectTransaction,
681 lsp_store: &mut LspStore,
682 peer_id: PeerId,
683 _: &clock::Global,
684 cx: &mut App,
685 ) -> proto::PerformRenameResponse {
686 let transaction = lsp_store.buffer_store().update(cx, |buffer_store, cx| {
687 buffer_store.serialize_project_transaction_for_peer(response, peer_id, cx)
688 });
689 proto::PerformRenameResponse {
690 transaction: Some(transaction),
691 }
692 }
693
694 async fn response_from_proto(
695 self,
696 message: proto::PerformRenameResponse,
697 lsp_store: Entity<LspStore>,
698 _: Entity<Buffer>,
699 mut cx: AsyncApp,
700 ) -> Result<ProjectTransaction> {
701 let message = message.transaction.context("missing transaction")?;
702 lsp_store
703 .update(&mut cx, |lsp_store, cx| {
704 lsp_store.buffer_store().update(cx, |buffer_store, cx| {
705 buffer_store.deserialize_project_transaction(message, self.push_to_history, cx)
706 })
707 })
708 .await
709 }
710
711 fn buffer_id_from_proto(message: &proto::PerformRename) -> Result<BufferId> {
712 BufferId::new(message.buffer_id)
713 }
714}
715
716#[async_trait(?Send)]
717impl LspCommand for GetDefinitions {
718 type Response = Vec<LocationLink>;
719 type LspRequest = lsp::request::GotoDefinition;
720 type ProtoRequest = proto::GetDefinition;
721
722 fn display_name(&self) -> &str {
723 "Get definition"
724 }
725
726 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
727 capabilities
728 .server_capabilities
729 .definition_provider
730 .is_some_and(|capability| match capability {
731 OneOf::Left(supported) => supported,
732 OneOf::Right(_options) => true,
733 })
734 }
735
736 fn to_lsp(
737 &self,
738 path: &Path,
739 _: &Buffer,
740 _: &Arc<LanguageServer>,
741 _: &App,
742 ) -> Result<lsp::GotoDefinitionParams> {
743 Ok(lsp::GotoDefinitionParams {
744 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
745 work_done_progress_params: Default::default(),
746 partial_result_params: Default::default(),
747 })
748 }
749
750 async fn response_from_lsp(
751 self,
752 message: Option<lsp::GotoDefinitionResponse>,
753 lsp_store: Entity<LspStore>,
754 buffer: Entity<Buffer>,
755 server_id: LanguageServerId,
756 cx: AsyncApp,
757 ) -> Result<Vec<LocationLink>> {
758 location_links_from_lsp(message, lsp_store, buffer, server_id, cx).await
759 }
760
761 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
762 proto::GetDefinition {
763 project_id,
764 buffer_id: buffer.remote_id().into(),
765 position: Some(language::proto::serialize_anchor(
766 &buffer.anchor_before(self.position),
767 )),
768 version: serialize_version(&buffer.version()),
769 }
770 }
771
772 async fn from_proto(
773 message: proto::GetDefinition,
774 _: Entity<LspStore>,
775 buffer: Entity<Buffer>,
776 mut cx: AsyncApp,
777 ) -> Result<Self> {
778 let position = message
779 .position
780 .and_then(deserialize_anchor)
781 .context("invalid position")?;
782 buffer
783 .update(&mut cx, |buffer, _| {
784 buffer.wait_for_version(deserialize_version(&message.version))
785 })
786 .await?;
787 Ok(Self {
788 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
789 })
790 }
791
792 fn response_to_proto(
793 response: Vec<LocationLink>,
794 lsp_store: &mut LspStore,
795 peer_id: PeerId,
796 _: &clock::Global,
797 cx: &mut App,
798 ) -> proto::GetDefinitionResponse {
799 let links = location_links_to_proto(response, lsp_store, peer_id, cx);
800 proto::GetDefinitionResponse { links }
801 }
802
803 async fn response_from_proto(
804 self,
805 message: proto::GetDefinitionResponse,
806 lsp_store: Entity<LspStore>,
807 _: Entity<Buffer>,
808 cx: AsyncApp,
809 ) -> Result<Vec<LocationLink>> {
810 location_links_from_proto(message.links, lsp_store, cx).await
811 }
812
813 fn buffer_id_from_proto(message: &proto::GetDefinition) -> Result<BufferId> {
814 BufferId::new(message.buffer_id)
815 }
816}
817
818#[async_trait(?Send)]
819impl LspCommand for GetDeclarations {
820 type Response = Vec<LocationLink>;
821 type LspRequest = lsp::request::GotoDeclaration;
822 type ProtoRequest = proto::GetDeclaration;
823
824 fn display_name(&self) -> &str {
825 "Get declaration"
826 }
827
828 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
829 capabilities
830 .server_capabilities
831 .declaration_provider
832 .is_some_and(|capability| match capability {
833 lsp::DeclarationCapability::Simple(supported) => supported,
834 lsp::DeclarationCapability::RegistrationOptions(..) => true,
835 lsp::DeclarationCapability::Options(..) => true,
836 })
837 }
838
839 fn to_lsp(
840 &self,
841 path: &Path,
842 _: &Buffer,
843 _: &Arc<LanguageServer>,
844 _: &App,
845 ) -> Result<lsp::GotoDeclarationParams> {
846 Ok(lsp::GotoDeclarationParams {
847 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
848 work_done_progress_params: Default::default(),
849 partial_result_params: Default::default(),
850 })
851 }
852
853 async fn response_from_lsp(
854 self,
855 message: Option<lsp::GotoDeclarationResponse>,
856 lsp_store: Entity<LspStore>,
857 buffer: Entity<Buffer>,
858 server_id: LanguageServerId,
859 cx: AsyncApp,
860 ) -> Result<Vec<LocationLink>> {
861 location_links_from_lsp(message, lsp_store, buffer, server_id, cx).await
862 }
863
864 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDeclaration {
865 proto::GetDeclaration {
866 project_id,
867 buffer_id: buffer.remote_id().into(),
868 position: Some(language::proto::serialize_anchor(
869 &buffer.anchor_before(self.position),
870 )),
871 version: serialize_version(&buffer.version()),
872 }
873 }
874
875 async fn from_proto(
876 message: proto::GetDeclaration,
877 _: Entity<LspStore>,
878 buffer: Entity<Buffer>,
879 mut cx: AsyncApp,
880 ) -> Result<Self> {
881 let position = message
882 .position
883 .and_then(deserialize_anchor)
884 .context("invalid position")?;
885 buffer
886 .update(&mut cx, |buffer, _| {
887 buffer.wait_for_version(deserialize_version(&message.version))
888 })
889 .await?;
890 Ok(Self {
891 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
892 })
893 }
894
895 fn response_to_proto(
896 response: Vec<LocationLink>,
897 lsp_store: &mut LspStore,
898 peer_id: PeerId,
899 _: &clock::Global,
900 cx: &mut App,
901 ) -> proto::GetDeclarationResponse {
902 let links = location_links_to_proto(response, lsp_store, peer_id, cx);
903 proto::GetDeclarationResponse { links }
904 }
905
906 async fn response_from_proto(
907 self,
908 message: proto::GetDeclarationResponse,
909 lsp_store: Entity<LspStore>,
910 _: Entity<Buffer>,
911 cx: AsyncApp,
912 ) -> Result<Vec<LocationLink>> {
913 location_links_from_proto(message.links, lsp_store, cx).await
914 }
915
916 fn buffer_id_from_proto(message: &proto::GetDeclaration) -> Result<BufferId> {
917 BufferId::new(message.buffer_id)
918 }
919}
920
921#[async_trait(?Send)]
922impl LspCommand for GetImplementations {
923 type Response = Vec<LocationLink>;
924 type LspRequest = lsp::request::GotoImplementation;
925 type ProtoRequest = proto::GetImplementation;
926
927 fn display_name(&self) -> &str {
928 "Get implementation"
929 }
930
931 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
932 capabilities
933 .server_capabilities
934 .implementation_provider
935 .is_some_and(|capability| match capability {
936 lsp::ImplementationProviderCapability::Simple(enabled) => enabled,
937 lsp::ImplementationProviderCapability::Options(_options) => true,
938 })
939 }
940
941 fn to_lsp(
942 &self,
943 path: &Path,
944 _: &Buffer,
945 _: &Arc<LanguageServer>,
946 _: &App,
947 ) -> Result<lsp::GotoImplementationParams> {
948 Ok(lsp::GotoImplementationParams {
949 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
950 work_done_progress_params: Default::default(),
951 partial_result_params: Default::default(),
952 })
953 }
954
955 async fn response_from_lsp(
956 self,
957 message: Option<lsp::GotoImplementationResponse>,
958 lsp_store: Entity<LspStore>,
959 buffer: Entity<Buffer>,
960 server_id: LanguageServerId,
961 cx: AsyncApp,
962 ) -> Result<Vec<LocationLink>> {
963 location_links_from_lsp(message, lsp_store, buffer, server_id, cx).await
964 }
965
966 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetImplementation {
967 proto::GetImplementation {
968 project_id,
969 buffer_id: buffer.remote_id().into(),
970 position: Some(language::proto::serialize_anchor(
971 &buffer.anchor_before(self.position),
972 )),
973 version: serialize_version(&buffer.version()),
974 }
975 }
976
977 async fn from_proto(
978 message: proto::GetImplementation,
979 _: Entity<LspStore>,
980 buffer: Entity<Buffer>,
981 mut cx: AsyncApp,
982 ) -> Result<Self> {
983 let position = message
984 .position
985 .and_then(deserialize_anchor)
986 .context("invalid position")?;
987 buffer
988 .update(&mut cx, |buffer, _| {
989 buffer.wait_for_version(deserialize_version(&message.version))
990 })
991 .await?;
992 Ok(Self {
993 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
994 })
995 }
996
997 fn response_to_proto(
998 response: Vec<LocationLink>,
999 lsp_store: &mut LspStore,
1000 peer_id: PeerId,
1001 _: &clock::Global,
1002 cx: &mut App,
1003 ) -> proto::GetImplementationResponse {
1004 let links = location_links_to_proto(response, lsp_store, peer_id, cx);
1005 proto::GetImplementationResponse { links }
1006 }
1007
1008 async fn response_from_proto(
1009 self,
1010 message: proto::GetImplementationResponse,
1011 project: Entity<LspStore>,
1012 _: Entity<Buffer>,
1013 cx: AsyncApp,
1014 ) -> Result<Vec<LocationLink>> {
1015 location_links_from_proto(message.links, project, cx).await
1016 }
1017
1018 fn buffer_id_from_proto(message: &proto::GetImplementation) -> Result<BufferId> {
1019 BufferId::new(message.buffer_id)
1020 }
1021}
1022
1023#[async_trait(?Send)]
1024impl LspCommand for GetTypeDefinitions {
1025 type Response = Vec<LocationLink>;
1026 type LspRequest = lsp::request::GotoTypeDefinition;
1027 type ProtoRequest = proto::GetTypeDefinition;
1028
1029 fn display_name(&self) -> &str {
1030 "Get type definition"
1031 }
1032
1033 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1034 !matches!(
1035 &capabilities.server_capabilities.type_definition_provider,
1036 None | Some(lsp::TypeDefinitionProviderCapability::Simple(false))
1037 )
1038 }
1039
1040 fn to_lsp(
1041 &self,
1042 path: &Path,
1043 _: &Buffer,
1044 _: &Arc<LanguageServer>,
1045 _: &App,
1046 ) -> Result<lsp::GotoTypeDefinitionParams> {
1047 Ok(lsp::GotoTypeDefinitionParams {
1048 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
1049 work_done_progress_params: Default::default(),
1050 partial_result_params: Default::default(),
1051 })
1052 }
1053
1054 async fn response_from_lsp(
1055 self,
1056 message: Option<lsp::GotoTypeDefinitionResponse>,
1057 project: Entity<LspStore>,
1058 buffer: Entity<Buffer>,
1059 server_id: LanguageServerId,
1060 cx: AsyncApp,
1061 ) -> Result<Vec<LocationLink>> {
1062 location_links_from_lsp(message, project, buffer, server_id, cx).await
1063 }
1064
1065 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition {
1066 proto::GetTypeDefinition {
1067 project_id,
1068 buffer_id: buffer.remote_id().into(),
1069 position: Some(language::proto::serialize_anchor(
1070 &buffer.anchor_before(self.position),
1071 )),
1072 version: serialize_version(&buffer.version()),
1073 }
1074 }
1075
1076 async fn from_proto(
1077 message: proto::GetTypeDefinition,
1078 _: Entity<LspStore>,
1079 buffer: Entity<Buffer>,
1080 mut cx: AsyncApp,
1081 ) -> Result<Self> {
1082 let position = message
1083 .position
1084 .and_then(deserialize_anchor)
1085 .context("invalid position")?;
1086 buffer
1087 .update(&mut cx, |buffer, _| {
1088 buffer.wait_for_version(deserialize_version(&message.version))
1089 })
1090 .await?;
1091 Ok(Self {
1092 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1093 })
1094 }
1095
1096 fn response_to_proto(
1097 response: Vec<LocationLink>,
1098 lsp_store: &mut LspStore,
1099 peer_id: PeerId,
1100 _: &clock::Global,
1101 cx: &mut App,
1102 ) -> proto::GetTypeDefinitionResponse {
1103 let links = location_links_to_proto(response, lsp_store, peer_id, cx);
1104 proto::GetTypeDefinitionResponse { links }
1105 }
1106
1107 async fn response_from_proto(
1108 self,
1109 message: proto::GetTypeDefinitionResponse,
1110 project: Entity<LspStore>,
1111 _: Entity<Buffer>,
1112 cx: AsyncApp,
1113 ) -> Result<Vec<LocationLink>> {
1114 location_links_from_proto(message.links, project, cx).await
1115 }
1116
1117 fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> Result<BufferId> {
1118 BufferId::new(message.buffer_id)
1119 }
1120}
1121
1122fn language_server_for_buffer(
1123 lsp_store: &Entity<LspStore>,
1124 buffer: &Entity<Buffer>,
1125 server_id: LanguageServerId,
1126 cx: &mut AsyncApp,
1127) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
1128 lsp_store
1129 .update(cx, |lsp_store, cx| {
1130 buffer.update(cx, |buffer, cx| {
1131 lsp_store
1132 .language_server_for_local_buffer(buffer, server_id, cx)
1133 .map(|(adapter, server)| (adapter.clone(), server.clone()))
1134 })
1135 })
1136 .context("no language server found for buffer")
1137}
1138
1139pub async fn location_links_from_proto(
1140 proto_links: Vec<proto::LocationLink>,
1141 lsp_store: Entity<LspStore>,
1142 mut cx: AsyncApp,
1143) -> Result<Vec<LocationLink>> {
1144 let mut links = Vec::new();
1145
1146 for link in proto_links {
1147 links.push(location_link_from_proto(link, lsp_store.clone(), &mut cx).await?)
1148 }
1149
1150 Ok(links)
1151}
1152
1153pub fn location_link_from_proto(
1154 link: proto::LocationLink,
1155 lsp_store: Entity<LspStore>,
1156 cx: &mut AsyncApp,
1157) -> Task<Result<LocationLink>> {
1158 cx.spawn(async move |cx| {
1159 let origin = match link.origin {
1160 Some(origin) => {
1161 let buffer_id = BufferId::new(origin.buffer_id)?;
1162 let buffer = lsp_store
1163 .update(cx, |lsp_store, cx| {
1164 lsp_store.wait_for_remote_buffer(buffer_id, cx)
1165 })
1166 .await?;
1167 let start = origin
1168 .start
1169 .and_then(deserialize_anchor)
1170 .context("missing origin start")?;
1171 let end = origin
1172 .end
1173 .and_then(deserialize_anchor)
1174 .context("missing origin end")?;
1175 buffer
1176 .update(cx, |buffer, _| buffer.wait_for_anchors([start, end]))
1177 .await?;
1178 Some(Location {
1179 buffer,
1180 range: start..end,
1181 })
1182 }
1183 None => None,
1184 };
1185
1186 let target = link.target.context("missing target")?;
1187 let buffer_id = BufferId::new(target.buffer_id)?;
1188 let buffer = lsp_store
1189 .update(cx, |lsp_store, cx| {
1190 lsp_store.wait_for_remote_buffer(buffer_id, cx)
1191 })
1192 .await?;
1193 let start = target
1194 .start
1195 .and_then(deserialize_anchor)
1196 .context("missing target start")?;
1197 let end = target
1198 .end
1199 .and_then(deserialize_anchor)
1200 .context("missing target end")?;
1201 buffer
1202 .update(cx, |buffer, _| buffer.wait_for_anchors([start, end]))
1203 .await?;
1204 let target = Location {
1205 buffer,
1206 range: start..end,
1207 };
1208 Ok(LocationLink { origin, target })
1209 })
1210}
1211
1212pub async fn location_links_from_lsp(
1213 message: Option<lsp::GotoDefinitionResponse>,
1214 lsp_store: Entity<LspStore>,
1215 buffer: Entity<Buffer>,
1216 server_id: LanguageServerId,
1217 mut cx: AsyncApp,
1218) -> Result<Vec<LocationLink>> {
1219 let message = match message {
1220 Some(message) => message,
1221 None => return Ok(Vec::new()),
1222 };
1223
1224 let mut unresolved_links = Vec::new();
1225 match message {
1226 lsp::GotoDefinitionResponse::Scalar(loc) => {
1227 unresolved_links.push((None, loc.uri, loc.range));
1228 }
1229
1230 lsp::GotoDefinitionResponse::Array(locs) => {
1231 unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
1232 }
1233
1234 lsp::GotoDefinitionResponse::Link(links) => {
1235 unresolved_links.extend(links.into_iter().map(|l| {
1236 (
1237 l.origin_selection_range,
1238 l.target_uri,
1239 l.target_selection_range,
1240 )
1241 }));
1242 }
1243 }
1244
1245 let (_, language_server) = language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
1246 let mut definitions = Vec::new();
1247 for (origin_range, target_uri, target_range) in unresolved_links {
1248 let target_buffer_handle = lsp_store
1249 .update(&mut cx, |this, cx| {
1250 this.open_local_buffer_via_lsp(target_uri, language_server.server_id(), cx)
1251 })
1252 .await?;
1253
1254 cx.update(|cx| {
1255 let origin_location = origin_range.map(|origin_range| {
1256 let origin_buffer = buffer.read(cx);
1257 let origin_start =
1258 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
1259 let origin_end =
1260 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
1261 Location {
1262 buffer: buffer.clone(),
1263 range: origin_buffer.anchor_after(origin_start)
1264 ..origin_buffer.anchor_before(origin_end),
1265 }
1266 });
1267
1268 let target_buffer = target_buffer_handle.read(cx);
1269 let target_start =
1270 target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
1271 let target_end =
1272 target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
1273 let target_location = Location {
1274 buffer: target_buffer_handle,
1275 range: target_buffer.anchor_after(target_start)
1276 ..target_buffer.anchor_before(target_end),
1277 };
1278
1279 definitions.push(LocationLink {
1280 origin: origin_location,
1281 target: target_location,
1282 })
1283 });
1284 }
1285 Ok(definitions)
1286}
1287
1288pub async fn location_link_from_lsp(
1289 link: lsp::LocationLink,
1290 lsp_store: &Entity<LspStore>,
1291 buffer: &Entity<Buffer>,
1292 server_id: LanguageServerId,
1293 cx: &mut AsyncApp,
1294) -> Result<LocationLink> {
1295 let (_, language_server) = language_server_for_buffer(lsp_store, buffer, server_id, cx)?;
1296
1297 let (origin_range, target_uri, target_range) = (
1298 link.origin_selection_range,
1299 link.target_uri,
1300 link.target_selection_range,
1301 );
1302
1303 let target_buffer_handle = lsp_store
1304 .update(cx, |lsp_store, cx| {
1305 lsp_store.open_local_buffer_via_lsp(target_uri, language_server.server_id(), cx)
1306 })
1307 .await?;
1308
1309 Ok(cx.update(|cx| {
1310 let origin_location = origin_range.map(|origin_range| {
1311 let origin_buffer = buffer.read(cx);
1312 let origin_start =
1313 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
1314 let origin_end =
1315 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
1316 Location {
1317 buffer: buffer.clone(),
1318 range: origin_buffer.anchor_after(origin_start)
1319 ..origin_buffer.anchor_before(origin_end),
1320 }
1321 });
1322
1323 let target_buffer = target_buffer_handle.read(cx);
1324 let target_start =
1325 target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
1326 let target_end =
1327 target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
1328 let target_location = Location {
1329 buffer: target_buffer_handle,
1330 range: target_buffer.anchor_after(target_start)
1331 ..target_buffer.anchor_before(target_end),
1332 };
1333
1334 LocationLink {
1335 origin: origin_location,
1336 target: target_location,
1337 }
1338 }))
1339}
1340
1341pub fn location_links_to_proto(
1342 links: Vec<LocationLink>,
1343 lsp_store: &mut LspStore,
1344 peer_id: PeerId,
1345 cx: &mut App,
1346) -> Vec<proto::LocationLink> {
1347 links
1348 .into_iter()
1349 .map(|definition| location_link_to_proto(definition, lsp_store, peer_id, cx))
1350 .collect()
1351}
1352
1353pub fn location_link_to_proto(
1354 location: LocationLink,
1355 lsp_store: &mut LspStore,
1356 peer_id: PeerId,
1357 cx: &mut App,
1358) -> proto::LocationLink {
1359 let origin = location.origin.map(|origin| {
1360 lsp_store
1361 .buffer_store()
1362 .update(cx, |buffer_store, cx| {
1363 buffer_store.create_buffer_for_peer(&origin.buffer, peer_id, cx)
1364 })
1365 .detach_and_log_err(cx);
1366
1367 let buffer_id = origin.buffer.read(cx).remote_id().into();
1368 proto::Location {
1369 start: Some(serialize_anchor(&origin.range.start)),
1370 end: Some(serialize_anchor(&origin.range.end)),
1371 buffer_id,
1372 }
1373 });
1374
1375 lsp_store
1376 .buffer_store()
1377 .update(cx, |buffer_store, cx| {
1378 buffer_store.create_buffer_for_peer(&location.target.buffer, peer_id, cx)
1379 })
1380 .detach_and_log_err(cx);
1381
1382 let buffer_id = location.target.buffer.read(cx).remote_id().into();
1383 let target = proto::Location {
1384 start: Some(serialize_anchor(&location.target.range.start)),
1385 end: Some(serialize_anchor(&location.target.range.end)),
1386 buffer_id,
1387 };
1388
1389 proto::LocationLink {
1390 origin,
1391 target: Some(target),
1392 }
1393}
1394
1395#[async_trait(?Send)]
1396impl LspCommand for GetReferences {
1397 type Response = Vec<Location>;
1398 type LspRequest = lsp::request::References;
1399 type ProtoRequest = proto::GetReferences;
1400
1401 fn display_name(&self) -> &str {
1402 "Find all references"
1403 }
1404
1405 fn status(&self) -> Option<String> {
1406 Some("Finding references...".to_owned())
1407 }
1408
1409 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1410 match &capabilities.server_capabilities.references_provider {
1411 Some(OneOf::Left(has_support)) => *has_support,
1412 Some(OneOf::Right(_)) => true,
1413 None => false,
1414 }
1415 }
1416
1417 fn to_lsp(
1418 &self,
1419 path: &Path,
1420 _: &Buffer,
1421 _: &Arc<LanguageServer>,
1422 _: &App,
1423 ) -> Result<lsp::ReferenceParams> {
1424 Ok(lsp::ReferenceParams {
1425 text_document_position: make_lsp_text_document_position(path, self.position)?,
1426 work_done_progress_params: Default::default(),
1427 partial_result_params: Default::default(),
1428 context: lsp::ReferenceContext {
1429 include_declaration: true,
1430 },
1431 })
1432 }
1433
1434 async fn response_from_lsp(
1435 self,
1436 locations: Option<Vec<lsp::Location>>,
1437 lsp_store: Entity<LspStore>,
1438 buffer: Entity<Buffer>,
1439 server_id: LanguageServerId,
1440 mut cx: AsyncApp,
1441 ) -> Result<Vec<Location>> {
1442 let mut references = Vec::new();
1443 let (_, language_server) =
1444 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
1445
1446 if let Some(locations) = locations {
1447 for lsp_location in locations {
1448 let target_buffer_handle = lsp_store
1449 .update(&mut cx, |lsp_store, cx| {
1450 lsp_store.open_local_buffer_via_lsp(
1451 lsp_location.uri,
1452 language_server.server_id(),
1453 cx,
1454 )
1455 })
1456 .await?;
1457
1458 target_buffer_handle
1459 .clone()
1460 .read_with(&cx, |target_buffer, _| {
1461 let target_start = target_buffer
1462 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
1463 let target_end = target_buffer
1464 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
1465 references.push(Location {
1466 buffer: target_buffer_handle,
1467 range: target_buffer.anchor_after(target_start)
1468 ..target_buffer.anchor_before(target_end),
1469 });
1470 });
1471 }
1472 }
1473
1474 Ok(references)
1475 }
1476
1477 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
1478 proto::GetReferences {
1479 project_id,
1480 buffer_id: buffer.remote_id().into(),
1481 position: Some(language::proto::serialize_anchor(
1482 &buffer.anchor_before(self.position),
1483 )),
1484 version: serialize_version(&buffer.version()),
1485 }
1486 }
1487
1488 async fn from_proto(
1489 message: proto::GetReferences,
1490 _: Entity<LspStore>,
1491 buffer: Entity<Buffer>,
1492 mut cx: AsyncApp,
1493 ) -> Result<Self> {
1494 let position = message
1495 .position
1496 .and_then(deserialize_anchor)
1497 .context("invalid position")?;
1498 buffer
1499 .update(&mut cx, |buffer, _| {
1500 buffer.wait_for_version(deserialize_version(&message.version))
1501 })
1502 .await?;
1503 Ok(Self {
1504 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1505 })
1506 }
1507
1508 fn response_to_proto(
1509 response: Vec<Location>,
1510 lsp_store: &mut LspStore,
1511 peer_id: PeerId,
1512 _: &clock::Global,
1513 cx: &mut App,
1514 ) -> proto::GetReferencesResponse {
1515 let locations = response
1516 .into_iter()
1517 .map(|definition| {
1518 lsp_store
1519 .buffer_store()
1520 .update(cx, |buffer_store, cx| {
1521 buffer_store.create_buffer_for_peer(&definition.buffer, peer_id, cx)
1522 })
1523 .detach_and_log_err(cx);
1524 let buffer_id = definition.buffer.read(cx).remote_id();
1525 proto::Location {
1526 start: Some(serialize_anchor(&definition.range.start)),
1527 end: Some(serialize_anchor(&definition.range.end)),
1528 buffer_id: buffer_id.into(),
1529 }
1530 })
1531 .collect();
1532 proto::GetReferencesResponse { locations }
1533 }
1534
1535 async fn response_from_proto(
1536 self,
1537 message: proto::GetReferencesResponse,
1538 project: Entity<LspStore>,
1539 _: Entity<Buffer>,
1540 mut cx: AsyncApp,
1541 ) -> Result<Vec<Location>> {
1542 let mut locations = Vec::new();
1543 for location in message.locations {
1544 let buffer_id = BufferId::new(location.buffer_id)?;
1545 let target_buffer = project
1546 .update(&mut cx, |this, cx| {
1547 this.wait_for_remote_buffer(buffer_id, cx)
1548 })
1549 .await?;
1550 let start = location
1551 .start
1552 .and_then(deserialize_anchor)
1553 .context("missing target start")?;
1554 let end = location
1555 .end
1556 .and_then(deserialize_anchor)
1557 .context("missing target end")?;
1558 target_buffer
1559 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
1560 .await?;
1561 locations.push(Location {
1562 buffer: target_buffer,
1563 range: start..end,
1564 })
1565 }
1566 Ok(locations)
1567 }
1568
1569 fn buffer_id_from_proto(message: &proto::GetReferences) -> Result<BufferId> {
1570 BufferId::new(message.buffer_id)
1571 }
1572}
1573
1574#[async_trait(?Send)]
1575impl LspCommand for GetDocumentHighlights {
1576 type Response = Vec<DocumentHighlight>;
1577 type LspRequest = lsp::request::DocumentHighlightRequest;
1578 type ProtoRequest = proto::GetDocumentHighlights;
1579
1580 fn display_name(&self) -> &str {
1581 "Get document highlights"
1582 }
1583
1584 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1585 capabilities
1586 .server_capabilities
1587 .document_highlight_provider
1588 .is_some_and(|capability| match capability {
1589 OneOf::Left(supported) => supported,
1590 OneOf::Right(_options) => true,
1591 })
1592 }
1593
1594 fn to_lsp(
1595 &self,
1596 path: &Path,
1597 _: &Buffer,
1598 _: &Arc<LanguageServer>,
1599 _: &App,
1600 ) -> Result<lsp::DocumentHighlightParams> {
1601 Ok(lsp::DocumentHighlightParams {
1602 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
1603 work_done_progress_params: Default::default(),
1604 partial_result_params: Default::default(),
1605 })
1606 }
1607
1608 async fn response_from_lsp(
1609 self,
1610 lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
1611 _: Entity<LspStore>,
1612 buffer: Entity<Buffer>,
1613 _: LanguageServerId,
1614 cx: AsyncApp,
1615 ) -> Result<Vec<DocumentHighlight>> {
1616 Ok(buffer.read_with(&cx, |buffer, _| {
1617 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
1618 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
1619 lsp_highlights
1620 .into_iter()
1621 .map(|lsp_highlight| {
1622 let start = buffer
1623 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
1624 let end = buffer
1625 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
1626 DocumentHighlight {
1627 range: buffer.anchor_after(start)..buffer.anchor_before(end),
1628 kind: lsp_highlight
1629 .kind
1630 .unwrap_or(lsp::DocumentHighlightKind::READ),
1631 }
1632 })
1633 .collect()
1634 }))
1635 }
1636
1637 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
1638 proto::GetDocumentHighlights {
1639 project_id,
1640 buffer_id: buffer.remote_id().into(),
1641 position: Some(language::proto::serialize_anchor(
1642 &buffer.anchor_before(self.position),
1643 )),
1644 version: serialize_version(&buffer.version()),
1645 }
1646 }
1647
1648 async fn from_proto(
1649 message: proto::GetDocumentHighlights,
1650 _: Entity<LspStore>,
1651 buffer: Entity<Buffer>,
1652 mut cx: AsyncApp,
1653 ) -> Result<Self> {
1654 let position = message
1655 .position
1656 .and_then(deserialize_anchor)
1657 .context("invalid position")?;
1658 buffer
1659 .update(&mut cx, |buffer, _| {
1660 buffer.wait_for_version(deserialize_version(&message.version))
1661 })
1662 .await?;
1663 Ok(Self {
1664 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1665 })
1666 }
1667
1668 fn response_to_proto(
1669 response: Vec<DocumentHighlight>,
1670 _: &mut LspStore,
1671 _: PeerId,
1672 _: &clock::Global,
1673 _: &mut App,
1674 ) -> proto::GetDocumentHighlightsResponse {
1675 let highlights = response
1676 .into_iter()
1677 .map(|highlight| proto::DocumentHighlight {
1678 start: Some(serialize_anchor(&highlight.range.start)),
1679 end: Some(serialize_anchor(&highlight.range.end)),
1680 kind: match highlight.kind {
1681 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
1682 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
1683 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
1684 _ => proto::document_highlight::Kind::Text.into(),
1685 },
1686 })
1687 .collect();
1688 proto::GetDocumentHighlightsResponse { highlights }
1689 }
1690
1691 async fn response_from_proto(
1692 self,
1693 message: proto::GetDocumentHighlightsResponse,
1694 _: Entity<LspStore>,
1695 buffer: Entity<Buffer>,
1696 mut cx: AsyncApp,
1697 ) -> Result<Vec<DocumentHighlight>> {
1698 let mut highlights = Vec::new();
1699 for highlight in message.highlights {
1700 let start = highlight
1701 .start
1702 .and_then(deserialize_anchor)
1703 .context("missing target start")?;
1704 let end = highlight
1705 .end
1706 .and_then(deserialize_anchor)
1707 .context("missing target end")?;
1708 buffer
1709 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
1710 .await?;
1711 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
1712 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
1713 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
1714 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
1715 None => DocumentHighlightKind::TEXT,
1716 };
1717 highlights.push(DocumentHighlight {
1718 range: start..end,
1719 kind,
1720 });
1721 }
1722 Ok(highlights)
1723 }
1724
1725 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> Result<BufferId> {
1726 BufferId::new(message.buffer_id)
1727 }
1728}
1729
1730#[async_trait(?Send)]
1731impl LspCommand for GetDocumentSymbols {
1732 type Response = Vec<DocumentSymbol>;
1733 type LspRequest = lsp::request::DocumentSymbolRequest;
1734 type ProtoRequest = proto::GetDocumentSymbols;
1735
1736 fn display_name(&self) -> &str {
1737 "Get document symbols"
1738 }
1739
1740 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1741 capabilities
1742 .server_capabilities
1743 .document_symbol_provider
1744 .is_some_and(|capability| match capability {
1745 OneOf::Left(supported) => supported,
1746 OneOf::Right(_options) => true,
1747 })
1748 }
1749
1750 fn to_lsp(
1751 &self,
1752 path: &Path,
1753 _: &Buffer,
1754 _: &Arc<LanguageServer>,
1755 _: &App,
1756 ) -> Result<lsp::DocumentSymbolParams> {
1757 Ok(lsp::DocumentSymbolParams {
1758 text_document: make_text_document_identifier(path)?,
1759 work_done_progress_params: Default::default(),
1760 partial_result_params: Default::default(),
1761 })
1762 }
1763
1764 async fn response_from_lsp(
1765 self,
1766 lsp_symbols: Option<lsp::DocumentSymbolResponse>,
1767 _: Entity<LspStore>,
1768 _: Entity<Buffer>,
1769 _: LanguageServerId,
1770 _: AsyncApp,
1771 ) -> Result<Vec<DocumentSymbol>> {
1772 let Some(lsp_symbols) = lsp_symbols else {
1773 return Ok(Vec::new());
1774 };
1775
1776 let symbols = match lsp_symbols {
1777 lsp::DocumentSymbolResponse::Flat(symbol_information) => symbol_information
1778 .into_iter()
1779 .map(|lsp_symbol| DocumentSymbol {
1780 name: lsp_symbol.name,
1781 kind: lsp_symbol.kind,
1782 range: range_from_lsp(lsp_symbol.location.range),
1783 selection_range: range_from_lsp(lsp_symbol.location.range),
1784 children: Vec::new(),
1785 })
1786 .collect(),
1787 lsp::DocumentSymbolResponse::Nested(nested_responses) => {
1788 fn convert_symbol(lsp_symbol: lsp::DocumentSymbol) -> DocumentSymbol {
1789 DocumentSymbol {
1790 name: lsp_symbol.name,
1791 kind: lsp_symbol.kind,
1792 range: range_from_lsp(lsp_symbol.range),
1793 selection_range: range_from_lsp(lsp_symbol.selection_range),
1794 children: lsp_symbol
1795 .children
1796 .map(|children| {
1797 children.into_iter().map(convert_symbol).collect::<Vec<_>>()
1798 })
1799 .unwrap_or_default(),
1800 }
1801 }
1802 nested_responses.into_iter().map(convert_symbol).collect()
1803 }
1804 };
1805 Ok(symbols)
1806 }
1807
1808 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentSymbols {
1809 proto::GetDocumentSymbols {
1810 project_id,
1811 buffer_id: buffer.remote_id().into(),
1812 version: serialize_version(&buffer.version()),
1813 }
1814 }
1815
1816 async fn from_proto(
1817 message: proto::GetDocumentSymbols,
1818 _: Entity<LspStore>,
1819 buffer: Entity<Buffer>,
1820 mut cx: AsyncApp,
1821 ) -> Result<Self> {
1822 buffer
1823 .update(&mut cx, |buffer, _| {
1824 buffer.wait_for_version(deserialize_version(&message.version))
1825 })
1826 .await?;
1827 Ok(Self)
1828 }
1829
1830 fn response_to_proto(
1831 response: Vec<DocumentSymbol>,
1832 _: &mut LspStore,
1833 _: PeerId,
1834 _: &clock::Global,
1835 _: &mut App,
1836 ) -> proto::GetDocumentSymbolsResponse {
1837 let symbols = response
1838 .into_iter()
1839 .map(|symbol| {
1840 fn convert_symbol_to_proto(symbol: DocumentSymbol) -> proto::DocumentSymbol {
1841 proto::DocumentSymbol {
1842 name: symbol.name.clone(),
1843 kind: unsafe { mem::transmute::<lsp::SymbolKind, i32>(symbol.kind) },
1844 start: Some(proto::PointUtf16 {
1845 row: symbol.range.start.0.row,
1846 column: symbol.range.start.0.column,
1847 }),
1848 end: Some(proto::PointUtf16 {
1849 row: symbol.range.end.0.row,
1850 column: symbol.range.end.0.column,
1851 }),
1852 selection_start: Some(proto::PointUtf16 {
1853 row: symbol.selection_range.start.0.row,
1854 column: symbol.selection_range.start.0.column,
1855 }),
1856 selection_end: Some(proto::PointUtf16 {
1857 row: symbol.selection_range.end.0.row,
1858 column: symbol.selection_range.end.0.column,
1859 }),
1860 children: symbol
1861 .children
1862 .into_iter()
1863 .map(convert_symbol_to_proto)
1864 .collect(),
1865 }
1866 }
1867 convert_symbol_to_proto(symbol)
1868 })
1869 .collect::<Vec<_>>();
1870
1871 proto::GetDocumentSymbolsResponse { symbols }
1872 }
1873
1874 async fn response_from_proto(
1875 self,
1876 message: proto::GetDocumentSymbolsResponse,
1877 _: Entity<LspStore>,
1878 _: Entity<Buffer>,
1879 _: AsyncApp,
1880 ) -> Result<Vec<DocumentSymbol>> {
1881 let mut symbols = Vec::with_capacity(message.symbols.len());
1882 for serialized_symbol in message.symbols {
1883 fn deserialize_symbol_with_children(
1884 serialized_symbol: proto::DocumentSymbol,
1885 ) -> Result<DocumentSymbol> {
1886 let kind =
1887 unsafe { mem::transmute::<i32, lsp::SymbolKind>(serialized_symbol.kind) };
1888
1889 let start = serialized_symbol.start.context("invalid start")?;
1890 let end = serialized_symbol.end.context("invalid end")?;
1891
1892 let selection_start = serialized_symbol
1893 .selection_start
1894 .context("invalid selection start")?;
1895 let selection_end = serialized_symbol
1896 .selection_end
1897 .context("invalid selection end")?;
1898
1899 Ok(DocumentSymbol {
1900 name: serialized_symbol.name,
1901 kind,
1902 range: Unclipped(PointUtf16::new(start.row, start.column))
1903 ..Unclipped(PointUtf16::new(end.row, end.column)),
1904 selection_range: Unclipped(PointUtf16::new(
1905 selection_start.row,
1906 selection_start.column,
1907 ))
1908 ..Unclipped(PointUtf16::new(selection_end.row, selection_end.column)),
1909 children: serialized_symbol
1910 .children
1911 .into_iter()
1912 .filter_map(|symbol| deserialize_symbol_with_children(symbol).ok())
1913 .collect::<Vec<_>>(),
1914 })
1915 }
1916
1917 symbols.push(deserialize_symbol_with_children(serialized_symbol)?);
1918 }
1919
1920 Ok(symbols)
1921 }
1922
1923 fn buffer_id_from_proto(message: &proto::GetDocumentSymbols) -> Result<BufferId> {
1924 BufferId::new(message.buffer_id)
1925 }
1926}
1927
1928#[async_trait(?Send)]
1929impl LspCommand for GetSignatureHelp {
1930 type Response = Option<SignatureHelp>;
1931 type LspRequest = lsp::SignatureHelpRequest;
1932 type ProtoRequest = proto::GetSignatureHelp;
1933
1934 fn display_name(&self) -> &str {
1935 "Get signature help"
1936 }
1937
1938 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1939 capabilities
1940 .server_capabilities
1941 .signature_help_provider
1942 .is_some()
1943 }
1944
1945 fn to_lsp(
1946 &self,
1947 path: &Path,
1948 _: &Buffer,
1949 _: &Arc<LanguageServer>,
1950 _cx: &App,
1951 ) -> Result<lsp::SignatureHelpParams> {
1952 Ok(lsp::SignatureHelpParams {
1953 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
1954 context: None,
1955 work_done_progress_params: Default::default(),
1956 })
1957 }
1958
1959 async fn response_from_lsp(
1960 self,
1961 message: Option<lsp::SignatureHelp>,
1962 lsp_store: Entity<LspStore>,
1963 _: Entity<Buffer>,
1964 id: LanguageServerId,
1965 cx: AsyncApp,
1966 ) -> Result<Self::Response> {
1967 let Some(message) = message else {
1968 return Ok(None);
1969 };
1970 Ok(cx.update(|cx| {
1971 SignatureHelp::new(
1972 message,
1973 Some(lsp_store.read(cx).languages.clone()),
1974 Some(id),
1975 cx,
1976 )
1977 }))
1978 }
1979
1980 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1981 let offset = buffer.point_utf16_to_offset(self.position);
1982 proto::GetSignatureHelp {
1983 project_id,
1984 buffer_id: buffer.remote_id().to_proto(),
1985 position: Some(serialize_anchor(&buffer.anchor_after(offset))),
1986 version: serialize_version(&buffer.version()),
1987 }
1988 }
1989
1990 async fn from_proto(
1991 payload: Self::ProtoRequest,
1992 _: Entity<LspStore>,
1993 buffer: Entity<Buffer>,
1994 mut cx: AsyncApp,
1995 ) -> Result<Self> {
1996 buffer
1997 .update(&mut cx, |buffer, _| {
1998 buffer.wait_for_version(deserialize_version(&payload.version))
1999 })
2000 .await
2001 .with_context(|| format!("waiting for version for buffer {}", buffer.entity_id()))?;
2002 let buffer_snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
2003 Ok(Self {
2004 position: payload
2005 .position
2006 .and_then(deserialize_anchor)
2007 .context("invalid position")?
2008 .to_point_utf16(&buffer_snapshot),
2009 })
2010 }
2011
2012 fn response_to_proto(
2013 response: Self::Response,
2014 _: &mut LspStore,
2015 _: PeerId,
2016 _: &Global,
2017 _: &mut App,
2018 ) -> proto::GetSignatureHelpResponse {
2019 proto::GetSignatureHelpResponse {
2020 signature_help: response
2021 .map(|signature_help| lsp_to_proto_signature(signature_help.original_data)),
2022 }
2023 }
2024
2025 async fn response_from_proto(
2026 self,
2027 response: proto::GetSignatureHelpResponse,
2028 lsp_store: Entity<LspStore>,
2029 _: Entity<Buffer>,
2030 cx: AsyncApp,
2031 ) -> Result<Self::Response> {
2032 Ok(cx.update(|cx| {
2033 response
2034 .signature_help
2035 .map(proto_to_lsp_signature)
2036 .and_then(|signature| {
2037 SignatureHelp::new(
2038 signature,
2039 Some(lsp_store.read(cx).languages.clone()),
2040 None,
2041 cx,
2042 )
2043 })
2044 }))
2045 }
2046
2047 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
2048 BufferId::new(message.buffer_id)
2049 }
2050}
2051
2052#[async_trait(?Send)]
2053impl LspCommand for GetHover {
2054 type Response = Option<Hover>;
2055 type LspRequest = lsp::request::HoverRequest;
2056 type ProtoRequest = proto::GetHover;
2057
2058 fn display_name(&self) -> &str {
2059 "Get hover"
2060 }
2061
2062 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2063 match capabilities.server_capabilities.hover_provider {
2064 Some(lsp::HoverProviderCapability::Simple(enabled)) => enabled,
2065 Some(lsp::HoverProviderCapability::Options(_)) => true,
2066 None => false,
2067 }
2068 }
2069
2070 fn to_lsp(
2071 &self,
2072 path: &Path,
2073 _: &Buffer,
2074 _: &Arc<LanguageServer>,
2075 _: &App,
2076 ) -> Result<lsp::HoverParams> {
2077 Ok(lsp::HoverParams {
2078 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
2079 work_done_progress_params: Default::default(),
2080 })
2081 }
2082
2083 async fn response_from_lsp(
2084 self,
2085 message: Option<lsp::Hover>,
2086 _: Entity<LspStore>,
2087 buffer: Entity<Buffer>,
2088 _: LanguageServerId,
2089 cx: AsyncApp,
2090 ) -> Result<Self::Response> {
2091 let Some(hover) = message else {
2092 return Ok(None);
2093 };
2094
2095 let (language, range) = buffer.read_with(&cx, |buffer, _| {
2096 (
2097 buffer.language().cloned(),
2098 hover.range.map(|range| {
2099 let token_start =
2100 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
2101 let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
2102 buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
2103 }),
2104 )
2105 });
2106
2107 fn hover_blocks_from_marked_string(marked_string: lsp::MarkedString) -> Option<HoverBlock> {
2108 let block = match marked_string {
2109 lsp::MarkedString::String(content) => HoverBlock {
2110 text: content,
2111 kind: HoverBlockKind::Markdown,
2112 },
2113 lsp::MarkedString::LanguageString(lsp::LanguageString { language, value }) => {
2114 HoverBlock {
2115 text: value,
2116 kind: HoverBlockKind::Code { language },
2117 }
2118 }
2119 };
2120 if block.text.is_empty() {
2121 None
2122 } else {
2123 Some(block)
2124 }
2125 }
2126
2127 let contents = match hover.contents {
2128 lsp::HoverContents::Scalar(marked_string) => {
2129 hover_blocks_from_marked_string(marked_string)
2130 .into_iter()
2131 .collect()
2132 }
2133 lsp::HoverContents::Array(marked_strings) => marked_strings
2134 .into_iter()
2135 .filter_map(hover_blocks_from_marked_string)
2136 .collect(),
2137 lsp::HoverContents::Markup(markup_content) => vec![HoverBlock {
2138 text: markup_content.value,
2139 kind: if markup_content.kind == lsp::MarkupKind::Markdown {
2140 HoverBlockKind::Markdown
2141 } else {
2142 HoverBlockKind::PlainText
2143 },
2144 }],
2145 };
2146
2147 Ok(Some(Hover {
2148 contents,
2149 range,
2150 language,
2151 }))
2152 }
2153
2154 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
2155 proto::GetHover {
2156 project_id,
2157 buffer_id: buffer.remote_id().into(),
2158 position: Some(language::proto::serialize_anchor(
2159 &buffer.anchor_before(self.position),
2160 )),
2161 version: serialize_version(&buffer.version),
2162 }
2163 }
2164
2165 async fn from_proto(
2166 message: Self::ProtoRequest,
2167 _: Entity<LspStore>,
2168 buffer: Entity<Buffer>,
2169 mut cx: AsyncApp,
2170 ) -> Result<Self> {
2171 let position = message
2172 .position
2173 .and_then(deserialize_anchor)
2174 .context("invalid position")?;
2175 buffer
2176 .update(&mut cx, |buffer, _| {
2177 buffer.wait_for_version(deserialize_version(&message.version))
2178 })
2179 .await?;
2180 Ok(Self {
2181 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
2182 })
2183 }
2184
2185 fn response_to_proto(
2186 response: Self::Response,
2187 _: &mut LspStore,
2188 _: PeerId,
2189 _: &clock::Global,
2190 _: &mut App,
2191 ) -> proto::GetHoverResponse {
2192 if let Some(response) = response {
2193 let (start, end) = if let Some(range) = response.range {
2194 (
2195 Some(language::proto::serialize_anchor(&range.start)),
2196 Some(language::proto::serialize_anchor(&range.end)),
2197 )
2198 } else {
2199 (None, None)
2200 };
2201
2202 let contents = response
2203 .contents
2204 .into_iter()
2205 .map(|block| proto::HoverBlock {
2206 text: block.text,
2207 is_markdown: block.kind == HoverBlockKind::Markdown,
2208 language: if let HoverBlockKind::Code { language } = block.kind {
2209 Some(language)
2210 } else {
2211 None
2212 },
2213 })
2214 .collect();
2215
2216 proto::GetHoverResponse {
2217 start,
2218 end,
2219 contents,
2220 }
2221 } else {
2222 proto::GetHoverResponse {
2223 start: None,
2224 end: None,
2225 contents: Vec::new(),
2226 }
2227 }
2228 }
2229
2230 async fn response_from_proto(
2231 self,
2232 message: proto::GetHoverResponse,
2233 _: Entity<LspStore>,
2234 buffer: Entity<Buffer>,
2235 mut cx: AsyncApp,
2236 ) -> Result<Self::Response> {
2237 let contents: Vec<_> = message
2238 .contents
2239 .into_iter()
2240 .map(|block| HoverBlock {
2241 text: block.text,
2242 kind: if let Some(language) = block.language {
2243 HoverBlockKind::Code { language }
2244 } else if block.is_markdown {
2245 HoverBlockKind::Markdown
2246 } else {
2247 HoverBlockKind::PlainText
2248 },
2249 })
2250 .collect();
2251 if contents.is_empty() {
2252 return Ok(None);
2253 }
2254
2255 let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned());
2256 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
2257 language::proto::deserialize_anchor(start)
2258 .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
2259 } else {
2260 None
2261 };
2262 if let Some(range) = range.as_ref() {
2263 buffer
2264 .update(&mut cx, |buffer, _| {
2265 buffer.wait_for_anchors([range.start, range.end])
2266 })
2267 .await?;
2268 }
2269
2270 Ok(Some(Hover {
2271 contents,
2272 range,
2273 language,
2274 }))
2275 }
2276
2277 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
2278 BufferId::new(message.buffer_id)
2279 }
2280}
2281
2282impl GetCompletions {
2283 pub fn can_resolve_completions(capabilities: &lsp::ServerCapabilities) -> bool {
2284 capabilities
2285 .completion_provider
2286 .as_ref()
2287 .and_then(|options| options.resolve_provider)
2288 .unwrap_or(false)
2289 }
2290}
2291
2292#[async_trait(?Send)]
2293impl LspCommand for GetCompletions {
2294 type Response = CoreCompletionResponse;
2295 type LspRequest = lsp::request::Completion;
2296 type ProtoRequest = proto::GetCompletions;
2297
2298 fn display_name(&self) -> &str {
2299 "Get completion"
2300 }
2301
2302 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2303 capabilities
2304 .server_capabilities
2305 .completion_provider
2306 .is_some()
2307 }
2308
2309 fn to_lsp(
2310 &self,
2311 path: &Path,
2312 _: &Buffer,
2313 _: &Arc<LanguageServer>,
2314 _: &App,
2315 ) -> Result<lsp::CompletionParams> {
2316 Ok(lsp::CompletionParams {
2317 text_document_position: make_lsp_text_document_position(path, self.position)?,
2318 context: Some(self.context.clone()),
2319 work_done_progress_params: Default::default(),
2320 partial_result_params: Default::default(),
2321 })
2322 }
2323
2324 async fn response_from_lsp(
2325 self,
2326 completions: Option<lsp::CompletionResponse>,
2327 lsp_store: Entity<LspStore>,
2328 buffer: Entity<Buffer>,
2329 server_id: LanguageServerId,
2330 mut cx: AsyncApp,
2331 ) -> Result<Self::Response> {
2332 let mut response_list = None;
2333 let (mut completions, mut is_incomplete) = if let Some(completions) = completions {
2334 match completions {
2335 lsp::CompletionResponse::Array(completions) => (completions, false),
2336 lsp::CompletionResponse::List(mut list) => {
2337 let is_incomplete = list.is_incomplete;
2338 let items = std::mem::take(&mut list.items);
2339 response_list = Some(list);
2340 (items, is_incomplete)
2341 }
2342 }
2343 } else {
2344 (Vec::new(), false)
2345 };
2346
2347 let unfiltered_completions_count = completions.len();
2348
2349 let language_server_adapter = lsp_store
2350 .read_with(&cx, |lsp_store, _| {
2351 lsp_store.language_server_adapter_for_id(server_id)
2352 })
2353 .with_context(|| format!("no language server with id {server_id}"))?;
2354
2355 let lsp_defaults = response_list
2356 .as_ref()
2357 .and_then(|list| list.item_defaults.clone())
2358 .map(Arc::new);
2359
2360 let mut completion_edits = Vec::new();
2361 buffer.update(&mut cx, |buffer, _cx| {
2362 let snapshot = buffer.snapshot();
2363 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
2364
2365 let mut range_for_token = None;
2366 completions.retain(|lsp_completion| {
2367 let lsp_edit = lsp_completion.text_edit.clone().or_else(|| {
2368 let default_text_edit = lsp_defaults.as_deref()?.edit_range.as_ref()?;
2369 let new_text = lsp_completion
2370 .text_edit_text
2371 .as_ref()
2372 .unwrap_or(&lsp_completion.label)
2373 .clone();
2374 match default_text_edit {
2375 CompletionListItemDefaultsEditRange::Range(range) => {
2376 Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
2377 range: *range,
2378 new_text,
2379 }))
2380 }
2381 CompletionListItemDefaultsEditRange::InsertAndReplace {
2382 insert,
2383 replace,
2384 } => Some(lsp::CompletionTextEdit::InsertAndReplace(
2385 lsp::InsertReplaceEdit {
2386 new_text,
2387 insert: *insert,
2388 replace: *replace,
2389 },
2390 )),
2391 }
2392 });
2393
2394 let edit = match lsp_edit {
2395 // If the language server provides a range to overwrite, then
2396 // check that the range is valid.
2397 Some(completion_text_edit) => {
2398 match parse_completion_text_edit(&completion_text_edit, &snapshot) {
2399 Some(edit) => edit,
2400 None => return false,
2401 }
2402 }
2403 // If the language server does not provide a range, then infer
2404 // the range based on the syntax tree.
2405 None => {
2406 if self.position != clipped_position {
2407 log::info!("completion out of expected range ");
2408 return false;
2409 }
2410
2411 let default_edit_range = lsp_defaults.as_ref().and_then(|lsp_defaults| {
2412 lsp_defaults
2413 .edit_range
2414 .as_ref()
2415 .and_then(|range| match range {
2416 CompletionListItemDefaultsEditRange::Range(r) => Some(r),
2417 _ => None,
2418 })
2419 });
2420
2421 let range = if let Some(range) = default_edit_range {
2422 let range = range_from_lsp(*range);
2423 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
2424 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
2425 if start != range.start.0 || end != range.end.0 {
2426 log::info!("completion out of expected range");
2427 return false;
2428 }
2429
2430 snapshot.anchor_before(start)..snapshot.anchor_after(end)
2431 } else {
2432 range_for_token
2433 .get_or_insert_with(|| {
2434 let offset = self.position.to_offset(&snapshot);
2435 let (range, kind) = snapshot.surrounding_word(
2436 offset,
2437 Some(CharScopeContext::Completion),
2438 );
2439 let range = if kind == Some(CharKind::Word) {
2440 range
2441 } else {
2442 offset..offset
2443 };
2444
2445 snapshot.anchor_before(range.start)
2446 ..snapshot.anchor_after(range.end)
2447 })
2448 .clone()
2449 };
2450
2451 // We already know text_edit is None here
2452 let text = lsp_completion
2453 .insert_text
2454 .as_ref()
2455 .unwrap_or(&lsp_completion.label)
2456 .clone();
2457
2458 ParsedCompletionEdit {
2459 replace_range: range,
2460 insert_range: None,
2461 new_text: text,
2462 }
2463 }
2464 };
2465
2466 completion_edits.push(edit);
2467 true
2468 });
2469 });
2470
2471 // If completions were filtered out due to errors that may be transient, mark the result
2472 // incomplete so that it is re-queried.
2473 if unfiltered_completions_count != completions.len() {
2474 is_incomplete = true;
2475 }
2476
2477 language_server_adapter
2478 .process_completions(&mut completions)
2479 .await;
2480
2481 let completions = completions
2482 .into_iter()
2483 .zip(completion_edits)
2484 .map(|(mut lsp_completion, mut edit)| {
2485 LineEnding::normalize(&mut edit.new_text);
2486 if lsp_completion.data.is_none()
2487 && let Some(default_data) = lsp_defaults
2488 .as_ref()
2489 .and_then(|item_defaults| item_defaults.data.clone())
2490 {
2491 // Servers (e.g. JDTLS) prefer unchanged completions, when resolving the items later,
2492 // so we do not insert the defaults here, but `data` is needed for resolving, so this is an exception.
2493 lsp_completion.data = Some(default_data);
2494 }
2495 CoreCompletion {
2496 replace_range: edit.replace_range,
2497 new_text: edit.new_text,
2498 source: CompletionSource::Lsp {
2499 insert_range: edit.insert_range,
2500 server_id,
2501 lsp_completion: Box::new(lsp_completion),
2502 lsp_defaults: lsp_defaults.clone(),
2503 resolved: false,
2504 },
2505 }
2506 })
2507 .collect();
2508
2509 Ok(CoreCompletionResponse {
2510 completions,
2511 is_incomplete,
2512 })
2513 }
2514
2515 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
2516 let anchor = buffer.anchor_after(self.position);
2517 proto::GetCompletions {
2518 project_id,
2519 buffer_id: buffer.remote_id().into(),
2520 position: Some(language::proto::serialize_anchor(&anchor)),
2521 version: serialize_version(&buffer.version()),
2522 server_id: self.server_id.map(|id| id.to_proto()),
2523 }
2524 }
2525
2526 async fn from_proto(
2527 message: proto::GetCompletions,
2528 _: Entity<LspStore>,
2529 buffer: Entity<Buffer>,
2530 mut cx: AsyncApp,
2531 ) -> Result<Self> {
2532 let version = deserialize_version(&message.version);
2533 buffer
2534 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))
2535 .await?;
2536 let position = message
2537 .position
2538 .and_then(language::proto::deserialize_anchor)
2539 .map(|p| {
2540 buffer.read_with(&cx, |buffer, _| {
2541 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
2542 })
2543 })
2544 .context("invalid position")?;
2545 Ok(Self {
2546 position,
2547 context: CompletionContext {
2548 trigger_kind: CompletionTriggerKind::INVOKED,
2549 trigger_character: None,
2550 },
2551 server_id: message
2552 .server_id
2553 .map(|id| lsp::LanguageServerId::from_proto(id)),
2554 })
2555 }
2556
2557 fn response_to_proto(
2558 response: CoreCompletionResponse,
2559 _: &mut LspStore,
2560 _: PeerId,
2561 buffer_version: &clock::Global,
2562 _: &mut App,
2563 ) -> proto::GetCompletionsResponse {
2564 proto::GetCompletionsResponse {
2565 completions: response
2566 .completions
2567 .iter()
2568 .map(LspStore::serialize_completion)
2569 .collect(),
2570 version: serialize_version(buffer_version),
2571 can_reuse: !response.is_incomplete,
2572 }
2573 }
2574
2575 async fn response_from_proto(
2576 self,
2577 message: proto::GetCompletionsResponse,
2578 _project: Entity<LspStore>,
2579 buffer: Entity<Buffer>,
2580 mut cx: AsyncApp,
2581 ) -> Result<Self::Response> {
2582 buffer
2583 .update(&mut cx, |buffer, _| {
2584 buffer.wait_for_version(deserialize_version(&message.version))
2585 })
2586 .await?;
2587
2588 let completions = message
2589 .completions
2590 .into_iter()
2591 .map(LspStore::deserialize_completion)
2592 .collect::<Result<Vec<_>>>()?;
2593
2594 Ok(CoreCompletionResponse {
2595 completions,
2596 is_incomplete: !message.can_reuse,
2597 })
2598 }
2599
2600 fn buffer_id_from_proto(message: &proto::GetCompletions) -> Result<BufferId> {
2601 BufferId::new(message.buffer_id)
2602 }
2603}
2604
2605pub struct ParsedCompletionEdit {
2606 pub replace_range: Range<Anchor>,
2607 pub insert_range: Option<Range<Anchor>>,
2608 pub new_text: String,
2609}
2610
2611pub(crate) fn parse_completion_text_edit(
2612 edit: &lsp::CompletionTextEdit,
2613 snapshot: &BufferSnapshot,
2614) -> Option<ParsedCompletionEdit> {
2615 let (replace_range, insert_range, new_text) = match edit {
2616 lsp::CompletionTextEdit::Edit(edit) => (edit.range, None, &edit.new_text),
2617 lsp::CompletionTextEdit::InsertAndReplace(edit) => {
2618 (edit.replace, Some(edit.insert), &edit.new_text)
2619 }
2620 };
2621
2622 let replace_range = {
2623 let range = range_from_lsp(replace_range);
2624 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
2625 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
2626 if start != range.start.0 || end != range.end.0 {
2627 log::info!(
2628 "completion out of expected range, start: {start:?}, end: {end:?}, range: {range:?}"
2629 );
2630 return None;
2631 }
2632 snapshot.anchor_before(start)..snapshot.anchor_after(end)
2633 };
2634
2635 let insert_range = match insert_range {
2636 None => None,
2637 Some(insert_range) => {
2638 let range = range_from_lsp(insert_range);
2639 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
2640 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
2641 if start != range.start.0 || end != range.end.0 {
2642 log::info!("completion (insert) out of expected range");
2643 return None;
2644 }
2645 Some(snapshot.anchor_before(start)..snapshot.anchor_after(end))
2646 }
2647 };
2648
2649 Some(ParsedCompletionEdit {
2650 insert_range,
2651 replace_range,
2652 new_text: new_text.clone(),
2653 })
2654}
2655
2656#[async_trait(?Send)]
2657impl LspCommand for GetCodeActions {
2658 type Response = Vec<CodeAction>;
2659 type LspRequest = lsp::request::CodeActionRequest;
2660 type ProtoRequest = proto::GetCodeActions;
2661
2662 fn display_name(&self) -> &str {
2663 "Get code actions"
2664 }
2665
2666 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2667 match &capabilities.server_capabilities.code_action_provider {
2668 None => false,
2669 Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
2670 _ => {
2671 // If we do know that we want specific code actions AND we know that
2672 // the server only supports specific code actions, then we want to filter
2673 // down to the ones that are supported.
2674 if let Some((requested, supported)) = self
2675 .kinds
2676 .as_ref()
2677 .zip(Self::supported_code_action_kinds(capabilities))
2678 {
2679 requested.iter().any(|requested_kind| {
2680 supported.iter().any(|supported_kind| {
2681 code_action_kind_matches(requested_kind, supported_kind)
2682 })
2683 })
2684 } else {
2685 true
2686 }
2687 }
2688 }
2689 }
2690
2691 fn to_lsp(
2692 &self,
2693 path: &Path,
2694 buffer: &Buffer,
2695 language_server: &Arc<LanguageServer>,
2696 _: &App,
2697 ) -> Result<lsp::CodeActionParams> {
2698 let mut relevant_diagnostics = Vec::new();
2699 for entry in buffer
2700 .snapshot()
2701 .diagnostics_in_range::<_, language::PointUtf16>(self.range.clone(), false)
2702 {
2703 relevant_diagnostics.push(entry.to_lsp_diagnostic_stub()?);
2704 }
2705
2706 let only = if let Some(requested) = &self.kinds {
2707 if let Some(supported_kinds) =
2708 Self::supported_code_action_kinds(language_server.adapter_server_capabilities())
2709 {
2710 let filtered = requested
2711 .iter()
2712 .filter(|requested_kind| {
2713 supported_kinds.iter().any(|supported_kind| {
2714 code_action_kind_matches(requested_kind, supported_kind)
2715 })
2716 })
2717 .cloned()
2718 .collect();
2719 Some(filtered)
2720 } else {
2721 Some(requested.clone())
2722 }
2723 } else {
2724 None
2725 };
2726
2727 Ok(lsp::CodeActionParams {
2728 text_document: make_text_document_identifier(path)?,
2729 range: range_to_lsp(self.range.to_point_utf16(buffer))?,
2730 work_done_progress_params: Default::default(),
2731 partial_result_params: Default::default(),
2732 context: lsp::CodeActionContext {
2733 diagnostics: relevant_diagnostics,
2734 only,
2735 ..lsp::CodeActionContext::default()
2736 },
2737 })
2738 }
2739
2740 async fn response_from_lsp(
2741 self,
2742 actions: Option<lsp::CodeActionResponse>,
2743 lsp_store: Entity<LspStore>,
2744 _: Entity<Buffer>,
2745 server_id: LanguageServerId,
2746 cx: AsyncApp,
2747 ) -> Result<Vec<CodeAction>> {
2748 let requested_kinds = self.kinds.as_ref();
2749
2750 let language_server = cx.update(|cx| {
2751 lsp_store
2752 .read(cx)
2753 .language_server_for_id(server_id)
2754 .with_context(|| {
2755 format!("Missing the language server that just returned a response {server_id}")
2756 })
2757 })?;
2758
2759 let server_capabilities = language_server.capabilities();
2760 let available_commands = server_capabilities
2761 .execute_command_provider
2762 .as_ref()
2763 .map(|options| options.commands.as_slice())
2764 .unwrap_or_default();
2765 Ok(actions
2766 .unwrap_or_default()
2767 .into_iter()
2768 .filter_map(|entry| {
2769 let (lsp_action, resolved) = match entry {
2770 lsp::CodeActionOrCommand::CodeAction(lsp_action) => {
2771 if let Some(command) = lsp_action.command.as_ref()
2772 && !available_commands.contains(&command.command)
2773 {
2774 return None;
2775 }
2776 (LspAction::Action(Box::new(lsp_action)), false)
2777 }
2778 lsp::CodeActionOrCommand::Command(command) => {
2779 if available_commands.contains(&command.command) {
2780 (LspAction::Command(command), true)
2781 } else {
2782 return None;
2783 }
2784 }
2785 };
2786
2787 if let Some((kinds, kind)) = requested_kinds.zip(lsp_action.action_kind())
2788 && !kinds
2789 .iter()
2790 .any(|requested_kind| code_action_kind_matches(requested_kind, &kind))
2791 {
2792 return None;
2793 }
2794
2795 Some(CodeAction {
2796 server_id,
2797 range: self.range.clone(),
2798 lsp_action,
2799 resolved,
2800 })
2801 })
2802 .collect())
2803 }
2804
2805 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
2806 proto::GetCodeActions {
2807 project_id,
2808 buffer_id: buffer.remote_id().into(),
2809 start: Some(language::proto::serialize_anchor(&self.range.start)),
2810 end: Some(language::proto::serialize_anchor(&self.range.end)),
2811 version: serialize_version(&buffer.version()),
2812 }
2813 }
2814
2815 async fn from_proto(
2816 message: proto::GetCodeActions,
2817 _: Entity<LspStore>,
2818 buffer: Entity<Buffer>,
2819 mut cx: AsyncApp,
2820 ) -> Result<Self> {
2821 let start = message
2822 .start
2823 .and_then(language::proto::deserialize_anchor)
2824 .context("invalid start")?;
2825 let end = message
2826 .end
2827 .and_then(language::proto::deserialize_anchor)
2828 .context("invalid end")?;
2829 buffer
2830 .update(&mut cx, |buffer, _| {
2831 buffer.wait_for_version(deserialize_version(&message.version))
2832 })
2833 .await?;
2834
2835 Ok(Self {
2836 range: start..end,
2837 kinds: None,
2838 })
2839 }
2840
2841 fn response_to_proto(
2842 code_actions: Vec<CodeAction>,
2843 _: &mut LspStore,
2844 _: PeerId,
2845 buffer_version: &clock::Global,
2846 _: &mut App,
2847 ) -> proto::GetCodeActionsResponse {
2848 proto::GetCodeActionsResponse {
2849 actions: code_actions
2850 .iter()
2851 .map(LspStore::serialize_code_action)
2852 .collect(),
2853 version: serialize_version(buffer_version),
2854 }
2855 }
2856
2857 async fn response_from_proto(
2858 self,
2859 message: proto::GetCodeActionsResponse,
2860 _: Entity<LspStore>,
2861 buffer: Entity<Buffer>,
2862 mut cx: AsyncApp,
2863 ) -> Result<Vec<CodeAction>> {
2864 buffer
2865 .update(&mut cx, |buffer, _| {
2866 buffer.wait_for_version(deserialize_version(&message.version))
2867 })
2868 .await?;
2869 message
2870 .actions
2871 .into_iter()
2872 .map(LspStore::deserialize_code_action)
2873 .collect()
2874 }
2875
2876 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> Result<BufferId> {
2877 BufferId::new(message.buffer_id)
2878 }
2879}
2880
2881impl GetCodeActions {
2882 fn supported_code_action_kinds(
2883 capabilities: AdapterServerCapabilities,
2884 ) -> Option<Vec<CodeActionKind>> {
2885 match capabilities.server_capabilities.code_action_provider {
2886 Some(lsp::CodeActionProviderCapability::Options(CodeActionOptions {
2887 code_action_kinds: Some(supported_action_kinds),
2888 ..
2889 })) => Some(supported_action_kinds),
2890 _ => capabilities.code_action_kinds,
2891 }
2892 }
2893
2894 pub fn can_resolve_actions(capabilities: &ServerCapabilities) -> bool {
2895 capabilities
2896 .code_action_provider
2897 .as_ref()
2898 .and_then(|options| match options {
2899 lsp::CodeActionProviderCapability::Simple(_is_supported) => None,
2900 lsp::CodeActionProviderCapability::Options(options) => options.resolve_provider,
2901 })
2902 .unwrap_or(false)
2903 }
2904}
2905
2906impl OnTypeFormatting {
2907 pub fn supports_on_type_formatting(trigger: &str, capabilities: &ServerCapabilities) -> bool {
2908 let Some(on_type_formatting_options) = &capabilities.document_on_type_formatting_provider
2909 else {
2910 return false;
2911 };
2912 on_type_formatting_options
2913 .first_trigger_character
2914 .contains(trigger)
2915 || on_type_formatting_options
2916 .more_trigger_character
2917 .iter()
2918 .flatten()
2919 .any(|chars| chars.contains(trigger))
2920 }
2921}
2922
2923#[async_trait(?Send)]
2924impl LspCommand for OnTypeFormatting {
2925 type Response = Option<Transaction>;
2926 type LspRequest = lsp::request::OnTypeFormatting;
2927 type ProtoRequest = proto::OnTypeFormatting;
2928
2929 fn display_name(&self) -> &str {
2930 "Formatting on typing"
2931 }
2932
2933 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2934 Self::supports_on_type_formatting(&self.trigger, &capabilities.server_capabilities)
2935 }
2936
2937 fn to_lsp(
2938 &self,
2939 path: &Path,
2940 _: &Buffer,
2941 _: &Arc<LanguageServer>,
2942 _: &App,
2943 ) -> Result<lsp::DocumentOnTypeFormattingParams> {
2944 Ok(lsp::DocumentOnTypeFormattingParams {
2945 text_document_position: make_lsp_text_document_position(path, self.position)?,
2946 ch: self.trigger.clone(),
2947 options: self.options.clone(),
2948 })
2949 }
2950
2951 async fn response_from_lsp(
2952 self,
2953 message: Option<Vec<lsp::TextEdit>>,
2954 lsp_store: Entity<LspStore>,
2955 buffer: Entity<Buffer>,
2956 server_id: LanguageServerId,
2957 mut cx: AsyncApp,
2958 ) -> Result<Option<Transaction>> {
2959 if let Some(edits) = message {
2960 let (lsp_adapter, lsp_server) =
2961 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
2962 LocalLspStore::deserialize_text_edits(
2963 lsp_store,
2964 buffer,
2965 edits,
2966 self.push_to_history,
2967 lsp_adapter,
2968 lsp_server,
2969 &mut cx,
2970 )
2971 .await
2972 } else {
2973 Ok(None)
2974 }
2975 }
2976
2977 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
2978 proto::OnTypeFormatting {
2979 project_id,
2980 buffer_id: buffer.remote_id().into(),
2981 position: Some(language::proto::serialize_anchor(
2982 &buffer.anchor_before(self.position),
2983 )),
2984 trigger: self.trigger.clone(),
2985 version: serialize_version(&buffer.version()),
2986 }
2987 }
2988
2989 async fn from_proto(
2990 message: proto::OnTypeFormatting,
2991 _: Entity<LspStore>,
2992 buffer: Entity<Buffer>,
2993 mut cx: AsyncApp,
2994 ) -> Result<Self> {
2995 let position = message
2996 .position
2997 .and_then(deserialize_anchor)
2998 .context("invalid position")?;
2999 buffer
3000 .update(&mut cx, |buffer, _| {
3001 buffer.wait_for_version(deserialize_version(&message.version))
3002 })
3003 .await?;
3004
3005 let options = buffer.update(&mut cx, |buffer, cx| {
3006 lsp_formatting_options(LanguageSettings::for_buffer(buffer, cx).as_ref())
3007 });
3008
3009 Ok(Self {
3010 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
3011 trigger: message.trigger.clone(),
3012 options,
3013 push_to_history: false,
3014 })
3015 }
3016
3017 fn response_to_proto(
3018 response: Option<Transaction>,
3019 _: &mut LspStore,
3020 _: PeerId,
3021 _: &clock::Global,
3022 _: &mut App,
3023 ) -> proto::OnTypeFormattingResponse {
3024 proto::OnTypeFormattingResponse {
3025 transaction: response
3026 .map(|transaction| language::proto::serialize_transaction(&transaction)),
3027 }
3028 }
3029
3030 async fn response_from_proto(
3031 self,
3032 message: proto::OnTypeFormattingResponse,
3033 _: Entity<LspStore>,
3034 _: Entity<Buffer>,
3035 _: AsyncApp,
3036 ) -> Result<Option<Transaction>> {
3037 let Some(transaction) = message.transaction else {
3038 return Ok(None);
3039 };
3040 Ok(Some(language::proto::deserialize_transaction(transaction)?))
3041 }
3042
3043 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> Result<BufferId> {
3044 BufferId::new(message.buffer_id)
3045 }
3046}
3047
3048impl InlayHints {
3049 pub async fn lsp_to_project_hint(
3050 lsp_hint: lsp::InlayHint,
3051 buffer_handle: &Entity<Buffer>,
3052 server_id: LanguageServerId,
3053 resolve_state: ResolveState,
3054 force_no_type_left_padding: bool,
3055 cx: &mut AsyncApp,
3056 ) -> anyhow::Result<InlayHint> {
3057 let kind = lsp_hint.kind.and_then(|kind| match kind {
3058 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
3059 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
3060 _ => None,
3061 });
3062
3063 let position = buffer_handle.read_with(cx, |buffer, _| {
3064 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
3065 if kind == Some(InlayHintKind::Parameter) {
3066 buffer.anchor_before(position)
3067 } else {
3068 buffer.anchor_after(position)
3069 }
3070 });
3071 let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
3072 .await
3073 .context("lsp to project inlay hint conversion")?;
3074 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
3075 false
3076 } else {
3077 lsp_hint.padding_left.unwrap_or(false)
3078 };
3079
3080 Ok(InlayHint {
3081 position,
3082 padding_left,
3083 padding_right: lsp_hint.padding_right.unwrap_or(false),
3084 label,
3085 kind,
3086 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
3087 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
3088 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
3089 InlayHintTooltip::MarkupContent(MarkupContent {
3090 kind: match markup_content.kind {
3091 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
3092 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
3093 },
3094 value: markup_content.value,
3095 })
3096 }
3097 }),
3098 resolve_state,
3099 })
3100 }
3101
3102 async fn lsp_inlay_label_to_project(
3103 lsp_label: lsp::InlayHintLabel,
3104 server_id: LanguageServerId,
3105 ) -> anyhow::Result<InlayHintLabel> {
3106 let label = match lsp_label {
3107 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
3108 lsp::InlayHintLabel::LabelParts(lsp_parts) => {
3109 let mut parts = Vec::with_capacity(lsp_parts.len());
3110 for lsp_part in lsp_parts {
3111 parts.push(InlayHintLabelPart {
3112 value: lsp_part.value,
3113 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
3114 lsp::InlayHintLabelPartTooltip::String(s) => {
3115 InlayHintLabelPartTooltip::String(s)
3116 }
3117 lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
3118 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
3119 kind: match markup_content.kind {
3120 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
3121 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
3122 },
3123 value: markup_content.value,
3124 })
3125 }
3126 }),
3127 location: Some(server_id).zip(lsp_part.location),
3128 });
3129 }
3130 InlayHintLabel::LabelParts(parts)
3131 }
3132 };
3133
3134 Ok(label)
3135 }
3136
3137 pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
3138 let (state, lsp_resolve_state) = match response_hint.resolve_state {
3139 ResolveState::Resolved => (0, None),
3140 ResolveState::CanResolve(server_id, resolve_data) => (
3141 1,
3142 Some(proto::resolve_state::LspResolveState {
3143 server_id: server_id.0 as u64,
3144 value: resolve_data.map(|json_data| {
3145 serde_json::to_string(&json_data)
3146 .expect("failed to serialize resolve json data")
3147 }),
3148 }),
3149 ),
3150 ResolveState::Resolving => (2, None),
3151 };
3152 let resolve_state = Some(proto::ResolveState {
3153 state,
3154 lsp_resolve_state,
3155 });
3156 proto::InlayHint {
3157 position: Some(language::proto::serialize_anchor(&response_hint.position)),
3158 padding_left: response_hint.padding_left,
3159 padding_right: response_hint.padding_right,
3160 label: Some(proto::InlayHintLabel {
3161 label: Some(match response_hint.label {
3162 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
3163 InlayHintLabel::LabelParts(label_parts) => {
3164 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
3165 parts: label_parts.into_iter().map(|label_part| {
3166 let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
3167 let location_range_start = label_part.location.as_ref().map(|(_, location)| point_from_lsp(location.range.start).0).map(|point| proto::PointUtf16 { row: point.row, column: point.column });
3168 let location_range_end = label_part.location.as_ref().map(|(_, location)| point_from_lsp(location.range.end).0).map(|point| proto::PointUtf16 { row: point.row, column: point.column });
3169 proto::InlayHintLabelPart {
3170 value: label_part.value,
3171 tooltip: label_part.tooltip.map(|tooltip| {
3172 let proto_tooltip = match tooltip {
3173 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
3174 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
3175 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
3176 value: markup_content.value,
3177 }),
3178 };
3179 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
3180 }),
3181 location_url,
3182 location_range_start,
3183 location_range_end,
3184 language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
3185 }}).collect()
3186 })
3187 }
3188 }),
3189 }),
3190 kind: response_hint.kind.map(|kind| kind.name().to_string()),
3191 tooltip: response_hint.tooltip.map(|response_tooltip| {
3192 let proto_tooltip = match response_tooltip {
3193 InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
3194 InlayHintTooltip::MarkupContent(markup_content) => {
3195 proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
3196 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
3197 value: markup_content.value,
3198 })
3199 }
3200 };
3201 proto::InlayHintTooltip {
3202 content: Some(proto_tooltip),
3203 }
3204 }),
3205 resolve_state,
3206 }
3207 }
3208
3209 pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
3210 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
3211 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
3212 });
3213 let resolve_state_data = resolve_state
3214 .lsp_resolve_state.as_ref()
3215 .map(|lsp_resolve_state| {
3216 let value = lsp_resolve_state.value.as_deref().map(|value| {
3217 serde_json::from_str::<Option<lsp::LSPAny>>(value)
3218 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
3219 }).transpose()?.flatten();
3220 anyhow::Ok((LanguageServerId(lsp_resolve_state.server_id as usize), value))
3221 })
3222 .transpose()?;
3223 let resolve_state = match resolve_state.state {
3224 0 => ResolveState::Resolved,
3225 1 => {
3226 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
3227 format!(
3228 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
3229 )
3230 })?;
3231 ResolveState::CanResolve(server_id, lsp_resolve_state)
3232 }
3233 2 => ResolveState::Resolving,
3234 invalid => {
3235 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
3236 }
3237 };
3238 Ok(InlayHint {
3239 position: message_hint
3240 .position
3241 .and_then(language::proto::deserialize_anchor)
3242 .context("invalid position")?,
3243 label: match message_hint
3244 .label
3245 .and_then(|label| label.label)
3246 .context("missing label")?
3247 {
3248 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
3249 proto::inlay_hint_label::Label::LabelParts(parts) => {
3250 let mut label_parts = Vec::new();
3251 for part in parts.parts {
3252 label_parts.push(InlayHintLabelPart {
3253 value: part.value,
3254 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
3255 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
3256 InlayHintLabelPartTooltip::String(s)
3257 }
3258 Some(
3259 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
3260 markup_content,
3261 ),
3262 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
3263 kind: if markup_content.is_markdown {
3264 HoverBlockKind::Markdown
3265 } else {
3266 HoverBlockKind::PlainText
3267 },
3268 value: markup_content.value,
3269 }),
3270 None => InlayHintLabelPartTooltip::String(String::new()),
3271 }),
3272 location: {
3273 match part
3274 .location_url
3275 .zip(
3276 part.location_range_start.and_then(|start| {
3277 Some(start..part.location_range_end?)
3278 }),
3279 )
3280 .zip(part.language_server_id)
3281 {
3282 Some(((uri, range), server_id)) => Some((
3283 LanguageServerId(server_id as usize),
3284 lsp::Location {
3285 uri: lsp::Uri::from_str(&uri).with_context(|| {
3286 format!("invalid uri in hint part {uri:?}")
3287 })?,
3288 range: lsp::Range::new(
3289 point_to_lsp(PointUtf16::new(
3290 range.start.row,
3291 range.start.column,
3292 )),
3293 point_to_lsp(PointUtf16::new(
3294 range.end.row,
3295 range.end.column,
3296 )),
3297 ),
3298 },
3299 )),
3300 None => None,
3301 }
3302 },
3303 });
3304 }
3305
3306 InlayHintLabel::LabelParts(label_parts)
3307 }
3308 },
3309 padding_left: message_hint.padding_left,
3310 padding_right: message_hint.padding_right,
3311 kind: message_hint
3312 .kind
3313 .as_deref()
3314 .and_then(InlayHintKind::from_name),
3315 tooltip: message_hint.tooltip.and_then(|tooltip| {
3316 Some(match tooltip.content? {
3317 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
3318 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
3319 InlayHintTooltip::MarkupContent(MarkupContent {
3320 kind: if markup_content.is_markdown {
3321 HoverBlockKind::Markdown
3322 } else {
3323 HoverBlockKind::PlainText
3324 },
3325 value: markup_content.value,
3326 })
3327 }
3328 })
3329 }),
3330 resolve_state,
3331 })
3332 }
3333
3334 pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
3335 lsp::InlayHint {
3336 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
3337 kind: hint.kind.map(|kind| match kind {
3338 InlayHintKind::Type => lsp::InlayHintKind::TYPE,
3339 InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
3340 }),
3341 text_edits: None,
3342 tooltip: hint.tooltip.and_then(|tooltip| {
3343 Some(match tooltip {
3344 InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
3345 InlayHintTooltip::MarkupContent(markup_content) => {
3346 lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
3347 kind: match markup_content.kind {
3348 HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
3349 HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
3350 HoverBlockKind::Code { .. } => return None,
3351 },
3352 value: markup_content.value,
3353 })
3354 }
3355 })
3356 }),
3357 label: match hint.label {
3358 InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
3359 InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
3360 label_parts
3361 .into_iter()
3362 .map(|part| lsp::InlayHintLabelPart {
3363 value: part.value,
3364 tooltip: part.tooltip.and_then(|tooltip| {
3365 Some(match tooltip {
3366 InlayHintLabelPartTooltip::String(s) => {
3367 lsp::InlayHintLabelPartTooltip::String(s)
3368 }
3369 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
3370 lsp::InlayHintLabelPartTooltip::MarkupContent(
3371 lsp::MarkupContent {
3372 kind: match markup_content.kind {
3373 HoverBlockKind::PlainText => {
3374 lsp::MarkupKind::PlainText
3375 }
3376 HoverBlockKind::Markdown => {
3377 lsp::MarkupKind::Markdown
3378 }
3379 HoverBlockKind::Code { .. } => return None,
3380 },
3381 value: markup_content.value,
3382 },
3383 )
3384 }
3385 })
3386 }),
3387 location: part.location.map(|(_, location)| location),
3388 command: None,
3389 })
3390 .collect(),
3391 ),
3392 },
3393 padding_left: Some(hint.padding_left),
3394 padding_right: Some(hint.padding_right),
3395 data: match hint.resolve_state {
3396 ResolveState::CanResolve(_, data) => data,
3397 ResolveState::Resolving | ResolveState::Resolved => None,
3398 },
3399 }
3400 }
3401
3402 pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
3403 capabilities
3404 .inlay_hint_provider
3405 .as_ref()
3406 .and_then(|options| match options {
3407 OneOf::Left(_is_supported) => None,
3408 OneOf::Right(capabilities) => match capabilities {
3409 lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
3410 lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
3411 o.inlay_hint_options.resolve_provider
3412 }
3413 },
3414 })
3415 .unwrap_or(false)
3416 }
3417
3418 pub fn check_capabilities(capabilities: &ServerCapabilities) -> bool {
3419 capabilities
3420 .inlay_hint_provider
3421 .as_ref()
3422 .is_some_and(|inlay_hint_provider| match inlay_hint_provider {
3423 lsp::OneOf::Left(enabled) => *enabled,
3424 lsp::OneOf::Right(_) => true,
3425 })
3426 }
3427}
3428
3429#[async_trait(?Send)]
3430impl LspCommand for InlayHints {
3431 type Response = Vec<InlayHint>;
3432 type LspRequest = lsp::InlayHintRequest;
3433 type ProtoRequest = proto::InlayHints;
3434
3435 fn display_name(&self) -> &str {
3436 "Inlay hints"
3437 }
3438
3439 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
3440 Self::check_capabilities(&capabilities.server_capabilities)
3441 }
3442
3443 fn to_lsp(
3444 &self,
3445 path: &Path,
3446 buffer: &Buffer,
3447 _: &Arc<LanguageServer>,
3448 _: &App,
3449 ) -> Result<lsp::InlayHintParams> {
3450 Ok(lsp::InlayHintParams {
3451 text_document: lsp::TextDocumentIdentifier {
3452 uri: file_path_to_lsp_url(path)?,
3453 },
3454 range: range_to_lsp(self.range.to_point_utf16(buffer))?,
3455 work_done_progress_params: Default::default(),
3456 })
3457 }
3458
3459 async fn response_from_lsp(
3460 self,
3461 message: Option<Vec<lsp::InlayHint>>,
3462 lsp_store: Entity<LspStore>,
3463 buffer: Entity<Buffer>,
3464 server_id: LanguageServerId,
3465 mut cx: AsyncApp,
3466 ) -> anyhow::Result<Vec<InlayHint>> {
3467 let (lsp_adapter, lsp_server) =
3468 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
3469 // `typescript-language-server` adds padding to the left for type hints, turning
3470 // `const foo: boolean` into `const foo : boolean` which looks odd.
3471 // `rust-analyzer` does not have the padding for this case, and we have to accommodate both.
3472 //
3473 // We could trim the whole string, but being pessimistic on par with the situation above,
3474 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
3475 // Hence let's use a heuristic first to handle the most awkward case and look for more.
3476 let force_no_type_left_padding =
3477 lsp_adapter.name.0.as_ref() == "typescript-language-server";
3478
3479 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
3480 let resolve_state = if InlayHints::can_resolve_inlays(&lsp_server.capabilities()) {
3481 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
3482 } else {
3483 ResolveState::Resolved
3484 };
3485
3486 let buffer = buffer.clone();
3487 cx.spawn(async move |cx| {
3488 InlayHints::lsp_to_project_hint(
3489 lsp_hint,
3490 &buffer,
3491 server_id,
3492 resolve_state,
3493 force_no_type_left_padding,
3494 cx,
3495 )
3496 .await
3497 })
3498 });
3499 future::join_all(hints)
3500 .await
3501 .into_iter()
3502 .collect::<anyhow::Result<_>>()
3503 .context("lsp to project inlay hints conversion")
3504 }
3505
3506 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
3507 proto::InlayHints {
3508 project_id,
3509 buffer_id: buffer.remote_id().into(),
3510 start: Some(language::proto::serialize_anchor(&self.range.start)),
3511 end: Some(language::proto::serialize_anchor(&self.range.end)),
3512 version: serialize_version(&buffer.version()),
3513 }
3514 }
3515
3516 async fn from_proto(
3517 message: proto::InlayHints,
3518 _: Entity<LspStore>,
3519 buffer: Entity<Buffer>,
3520 mut cx: AsyncApp,
3521 ) -> Result<Self> {
3522 let start = message
3523 .start
3524 .and_then(language::proto::deserialize_anchor)
3525 .context("invalid start")?;
3526 let end = message
3527 .end
3528 .and_then(language::proto::deserialize_anchor)
3529 .context("invalid end")?;
3530 buffer
3531 .update(&mut cx, |buffer, _| {
3532 buffer.wait_for_version(deserialize_version(&message.version))
3533 })
3534 .await?;
3535
3536 Ok(Self { range: start..end })
3537 }
3538
3539 fn response_to_proto(
3540 response: Vec<InlayHint>,
3541 _: &mut LspStore,
3542 _: PeerId,
3543 buffer_version: &clock::Global,
3544 _: &mut App,
3545 ) -> proto::InlayHintsResponse {
3546 proto::InlayHintsResponse {
3547 hints: response
3548 .into_iter()
3549 .map(InlayHints::project_to_proto_hint)
3550 .collect(),
3551 version: serialize_version(buffer_version),
3552 }
3553 }
3554
3555 async fn response_from_proto(
3556 self,
3557 message: proto::InlayHintsResponse,
3558 _: Entity<LspStore>,
3559 buffer: Entity<Buffer>,
3560 mut cx: AsyncApp,
3561 ) -> anyhow::Result<Vec<InlayHint>> {
3562 buffer
3563 .update(&mut cx, |buffer, _| {
3564 buffer.wait_for_version(deserialize_version(&message.version))
3565 })
3566 .await?;
3567
3568 let mut hints = Vec::new();
3569 for message_hint in message.hints {
3570 hints.push(InlayHints::proto_to_project_hint(message_hint)?);
3571 }
3572
3573 Ok(hints)
3574 }
3575
3576 fn buffer_id_from_proto(message: &proto::InlayHints) -> Result<BufferId> {
3577 BufferId::new(message.buffer_id)
3578 }
3579}
3580
3581#[async_trait(?Send)]
3582impl LspCommand for SemanticTokensFull {
3583 type Response = SemanticTokensResponse;
3584 type LspRequest = lsp::SemanticTokensFullRequest;
3585 type ProtoRequest = proto::SemanticTokens;
3586
3587 fn display_name(&self) -> &str {
3588 "Semantic tokens full"
3589 }
3590
3591 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
3592 capabilities
3593 .server_capabilities
3594 .semantic_tokens_provider
3595 .as_ref()
3596 .is_some_and(|semantic_tokens_provider| {
3597 let options = match semantic_tokens_provider {
3598 lsp::SemanticTokensServerCapabilities::SemanticTokensOptions(opts) => opts,
3599 lsp::SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions(
3600 opts,
3601 ) => &opts.semantic_tokens_options,
3602 };
3603
3604 match options.full {
3605 Some(lsp::SemanticTokensFullOptions::Bool(is_supported)) => is_supported,
3606 Some(lsp::SemanticTokensFullOptions::Delta { .. }) => true,
3607 None => false,
3608 }
3609 })
3610 }
3611
3612 fn to_lsp(
3613 &self,
3614 path: &Path,
3615 _: &Buffer,
3616 _: &Arc<LanguageServer>,
3617 _: &App,
3618 ) -> Result<lsp::SemanticTokensParams> {
3619 Ok(lsp::SemanticTokensParams {
3620 text_document: lsp::TextDocumentIdentifier {
3621 uri: file_path_to_lsp_url(path)?,
3622 },
3623 partial_result_params: Default::default(),
3624 work_done_progress_params: Default::default(),
3625 })
3626 }
3627
3628 async fn response_from_lsp(
3629 self,
3630 message: Option<lsp::SemanticTokensResult>,
3631 _: Entity<LspStore>,
3632 _: Entity<Buffer>,
3633 _: LanguageServerId,
3634 _: AsyncApp,
3635 ) -> anyhow::Result<SemanticTokensResponse> {
3636 match message {
3637 Some(lsp::SemanticTokensResult::Tokens(tokens)) => Ok(SemanticTokensResponse::Full {
3638 data: tokens.data,
3639 result_id: tokens.result_id.map(SharedString::new),
3640 }),
3641 Some(lsp::SemanticTokensResult::Partial(_)) => {
3642 anyhow::bail!(
3643 "Unexpected semantic tokens response with partial result for inlay hints"
3644 )
3645 }
3646 None => Ok(Default::default()),
3647 }
3648 }
3649
3650 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::SemanticTokens {
3651 proto::SemanticTokens {
3652 project_id,
3653 buffer_id: buffer.remote_id().into(),
3654 version: serialize_version(&buffer.version()),
3655 for_server: self.for_server.map(|id| id.to_proto()),
3656 }
3657 }
3658
3659 async fn from_proto(
3660 message: proto::SemanticTokens,
3661 _: Entity<LspStore>,
3662 buffer: Entity<Buffer>,
3663 mut cx: AsyncApp,
3664 ) -> Result<Self> {
3665 buffer
3666 .update(&mut cx, |buffer, _| {
3667 buffer.wait_for_version(deserialize_version(&message.version))
3668 })
3669 .await?;
3670
3671 Ok(Self {
3672 for_server: message
3673 .for_server
3674 .map(|id| LanguageServerId::from_proto(id)),
3675 })
3676 }
3677
3678 fn response_to_proto(
3679 response: SemanticTokensResponse,
3680 _: &mut LspStore,
3681 _: PeerId,
3682 buffer_version: &clock::Global,
3683 _: &mut App,
3684 ) -> proto::SemanticTokensResponse {
3685 match response {
3686 SemanticTokensResponse::Full { data, result_id } => proto::SemanticTokensResponse {
3687 data,
3688 edits: Vec::new(),
3689 result_id: result_id.map(|s| s.to_string()),
3690 version: serialize_version(buffer_version),
3691 },
3692 SemanticTokensResponse::Delta { edits, result_id } => proto::SemanticTokensResponse {
3693 data: Vec::new(),
3694 edits: edits
3695 .into_iter()
3696 .map(|edit| proto::SemanticTokensEdit {
3697 start: edit.start,
3698 delete_count: edit.delete_count,
3699 data: edit.data,
3700 })
3701 .collect(),
3702 result_id: result_id.map(|s| s.to_string()),
3703 version: serialize_version(buffer_version),
3704 },
3705 }
3706 }
3707
3708 async fn response_from_proto(
3709 self,
3710 message: proto::SemanticTokensResponse,
3711 _: Entity<LspStore>,
3712 buffer: Entity<Buffer>,
3713 mut cx: AsyncApp,
3714 ) -> anyhow::Result<SemanticTokensResponse> {
3715 buffer
3716 .update(&mut cx, |buffer, _| {
3717 buffer.wait_for_version(deserialize_version(&message.version))
3718 })
3719 .await?;
3720
3721 Ok(SemanticTokensResponse::Full {
3722 data: message.data,
3723 result_id: message.result_id.map(SharedString::new),
3724 })
3725 }
3726
3727 fn buffer_id_from_proto(message: &proto::SemanticTokens) -> Result<BufferId> {
3728 BufferId::new(message.buffer_id)
3729 }
3730}
3731
3732#[async_trait(?Send)]
3733impl LspCommand for SemanticTokensDelta {
3734 type Response = SemanticTokensResponse;
3735 type LspRequest = lsp::SemanticTokensFullDeltaRequest;
3736 type ProtoRequest = proto::SemanticTokens;
3737
3738 fn display_name(&self) -> &str {
3739 "Semantic tokens delta"
3740 }
3741
3742 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
3743 capabilities
3744 .server_capabilities
3745 .semantic_tokens_provider
3746 .as_ref()
3747 .is_some_and(|semantic_tokens_provider| {
3748 let options = match semantic_tokens_provider {
3749 lsp::SemanticTokensServerCapabilities::SemanticTokensOptions(opts) => opts,
3750 lsp::SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions(
3751 opts,
3752 ) => &opts.semantic_tokens_options,
3753 };
3754
3755 match options.full {
3756 Some(lsp::SemanticTokensFullOptions::Delta { delta }) => delta.unwrap_or(false),
3757 // `full: true` (instead of `full: { delta: true }`) means no support for delta.
3758 _ => false,
3759 }
3760 })
3761 }
3762
3763 fn to_lsp(
3764 &self,
3765 path: &Path,
3766 _: &Buffer,
3767 _: &Arc<LanguageServer>,
3768 _: &App,
3769 ) -> Result<lsp::SemanticTokensDeltaParams> {
3770 Ok(lsp::SemanticTokensDeltaParams {
3771 text_document: lsp::TextDocumentIdentifier {
3772 uri: file_path_to_lsp_url(path)?,
3773 },
3774 previous_result_id: self.previous_result_id.clone().map(|s| s.to_string()),
3775 partial_result_params: Default::default(),
3776 work_done_progress_params: Default::default(),
3777 })
3778 }
3779
3780 async fn response_from_lsp(
3781 self,
3782 message: Option<lsp::SemanticTokensFullDeltaResult>,
3783 _: Entity<LspStore>,
3784 _: Entity<Buffer>,
3785 _: LanguageServerId,
3786 _: AsyncApp,
3787 ) -> anyhow::Result<SemanticTokensResponse> {
3788 match message {
3789 Some(lsp::SemanticTokensFullDeltaResult::Tokens(tokens)) => {
3790 Ok(SemanticTokensResponse::Full {
3791 data: tokens.data,
3792 result_id: tokens.result_id.map(SharedString::new),
3793 })
3794 }
3795 Some(lsp::SemanticTokensFullDeltaResult::TokensDelta(delta)) => {
3796 Ok(SemanticTokensResponse::Delta {
3797 edits: delta
3798 .edits
3799 .into_iter()
3800 .map(|e| SemanticTokensEdit {
3801 start: e.start,
3802 delete_count: e.delete_count,
3803 data: e.data.unwrap_or_default(),
3804 })
3805 .collect(),
3806 result_id: delta.result_id.map(SharedString::new),
3807 })
3808 }
3809 Some(lsp::SemanticTokensFullDeltaResult::PartialTokensDelta { .. }) => {
3810 anyhow::bail!(
3811 "Unexpected semantic tokens response with partial result for inlay hints"
3812 )
3813 }
3814 None => Ok(Default::default()),
3815 }
3816 }
3817
3818 fn to_proto(&self, _: u64, _: &Buffer) -> proto::SemanticTokens {
3819 unimplemented!("Delta requests are never initialted on the remote client side")
3820 }
3821
3822 async fn from_proto(
3823 _: proto::SemanticTokens,
3824 _: Entity<LspStore>,
3825 _: Entity<Buffer>,
3826 _: AsyncApp,
3827 ) -> Result<Self> {
3828 unimplemented!("Delta requests are never initialted on the remote client side")
3829 }
3830
3831 fn response_to_proto(
3832 response: SemanticTokensResponse,
3833 _: &mut LspStore,
3834 _: PeerId,
3835 buffer_version: &clock::Global,
3836 _: &mut App,
3837 ) -> proto::SemanticTokensResponse {
3838 match response {
3839 SemanticTokensResponse::Full { data, result_id } => proto::SemanticTokensResponse {
3840 data,
3841 edits: Vec::new(),
3842 result_id: result_id.map(|s| s.to_string()),
3843 version: serialize_version(buffer_version),
3844 },
3845 SemanticTokensResponse::Delta { edits, result_id } => proto::SemanticTokensResponse {
3846 data: Vec::new(),
3847 edits: edits
3848 .into_iter()
3849 .map(|edit| proto::SemanticTokensEdit {
3850 start: edit.start,
3851 delete_count: edit.delete_count,
3852 data: edit.data,
3853 })
3854 .collect(),
3855 result_id: result_id.map(|s| s.to_string()),
3856 version: serialize_version(buffer_version),
3857 },
3858 }
3859 }
3860
3861 async fn response_from_proto(
3862 self,
3863 message: proto::SemanticTokensResponse,
3864 _: Entity<LspStore>,
3865 buffer: Entity<Buffer>,
3866 mut cx: AsyncApp,
3867 ) -> anyhow::Result<SemanticTokensResponse> {
3868 buffer
3869 .update(&mut cx, |buffer, _| {
3870 buffer.wait_for_version(deserialize_version(&message.version))
3871 })
3872 .await?;
3873
3874 Ok(SemanticTokensResponse::Full {
3875 data: message.data,
3876 result_id: message.result_id.map(SharedString::new),
3877 })
3878 }
3879
3880 fn buffer_id_from_proto(message: &proto::SemanticTokens) -> Result<BufferId> {
3881 BufferId::new(message.buffer_id)
3882 }
3883}
3884
3885#[async_trait(?Send)]
3886impl LspCommand for GetCodeLens {
3887 type Response = Vec<CodeAction>;
3888 type LspRequest = lsp::CodeLensRequest;
3889 type ProtoRequest = proto::GetCodeLens;
3890
3891 fn display_name(&self) -> &str {
3892 "Code Lens"
3893 }
3894
3895 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
3896 capabilities
3897 .server_capabilities
3898 .code_lens_provider
3899 .is_some()
3900 }
3901
3902 fn to_lsp(
3903 &self,
3904 path: &Path,
3905 _: &Buffer,
3906 _: &Arc<LanguageServer>,
3907 _: &App,
3908 ) -> Result<lsp::CodeLensParams> {
3909 Ok(lsp::CodeLensParams {
3910 text_document: lsp::TextDocumentIdentifier {
3911 uri: file_path_to_lsp_url(path)?,
3912 },
3913 work_done_progress_params: lsp::WorkDoneProgressParams::default(),
3914 partial_result_params: lsp::PartialResultParams::default(),
3915 })
3916 }
3917
3918 async fn response_from_lsp(
3919 self,
3920 message: Option<Vec<lsp::CodeLens>>,
3921 lsp_store: Entity<LspStore>,
3922 buffer: Entity<Buffer>,
3923 server_id: LanguageServerId,
3924 cx: AsyncApp,
3925 ) -> anyhow::Result<Vec<CodeAction>> {
3926 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
3927 let language_server = cx.update(|cx| {
3928 lsp_store
3929 .read(cx)
3930 .language_server_for_id(server_id)
3931 .with_context(|| {
3932 format!("Missing the language server that just returned a response {server_id}")
3933 })
3934 })?;
3935
3936 let can_resolve = Self::can_resolve_lens(&language_server.capabilities());
3937 let mut code_lenses = message.unwrap_or_default();
3938
3939 if can_resolve {
3940 let request_timeout = cx.update(|cx| {
3941 ProjectSettings::get_global(cx)
3942 .global_lsp_settings
3943 .get_request_timeout()
3944 });
3945
3946 for lens in &mut code_lenses {
3947 if lens.command.is_none() {
3948 match language_server
3949 .request::<lsp::request::CodeLensResolve>(lens.clone(), request_timeout)
3950 .await
3951 .into_response()
3952 {
3953 Ok(resolved) => *lens = resolved,
3954 Err(e) => log::warn!("Failed to resolve code lens: {e:#}"),
3955 }
3956 }
3957 }
3958
3959 Self::fill_unresolved_lens_titles(&mut code_lenses);
3960 }
3961
3962 Ok(code_lenses
3963 .into_iter()
3964 .map(|code_lens| {
3965 let code_lens_range = range_from_lsp(code_lens.range);
3966 let start = snapshot.clip_point_utf16(code_lens_range.start, Bias::Left);
3967 let end = snapshot.clip_point_utf16(code_lens_range.end, Bias::Right);
3968 let range = snapshot.anchor_before(start)..snapshot.anchor_after(end);
3969 let resolved = code_lens.command.is_some();
3970 CodeAction {
3971 server_id,
3972 range,
3973 lsp_action: LspAction::CodeLens(code_lens),
3974 resolved,
3975 }
3976 })
3977 .collect())
3978 }
3979
3980 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeLens {
3981 proto::GetCodeLens {
3982 project_id,
3983 buffer_id: buffer.remote_id().into(),
3984 version: serialize_version(&buffer.version()),
3985 }
3986 }
3987
3988 async fn from_proto(
3989 message: proto::GetCodeLens,
3990 _: Entity<LspStore>,
3991 buffer: Entity<Buffer>,
3992 mut cx: AsyncApp,
3993 ) -> Result<Self> {
3994 buffer
3995 .update(&mut cx, |buffer, _| {
3996 buffer.wait_for_version(deserialize_version(&message.version))
3997 })
3998 .await?;
3999 Ok(Self)
4000 }
4001
4002 fn response_to_proto(
4003 response: Vec<CodeAction>,
4004 _: &mut LspStore,
4005 _: PeerId,
4006 buffer_version: &clock::Global,
4007 _: &mut App,
4008 ) -> proto::GetCodeLensResponse {
4009 proto::GetCodeLensResponse {
4010 lens_actions: response
4011 .iter()
4012 .map(LspStore::serialize_code_action)
4013 .collect(),
4014 version: serialize_version(buffer_version),
4015 }
4016 }
4017
4018 async fn response_from_proto(
4019 self,
4020 message: proto::GetCodeLensResponse,
4021 _: Entity<LspStore>,
4022 buffer: Entity<Buffer>,
4023 mut cx: AsyncApp,
4024 ) -> anyhow::Result<Vec<CodeAction>> {
4025 buffer
4026 .update(&mut cx, |buffer, _| {
4027 buffer.wait_for_version(deserialize_version(&message.version))
4028 })
4029 .await?;
4030 message
4031 .lens_actions
4032 .into_iter()
4033 .map(LspStore::deserialize_code_action)
4034 .collect::<Result<Vec<_>>>()
4035 .context("deserializing proto code lens response")
4036 }
4037
4038 fn buffer_id_from_proto(message: &proto::GetCodeLens) -> Result<BufferId> {
4039 BufferId::new(message.buffer_id)
4040 }
4041}
4042
4043impl LinkedEditingRange {
4044 pub fn check_server_capabilities(capabilities: ServerCapabilities) -> bool {
4045 let Some(linked_editing_options) = capabilities.linked_editing_range_provider else {
4046 return false;
4047 };
4048 if let LinkedEditingRangeServerCapabilities::Simple(false) = linked_editing_options {
4049 return false;
4050 }
4051 true
4052 }
4053}
4054
4055#[async_trait(?Send)]
4056impl LspCommand for LinkedEditingRange {
4057 type Response = Vec<Range<Anchor>>;
4058 type LspRequest = lsp::request::LinkedEditingRange;
4059 type ProtoRequest = proto::LinkedEditingRange;
4060
4061 fn display_name(&self) -> &str {
4062 "Linked editing range"
4063 }
4064
4065 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
4066 Self::check_server_capabilities(capabilities.server_capabilities)
4067 }
4068
4069 fn to_lsp(
4070 &self,
4071 path: &Path,
4072 buffer: &Buffer,
4073 _server: &Arc<LanguageServer>,
4074 _: &App,
4075 ) -> Result<lsp::LinkedEditingRangeParams> {
4076 let position = self.position.to_point_utf16(&buffer.snapshot());
4077 Ok(lsp::LinkedEditingRangeParams {
4078 text_document_position_params: make_lsp_text_document_position(path, position)?,
4079 work_done_progress_params: Default::default(),
4080 })
4081 }
4082
4083 async fn response_from_lsp(
4084 self,
4085 message: Option<lsp::LinkedEditingRanges>,
4086 _: Entity<LspStore>,
4087 buffer: Entity<Buffer>,
4088 _server_id: LanguageServerId,
4089 cx: AsyncApp,
4090 ) -> Result<Vec<Range<Anchor>>> {
4091 if let Some(lsp::LinkedEditingRanges { mut ranges, .. }) = message {
4092 ranges.sort_by_key(|range| range.start);
4093
4094 Ok(buffer.read_with(&cx, |buffer, _| {
4095 ranges
4096 .into_iter()
4097 .map(|range| {
4098 let start =
4099 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
4100 let end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
4101 buffer.anchor_before(start)..buffer.anchor_after(end)
4102 })
4103 .collect()
4104 }))
4105 } else {
4106 Ok(vec![])
4107 }
4108 }
4109
4110 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LinkedEditingRange {
4111 proto::LinkedEditingRange {
4112 project_id,
4113 buffer_id: buffer.remote_id().to_proto(),
4114 position: Some(serialize_anchor(&self.position)),
4115 version: serialize_version(&buffer.version()),
4116 }
4117 }
4118
4119 async fn from_proto(
4120 message: proto::LinkedEditingRange,
4121 _: Entity<LspStore>,
4122 buffer: Entity<Buffer>,
4123 mut cx: AsyncApp,
4124 ) -> Result<Self> {
4125 let position = message.position.context("invalid position")?;
4126 buffer
4127 .update(&mut cx, |buffer, _| {
4128 buffer.wait_for_version(deserialize_version(&message.version))
4129 })
4130 .await?;
4131 let position = deserialize_anchor(position).context("invalid position")?;
4132 buffer
4133 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([position]))
4134 .await?;
4135 Ok(Self { position })
4136 }
4137
4138 fn response_to_proto(
4139 response: Vec<Range<Anchor>>,
4140 _: &mut LspStore,
4141 _: PeerId,
4142 buffer_version: &clock::Global,
4143 _: &mut App,
4144 ) -> proto::LinkedEditingRangeResponse {
4145 proto::LinkedEditingRangeResponse {
4146 items: response
4147 .into_iter()
4148 .map(|range| proto::AnchorRange {
4149 start: Some(serialize_anchor(&range.start)),
4150 end: Some(serialize_anchor(&range.end)),
4151 })
4152 .collect(),
4153 version: serialize_version(buffer_version),
4154 }
4155 }
4156
4157 async fn response_from_proto(
4158 self,
4159 message: proto::LinkedEditingRangeResponse,
4160 _: Entity<LspStore>,
4161 buffer: Entity<Buffer>,
4162 mut cx: AsyncApp,
4163 ) -> Result<Vec<Range<Anchor>>> {
4164 buffer
4165 .update(&mut cx, |buffer, _| {
4166 buffer.wait_for_version(deserialize_version(&message.version))
4167 })
4168 .await?;
4169 let items: Vec<Range<Anchor>> = message
4170 .items
4171 .into_iter()
4172 .filter_map(|range| {
4173 let start = deserialize_anchor(range.start?)?;
4174 let end = deserialize_anchor(range.end?)?;
4175 Some(start..end)
4176 })
4177 .collect();
4178 for range in &items {
4179 buffer
4180 .update(&mut cx, |buffer, _| {
4181 buffer.wait_for_anchors([range.start, range.end])
4182 })
4183 .await?;
4184 }
4185 Ok(items)
4186 }
4187
4188 fn buffer_id_from_proto(message: &proto::LinkedEditingRange) -> Result<BufferId> {
4189 BufferId::new(message.buffer_id)
4190 }
4191}
4192
4193impl GetDocumentDiagnostics {
4194 pub fn diagnostics_from_proto(
4195 response: proto::GetDocumentDiagnosticsResponse,
4196 ) -> Vec<LspPullDiagnostics> {
4197 response
4198 .pulled_diagnostics
4199 .into_iter()
4200 .filter_map(|diagnostics| {
4201 Some(LspPullDiagnostics::Response {
4202 registration_id: diagnostics.registration_id.map(SharedString::from),
4203 server_id: LanguageServerId::from_proto(diagnostics.server_id),
4204 uri: lsp::Uri::from_str(diagnostics.uri.as_str()).log_err()?,
4205 diagnostics: if diagnostics.changed {
4206 PulledDiagnostics::Unchanged {
4207 result_id: SharedString::new(diagnostics.result_id?),
4208 }
4209 } else {
4210 PulledDiagnostics::Changed {
4211 result_id: diagnostics.result_id.map(SharedString::new),
4212 diagnostics: diagnostics
4213 .diagnostics
4214 .into_iter()
4215 .filter_map(|diagnostic| {
4216 GetDocumentDiagnostics::deserialize_lsp_diagnostic(diagnostic)
4217 .context("deserializing diagnostics")
4218 .log_err()
4219 })
4220 .collect(),
4221 }
4222 },
4223 })
4224 })
4225 .collect()
4226 }
4227
4228 pub fn deserialize_lsp_diagnostic(diagnostic: proto::LspDiagnostic) -> Result<lsp::Diagnostic> {
4229 let start = diagnostic.start.context("invalid start range")?;
4230 let end = diagnostic.end.context("invalid end range")?;
4231
4232 let range = Range::<PointUtf16> {
4233 start: PointUtf16 {
4234 row: start.row,
4235 column: start.column,
4236 },
4237 end: PointUtf16 {
4238 row: end.row,
4239 column: end.column,
4240 },
4241 };
4242
4243 let data = diagnostic.data.and_then(|data| Value::from_str(&data).ok());
4244 let code = diagnostic.code.map(lsp::NumberOrString::String);
4245
4246 let related_information = diagnostic
4247 .related_information
4248 .into_iter()
4249 .map(|info| {
4250 let start = info.location_range_start.unwrap();
4251 let end = info.location_range_end.unwrap();
4252
4253 lsp::DiagnosticRelatedInformation {
4254 location: lsp::Location {
4255 range: lsp::Range {
4256 start: point_to_lsp(PointUtf16::new(start.row, start.column)),
4257 end: point_to_lsp(PointUtf16::new(end.row, end.column)),
4258 },
4259 uri: lsp::Uri::from_str(&info.location_url.unwrap()).unwrap(),
4260 },
4261 message: info.message,
4262 }
4263 })
4264 .collect::<Vec<_>>();
4265
4266 let tags = diagnostic
4267 .tags
4268 .into_iter()
4269 .filter_map(|tag| match proto::LspDiagnosticTag::from_i32(tag) {
4270 Some(proto::LspDiagnosticTag::Unnecessary) => Some(lsp::DiagnosticTag::UNNECESSARY),
4271 Some(proto::LspDiagnosticTag::Deprecated) => Some(lsp::DiagnosticTag::DEPRECATED),
4272 _ => None,
4273 })
4274 .collect::<Vec<_>>();
4275
4276 Ok(lsp::Diagnostic {
4277 range: language::range_to_lsp(range)?,
4278 severity: match proto::lsp_diagnostic::Severity::from_i32(diagnostic.severity).unwrap()
4279 {
4280 proto::lsp_diagnostic::Severity::Error => Some(lsp::DiagnosticSeverity::ERROR),
4281 proto::lsp_diagnostic::Severity::Warning => Some(lsp::DiagnosticSeverity::WARNING),
4282 proto::lsp_diagnostic::Severity::Information => {
4283 Some(lsp::DiagnosticSeverity::INFORMATION)
4284 }
4285 proto::lsp_diagnostic::Severity::Hint => Some(lsp::DiagnosticSeverity::HINT),
4286 _ => None,
4287 },
4288 code,
4289 code_description: diagnostic
4290 .code_description
4291 .map(|code_description| CodeDescription {
4292 href: Some(lsp::Uri::from_str(&code_description).unwrap()),
4293 }),
4294 related_information: Some(related_information),
4295 tags: Some(tags),
4296 source: diagnostic.source.clone(),
4297 message: diagnostic.message,
4298 data,
4299 })
4300 }
4301
4302 pub fn serialize_lsp_diagnostic(diagnostic: lsp::Diagnostic) -> Result<proto::LspDiagnostic> {
4303 let range = language::range_from_lsp(diagnostic.range);
4304 let related_information = diagnostic
4305 .related_information
4306 .unwrap_or_default()
4307 .into_iter()
4308 .map(|related_information| {
4309 let location_range_start =
4310 point_from_lsp(related_information.location.range.start).0;
4311 let location_range_end = point_from_lsp(related_information.location.range.end).0;
4312
4313 Ok(proto::LspDiagnosticRelatedInformation {
4314 location_url: Some(related_information.location.uri.to_string()),
4315 location_range_start: Some(proto::PointUtf16 {
4316 row: location_range_start.row,
4317 column: location_range_start.column,
4318 }),
4319 location_range_end: Some(proto::PointUtf16 {
4320 row: location_range_end.row,
4321 column: location_range_end.column,
4322 }),
4323 message: related_information.message,
4324 })
4325 })
4326 .collect::<Result<Vec<_>>>()?;
4327
4328 let tags = diagnostic
4329 .tags
4330 .unwrap_or_default()
4331 .into_iter()
4332 .map(|tag| match tag {
4333 lsp::DiagnosticTag::UNNECESSARY => proto::LspDiagnosticTag::Unnecessary,
4334 lsp::DiagnosticTag::DEPRECATED => proto::LspDiagnosticTag::Deprecated,
4335 _ => proto::LspDiagnosticTag::None,
4336 } as i32)
4337 .collect();
4338
4339 Ok(proto::LspDiagnostic {
4340 start: Some(proto::PointUtf16 {
4341 row: range.start.0.row,
4342 column: range.start.0.column,
4343 }),
4344 end: Some(proto::PointUtf16 {
4345 row: range.end.0.row,
4346 column: range.end.0.column,
4347 }),
4348 severity: match diagnostic.severity {
4349 Some(lsp::DiagnosticSeverity::ERROR) => proto::lsp_diagnostic::Severity::Error,
4350 Some(lsp::DiagnosticSeverity::WARNING) => proto::lsp_diagnostic::Severity::Warning,
4351 Some(lsp::DiagnosticSeverity::INFORMATION) => {
4352 proto::lsp_diagnostic::Severity::Information
4353 }
4354 Some(lsp::DiagnosticSeverity::HINT) => proto::lsp_diagnostic::Severity::Hint,
4355 _ => proto::lsp_diagnostic::Severity::None,
4356 } as i32,
4357 code: diagnostic.code.as_ref().map(|code| match code {
4358 lsp::NumberOrString::Number(code) => code.to_string(),
4359 lsp::NumberOrString::String(code) => code.clone(),
4360 }),
4361 source: diagnostic.source.clone(),
4362 related_information,
4363 tags,
4364 code_description: diagnostic
4365 .code_description
4366 .and_then(|desc| desc.href.map(|url| url.to_string())),
4367 message: diagnostic.message,
4368 data: diagnostic.data.as_ref().map(|data| data.to_string()),
4369 })
4370 }
4371
4372 pub fn deserialize_workspace_diagnostics_report(
4373 report: lsp::WorkspaceDiagnosticReportResult,
4374 server_id: LanguageServerId,
4375 registration_id: Option<SharedString>,
4376 ) -> Vec<WorkspaceLspPullDiagnostics> {
4377 let mut pulled_diagnostics = HashMap::default();
4378 match report {
4379 lsp::WorkspaceDiagnosticReportResult::Report(workspace_diagnostic_report) => {
4380 for report in workspace_diagnostic_report.items {
4381 match report {
4382 lsp::WorkspaceDocumentDiagnosticReport::Full(report) => {
4383 process_full_workspace_diagnostics_report(
4384 &mut pulled_diagnostics,
4385 server_id,
4386 report,
4387 registration_id.clone(),
4388 )
4389 }
4390 lsp::WorkspaceDocumentDiagnosticReport::Unchanged(report) => {
4391 process_unchanged_workspace_diagnostics_report(
4392 &mut pulled_diagnostics,
4393 server_id,
4394 report,
4395 registration_id.clone(),
4396 )
4397 }
4398 }
4399 }
4400 }
4401 lsp::WorkspaceDiagnosticReportResult::Partial(
4402 workspace_diagnostic_report_partial_result,
4403 ) => {
4404 for report in workspace_diagnostic_report_partial_result.items {
4405 match report {
4406 lsp::WorkspaceDocumentDiagnosticReport::Full(report) => {
4407 process_full_workspace_diagnostics_report(
4408 &mut pulled_diagnostics,
4409 server_id,
4410 report,
4411 registration_id.clone(),
4412 )
4413 }
4414 lsp::WorkspaceDocumentDiagnosticReport::Unchanged(report) => {
4415 process_unchanged_workspace_diagnostics_report(
4416 &mut pulled_diagnostics,
4417 server_id,
4418 report,
4419 registration_id.clone(),
4420 )
4421 }
4422 }
4423 }
4424 }
4425 }
4426 pulled_diagnostics.into_values().collect()
4427 }
4428}
4429
4430#[derive(Debug)]
4431pub struct WorkspaceLspPullDiagnostics {
4432 pub version: Option<i32>,
4433 pub diagnostics: LspPullDiagnostics,
4434}
4435
4436fn process_full_workspace_diagnostics_report(
4437 diagnostics: &mut HashMap<lsp::Uri, WorkspaceLspPullDiagnostics>,
4438 server_id: LanguageServerId,
4439 report: lsp::WorkspaceFullDocumentDiagnosticReport,
4440 registration_id: Option<SharedString>,
4441) {
4442 let mut new_diagnostics = HashMap::default();
4443 process_full_diagnostics_report(
4444 &mut new_diagnostics,
4445 server_id,
4446 report.uri,
4447 report.full_document_diagnostic_report,
4448 registration_id,
4449 );
4450 diagnostics.extend(new_diagnostics.into_iter().map(|(uri, diagnostics)| {
4451 (
4452 uri,
4453 WorkspaceLspPullDiagnostics {
4454 version: report.version.map(|v| v as i32),
4455 diagnostics,
4456 },
4457 )
4458 }));
4459}
4460
4461fn process_unchanged_workspace_diagnostics_report(
4462 diagnostics: &mut HashMap<lsp::Uri, WorkspaceLspPullDiagnostics>,
4463 server_id: LanguageServerId,
4464 report: lsp::WorkspaceUnchangedDocumentDiagnosticReport,
4465 registration_id: Option<SharedString>,
4466) {
4467 let mut new_diagnostics = HashMap::default();
4468 process_unchanged_diagnostics_report(
4469 &mut new_diagnostics,
4470 server_id,
4471 report.uri,
4472 report.unchanged_document_diagnostic_report,
4473 registration_id,
4474 );
4475 diagnostics.extend(new_diagnostics.into_iter().map(|(uri, diagnostics)| {
4476 (
4477 uri,
4478 WorkspaceLspPullDiagnostics {
4479 version: report.version.map(|v| v as i32),
4480 diagnostics,
4481 },
4482 )
4483 }));
4484}
4485
4486#[async_trait(?Send)]
4487impl LspCommand for GetDocumentDiagnostics {
4488 type Response = Vec<LspPullDiagnostics>;
4489 type LspRequest = lsp::request::DocumentDiagnosticRequest;
4490 type ProtoRequest = proto::GetDocumentDiagnostics;
4491
4492 fn display_name(&self) -> &str {
4493 "Get diagnostics"
4494 }
4495
4496 fn check_capabilities(&self, _: AdapterServerCapabilities) -> bool {
4497 true
4498 }
4499
4500 fn to_lsp(
4501 &self,
4502 path: &Path,
4503 _: &Buffer,
4504 _: &Arc<LanguageServer>,
4505 _: &App,
4506 ) -> Result<lsp::DocumentDiagnosticParams> {
4507 Ok(lsp::DocumentDiagnosticParams {
4508 text_document: lsp::TextDocumentIdentifier {
4509 uri: file_path_to_lsp_url(path)?,
4510 },
4511 identifier: self.identifier.as_ref().map(ToString::to_string),
4512 previous_result_id: self.previous_result_id.as_ref().map(ToString::to_string),
4513 partial_result_params: Default::default(),
4514 work_done_progress_params: Default::default(),
4515 })
4516 }
4517
4518 async fn response_from_lsp(
4519 self,
4520 message: lsp::DocumentDiagnosticReportResult,
4521 _: Entity<LspStore>,
4522 buffer: Entity<Buffer>,
4523 server_id: LanguageServerId,
4524 cx: AsyncApp,
4525 ) -> Result<Self::Response> {
4526 let url = buffer.read_with(&cx, |buffer, cx| {
4527 buffer
4528 .file()
4529 .and_then(|file| file.as_local())
4530 .map(|file| {
4531 let abs_path = file.abs_path(cx);
4532 file_path_to_lsp_url(&abs_path)
4533 })
4534 .transpose()?
4535 .with_context(|| format!("missing url on buffer {}", buffer.remote_id()))
4536 })?;
4537
4538 let mut pulled_diagnostics = HashMap::default();
4539 match message {
4540 lsp::DocumentDiagnosticReportResult::Report(report) => match report {
4541 lsp::DocumentDiagnosticReport::Full(report) => {
4542 if let Some(related_documents) = report.related_documents {
4543 process_related_documents(
4544 &mut pulled_diagnostics,
4545 server_id,
4546 related_documents,
4547 self.registration_id.clone(),
4548 );
4549 }
4550 process_full_diagnostics_report(
4551 &mut pulled_diagnostics,
4552 server_id,
4553 url,
4554 report.full_document_diagnostic_report,
4555 self.registration_id,
4556 );
4557 }
4558 lsp::DocumentDiagnosticReport::Unchanged(report) => {
4559 if let Some(related_documents) = report.related_documents {
4560 process_related_documents(
4561 &mut pulled_diagnostics,
4562 server_id,
4563 related_documents,
4564 self.registration_id.clone(),
4565 );
4566 }
4567 process_unchanged_diagnostics_report(
4568 &mut pulled_diagnostics,
4569 server_id,
4570 url,
4571 report.unchanged_document_diagnostic_report,
4572 self.registration_id,
4573 );
4574 }
4575 },
4576 lsp::DocumentDiagnosticReportResult::Partial(report) => {
4577 if let Some(related_documents) = report.related_documents {
4578 process_related_documents(
4579 &mut pulled_diagnostics,
4580 server_id,
4581 related_documents,
4582 self.registration_id,
4583 );
4584 }
4585 }
4586 }
4587
4588 Ok(pulled_diagnostics.into_values().collect())
4589 }
4590
4591 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentDiagnostics {
4592 proto::GetDocumentDiagnostics {
4593 project_id,
4594 buffer_id: buffer.remote_id().into(),
4595 version: serialize_version(&buffer.version()),
4596 }
4597 }
4598
4599 async fn from_proto(
4600 _: proto::GetDocumentDiagnostics,
4601 _: Entity<LspStore>,
4602 _: Entity<Buffer>,
4603 _: AsyncApp,
4604 ) -> Result<Self> {
4605 anyhow::bail!(
4606 "proto::GetDocumentDiagnostics is not expected to be converted from proto directly, as it needs `previous_result_id` fetched first"
4607 )
4608 }
4609
4610 fn response_to_proto(
4611 response: Self::Response,
4612 _: &mut LspStore,
4613 _: PeerId,
4614 _: &clock::Global,
4615 _: &mut App,
4616 ) -> proto::GetDocumentDiagnosticsResponse {
4617 let pulled_diagnostics = response
4618 .into_iter()
4619 .filter_map(|diagnostics| match diagnostics {
4620 LspPullDiagnostics::Default => None,
4621 LspPullDiagnostics::Response {
4622 server_id,
4623 uri,
4624 diagnostics,
4625 registration_id,
4626 } => {
4627 let mut changed = false;
4628 let (diagnostics, result_id) = match diagnostics {
4629 PulledDiagnostics::Unchanged { result_id } => (Vec::new(), Some(result_id)),
4630 PulledDiagnostics::Changed {
4631 result_id,
4632 diagnostics,
4633 } => {
4634 changed = true;
4635 (diagnostics, result_id)
4636 }
4637 };
4638 Some(proto::PulledDiagnostics {
4639 changed,
4640 result_id: result_id.map(|id| id.to_string()),
4641 uri: uri.to_string(),
4642 server_id: server_id.to_proto(),
4643 diagnostics: diagnostics
4644 .into_iter()
4645 .filter_map(|diagnostic| {
4646 GetDocumentDiagnostics::serialize_lsp_diagnostic(diagnostic)
4647 .context("serializing diagnostics")
4648 .log_err()
4649 })
4650 .collect(),
4651 registration_id: registration_id.as_ref().map(ToString::to_string),
4652 })
4653 }
4654 })
4655 .collect();
4656
4657 proto::GetDocumentDiagnosticsResponse { pulled_diagnostics }
4658 }
4659
4660 async fn response_from_proto(
4661 self,
4662 response: proto::GetDocumentDiagnosticsResponse,
4663 _: Entity<LspStore>,
4664 _: Entity<Buffer>,
4665 _: AsyncApp,
4666 ) -> Result<Self::Response> {
4667 Ok(Self::diagnostics_from_proto(response))
4668 }
4669
4670 fn buffer_id_from_proto(message: &proto::GetDocumentDiagnostics) -> Result<BufferId> {
4671 BufferId::new(message.buffer_id)
4672 }
4673}
4674
4675#[async_trait(?Send)]
4676impl LspCommand for GetDocumentColor {
4677 type Response = Vec<DocumentColor>;
4678 type LspRequest = lsp::request::DocumentColor;
4679 type ProtoRequest = proto::GetDocumentColor;
4680
4681 fn display_name(&self) -> &str {
4682 "Document color"
4683 }
4684
4685 fn check_capabilities(&self, server_capabilities: AdapterServerCapabilities) -> bool {
4686 server_capabilities
4687 .server_capabilities
4688 .color_provider
4689 .as_ref()
4690 .is_some_and(|capability| match capability {
4691 lsp::ColorProviderCapability::Simple(supported) => *supported,
4692 lsp::ColorProviderCapability::ColorProvider(..) => true,
4693 lsp::ColorProviderCapability::Options(..) => true,
4694 })
4695 }
4696
4697 fn to_lsp(
4698 &self,
4699 path: &Path,
4700 _: &Buffer,
4701 _: &Arc<LanguageServer>,
4702 _: &App,
4703 ) -> Result<lsp::DocumentColorParams> {
4704 Ok(lsp::DocumentColorParams {
4705 text_document: make_text_document_identifier(path)?,
4706 work_done_progress_params: Default::default(),
4707 partial_result_params: Default::default(),
4708 })
4709 }
4710
4711 async fn response_from_lsp(
4712 self,
4713 message: Vec<lsp::ColorInformation>,
4714 _: Entity<LspStore>,
4715 _: Entity<Buffer>,
4716 _: LanguageServerId,
4717 _: AsyncApp,
4718 ) -> Result<Self::Response> {
4719 Ok(message
4720 .into_iter()
4721 .map(|color| DocumentColor {
4722 lsp_range: color.range,
4723 color: color.color,
4724 resolved: false,
4725 color_presentations: Vec::new(),
4726 })
4727 .collect())
4728 }
4729
4730 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
4731 proto::GetDocumentColor {
4732 project_id,
4733 buffer_id: buffer.remote_id().to_proto(),
4734 version: serialize_version(&buffer.version()),
4735 }
4736 }
4737
4738 async fn from_proto(
4739 _: Self::ProtoRequest,
4740 _: Entity<LspStore>,
4741 _: Entity<Buffer>,
4742 _: AsyncApp,
4743 ) -> Result<Self> {
4744 Ok(Self {})
4745 }
4746
4747 fn response_to_proto(
4748 response: Self::Response,
4749 _: &mut LspStore,
4750 _: PeerId,
4751 buffer_version: &clock::Global,
4752 _: &mut App,
4753 ) -> proto::GetDocumentColorResponse {
4754 proto::GetDocumentColorResponse {
4755 colors: response
4756 .into_iter()
4757 .map(|color| {
4758 let start = point_from_lsp(color.lsp_range.start).0;
4759 let end = point_from_lsp(color.lsp_range.end).0;
4760 proto::ColorInformation {
4761 red: color.color.red,
4762 green: color.color.green,
4763 blue: color.color.blue,
4764 alpha: color.color.alpha,
4765 lsp_range_start: Some(proto::PointUtf16 {
4766 row: start.row,
4767 column: start.column,
4768 }),
4769 lsp_range_end: Some(proto::PointUtf16 {
4770 row: end.row,
4771 column: end.column,
4772 }),
4773 }
4774 })
4775 .collect(),
4776 version: serialize_version(buffer_version),
4777 }
4778 }
4779
4780 async fn response_from_proto(
4781 self,
4782 message: proto::GetDocumentColorResponse,
4783 _: Entity<LspStore>,
4784 _: Entity<Buffer>,
4785 _: AsyncApp,
4786 ) -> Result<Self::Response> {
4787 Ok(message
4788 .colors
4789 .into_iter()
4790 .filter_map(|color| {
4791 let start = color.lsp_range_start?;
4792 let start = PointUtf16::new(start.row, start.column);
4793 let end = color.lsp_range_end?;
4794 let end = PointUtf16::new(end.row, end.column);
4795 Some(DocumentColor {
4796 resolved: false,
4797 color_presentations: Vec::new(),
4798 lsp_range: lsp::Range {
4799 start: point_to_lsp(start),
4800 end: point_to_lsp(end),
4801 },
4802 color: lsp::Color {
4803 red: color.red,
4804 green: color.green,
4805 blue: color.blue,
4806 alpha: color.alpha,
4807 },
4808 })
4809 })
4810 .collect())
4811 }
4812
4813 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
4814 BufferId::new(message.buffer_id)
4815 }
4816}
4817
4818#[async_trait(?Send)]
4819impl LspCommand for GetFoldingRanges {
4820 type Response = Vec<LspFoldingRange>;
4821 type LspRequest = lsp::request::FoldingRangeRequest;
4822 type ProtoRequest = proto::GetFoldingRanges;
4823
4824 fn display_name(&self) -> &str {
4825 "Folding ranges"
4826 }
4827
4828 fn check_capabilities(&self, server_capabilities: AdapterServerCapabilities) -> bool {
4829 server_capabilities
4830 .server_capabilities
4831 .folding_range_provider
4832 .as_ref()
4833 .is_some_and(|capability| match capability {
4834 lsp::FoldingRangeProviderCapability::Simple(supported) => *supported,
4835 lsp::FoldingRangeProviderCapability::FoldingProvider(..)
4836 | lsp::FoldingRangeProviderCapability::Options(..) => true,
4837 })
4838 }
4839
4840 fn to_lsp(
4841 &self,
4842 path: &Path,
4843 _: &Buffer,
4844 _: &Arc<LanguageServer>,
4845 _: &App,
4846 ) -> Result<lsp::FoldingRangeParams> {
4847 Ok(lsp::FoldingRangeParams {
4848 text_document: make_text_document_identifier(path)?,
4849 work_done_progress_params: Default::default(),
4850 partial_result_params: Default::default(),
4851 })
4852 }
4853
4854 async fn response_from_lsp(
4855 self,
4856 message: Option<Vec<lsp::FoldingRange>>,
4857 _: Entity<LspStore>,
4858 buffer: Entity<Buffer>,
4859 _: LanguageServerId,
4860 cx: AsyncApp,
4861 ) -> Result<Self::Response> {
4862 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
4863 let max_point = snapshot.max_point_utf16();
4864 Ok(message
4865 .unwrap_or_default()
4866 .into_iter()
4867 .filter(|range| range.start_line < range.end_line)
4868 .filter(|range| range.start_line <= max_point.row && range.end_line <= max_point.row)
4869 .map(|folding_range| {
4870 let start_col = folding_range.start_character.unwrap_or(u32::MAX);
4871 let end_col = folding_range.end_character.unwrap_or(u32::MAX);
4872 let start = snapshot.clip_point_utf16(
4873 Unclipped(PointUtf16::new(folding_range.start_line, start_col)),
4874 Bias::Right,
4875 );
4876 let end = snapshot.clip_point_utf16(
4877 Unclipped(PointUtf16::new(folding_range.end_line, end_col)),
4878 Bias::Left,
4879 );
4880 let start = snapshot.anchor_after(start);
4881 let end = snapshot.anchor_before(end);
4882 let collapsed_text = folding_range
4883 .collapsed_text
4884 .filter(|t| !t.is_empty())
4885 .map(|t| SharedString::from(crate::lsp_store::collapse_newlines(&t, " ")));
4886 LspFoldingRange {
4887 range: start..end,
4888 collapsed_text,
4889 }
4890 })
4891 .collect())
4892 }
4893
4894 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
4895 proto::GetFoldingRanges {
4896 project_id,
4897 buffer_id: buffer.remote_id().to_proto(),
4898 version: serialize_version(&buffer.version()),
4899 }
4900 }
4901
4902 async fn from_proto(
4903 _: Self::ProtoRequest,
4904 _: Entity<LspStore>,
4905 _: Entity<Buffer>,
4906 _: AsyncApp,
4907 ) -> Result<Self> {
4908 Ok(Self)
4909 }
4910
4911 fn response_to_proto(
4912 response: Self::Response,
4913 _: &mut LspStore,
4914 _: PeerId,
4915 buffer_version: &clock::Global,
4916 _: &mut App,
4917 ) -> proto::GetFoldingRangesResponse {
4918 let mut ranges = Vec::with_capacity(response.len());
4919 let mut collapsed_texts = Vec::with_capacity(response.len());
4920 for folding_range in response {
4921 ranges.push(serialize_anchor_range(folding_range.range));
4922 collapsed_texts.push(
4923 folding_range
4924 .collapsed_text
4925 .map(|t| t.to_string())
4926 .unwrap_or_default(),
4927 );
4928 }
4929 proto::GetFoldingRangesResponse {
4930 ranges,
4931 collapsed_texts,
4932 version: serialize_version(buffer_version),
4933 }
4934 }
4935
4936 async fn response_from_proto(
4937 self,
4938 message: proto::GetFoldingRangesResponse,
4939 _: Entity<LspStore>,
4940 buffer: Entity<Buffer>,
4941 mut cx: AsyncApp,
4942 ) -> Result<Self::Response> {
4943 buffer
4944 .update(&mut cx, |buffer, _| {
4945 buffer.wait_for_version(deserialize_version(&message.version))
4946 })
4947 .await?;
4948 message
4949 .ranges
4950 .into_iter()
4951 .zip(
4952 message
4953 .collapsed_texts
4954 .into_iter()
4955 .map(Some)
4956 .chain(std::iter::repeat(None)),
4957 )
4958 .map(|(range, collapsed_text)| {
4959 Ok(LspFoldingRange {
4960 range: deserialize_anchor_range(range)?,
4961 collapsed_text: collapsed_text
4962 .filter(|t| !t.is_empty())
4963 .map(SharedString::from),
4964 })
4965 })
4966 .collect()
4967 }
4968
4969 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
4970 BufferId::new(message.buffer_id)
4971 }
4972}
4973
4974fn process_related_documents(
4975 diagnostics: &mut HashMap<lsp::Uri, LspPullDiagnostics>,
4976 server_id: LanguageServerId,
4977 documents: impl IntoIterator<Item = (lsp::Uri, lsp::DocumentDiagnosticReportKind)>,
4978 registration_id: Option<SharedString>,
4979) {
4980 for (url, report_kind) in documents {
4981 match report_kind {
4982 lsp::DocumentDiagnosticReportKind::Full(report) => process_full_diagnostics_report(
4983 diagnostics,
4984 server_id,
4985 url,
4986 report,
4987 registration_id.clone(),
4988 ),
4989 lsp::DocumentDiagnosticReportKind::Unchanged(report) => {
4990 process_unchanged_diagnostics_report(
4991 diagnostics,
4992 server_id,
4993 url,
4994 report,
4995 registration_id.clone(),
4996 )
4997 }
4998 }
4999 }
5000}
5001
5002fn process_unchanged_diagnostics_report(
5003 diagnostics: &mut HashMap<lsp::Uri, LspPullDiagnostics>,
5004 server_id: LanguageServerId,
5005 uri: lsp::Uri,
5006 report: lsp::UnchangedDocumentDiagnosticReport,
5007 registration_id: Option<SharedString>,
5008) {
5009 let result_id = SharedString::new(report.result_id);
5010 match diagnostics.entry(uri.clone()) {
5011 hash_map::Entry::Occupied(mut o) => match o.get_mut() {
5012 LspPullDiagnostics::Default => {
5013 o.insert(LspPullDiagnostics::Response {
5014 server_id,
5015 uri,
5016 diagnostics: PulledDiagnostics::Unchanged { result_id },
5017 registration_id,
5018 });
5019 }
5020 LspPullDiagnostics::Response {
5021 server_id: existing_server_id,
5022 uri: existing_uri,
5023 diagnostics: existing_diagnostics,
5024 ..
5025 } => {
5026 if server_id != *existing_server_id || &uri != existing_uri {
5027 debug_panic!(
5028 "Unexpected state: file {uri} has two different sets of diagnostics reported"
5029 );
5030 }
5031 match existing_diagnostics {
5032 PulledDiagnostics::Unchanged { .. } => {
5033 *existing_diagnostics = PulledDiagnostics::Unchanged { result_id };
5034 }
5035 PulledDiagnostics::Changed { .. } => {}
5036 }
5037 }
5038 },
5039 hash_map::Entry::Vacant(v) => {
5040 v.insert(LspPullDiagnostics::Response {
5041 server_id,
5042 uri,
5043 diagnostics: PulledDiagnostics::Unchanged { result_id },
5044 registration_id,
5045 });
5046 }
5047 }
5048}
5049
5050fn process_full_diagnostics_report(
5051 diagnostics: &mut HashMap<lsp::Uri, LspPullDiagnostics>,
5052 server_id: LanguageServerId,
5053 uri: lsp::Uri,
5054 report: lsp::FullDocumentDiagnosticReport,
5055 registration_id: Option<SharedString>,
5056) {
5057 let result_id = report.result_id.map(SharedString::new);
5058 match diagnostics.entry(uri.clone()) {
5059 hash_map::Entry::Occupied(mut o) => match o.get_mut() {
5060 LspPullDiagnostics::Default => {
5061 o.insert(LspPullDiagnostics::Response {
5062 server_id,
5063 uri,
5064 diagnostics: PulledDiagnostics::Changed {
5065 result_id,
5066 diagnostics: report.items,
5067 },
5068 registration_id,
5069 });
5070 }
5071 LspPullDiagnostics::Response {
5072 server_id: existing_server_id,
5073 uri: existing_uri,
5074 diagnostics: existing_diagnostics,
5075 ..
5076 } => {
5077 if server_id != *existing_server_id || &uri != existing_uri {
5078 debug_panic!(
5079 "Unexpected state: file {uri} has two different sets of diagnostics reported"
5080 );
5081 }
5082 match existing_diagnostics {
5083 PulledDiagnostics::Unchanged { .. } => {
5084 *existing_diagnostics = PulledDiagnostics::Changed {
5085 result_id,
5086 diagnostics: report.items,
5087 };
5088 }
5089 PulledDiagnostics::Changed {
5090 result_id: existing_result_id,
5091 diagnostics: existing_diagnostics,
5092 } => {
5093 if result_id.is_some() {
5094 *existing_result_id = result_id;
5095 }
5096 existing_diagnostics.extend(report.items);
5097 }
5098 }
5099 }
5100 },
5101 hash_map::Entry::Vacant(v) => {
5102 v.insert(LspPullDiagnostics::Response {
5103 server_id,
5104 uri,
5105 diagnostics: PulledDiagnostics::Changed {
5106 result_id,
5107 diagnostics: report.items,
5108 },
5109 registration_id,
5110 });
5111 }
5112 }
5113}