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