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