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