1use crate::{
2 DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel,
3 InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink,
4 MarkupContent, Project, ProjectTransaction, ResolveState,
5};
6use anyhow::{anyhow, Context, Result};
7use async_trait::async_trait;
8use client2::proto::{self, PeerId};
9use futures::future;
10use gpui2::{AppContext, AsyncAppContext, Handle};
11use language2::{
12 language_settings::{language_settings, InlayHintKind},
13 point_from_lsp, point_to_lsp,
14 proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
15 range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind,
16 CodeAction, Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction,
17 Unclipped,
18};
19use lsp2::{
20 CompletionListItemDefaultsEditRange, DocumentHighlightKind, LanguageServer, LanguageServerId,
21 OneOf, ServerCapabilities,
22};
23use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
24use text::LineEnding;
25
26pub fn lsp_formatting_options(tab_size: u32) -> lsp2::FormattingOptions {
27 lsp2::FormattingOptions {
28 tab_size,
29 insert_spaces: true,
30 insert_final_newline: Some(true),
31 ..lsp2::FormattingOptions::default()
32 }
33}
34
35#[async_trait(?Send)]
36pub(crate) trait LspCommand: 'static + Sized {
37 type Response: 'static + Default + Send;
38 type LspRequest: 'static + Send + lsp2::request::Request;
39 type ProtoRequest: 'static + Send + proto::RequestMessage;
40
41 fn check_capabilities(&self, _: &lsp2::ServerCapabilities) -> bool {
42 true
43 }
44
45 fn to_lsp(
46 &self,
47 path: &Path,
48 buffer: &Buffer,
49 language_server: &Arc<LanguageServer>,
50 cx: &AppContext,
51 ) -> <Self::LspRequest as lsp2::request::Request>::Params;
52
53 async fn response_from_lsp(
54 self,
55 message: <Self::LspRequest as lsp2::request::Request>::Result,
56 project: Handle<Project>,
57 buffer: Handle<Buffer>,
58 server_id: LanguageServerId,
59 cx: AsyncAppContext,
60 ) -> Result<Self::Response>;
61
62 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
63
64 async fn from_proto(
65 message: Self::ProtoRequest,
66 project: Handle<Project>,
67 buffer: Handle<Buffer>,
68 cx: AsyncAppContext,
69 ) -> Result<Self>;
70
71 fn response_to_proto(
72 response: Self::Response,
73 project: &mut Project,
74 peer_id: PeerId,
75 buffer_version: &clock::Global,
76 cx: &mut AppContext,
77 ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
78
79 async fn response_from_proto(
80 self,
81 message: <Self::ProtoRequest as proto::RequestMessage>::Response,
82 project: Handle<Project>,
83 buffer: Handle<Buffer>,
84 cx: AsyncAppContext,
85 ) -> Result<Self::Response>;
86
87 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64;
88}
89
90pub(crate) struct PrepareRename {
91 pub position: PointUtf16,
92}
93
94pub(crate) struct PerformRename {
95 pub position: PointUtf16,
96 pub new_name: String,
97 pub push_to_history: bool,
98}
99
100pub(crate) struct GetDefinition {
101 pub position: PointUtf16,
102}
103
104pub(crate) struct GetTypeDefinition {
105 pub position: PointUtf16,
106}
107
108pub(crate) struct GetReferences {
109 pub position: PointUtf16,
110}
111
112pub(crate) struct GetDocumentHighlights {
113 pub position: PointUtf16,
114}
115
116pub(crate) struct GetHover {
117 pub position: PointUtf16,
118}
119
120pub(crate) struct GetCompletions {
121 pub position: PointUtf16,
122}
123
124pub(crate) struct GetCodeActions {
125 pub range: Range<Anchor>,
126}
127
128pub(crate) struct OnTypeFormatting {
129 pub position: PointUtf16,
130 pub trigger: String,
131 pub options: FormattingOptions,
132 pub push_to_history: bool,
133}
134
135pub(crate) struct InlayHints {
136 pub range: Range<Anchor>,
137}
138
139pub(crate) struct FormattingOptions {
140 tab_size: u32,
141}
142
143impl From<lsp2::FormattingOptions> for FormattingOptions {
144 fn from(value: lsp2::FormattingOptions) -> Self {
145 Self {
146 tab_size: value.tab_size,
147 }
148 }
149}
150
151#[async_trait(?Send)]
152impl LspCommand for PrepareRename {
153 type Response = Option<Range<Anchor>>;
154 type LspRequest = lsp2::request::PrepareRenameRequest;
155 type ProtoRequest = proto::PrepareRename;
156
157 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
158 if let Some(lsp2::OneOf::Right(rename)) = &capabilities.rename_provider {
159 rename.prepare_provider == Some(true)
160 } else {
161 false
162 }
163 }
164
165 fn to_lsp(
166 &self,
167 path: &Path,
168 _: &Buffer,
169 _: &Arc<LanguageServer>,
170 _: &AppContext,
171 ) -> lsp2::TextDocumentPositionParams {
172 lsp2::TextDocumentPositionParams {
173 text_document: lsp2::TextDocumentIdentifier {
174 uri: lsp2::Url::from_file_path(path).unwrap(),
175 },
176 position: point_to_lsp(self.position),
177 }
178 }
179
180 async fn response_from_lsp(
181 self,
182 message: Option<lsp2::PrepareRenameResponse>,
183 _: Handle<Project>,
184 buffer: Handle<Buffer>,
185 _: LanguageServerId,
186 cx: AsyncAppContext,
187 ) -> Result<Option<Range<Anchor>>> {
188 buffer.update(&mut cx, |buffer, _| {
189 if let Some(
190 lsp2::PrepareRenameResponse::Range(range)
191 | lsp2::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
192 ) = message
193 {
194 let Range { start, end } = range_from_lsp(range);
195 if buffer.clip_point_utf16(start, Bias::Left) == start.0
196 && buffer.clip_point_utf16(end, Bias::Left) == end.0
197 {
198 return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
199 }
200 }
201 Ok(None)
202 })?
203 }
204
205 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
206 proto::PrepareRename {
207 project_id,
208 buffer_id: buffer.remote_id(),
209 position: Some(language2::proto::serialize_anchor(
210 &buffer.anchor_before(self.position),
211 )),
212 version: serialize_version(&buffer.version()),
213 }
214 }
215
216 async fn from_proto(
217 message: proto::PrepareRename,
218 _: Handle<Project>,
219 buffer: Handle<Buffer>,
220 mut cx: AsyncAppContext,
221 ) -> Result<Self> {
222 let position = message
223 .position
224 .and_then(deserialize_anchor)
225 .ok_or_else(|| anyhow!("invalid position"))?;
226 buffer
227 .update(&mut cx, |buffer, _| {
228 buffer.wait_for_version(deserialize_version(&message.version))
229 })?
230 .await?;
231
232 Ok(Self {
233 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
234 })
235 }
236
237 fn response_to_proto(
238 range: Option<Range<Anchor>>,
239 _: &mut Project,
240 _: PeerId,
241 buffer_version: &clock::Global,
242 _: &mut AppContext,
243 ) -> proto::PrepareRenameResponse {
244 proto::PrepareRenameResponse {
245 can_rename: range.is_some(),
246 start: range
247 .as_ref()
248 .map(|range| language2::proto::serialize_anchor(&range.start)),
249 end: range
250 .as_ref()
251 .map(|range| language2::proto::serialize_anchor(&range.end)),
252 version: serialize_version(buffer_version),
253 }
254 }
255
256 async fn response_from_proto(
257 self,
258 message: proto::PrepareRenameResponse,
259 _: Handle<Project>,
260 buffer: Handle<Buffer>,
261 mut cx: AsyncAppContext,
262 ) -> Result<Option<Range<Anchor>>> {
263 if message.can_rename {
264 buffer
265 .update(&mut cx, |buffer, _| {
266 buffer.wait_for_version(deserialize_version(&message.version))
267 })?
268 .await?;
269 let start = message.start.and_then(deserialize_anchor);
270 let end = message.end.and_then(deserialize_anchor);
271 Ok(start.zip(end).map(|(start, end)| start..end))
272 } else {
273 Ok(None)
274 }
275 }
276
277 fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
278 message.buffer_id
279 }
280}
281
282#[async_trait(?Send)]
283impl LspCommand for PerformRename {
284 type Response = ProjectTransaction;
285 type LspRequest = lsp2::request::Rename;
286 type ProtoRequest = proto::PerformRename;
287
288 fn to_lsp(
289 &self,
290 path: &Path,
291 _: &Buffer,
292 _: &Arc<LanguageServer>,
293 _: &AppContext,
294 ) -> lsp2::RenameParams {
295 lsp2::RenameParams {
296 text_document_position: lsp2::TextDocumentPositionParams {
297 text_document: lsp2::TextDocumentIdentifier {
298 uri: lsp2::Url::from_file_path(path).unwrap(),
299 },
300 position: point_to_lsp(self.position),
301 },
302 new_name: self.new_name.clone(),
303 work_done_progress_params: Default::default(),
304 }
305 }
306
307 async fn response_from_lsp(
308 self,
309 message: Option<lsp2::WorkspaceEdit>,
310 project: Handle<Project>,
311 buffer: Handle<Buffer>,
312 server_id: LanguageServerId,
313 mut cx: AsyncAppContext,
314 ) -> Result<ProjectTransaction> {
315 if let Some(edit) = message {
316 let (lsp_adapter, lsp_server) =
317 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
318 Project::deserialize_workspace_edit(
319 project,
320 edit,
321 self.push_to_history,
322 lsp_adapter,
323 lsp_server,
324 &mut cx,
325 )
326 .await
327 } else {
328 Ok(ProjectTransaction::default())
329 }
330 }
331
332 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
333 proto::PerformRename {
334 project_id,
335 buffer_id: buffer.remote_id(),
336 position: Some(language2::proto::serialize_anchor(
337 &buffer.anchor_before(self.position),
338 )),
339 new_name: self.new_name.clone(),
340 version: serialize_version(&buffer.version()),
341 }
342 }
343
344 async fn from_proto(
345 message: proto::PerformRename,
346 _: Handle<Project>,
347 buffer: Handle<Buffer>,
348 mut cx: AsyncAppContext,
349 ) -> Result<Self> {
350 let position = message
351 .position
352 .and_then(deserialize_anchor)
353 .ok_or_else(|| anyhow!("invalid position"))?;
354 buffer
355 .update(&mut cx, |buffer, _| {
356 buffer.wait_for_version(deserialize_version(&message.version))
357 })?
358 .await?;
359 Ok(Self {
360 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
361 new_name: message.new_name,
362 push_to_history: false,
363 })
364 }
365
366 fn response_to_proto(
367 response: ProjectTransaction,
368 project: &mut Project,
369 peer_id: PeerId,
370 _: &clock::Global,
371 cx: &mut AppContext,
372 ) -> proto::PerformRenameResponse {
373 let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
374 proto::PerformRenameResponse {
375 transaction: Some(transaction),
376 }
377 }
378
379 async fn response_from_proto(
380 self,
381 message: proto::PerformRenameResponse,
382 project: Handle<Project>,
383 _: Handle<Buffer>,
384 mut cx: AsyncAppContext,
385 ) -> Result<ProjectTransaction> {
386 let message = message
387 .transaction
388 .ok_or_else(|| anyhow!("missing transaction"))?;
389 project
390 .update(&mut cx, |project, cx| {
391 project.deserialize_project_transaction(message, self.push_to_history, cx)
392 })?
393 .await
394 }
395
396 fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
397 message.buffer_id
398 }
399}
400
401#[async_trait(?Send)]
402impl LspCommand for GetDefinition {
403 type Response = Vec<LocationLink>;
404 type LspRequest = lsp2::request::GotoDefinition;
405 type ProtoRequest = proto::GetDefinition;
406
407 fn to_lsp(
408 &self,
409 path: &Path,
410 _: &Buffer,
411 _: &Arc<LanguageServer>,
412 _: &AppContext,
413 ) -> lsp2::GotoDefinitionParams {
414 lsp2::GotoDefinitionParams {
415 text_document_position_params: lsp2::TextDocumentPositionParams {
416 text_document: lsp2::TextDocumentIdentifier {
417 uri: lsp2::Url::from_file_path(path).unwrap(),
418 },
419 position: point_to_lsp(self.position),
420 },
421 work_done_progress_params: Default::default(),
422 partial_result_params: Default::default(),
423 }
424 }
425
426 async fn response_from_lsp(
427 self,
428 message: Option<lsp2::GotoDefinitionResponse>,
429 project: Handle<Project>,
430 buffer: Handle<Buffer>,
431 server_id: LanguageServerId,
432 cx: AsyncAppContext,
433 ) -> Result<Vec<LocationLink>> {
434 location_links_from_lsp(message, project, buffer, server_id, cx).await
435 }
436
437 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
438 proto::GetDefinition {
439 project_id,
440 buffer_id: buffer.remote_id(),
441 position: Some(language2::proto::serialize_anchor(
442 &buffer.anchor_before(self.position),
443 )),
444 version: serialize_version(&buffer.version()),
445 }
446 }
447
448 async fn from_proto(
449 message: proto::GetDefinition,
450 _: Handle<Project>,
451 buffer: Handle<Buffer>,
452 mut cx: AsyncAppContext,
453 ) -> Result<Self> {
454 let position = message
455 .position
456 .and_then(deserialize_anchor)
457 .ok_or_else(|| anyhow!("invalid position"))?;
458 buffer
459 .update(&mut cx, |buffer, _| {
460 buffer.wait_for_version(deserialize_version(&message.version))
461 })?
462 .await?;
463 Ok(Self {
464 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
465 })
466 }
467
468 fn response_to_proto(
469 response: Vec<LocationLink>,
470 project: &mut Project,
471 peer_id: PeerId,
472 _: &clock::Global,
473 cx: &mut AppContext,
474 ) -> proto::GetDefinitionResponse {
475 let links = location_links_to_proto(response, project, peer_id, cx);
476 proto::GetDefinitionResponse { links }
477 }
478
479 async fn response_from_proto(
480 self,
481 message: proto::GetDefinitionResponse,
482 project: Handle<Project>,
483 _: Handle<Buffer>,
484 cx: AsyncAppContext,
485 ) -> Result<Vec<LocationLink>> {
486 location_links_from_proto(message.links, project, cx).await
487 }
488
489 fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
490 message.buffer_id
491 }
492}
493
494#[async_trait(?Send)]
495impl LspCommand for GetTypeDefinition {
496 type Response = Vec<LocationLink>;
497 type LspRequest = lsp2::request::GotoTypeDefinition;
498 type ProtoRequest = proto::GetTypeDefinition;
499
500 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
501 match &capabilities.type_definition_provider {
502 None => false,
503 Some(lsp2::TypeDefinitionProviderCapability::Simple(false)) => false,
504 _ => true,
505 }
506 }
507
508 fn to_lsp(
509 &self,
510 path: &Path,
511 _: &Buffer,
512 _: &Arc<LanguageServer>,
513 _: &AppContext,
514 ) -> lsp2::GotoTypeDefinitionParams {
515 lsp2::GotoTypeDefinitionParams {
516 text_document_position_params: lsp2::TextDocumentPositionParams {
517 text_document: lsp2::TextDocumentIdentifier {
518 uri: lsp2::Url::from_file_path(path).unwrap(),
519 },
520 position: point_to_lsp(self.position),
521 },
522 work_done_progress_params: Default::default(),
523 partial_result_params: Default::default(),
524 }
525 }
526
527 async fn response_from_lsp(
528 self,
529 message: Option<lsp2::GotoTypeDefinitionResponse>,
530 project: Handle<Project>,
531 buffer: Handle<Buffer>,
532 server_id: LanguageServerId,
533 cx: AsyncAppContext,
534 ) -> Result<Vec<LocationLink>> {
535 location_links_from_lsp(message, project, buffer, server_id, cx).await
536 }
537
538 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition {
539 proto::GetTypeDefinition {
540 project_id,
541 buffer_id: buffer.remote_id(),
542 position: Some(language2::proto::serialize_anchor(
543 &buffer.anchor_before(self.position),
544 )),
545 version: serialize_version(&buffer.version()),
546 }
547 }
548
549 async fn from_proto(
550 message: proto::GetTypeDefinition,
551 _: Handle<Project>,
552 buffer: Handle<Buffer>,
553 mut cx: AsyncAppContext,
554 ) -> Result<Self> {
555 let position = message
556 .position
557 .and_then(deserialize_anchor)
558 .ok_or_else(|| anyhow!("invalid position"))?;
559 buffer
560 .update(&mut cx, |buffer, _| {
561 buffer.wait_for_version(deserialize_version(&message.version))
562 })?
563 .await?;
564 Ok(Self {
565 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
566 })
567 }
568
569 fn response_to_proto(
570 response: Vec<LocationLink>,
571 project: &mut Project,
572 peer_id: PeerId,
573 _: &clock::Global,
574 cx: &mut AppContext,
575 ) -> proto::GetTypeDefinitionResponse {
576 let links = location_links_to_proto(response, project, peer_id, cx);
577 proto::GetTypeDefinitionResponse { links }
578 }
579
580 async fn response_from_proto(
581 self,
582 message: proto::GetTypeDefinitionResponse,
583 project: Handle<Project>,
584 _: Handle<Buffer>,
585 cx: AsyncAppContext,
586 ) -> Result<Vec<LocationLink>> {
587 location_links_from_proto(message.links, project, cx).await
588 }
589
590 fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> u64 {
591 message.buffer_id
592 }
593}
594
595fn language_server_for_buffer(
596 project: &Handle<Project>,
597 buffer: &Handle<Buffer>,
598 server_id: LanguageServerId,
599 cx: &mut AsyncAppContext,
600) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
601 project
602 .update(cx, |project, cx| {
603 project
604 .language_server_for_buffer(buffer.read(cx), server_id, cx)
605 .map(|(adapter, server)| (adapter.clone(), server.clone()))
606 })?
607 .ok_or_else(|| anyhow!("no language server found for buffer"))
608}
609
610async fn location_links_from_proto(
611 proto_links: Vec<proto::LocationLink>,
612 project: Handle<Project>,
613 mut cx: AsyncAppContext,
614) -> Result<Vec<LocationLink>> {
615 let mut links = Vec::new();
616
617 for link in proto_links {
618 let origin = match link.origin {
619 Some(origin) => {
620 let buffer = project
621 .update(&mut cx, |this, cx| {
622 this.wait_for_remote_buffer(origin.buffer_id, cx)
623 })?
624 .await?;
625 let start = origin
626 .start
627 .and_then(deserialize_anchor)
628 .ok_or_else(|| anyhow!("missing origin start"))?;
629 let end = origin
630 .end
631 .and_then(deserialize_anchor)
632 .ok_or_else(|| anyhow!("missing origin end"))?;
633 buffer
634 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
635 .await?;
636 Some(Location {
637 buffer,
638 range: start..end,
639 })
640 }
641 None => None,
642 };
643
644 let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
645 let buffer = project
646 .update(&mut cx, |this, cx| {
647 this.wait_for_remote_buffer(target.buffer_id, cx)
648 })?
649 .await?;
650 let start = target
651 .start
652 .and_then(deserialize_anchor)
653 .ok_or_else(|| anyhow!("missing target start"))?;
654 let end = target
655 .end
656 .and_then(deserialize_anchor)
657 .ok_or_else(|| anyhow!("missing target end"))?;
658 buffer
659 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
660 .await?;
661 let target = Location {
662 buffer,
663 range: start..end,
664 };
665
666 links.push(LocationLink { origin, target })
667 }
668
669 Ok(links)
670}
671
672async fn location_links_from_lsp(
673 message: Option<lsp2::GotoDefinitionResponse>,
674 project: Handle<Project>,
675 buffer: Handle<Buffer>,
676 server_id: LanguageServerId,
677 mut cx: AsyncAppContext,
678) -> Result<Vec<LocationLink>> {
679 let message = match message {
680 Some(message) => message,
681 None => return Ok(Vec::new()),
682 };
683
684 let mut unresolved_links = Vec::new();
685 match message {
686 lsp2::GotoDefinitionResponse::Scalar(loc) => {
687 unresolved_links.push((None, loc.uri, loc.range));
688 }
689
690 lsp2::GotoDefinitionResponse::Array(locs) => {
691 unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
692 }
693
694 lsp2::GotoDefinitionResponse::Link(links) => {
695 unresolved_links.extend(links.into_iter().map(|l| {
696 (
697 l.origin_selection_range,
698 l.target_uri,
699 l.target_selection_range,
700 )
701 }));
702 }
703 }
704
705 let (lsp_adapter, language_server) =
706 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
707 let mut definitions = Vec::new();
708 for (origin_range, target_uri, target_range) in unresolved_links {
709 let target_buffer_handle = project
710 .update(&mut cx, |this, cx| {
711 this.open_local_buffer_via_lsp(
712 target_uri,
713 language_server.server_id(),
714 lsp_adapter.name.clone(),
715 cx,
716 )
717 })?
718 .await?;
719
720 buffer.update(&mut cx, |origin_buffer, cx| {
721 let origin_location = origin_range.map(|origin_range| {
722 let origin_start =
723 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
724 let origin_end =
725 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
726 Location {
727 buffer: buffer.clone(),
728 range: origin_buffer.anchor_after(origin_start)
729 ..origin_buffer.anchor_before(origin_end),
730 }
731 });
732
733 let target_buffer = target_buffer_handle.read(cx);
734 let target_start =
735 target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
736 let target_end =
737 target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
738 let target_location = Location {
739 buffer: target_buffer_handle,
740 range: target_buffer.anchor_after(target_start)
741 ..target_buffer.anchor_before(target_end),
742 };
743
744 definitions.push(LocationLink {
745 origin: origin_location,
746 target: target_location,
747 })
748 })?;
749 }
750 Ok(definitions)
751}
752
753fn location_links_to_proto(
754 links: Vec<LocationLink>,
755 project: &mut Project,
756 peer_id: PeerId,
757 cx: &mut AppContext,
758) -> Vec<proto::LocationLink> {
759 links
760 .into_iter()
761 .map(|definition| {
762 let origin = definition.origin.map(|origin| {
763 let buffer_id = project.create_buffer_for_peer(&origin.buffer, peer_id, cx);
764 proto::Location {
765 start: Some(serialize_anchor(&origin.range.start)),
766 end: Some(serialize_anchor(&origin.range.end)),
767 buffer_id,
768 }
769 });
770
771 let buffer_id = project.create_buffer_for_peer(&definition.target.buffer, peer_id, cx);
772 let target = proto::Location {
773 start: Some(serialize_anchor(&definition.target.range.start)),
774 end: Some(serialize_anchor(&definition.target.range.end)),
775 buffer_id,
776 };
777
778 proto::LocationLink {
779 origin,
780 target: Some(target),
781 }
782 })
783 .collect()
784}
785
786#[async_trait(?Send)]
787impl LspCommand for GetReferences {
788 type Response = Vec<Location>;
789 type LspRequest = lsp2::request::References;
790 type ProtoRequest = proto::GetReferences;
791
792 fn to_lsp(
793 &self,
794 path: &Path,
795 _: &Buffer,
796 _: &Arc<LanguageServer>,
797 _: &AppContext,
798 ) -> lsp2::ReferenceParams {
799 lsp2::ReferenceParams {
800 text_document_position: lsp2::TextDocumentPositionParams {
801 text_document: lsp2::TextDocumentIdentifier {
802 uri: lsp2::Url::from_file_path(path).unwrap(),
803 },
804 position: point_to_lsp(self.position),
805 },
806 work_done_progress_params: Default::default(),
807 partial_result_params: Default::default(),
808 context: lsp2::ReferenceContext {
809 include_declaration: true,
810 },
811 }
812 }
813
814 async fn response_from_lsp(
815 self,
816 locations: Option<Vec<lsp2::Location>>,
817 project: Handle<Project>,
818 buffer: Handle<Buffer>,
819 server_id: LanguageServerId,
820 mut cx: AsyncAppContext,
821 ) -> Result<Vec<Location>> {
822 let mut references = Vec::new();
823 let (lsp_adapter, language_server) =
824 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
825
826 if let Some(locations) = locations {
827 for lsp_location in locations {
828 let target_buffer_handle = project
829 .update(&mut cx, |this, cx| {
830 this.open_local_buffer_via_lsp(
831 lsp_location.uri,
832 language_server.server_id(),
833 lsp_adapter.name.clone(),
834 cx,
835 )
836 })?
837 .await?;
838
839 target_buffer_handle.update(&mut cx, |target_buffer, cx| {
840 let target_start = target_buffer
841 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
842 let target_end = target_buffer
843 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
844 references.push(Location {
845 buffer: target_buffer_handle,
846 range: target_buffer.anchor_after(target_start)
847 ..target_buffer.anchor_before(target_end),
848 });
849 })?;
850 }
851 }
852
853 Ok(references)
854 }
855
856 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
857 proto::GetReferences {
858 project_id,
859 buffer_id: buffer.remote_id(),
860 position: Some(language2::proto::serialize_anchor(
861 &buffer.anchor_before(self.position),
862 )),
863 version: serialize_version(&buffer.version()),
864 }
865 }
866
867 async fn from_proto(
868 message: proto::GetReferences,
869 _: Handle<Project>,
870 buffer: Handle<Buffer>,
871 mut cx: AsyncAppContext,
872 ) -> Result<Self> {
873 let position = message
874 .position
875 .and_then(deserialize_anchor)
876 .ok_or_else(|| anyhow!("invalid position"))?;
877 buffer
878 .update(&mut cx, |buffer, _| {
879 buffer.wait_for_version(deserialize_version(&message.version))
880 })?
881 .await?;
882 Ok(Self {
883 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
884 })
885 }
886
887 fn response_to_proto(
888 response: Vec<Location>,
889 project: &mut Project,
890 peer_id: PeerId,
891 _: &clock::Global,
892 cx: &mut AppContext,
893 ) -> proto::GetReferencesResponse {
894 let locations = response
895 .into_iter()
896 .map(|definition| {
897 let buffer_id = project.create_buffer_for_peer(&definition.buffer, peer_id, cx);
898 proto::Location {
899 start: Some(serialize_anchor(&definition.range.start)),
900 end: Some(serialize_anchor(&definition.range.end)),
901 buffer_id,
902 }
903 })
904 .collect();
905 proto::GetReferencesResponse { locations }
906 }
907
908 async fn response_from_proto(
909 self,
910 message: proto::GetReferencesResponse,
911 project: Handle<Project>,
912 _: Handle<Buffer>,
913 mut cx: AsyncAppContext,
914 ) -> Result<Vec<Location>> {
915 let mut locations = Vec::new();
916 for location in message.locations {
917 let target_buffer = project
918 .update(&mut cx, |this, cx| {
919 this.wait_for_remote_buffer(location.buffer_id, cx)
920 })?
921 .await?;
922 let start = location
923 .start
924 .and_then(deserialize_anchor)
925 .ok_or_else(|| anyhow!("missing target start"))?;
926 let end = location
927 .end
928 .and_then(deserialize_anchor)
929 .ok_or_else(|| anyhow!("missing target end"))?;
930 target_buffer
931 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
932 .await?;
933 locations.push(Location {
934 buffer: target_buffer,
935 range: start..end,
936 })
937 }
938 Ok(locations)
939 }
940
941 fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
942 message.buffer_id
943 }
944}
945
946#[async_trait(?Send)]
947impl LspCommand for GetDocumentHighlights {
948 type Response = Vec<DocumentHighlight>;
949 type LspRequest = lsp2::request::DocumentHighlightRequest;
950 type ProtoRequest = proto::GetDocumentHighlights;
951
952 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
953 capabilities.document_highlight_provider.is_some()
954 }
955
956 fn to_lsp(
957 &self,
958 path: &Path,
959 _: &Buffer,
960 _: &Arc<LanguageServer>,
961 _: &AppContext,
962 ) -> lsp2::DocumentHighlightParams {
963 lsp2::DocumentHighlightParams {
964 text_document_position_params: lsp2::TextDocumentPositionParams {
965 text_document: lsp2::TextDocumentIdentifier {
966 uri: lsp2::Url::from_file_path(path).unwrap(),
967 },
968 position: point_to_lsp(self.position),
969 },
970 work_done_progress_params: Default::default(),
971 partial_result_params: Default::default(),
972 }
973 }
974
975 async fn response_from_lsp(
976 self,
977 lsp_highlights: Option<Vec<lsp2::DocumentHighlight>>,
978 _: Handle<Project>,
979 buffer: Handle<Buffer>,
980 _: LanguageServerId,
981 cx: AsyncAppContext,
982 ) -> Result<Vec<DocumentHighlight>> {
983 buffer.update(&mut cx, |buffer, _| {
984 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
985 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
986 lsp_highlights
987 .into_iter()
988 .map(|lsp_highlight| {
989 let start = buffer
990 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
991 let end = buffer
992 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
993 DocumentHighlight {
994 range: buffer.anchor_after(start)..buffer.anchor_before(end),
995 kind: lsp_highlight
996 .kind
997 .unwrap_or(lsp2::DocumentHighlightKind::READ),
998 }
999 })
1000 .collect()
1001 })
1002 }
1003
1004 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
1005 proto::GetDocumentHighlights {
1006 project_id,
1007 buffer_id: buffer.remote_id(),
1008 position: Some(language2::proto::serialize_anchor(
1009 &buffer.anchor_before(self.position),
1010 )),
1011 version: serialize_version(&buffer.version()),
1012 }
1013 }
1014
1015 async fn from_proto(
1016 message: proto::GetDocumentHighlights,
1017 _: Handle<Project>,
1018 buffer: Handle<Buffer>,
1019 mut cx: AsyncAppContext,
1020 ) -> Result<Self> {
1021 let position = message
1022 .position
1023 .and_then(deserialize_anchor)
1024 .ok_or_else(|| anyhow!("invalid position"))?;
1025 buffer
1026 .update(&mut cx, |buffer, _| {
1027 buffer.wait_for_version(deserialize_version(&message.version))
1028 })?
1029 .await?;
1030 Ok(Self {
1031 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1032 })
1033 }
1034
1035 fn response_to_proto(
1036 response: Vec<DocumentHighlight>,
1037 _: &mut Project,
1038 _: PeerId,
1039 _: &clock::Global,
1040 _: &mut AppContext,
1041 ) -> proto::GetDocumentHighlightsResponse {
1042 let highlights = response
1043 .into_iter()
1044 .map(|highlight| proto::DocumentHighlight {
1045 start: Some(serialize_anchor(&highlight.range.start)),
1046 end: Some(serialize_anchor(&highlight.range.end)),
1047 kind: match highlight.kind {
1048 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
1049 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
1050 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
1051 _ => proto::document_highlight::Kind::Text.into(),
1052 },
1053 })
1054 .collect();
1055 proto::GetDocumentHighlightsResponse { highlights }
1056 }
1057
1058 async fn response_from_proto(
1059 self,
1060 message: proto::GetDocumentHighlightsResponse,
1061 _: Handle<Project>,
1062 buffer: Handle<Buffer>,
1063 mut cx: AsyncAppContext,
1064 ) -> Result<Vec<DocumentHighlight>> {
1065 let mut highlights = Vec::new();
1066 for highlight in message.highlights {
1067 let start = highlight
1068 .start
1069 .and_then(deserialize_anchor)
1070 .ok_or_else(|| anyhow!("missing target start"))?;
1071 let end = highlight
1072 .end
1073 .and_then(deserialize_anchor)
1074 .ok_or_else(|| anyhow!("missing target end"))?;
1075 buffer
1076 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
1077 .await?;
1078 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
1079 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
1080 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
1081 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
1082 None => DocumentHighlightKind::TEXT,
1083 };
1084 highlights.push(DocumentHighlight {
1085 range: start..end,
1086 kind,
1087 });
1088 }
1089 Ok(highlights)
1090 }
1091
1092 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
1093 message.buffer_id
1094 }
1095}
1096
1097#[async_trait(?Send)]
1098impl LspCommand for GetHover {
1099 type Response = Option<Hover>;
1100 type LspRequest = lsp2::request::HoverRequest;
1101 type ProtoRequest = proto::GetHover;
1102
1103 fn to_lsp(
1104 &self,
1105 path: &Path,
1106 _: &Buffer,
1107 _: &Arc<LanguageServer>,
1108 _: &AppContext,
1109 ) -> lsp2::HoverParams {
1110 lsp2::HoverParams {
1111 text_document_position_params: lsp2::TextDocumentPositionParams {
1112 text_document: lsp2::TextDocumentIdentifier {
1113 uri: lsp2::Url::from_file_path(path).unwrap(),
1114 },
1115 position: point_to_lsp(self.position),
1116 },
1117 work_done_progress_params: Default::default(),
1118 }
1119 }
1120
1121 async fn response_from_lsp(
1122 self,
1123 message: Option<lsp2::Hover>,
1124 _: Handle<Project>,
1125 buffer: Handle<Buffer>,
1126 _: LanguageServerId,
1127 mut cx: AsyncAppContext,
1128 ) -> Result<Self::Response> {
1129 let Some(hover) = message else {
1130 return Ok(None);
1131 };
1132
1133 let (language, range) = buffer.update(&mut cx, |buffer, cx| {
1134 (
1135 buffer.language().cloned(),
1136 hover.range.map(|range| {
1137 let token_start =
1138 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1139 let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1140 buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1141 }),
1142 )
1143 })?;
1144
1145 fn hover_blocks_from_marked_string(
1146 marked_string: lsp2::MarkedString,
1147 ) -> Option<HoverBlock> {
1148 let block = match marked_string {
1149 lsp2::MarkedString::String(content) => HoverBlock {
1150 text: content,
1151 kind: HoverBlockKind::Markdown,
1152 },
1153 lsp2::MarkedString::LanguageString(lsp2::LanguageString { language, value }) => {
1154 HoverBlock {
1155 text: value,
1156 kind: HoverBlockKind::Code { language },
1157 }
1158 }
1159 };
1160 if block.text.is_empty() {
1161 None
1162 } else {
1163 Some(block)
1164 }
1165 }
1166
1167 let contents = match hover.contents {
1168 lsp2::HoverContents::Scalar(marked_string) => {
1169 hover_blocks_from_marked_string(marked_string)
1170 .into_iter()
1171 .collect()
1172 }
1173 lsp2::HoverContents::Array(marked_strings) => marked_strings
1174 .into_iter()
1175 .filter_map(hover_blocks_from_marked_string)
1176 .collect(),
1177 lsp2::HoverContents::Markup(markup_content) => vec![HoverBlock {
1178 text: markup_content.value,
1179 kind: if markup_content.kind == lsp2::MarkupKind::Markdown {
1180 HoverBlockKind::Markdown
1181 } else {
1182 HoverBlockKind::PlainText
1183 },
1184 }],
1185 };
1186
1187 Ok(Some(Hover {
1188 contents,
1189 range,
1190 language,
1191 }))
1192 }
1193
1194 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1195 proto::GetHover {
1196 project_id,
1197 buffer_id: buffer.remote_id(),
1198 position: Some(language2::proto::serialize_anchor(
1199 &buffer.anchor_before(self.position),
1200 )),
1201 version: serialize_version(&buffer.version),
1202 }
1203 }
1204
1205 async fn from_proto(
1206 message: Self::ProtoRequest,
1207 _: Handle<Project>,
1208 buffer: Handle<Buffer>,
1209 mut cx: AsyncAppContext,
1210 ) -> Result<Self> {
1211 let position = message
1212 .position
1213 .and_then(deserialize_anchor)
1214 .ok_or_else(|| anyhow!("invalid position"))?;
1215 buffer
1216 .update(&mut cx, |buffer, _| {
1217 buffer.wait_for_version(deserialize_version(&message.version))
1218 })?
1219 .await?;
1220 Ok(Self {
1221 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1222 })
1223 }
1224
1225 fn response_to_proto(
1226 response: Self::Response,
1227 _: &mut Project,
1228 _: PeerId,
1229 _: &clock::Global,
1230 _: &mut AppContext,
1231 ) -> proto::GetHoverResponse {
1232 if let Some(response) = response {
1233 let (start, end) = if let Some(range) = response.range {
1234 (
1235 Some(language2::proto::serialize_anchor(&range.start)),
1236 Some(language2::proto::serialize_anchor(&range.end)),
1237 )
1238 } else {
1239 (None, None)
1240 };
1241
1242 let contents = response
1243 .contents
1244 .into_iter()
1245 .map(|block| proto::HoverBlock {
1246 text: block.text,
1247 is_markdown: block.kind == HoverBlockKind::Markdown,
1248 language: if let HoverBlockKind::Code { language } = block.kind {
1249 Some(language)
1250 } else {
1251 None
1252 },
1253 })
1254 .collect();
1255
1256 proto::GetHoverResponse {
1257 start,
1258 end,
1259 contents,
1260 }
1261 } else {
1262 proto::GetHoverResponse {
1263 start: None,
1264 end: None,
1265 contents: Vec::new(),
1266 }
1267 }
1268 }
1269
1270 async fn response_from_proto(
1271 self,
1272 message: proto::GetHoverResponse,
1273 _: Handle<Project>,
1274 buffer: Handle<Buffer>,
1275 cx: AsyncAppContext,
1276 ) -> Result<Self::Response> {
1277 let contents: Vec<_> = message
1278 .contents
1279 .into_iter()
1280 .map(|block| HoverBlock {
1281 text: block.text,
1282 kind: if let Some(language) = block.language {
1283 HoverBlockKind::Code { language }
1284 } else if block.is_markdown {
1285 HoverBlockKind::Markdown
1286 } else {
1287 HoverBlockKind::PlainText
1288 },
1289 })
1290 .collect();
1291 if contents.is_empty() {
1292 return Ok(None);
1293 }
1294
1295 let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
1296 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1297 language2::proto::deserialize_anchor(start)
1298 .and_then(|start| language2::proto::deserialize_anchor(end).map(|end| start..end))
1299 } else {
1300 None
1301 };
1302
1303 Ok(Some(Hover {
1304 contents,
1305 range,
1306 language,
1307 }))
1308 }
1309
1310 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 {
1311 message.buffer_id
1312 }
1313}
1314
1315#[async_trait(?Send)]
1316impl LspCommand for GetCompletions {
1317 type Response = Vec<Completion>;
1318 type LspRequest = lsp2::request::Completion;
1319 type ProtoRequest = proto::GetCompletions;
1320
1321 fn to_lsp(
1322 &self,
1323 path: &Path,
1324 _: &Buffer,
1325 _: &Arc<LanguageServer>,
1326 _: &AppContext,
1327 ) -> lsp2::CompletionParams {
1328 lsp2::CompletionParams {
1329 text_document_position: lsp2::TextDocumentPositionParams::new(
1330 lsp2::TextDocumentIdentifier::new(lsp2::Url::from_file_path(path).unwrap()),
1331 point_to_lsp(self.position),
1332 ),
1333 context: Default::default(),
1334 work_done_progress_params: Default::default(),
1335 partial_result_params: Default::default(),
1336 }
1337 }
1338
1339 async fn response_from_lsp(
1340 self,
1341 completions: Option<lsp2::CompletionResponse>,
1342 _: Handle<Project>,
1343 buffer: Handle<Buffer>,
1344 server_id: LanguageServerId,
1345 cx: AsyncAppContext,
1346 ) -> Result<Vec<Completion>> {
1347 let mut response_list = None;
1348 let completions = if let Some(completions) = completions {
1349 match completions {
1350 lsp2::CompletionResponse::Array(completions) => completions,
1351
1352 lsp2::CompletionResponse::List(mut list) => {
1353 let items = std::mem::take(&mut list.items);
1354 response_list = Some(list);
1355 items
1356 }
1357 }
1358 } else {
1359 Default::default()
1360 };
1361
1362 let completions = buffer.update(&mut cx, |buffer, _| {
1363 let language = buffer.language().cloned();
1364 let snapshot = buffer.snapshot();
1365 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1366
1367 let mut range_for_token = None;
1368 completions
1369 .into_iter()
1370 .filter_map(move |mut lsp_completion| {
1371 let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() {
1372 // If the language server provides a range to overwrite, then
1373 // check that the range is valid.
1374 Some(lsp2::CompletionTextEdit::Edit(edit)) => {
1375 let range = range_from_lsp(edit.range);
1376 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1377 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1378 if start != range.start.0 || end != range.end.0 {
1379 log::info!("completion out of expected range");
1380 return None;
1381 }
1382 (
1383 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1384 edit.new_text.clone(),
1385 )
1386 }
1387
1388 // If the language server does not provide a range, then infer
1389 // the range based on the syntax tree.
1390 None => {
1391 if self.position != clipped_position {
1392 log::info!("completion out of expected range");
1393 return None;
1394 }
1395
1396 let default_edit_range = response_list
1397 .as_ref()
1398 .and_then(|list| list.item_defaults.as_ref())
1399 .and_then(|defaults| defaults.edit_range.as_ref())
1400 .and_then(|range| match range {
1401 CompletionListItemDefaultsEditRange::Range(r) => Some(r),
1402 _ => None,
1403 });
1404
1405 let range = if let Some(range) = default_edit_range {
1406 let range = range_from_lsp(range.clone());
1407 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1408 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1409 if start != range.start.0 || end != range.end.0 {
1410 log::info!("completion out of expected range");
1411 return None;
1412 }
1413
1414 snapshot.anchor_before(start)..snapshot.anchor_after(end)
1415 } else {
1416 range_for_token
1417 .get_or_insert_with(|| {
1418 let offset = self.position.to_offset(&snapshot);
1419 let (range, kind) = snapshot.surrounding_word(offset);
1420 let range = if kind == Some(CharKind::Word) {
1421 range
1422 } else {
1423 offset..offset
1424 };
1425
1426 snapshot.anchor_before(range.start)
1427 ..snapshot.anchor_after(range.end)
1428 })
1429 .clone()
1430 };
1431
1432 let text = lsp_completion
1433 .insert_text
1434 .as_ref()
1435 .unwrap_or(&lsp_completion.label)
1436 .clone();
1437 (range, text)
1438 }
1439
1440 Some(lsp2::CompletionTextEdit::InsertAndReplace(_)) => {
1441 log::info!("unsupported insert/replace completion");
1442 return None;
1443 }
1444 };
1445
1446 let language = language.clone();
1447 LineEnding::normalize(&mut new_text);
1448 Some(async move {
1449 let mut label = None;
1450 if let Some(language) = language {
1451 language.process_completion(&mut lsp_completion).await;
1452 label = language.label_for_completion(&lsp_completion).await;
1453 }
1454 Completion {
1455 old_range,
1456 new_text,
1457 label: label.unwrap_or_else(|| {
1458 language2::CodeLabel::plain(
1459 lsp_completion.label.clone(),
1460 lsp_completion.filter_text.as_deref(),
1461 )
1462 }),
1463 server_id,
1464 lsp_completion,
1465 }
1466 })
1467 })
1468 })?;
1469
1470 Ok(future::join_all(completions).await)
1471 }
1472
1473 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1474 let anchor = buffer.anchor_after(self.position);
1475 proto::GetCompletions {
1476 project_id,
1477 buffer_id: buffer.remote_id(),
1478 position: Some(language2::proto::serialize_anchor(&anchor)),
1479 version: serialize_version(&buffer.version()),
1480 }
1481 }
1482
1483 async fn from_proto(
1484 message: proto::GetCompletions,
1485 _: Handle<Project>,
1486 buffer: Handle<Buffer>,
1487 mut cx: AsyncAppContext,
1488 ) -> Result<Self> {
1489 let version = deserialize_version(&message.version);
1490 buffer
1491 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))?
1492 .await?;
1493 let position = message
1494 .position
1495 .and_then(language2::proto::deserialize_anchor)
1496 .map(|p| {
1497 buffer.update(&mut cx, |buffer, _| {
1498 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1499 })
1500 })
1501 .ok_or_else(|| anyhow!("invalid position"))??;
1502 Ok(Self { position })
1503 }
1504
1505 fn response_to_proto(
1506 completions: Vec<Completion>,
1507 _: &mut Project,
1508 _: PeerId,
1509 buffer_version: &clock::Global,
1510 _: &mut AppContext,
1511 ) -> proto::GetCompletionsResponse {
1512 proto::GetCompletionsResponse {
1513 completions: completions
1514 .iter()
1515 .map(language2::proto::serialize_completion)
1516 .collect(),
1517 version: serialize_version(&buffer_version),
1518 }
1519 }
1520
1521 async fn response_from_proto(
1522 self,
1523 message: proto::GetCompletionsResponse,
1524 _: Handle<Project>,
1525 buffer: Handle<Buffer>,
1526 mut cx: AsyncAppContext,
1527 ) -> Result<Vec<Completion>> {
1528 buffer
1529 .update(&mut cx, |buffer, _| {
1530 buffer.wait_for_version(deserialize_version(&message.version))
1531 })?
1532 .await?;
1533
1534 let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
1535 let completions = message.completions.into_iter().map(|completion| {
1536 language2::proto::deserialize_completion(completion, language.clone())
1537 });
1538 future::try_join_all(completions).await
1539 }
1540
1541 fn buffer_id_from_proto(message: &proto::GetCompletions) -> u64 {
1542 message.buffer_id
1543 }
1544}
1545
1546#[async_trait(?Send)]
1547impl LspCommand for GetCodeActions {
1548 type Response = Vec<CodeAction>;
1549 type LspRequest = lsp2::request::CodeActionRequest;
1550 type ProtoRequest = proto::GetCodeActions;
1551
1552 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1553 match &capabilities.code_action_provider {
1554 None => false,
1555 Some(lsp2::CodeActionProviderCapability::Simple(false)) => false,
1556 _ => true,
1557 }
1558 }
1559
1560 fn to_lsp(
1561 &self,
1562 path: &Path,
1563 buffer: &Buffer,
1564 language_server: &Arc<LanguageServer>,
1565 _: &AppContext,
1566 ) -> lsp2::CodeActionParams {
1567 let relevant_diagnostics = buffer
1568 .snapshot()
1569 .diagnostics_in_range::<_, usize>(self.range.clone(), false)
1570 .map(|entry| entry.to_lsp_diagnostic_stub())
1571 .collect();
1572 lsp2::CodeActionParams {
1573 text_document: lsp2::TextDocumentIdentifier::new(
1574 lsp2::Url::from_file_path(path).unwrap(),
1575 ),
1576 range: range_to_lsp(self.range.to_point_utf16(buffer)),
1577 work_done_progress_params: Default::default(),
1578 partial_result_params: Default::default(),
1579 context: lsp2::CodeActionContext {
1580 diagnostics: relevant_diagnostics,
1581 only: language_server.code_action_kinds(),
1582 ..lsp2::CodeActionContext::default()
1583 },
1584 }
1585 }
1586
1587 async fn response_from_lsp(
1588 self,
1589 actions: Option<lsp2::CodeActionResponse>,
1590 _: Handle<Project>,
1591 _: Handle<Buffer>,
1592 server_id: LanguageServerId,
1593 _: AsyncAppContext,
1594 ) -> Result<Vec<CodeAction>> {
1595 Ok(actions
1596 .unwrap_or_default()
1597 .into_iter()
1598 .filter_map(|entry| {
1599 if let lsp2::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1600 Some(CodeAction {
1601 server_id,
1602 range: self.range.clone(),
1603 lsp_action,
1604 })
1605 } else {
1606 None
1607 }
1608 })
1609 .collect())
1610 }
1611
1612 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
1613 proto::GetCodeActions {
1614 project_id,
1615 buffer_id: buffer.remote_id(),
1616 start: Some(language2::proto::serialize_anchor(&self.range.start)),
1617 end: Some(language2::proto::serialize_anchor(&self.range.end)),
1618 version: serialize_version(&buffer.version()),
1619 }
1620 }
1621
1622 async fn from_proto(
1623 message: proto::GetCodeActions,
1624 _: Handle<Project>,
1625 buffer: Handle<Buffer>,
1626 mut cx: AsyncAppContext,
1627 ) -> Result<Self> {
1628 let start = message
1629 .start
1630 .and_then(language2::proto::deserialize_anchor)
1631 .ok_or_else(|| anyhow!("invalid start"))?;
1632 let end = message
1633 .end
1634 .and_then(language2::proto::deserialize_anchor)
1635 .ok_or_else(|| anyhow!("invalid end"))?;
1636 buffer
1637 .update(&mut cx, |buffer, _| {
1638 buffer.wait_for_version(deserialize_version(&message.version))
1639 })?
1640 .await?;
1641
1642 Ok(Self { range: start..end })
1643 }
1644
1645 fn response_to_proto(
1646 code_actions: Vec<CodeAction>,
1647 _: &mut Project,
1648 _: PeerId,
1649 buffer_version: &clock::Global,
1650 _: &mut AppContext,
1651 ) -> proto::GetCodeActionsResponse {
1652 proto::GetCodeActionsResponse {
1653 actions: code_actions
1654 .iter()
1655 .map(language2::proto::serialize_code_action)
1656 .collect(),
1657 version: serialize_version(&buffer_version),
1658 }
1659 }
1660
1661 async fn response_from_proto(
1662 self,
1663 message: proto::GetCodeActionsResponse,
1664 _: Handle<Project>,
1665 buffer: Handle<Buffer>,
1666 mut cx: AsyncAppContext,
1667 ) -> Result<Vec<CodeAction>> {
1668 buffer
1669 .update(&mut cx, |buffer, _| {
1670 buffer.wait_for_version(deserialize_version(&message.version))
1671 })?
1672 .await?;
1673 message
1674 .actions
1675 .into_iter()
1676 .map(language2::proto::deserialize_code_action)
1677 .collect()
1678 }
1679
1680 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> u64 {
1681 message.buffer_id
1682 }
1683}
1684
1685#[async_trait(?Send)]
1686impl LspCommand for OnTypeFormatting {
1687 type Response = Option<Transaction>;
1688 type LspRequest = lsp2::request::OnTypeFormatting;
1689 type ProtoRequest = proto::OnTypeFormatting;
1690
1691 fn check_capabilities(&self, server_capabilities: &lsp2::ServerCapabilities) -> bool {
1692 let Some(on_type_formatting_options) =
1693 &server_capabilities.document_on_type_formatting_provider
1694 else {
1695 return false;
1696 };
1697 on_type_formatting_options
1698 .first_trigger_character
1699 .contains(&self.trigger)
1700 || on_type_formatting_options
1701 .more_trigger_character
1702 .iter()
1703 .flatten()
1704 .any(|chars| chars.contains(&self.trigger))
1705 }
1706
1707 fn to_lsp(
1708 &self,
1709 path: &Path,
1710 _: &Buffer,
1711 _: &Arc<LanguageServer>,
1712 _: &AppContext,
1713 ) -> lsp2::DocumentOnTypeFormattingParams {
1714 lsp2::DocumentOnTypeFormattingParams {
1715 text_document_position: lsp2::TextDocumentPositionParams::new(
1716 lsp2::TextDocumentIdentifier::new(lsp2::Url::from_file_path(path).unwrap()),
1717 point_to_lsp(self.position),
1718 ),
1719 ch: self.trigger.clone(),
1720 options: lsp_formatting_options(self.options.tab_size),
1721 }
1722 }
1723
1724 async fn response_from_lsp(
1725 self,
1726 message: Option<Vec<lsp2::TextEdit>>,
1727 project: Handle<Project>,
1728 buffer: Handle<Buffer>,
1729 server_id: LanguageServerId,
1730 mut cx: AsyncAppContext,
1731 ) -> Result<Option<Transaction>> {
1732 if let Some(edits) = message {
1733 let (lsp_adapter, lsp_server) =
1734 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1735 Project::deserialize_edits(
1736 project,
1737 buffer,
1738 edits,
1739 self.push_to_history,
1740 lsp_adapter,
1741 lsp_server,
1742 &mut cx,
1743 )
1744 .await
1745 } else {
1746 Ok(None)
1747 }
1748 }
1749
1750 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1751 proto::OnTypeFormatting {
1752 project_id,
1753 buffer_id: buffer.remote_id(),
1754 position: Some(language2::proto::serialize_anchor(
1755 &buffer.anchor_before(self.position),
1756 )),
1757 trigger: self.trigger.clone(),
1758 version: serialize_version(&buffer.version()),
1759 }
1760 }
1761
1762 async fn from_proto(
1763 message: proto::OnTypeFormatting,
1764 _: Handle<Project>,
1765 buffer: Handle<Buffer>,
1766 mut cx: AsyncAppContext,
1767 ) -> Result<Self> {
1768 let position = message
1769 .position
1770 .and_then(deserialize_anchor)
1771 .ok_or_else(|| anyhow!("invalid position"))?;
1772 buffer
1773 .update(&mut cx, |buffer, _| {
1774 buffer.wait_for_version(deserialize_version(&message.version))
1775 })?
1776 .await?;
1777
1778 let tab_size = buffer.update(&mut cx, |buffer, cx| {
1779 language_settings(buffer.language(), buffer.file(), cx).tab_size
1780 })?;
1781
1782 Ok(Self {
1783 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1784 trigger: message.trigger.clone(),
1785 options: lsp_formatting_options(tab_size.get()).into(),
1786 push_to_history: false,
1787 })
1788 }
1789
1790 fn response_to_proto(
1791 response: Option<Transaction>,
1792 _: &mut Project,
1793 _: PeerId,
1794 _: &clock::Global,
1795 _: &mut AppContext,
1796 ) -> proto::OnTypeFormattingResponse {
1797 proto::OnTypeFormattingResponse {
1798 transaction: response
1799 .map(|transaction| language2::proto::serialize_transaction(&transaction)),
1800 }
1801 }
1802
1803 async fn response_from_proto(
1804 self,
1805 message: proto::OnTypeFormattingResponse,
1806 _: Handle<Project>,
1807 _: Handle<Buffer>,
1808 _: AsyncAppContext,
1809 ) -> Result<Option<Transaction>> {
1810 let Some(transaction) = message.transaction else {
1811 return Ok(None);
1812 };
1813 Ok(Some(language2::proto::deserialize_transaction(
1814 transaction,
1815 )?))
1816 }
1817
1818 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 {
1819 message.buffer_id
1820 }
1821}
1822
1823impl InlayHints {
1824 pub async fn lsp_to_project_hint(
1825 lsp_hint: lsp2::InlayHint,
1826 buffer_handle: &Handle<Buffer>,
1827 server_id: LanguageServerId,
1828 resolve_state: ResolveState,
1829 force_no_type_left_padding: bool,
1830 cx: &mut AsyncAppContext,
1831 ) -> anyhow::Result<InlayHint> {
1832 let kind = lsp_hint.kind.and_then(|kind| match kind {
1833 lsp2::InlayHintKind::TYPE => Some(InlayHintKind::Type),
1834 lsp2::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
1835 _ => None,
1836 });
1837
1838 let position = buffer_handle.update(cx, |buffer, _| {
1839 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
1840 if kind == Some(InlayHintKind::Parameter) {
1841 buffer.anchor_before(position)
1842 } else {
1843 buffer.anchor_after(position)
1844 }
1845 })?;
1846 let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
1847 .await
1848 .context("lsp to project inlay hint conversion")?;
1849 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
1850 false
1851 } else {
1852 lsp_hint.padding_left.unwrap_or(false)
1853 };
1854
1855 Ok(InlayHint {
1856 position,
1857 padding_left,
1858 padding_right: lsp_hint.padding_right.unwrap_or(false),
1859 label,
1860 kind,
1861 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
1862 lsp2::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
1863 lsp2::InlayHintTooltip::MarkupContent(markup_content) => {
1864 InlayHintTooltip::MarkupContent(MarkupContent {
1865 kind: match markup_content.kind {
1866 lsp2::MarkupKind::PlainText => HoverBlockKind::PlainText,
1867 lsp2::MarkupKind::Markdown => HoverBlockKind::Markdown,
1868 },
1869 value: markup_content.value,
1870 })
1871 }
1872 }),
1873 resolve_state,
1874 })
1875 }
1876
1877 async fn lsp_inlay_label_to_project(
1878 lsp_label: lsp2::InlayHintLabel,
1879 server_id: LanguageServerId,
1880 ) -> anyhow::Result<InlayHintLabel> {
1881 let label = match lsp_label {
1882 lsp2::InlayHintLabel::String(s) => InlayHintLabel::String(s),
1883 lsp2::InlayHintLabel::LabelParts(lsp_parts) => {
1884 let mut parts = Vec::with_capacity(lsp_parts.len());
1885 for lsp_part in lsp_parts {
1886 parts.push(InlayHintLabelPart {
1887 value: lsp_part.value,
1888 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
1889 lsp2::InlayHintLabelPartTooltip::String(s) => {
1890 InlayHintLabelPartTooltip::String(s)
1891 }
1892 lsp2::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
1893 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
1894 kind: match markup_content.kind {
1895 lsp2::MarkupKind::PlainText => HoverBlockKind::PlainText,
1896 lsp2::MarkupKind::Markdown => HoverBlockKind::Markdown,
1897 },
1898 value: markup_content.value,
1899 })
1900 }
1901 }),
1902 location: Some(server_id).zip(lsp_part.location),
1903 });
1904 }
1905 InlayHintLabel::LabelParts(parts)
1906 }
1907 };
1908
1909 Ok(label)
1910 }
1911
1912 pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
1913 let (state, lsp_resolve_state) = match response_hint.resolve_state {
1914 ResolveState::Resolved => (0, None),
1915 ResolveState::CanResolve(server_id, resolve_data) => (
1916 1,
1917 resolve_data
1918 .map(|json_data| {
1919 serde_json::to_string(&json_data)
1920 .expect("failed to serialize resolve json data")
1921 })
1922 .map(|value| proto::resolve_state::LspResolveState {
1923 server_id: server_id.0 as u64,
1924 value,
1925 }),
1926 ),
1927 ResolveState::Resolving => (2, None),
1928 };
1929 let resolve_state = Some(proto::ResolveState {
1930 state,
1931 lsp_resolve_state,
1932 });
1933 proto::InlayHint {
1934 position: Some(language2::proto::serialize_anchor(&response_hint.position)),
1935 padding_left: response_hint.padding_left,
1936 padding_right: response_hint.padding_right,
1937 label: Some(proto::InlayHintLabel {
1938 label: Some(match response_hint.label {
1939 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
1940 InlayHintLabel::LabelParts(label_parts) => {
1941 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
1942 parts: label_parts.into_iter().map(|label_part| {
1943 let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
1944 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 });
1945 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 });
1946 proto::InlayHintLabelPart {
1947 value: label_part.value,
1948 tooltip: label_part.tooltip.map(|tooltip| {
1949 let proto_tooltip = match tooltip {
1950 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
1951 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
1952 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1953 value: markup_content.value,
1954 }),
1955 };
1956 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
1957 }),
1958 location_url,
1959 location_range_start,
1960 location_range_end,
1961 language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
1962 }}).collect()
1963 })
1964 }
1965 }),
1966 }),
1967 kind: response_hint.kind.map(|kind| kind.name().to_string()),
1968 tooltip: response_hint.tooltip.map(|response_tooltip| {
1969 let proto_tooltip = match response_tooltip {
1970 InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
1971 InlayHintTooltip::MarkupContent(markup_content) => {
1972 proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
1973 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1974 value: markup_content.value,
1975 })
1976 }
1977 };
1978 proto::InlayHintTooltip {
1979 content: Some(proto_tooltip),
1980 }
1981 }),
1982 resolve_state,
1983 }
1984 }
1985
1986 pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
1987 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
1988 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
1989 });
1990 let resolve_state_data = resolve_state
1991 .lsp_resolve_state.as_ref()
1992 .map(|lsp_resolve_state| {
1993 serde_json::from_str::<Option<lsp2::LSPAny>>(&lsp_resolve_state.value)
1994 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
1995 .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
1996 })
1997 .transpose()?;
1998 let resolve_state = match resolve_state.state {
1999 0 => ResolveState::Resolved,
2000 1 => {
2001 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2002 format!(
2003 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2004 )
2005 })?;
2006 ResolveState::CanResolve(server_id, lsp_resolve_state)
2007 }
2008 2 => ResolveState::Resolving,
2009 invalid => {
2010 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2011 }
2012 };
2013 Ok(InlayHint {
2014 position: message_hint
2015 .position
2016 .and_then(language2::proto::deserialize_anchor)
2017 .context("invalid position")?,
2018 label: match message_hint
2019 .label
2020 .and_then(|label| label.label)
2021 .context("missing label")?
2022 {
2023 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2024 proto::inlay_hint_label::Label::LabelParts(parts) => {
2025 let mut label_parts = Vec::new();
2026 for part in parts.parts {
2027 label_parts.push(InlayHintLabelPart {
2028 value: part.value,
2029 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2030 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2031 InlayHintLabelPartTooltip::String(s)
2032 }
2033 Some(
2034 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2035 markup_content,
2036 ),
2037 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2038 kind: if markup_content.is_markdown {
2039 HoverBlockKind::Markdown
2040 } else {
2041 HoverBlockKind::PlainText
2042 },
2043 value: markup_content.value,
2044 }),
2045 None => InlayHintLabelPartTooltip::String(String::new()),
2046 }),
2047 location: {
2048 match part
2049 .location_url
2050 .zip(
2051 part.location_range_start.and_then(|start| {
2052 Some(start..part.location_range_end?)
2053 }),
2054 )
2055 .zip(part.language_server_id)
2056 {
2057 Some(((uri, range), server_id)) => Some((
2058 LanguageServerId(server_id as usize),
2059 lsp2::Location {
2060 uri: lsp2::Url::parse(&uri)
2061 .context("invalid uri in hint part {part:?}")?,
2062 range: lsp2::Range::new(
2063 point_to_lsp(PointUtf16::new(
2064 range.start.row,
2065 range.start.column,
2066 )),
2067 point_to_lsp(PointUtf16::new(
2068 range.end.row,
2069 range.end.column,
2070 )),
2071 ),
2072 },
2073 )),
2074 None => None,
2075 }
2076 },
2077 });
2078 }
2079
2080 InlayHintLabel::LabelParts(label_parts)
2081 }
2082 },
2083 padding_left: message_hint.padding_left,
2084 padding_right: message_hint.padding_right,
2085 kind: message_hint
2086 .kind
2087 .as_deref()
2088 .and_then(InlayHintKind::from_name),
2089 tooltip: message_hint.tooltip.and_then(|tooltip| {
2090 Some(match tooltip.content? {
2091 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2092 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2093 InlayHintTooltip::MarkupContent(MarkupContent {
2094 kind: if markup_content.is_markdown {
2095 HoverBlockKind::Markdown
2096 } else {
2097 HoverBlockKind::PlainText
2098 },
2099 value: markup_content.value,
2100 })
2101 }
2102 })
2103 }),
2104 resolve_state,
2105 })
2106 }
2107
2108 pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp2::InlayHint {
2109 lsp2::InlayHint {
2110 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2111 kind: hint.kind.map(|kind| match kind {
2112 InlayHintKind::Type => lsp2::InlayHintKind::TYPE,
2113 InlayHintKind::Parameter => lsp2::InlayHintKind::PARAMETER,
2114 }),
2115 text_edits: None,
2116 tooltip: hint.tooltip.and_then(|tooltip| {
2117 Some(match tooltip {
2118 InlayHintTooltip::String(s) => lsp2::InlayHintTooltip::String(s),
2119 InlayHintTooltip::MarkupContent(markup_content) => {
2120 lsp2::InlayHintTooltip::MarkupContent(lsp2::MarkupContent {
2121 kind: match markup_content.kind {
2122 HoverBlockKind::PlainText => lsp2::MarkupKind::PlainText,
2123 HoverBlockKind::Markdown => lsp2::MarkupKind::Markdown,
2124 HoverBlockKind::Code { .. } => return None,
2125 },
2126 value: markup_content.value,
2127 })
2128 }
2129 })
2130 }),
2131 label: match hint.label {
2132 InlayHintLabel::String(s) => lsp2::InlayHintLabel::String(s),
2133 InlayHintLabel::LabelParts(label_parts) => lsp2::InlayHintLabel::LabelParts(
2134 label_parts
2135 .into_iter()
2136 .map(|part| lsp2::InlayHintLabelPart {
2137 value: part.value,
2138 tooltip: part.tooltip.and_then(|tooltip| {
2139 Some(match tooltip {
2140 InlayHintLabelPartTooltip::String(s) => {
2141 lsp2::InlayHintLabelPartTooltip::String(s)
2142 }
2143 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2144 lsp2::InlayHintLabelPartTooltip::MarkupContent(
2145 lsp2::MarkupContent {
2146 kind: match markup_content.kind {
2147 HoverBlockKind::PlainText => {
2148 lsp2::MarkupKind::PlainText
2149 }
2150 HoverBlockKind::Markdown => {
2151 lsp2::MarkupKind::Markdown
2152 }
2153 HoverBlockKind::Code { .. } => return None,
2154 },
2155 value: markup_content.value,
2156 },
2157 )
2158 }
2159 })
2160 }),
2161 location: part.location.map(|(_, location)| location),
2162 command: None,
2163 })
2164 .collect(),
2165 ),
2166 },
2167 padding_left: Some(hint.padding_left),
2168 padding_right: Some(hint.padding_right),
2169 data: match hint.resolve_state {
2170 ResolveState::CanResolve(_, data) => data,
2171 ResolveState::Resolving | ResolveState::Resolved => None,
2172 },
2173 }
2174 }
2175
2176 pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2177 capabilities
2178 .inlay_hint_provider
2179 .as_ref()
2180 .and_then(|options| match options {
2181 OneOf::Left(_is_supported) => None,
2182 OneOf::Right(capabilities) => match capabilities {
2183 lsp2::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2184 lsp2::InlayHintServerCapabilities::RegistrationOptions(o) => {
2185 o.inlay_hint_options.resolve_provider
2186 }
2187 },
2188 })
2189 .unwrap_or(false)
2190 }
2191}
2192
2193#[async_trait(?Send)]
2194impl LspCommand for InlayHints {
2195 type Response = Vec<InlayHint>;
2196 type LspRequest = lsp2::InlayHintRequest;
2197 type ProtoRequest = proto::InlayHints;
2198
2199 fn check_capabilities(&self, server_capabilities: &lsp2::ServerCapabilities) -> bool {
2200 let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else {
2201 return false;
2202 };
2203 match inlay_hint_provider {
2204 lsp2::OneOf::Left(enabled) => *enabled,
2205 lsp2::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2206 lsp2::InlayHintServerCapabilities::Options(_) => true,
2207 lsp2::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2208 },
2209 }
2210 }
2211
2212 fn to_lsp(
2213 &self,
2214 path: &Path,
2215 buffer: &Buffer,
2216 _: &Arc<LanguageServer>,
2217 _: &AppContext,
2218 ) -> lsp2::InlayHintParams {
2219 lsp2::InlayHintParams {
2220 text_document: lsp2::TextDocumentIdentifier {
2221 uri: lsp2::Url::from_file_path(path).unwrap(),
2222 },
2223 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2224 work_done_progress_params: Default::default(),
2225 }
2226 }
2227
2228 async fn response_from_lsp(
2229 self,
2230 message: Option<Vec<lsp2::InlayHint>>,
2231 project: Handle<Project>,
2232 buffer: Handle<Buffer>,
2233 server_id: LanguageServerId,
2234 mut cx: AsyncAppContext,
2235 ) -> anyhow::Result<Vec<InlayHint>> {
2236 let (lsp_adapter, lsp_server) =
2237 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2238 // `typescript-language-server` adds padding to the left for type hints, turning
2239 // `const foo: boolean` into `const foo : boolean` which looks odd.
2240 // `rust-analyzer` does not have the padding for this case, and we have to accomodate both.
2241 //
2242 // We could trim the whole string, but being pessimistic on par with the situation above,
2243 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2244 // Hence let's use a heuristic first to handle the most awkward case and look for more.
2245 let force_no_type_left_padding =
2246 lsp_adapter.name.0.as_ref() == "typescript-language-server";
2247
2248 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2249 let resolve_state = if InlayHints::can_resolve_inlays(lsp_server.capabilities()) {
2250 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2251 } else {
2252 ResolveState::Resolved
2253 };
2254
2255 let buffer = buffer.clone();
2256 cx.spawn(|mut cx| async move {
2257 InlayHints::lsp_to_project_hint(
2258 lsp_hint,
2259 &buffer,
2260 server_id,
2261 resolve_state,
2262 force_no_type_left_padding,
2263 &mut cx,
2264 )
2265 .await
2266 })
2267 });
2268 future::join_all(hints)
2269 .await
2270 .into_iter()
2271 .collect::<anyhow::Result<_>>()
2272 .context("lsp to project inlay hints conversion")
2273 }
2274
2275 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2276 proto::InlayHints {
2277 project_id,
2278 buffer_id: buffer.remote_id(),
2279 start: Some(language2::proto::serialize_anchor(&self.range.start)),
2280 end: Some(language2::proto::serialize_anchor(&self.range.end)),
2281 version: serialize_version(&buffer.version()),
2282 }
2283 }
2284
2285 async fn from_proto(
2286 message: proto::InlayHints,
2287 _: Handle<Project>,
2288 buffer: Handle<Buffer>,
2289 mut cx: AsyncAppContext,
2290 ) -> Result<Self> {
2291 let start = message
2292 .start
2293 .and_then(language2::proto::deserialize_anchor)
2294 .context("invalid start")?;
2295 let end = message
2296 .end
2297 .and_then(language2::proto::deserialize_anchor)
2298 .context("invalid end")?;
2299 buffer
2300 .update(&mut cx, |buffer, _| {
2301 buffer.wait_for_version(deserialize_version(&message.version))
2302 })?
2303 .await?;
2304
2305 Ok(Self { range: start..end })
2306 }
2307
2308 fn response_to_proto(
2309 response: Vec<InlayHint>,
2310 _: &mut Project,
2311 _: PeerId,
2312 buffer_version: &clock::Global,
2313 _: &mut AppContext,
2314 ) -> proto::InlayHintsResponse {
2315 proto::InlayHintsResponse {
2316 hints: response
2317 .into_iter()
2318 .map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
2319 .collect(),
2320 version: serialize_version(buffer_version),
2321 }
2322 }
2323
2324 async fn response_from_proto(
2325 self,
2326 message: proto::InlayHintsResponse,
2327 _: Handle<Project>,
2328 buffer: Handle<Buffer>,
2329 mut cx: AsyncAppContext,
2330 ) -> anyhow::Result<Vec<InlayHint>> {
2331 buffer
2332 .update(&mut cx, |buffer, _| {
2333 buffer.wait_for_version(deserialize_version(&message.version))
2334 })?
2335 .await?;
2336
2337 let mut hints = Vec::new();
2338 for message_hint in message.hints {
2339 hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2340 }
2341
2342 Ok(hints)
2343 }
2344
2345 fn buffer_id_from_proto(message: &proto::InlayHints) -> u64 {
2346 message.buffer_id
2347 }
2348}