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