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]
36pub(crate) trait LspCommand: 'static + Sized + Send {
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]
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 mut 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]
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]
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]
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]
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
840 .clone()
841 .update(&mut cx, |target_buffer, _| {
842 let target_start = target_buffer
843 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
844 let target_end = target_buffer
845 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
846 references.push(Location {
847 buffer: target_buffer_handle,
848 range: target_buffer.anchor_after(target_start)
849 ..target_buffer.anchor_before(target_end),
850 });
851 })?;
852 }
853 }
854
855 Ok(references)
856 }
857
858 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
859 proto::GetReferences {
860 project_id,
861 buffer_id: buffer.remote_id(),
862 position: Some(language2::proto::serialize_anchor(
863 &buffer.anchor_before(self.position),
864 )),
865 version: serialize_version(&buffer.version()),
866 }
867 }
868
869 async fn from_proto(
870 message: proto::GetReferences,
871 _: Handle<Project>,
872 buffer: Handle<Buffer>,
873 mut cx: AsyncAppContext,
874 ) -> Result<Self> {
875 let position = message
876 .position
877 .and_then(deserialize_anchor)
878 .ok_or_else(|| anyhow!("invalid position"))?;
879 buffer
880 .update(&mut cx, |buffer, _| {
881 buffer.wait_for_version(deserialize_version(&message.version))
882 })?
883 .await?;
884 Ok(Self {
885 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
886 })
887 }
888
889 fn response_to_proto(
890 response: Vec<Location>,
891 project: &mut Project,
892 peer_id: PeerId,
893 _: &clock::Global,
894 cx: &mut AppContext,
895 ) -> proto::GetReferencesResponse {
896 let locations = response
897 .into_iter()
898 .map(|definition| {
899 let buffer_id = project.create_buffer_for_peer(&definition.buffer, peer_id, cx);
900 proto::Location {
901 start: Some(serialize_anchor(&definition.range.start)),
902 end: Some(serialize_anchor(&definition.range.end)),
903 buffer_id,
904 }
905 })
906 .collect();
907 proto::GetReferencesResponse { locations }
908 }
909
910 async fn response_from_proto(
911 self,
912 message: proto::GetReferencesResponse,
913 project: Handle<Project>,
914 _: Handle<Buffer>,
915 mut cx: AsyncAppContext,
916 ) -> Result<Vec<Location>> {
917 let mut locations = Vec::new();
918 for location in message.locations {
919 let target_buffer = project
920 .update(&mut cx, |this, cx| {
921 this.wait_for_remote_buffer(location.buffer_id, cx)
922 })?
923 .await?;
924 let start = location
925 .start
926 .and_then(deserialize_anchor)
927 .ok_or_else(|| anyhow!("missing target start"))?;
928 let end = location
929 .end
930 .and_then(deserialize_anchor)
931 .ok_or_else(|| anyhow!("missing target end"))?;
932 target_buffer
933 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
934 .await?;
935 locations.push(Location {
936 buffer: target_buffer,
937 range: start..end,
938 })
939 }
940 Ok(locations)
941 }
942
943 fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
944 message.buffer_id
945 }
946}
947
948#[async_trait]
949impl LspCommand for GetDocumentHighlights {
950 type Response = Vec<DocumentHighlight>;
951 type LspRequest = lsp2::request::DocumentHighlightRequest;
952 type ProtoRequest = proto::GetDocumentHighlights;
953
954 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
955 capabilities.document_highlight_provider.is_some()
956 }
957
958 fn to_lsp(
959 &self,
960 path: &Path,
961 _: &Buffer,
962 _: &Arc<LanguageServer>,
963 _: &AppContext,
964 ) -> lsp2::DocumentHighlightParams {
965 lsp2::DocumentHighlightParams {
966 text_document_position_params: lsp2::TextDocumentPositionParams {
967 text_document: lsp2::TextDocumentIdentifier {
968 uri: lsp2::Url::from_file_path(path).unwrap(),
969 },
970 position: point_to_lsp(self.position),
971 },
972 work_done_progress_params: Default::default(),
973 partial_result_params: Default::default(),
974 }
975 }
976
977 async fn response_from_lsp(
978 self,
979 lsp_highlights: Option<Vec<lsp2::DocumentHighlight>>,
980 _: Handle<Project>,
981 buffer: Handle<Buffer>,
982 _: LanguageServerId,
983 mut cx: AsyncAppContext,
984 ) -> Result<Vec<DocumentHighlight>> {
985 buffer.update(&mut cx, |buffer, _| {
986 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
987 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
988 lsp_highlights
989 .into_iter()
990 .map(|lsp_highlight| {
991 let start = buffer
992 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
993 let end = buffer
994 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
995 DocumentHighlight {
996 range: buffer.anchor_after(start)..buffer.anchor_before(end),
997 kind: lsp_highlight
998 .kind
999 .unwrap_or(lsp2::DocumentHighlightKind::READ),
1000 }
1001 })
1002 .collect()
1003 })
1004 }
1005
1006 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
1007 proto::GetDocumentHighlights {
1008 project_id,
1009 buffer_id: buffer.remote_id(),
1010 position: Some(language2::proto::serialize_anchor(
1011 &buffer.anchor_before(self.position),
1012 )),
1013 version: serialize_version(&buffer.version()),
1014 }
1015 }
1016
1017 async fn from_proto(
1018 message: proto::GetDocumentHighlights,
1019 _: Handle<Project>,
1020 buffer: Handle<Buffer>,
1021 mut cx: AsyncAppContext,
1022 ) -> Result<Self> {
1023 let position = message
1024 .position
1025 .and_then(deserialize_anchor)
1026 .ok_or_else(|| anyhow!("invalid position"))?;
1027 buffer
1028 .update(&mut cx, |buffer, _| {
1029 buffer.wait_for_version(deserialize_version(&message.version))
1030 })?
1031 .await?;
1032 Ok(Self {
1033 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1034 })
1035 }
1036
1037 fn response_to_proto(
1038 response: Vec<DocumentHighlight>,
1039 _: &mut Project,
1040 _: PeerId,
1041 _: &clock::Global,
1042 _: &mut AppContext,
1043 ) -> proto::GetDocumentHighlightsResponse {
1044 let highlights = response
1045 .into_iter()
1046 .map(|highlight| proto::DocumentHighlight {
1047 start: Some(serialize_anchor(&highlight.range.start)),
1048 end: Some(serialize_anchor(&highlight.range.end)),
1049 kind: match highlight.kind {
1050 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
1051 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
1052 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
1053 _ => proto::document_highlight::Kind::Text.into(),
1054 },
1055 })
1056 .collect();
1057 proto::GetDocumentHighlightsResponse { highlights }
1058 }
1059
1060 async fn response_from_proto(
1061 self,
1062 message: proto::GetDocumentHighlightsResponse,
1063 _: Handle<Project>,
1064 buffer: Handle<Buffer>,
1065 mut cx: AsyncAppContext,
1066 ) -> Result<Vec<DocumentHighlight>> {
1067 let mut highlights = Vec::new();
1068 for highlight in message.highlights {
1069 let start = highlight
1070 .start
1071 .and_then(deserialize_anchor)
1072 .ok_or_else(|| anyhow!("missing target start"))?;
1073 let end = highlight
1074 .end
1075 .and_then(deserialize_anchor)
1076 .ok_or_else(|| anyhow!("missing target end"))?;
1077 buffer
1078 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
1079 .await?;
1080 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
1081 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
1082 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
1083 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
1084 None => DocumentHighlightKind::TEXT,
1085 };
1086 highlights.push(DocumentHighlight {
1087 range: start..end,
1088 kind,
1089 });
1090 }
1091 Ok(highlights)
1092 }
1093
1094 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
1095 message.buffer_id
1096 }
1097}
1098
1099#[async_trait]
1100impl LspCommand for GetHover {
1101 type Response = Option<Hover>;
1102 type LspRequest = lsp2::request::HoverRequest;
1103 type ProtoRequest = proto::GetHover;
1104
1105 fn to_lsp(
1106 &self,
1107 path: &Path,
1108 _: &Buffer,
1109 _: &Arc<LanguageServer>,
1110 _: &AppContext,
1111 ) -> lsp2::HoverParams {
1112 lsp2::HoverParams {
1113 text_document_position_params: lsp2::TextDocumentPositionParams {
1114 text_document: lsp2::TextDocumentIdentifier {
1115 uri: lsp2::Url::from_file_path(path).unwrap(),
1116 },
1117 position: point_to_lsp(self.position),
1118 },
1119 work_done_progress_params: Default::default(),
1120 }
1121 }
1122
1123 async fn response_from_lsp(
1124 self,
1125 message: Option<lsp2::Hover>,
1126 _: Handle<Project>,
1127 buffer: Handle<Buffer>,
1128 _: LanguageServerId,
1129 mut cx: AsyncAppContext,
1130 ) -> Result<Self::Response> {
1131 let Some(hover) = message else {
1132 return Ok(None);
1133 };
1134
1135 let (language, range) = buffer.update(&mut cx, |buffer, _| {
1136 (
1137 buffer.language().cloned(),
1138 hover.range.map(|range| {
1139 let token_start =
1140 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1141 let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1142 buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1143 }),
1144 )
1145 })?;
1146
1147 fn hover_blocks_from_marked_string(
1148 marked_string: lsp2::MarkedString,
1149 ) -> Option<HoverBlock> {
1150 let block = match marked_string {
1151 lsp2::MarkedString::String(content) => HoverBlock {
1152 text: content,
1153 kind: HoverBlockKind::Markdown,
1154 },
1155 lsp2::MarkedString::LanguageString(lsp2::LanguageString { language, value }) => {
1156 HoverBlock {
1157 text: value,
1158 kind: HoverBlockKind::Code { language },
1159 }
1160 }
1161 };
1162 if block.text.is_empty() {
1163 None
1164 } else {
1165 Some(block)
1166 }
1167 }
1168
1169 let contents = match hover.contents {
1170 lsp2::HoverContents::Scalar(marked_string) => {
1171 hover_blocks_from_marked_string(marked_string)
1172 .into_iter()
1173 .collect()
1174 }
1175 lsp2::HoverContents::Array(marked_strings) => marked_strings
1176 .into_iter()
1177 .filter_map(hover_blocks_from_marked_string)
1178 .collect(),
1179 lsp2::HoverContents::Markup(markup_content) => vec![HoverBlock {
1180 text: markup_content.value,
1181 kind: if markup_content.kind == lsp2::MarkupKind::Markdown {
1182 HoverBlockKind::Markdown
1183 } else {
1184 HoverBlockKind::PlainText
1185 },
1186 }],
1187 };
1188
1189 Ok(Some(Hover {
1190 contents,
1191 range,
1192 language,
1193 }))
1194 }
1195
1196 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1197 proto::GetHover {
1198 project_id,
1199 buffer_id: buffer.remote_id(),
1200 position: Some(language2::proto::serialize_anchor(
1201 &buffer.anchor_before(self.position),
1202 )),
1203 version: serialize_version(&buffer.version),
1204 }
1205 }
1206
1207 async fn from_proto(
1208 message: Self::ProtoRequest,
1209 _: Handle<Project>,
1210 buffer: Handle<Buffer>,
1211 mut cx: AsyncAppContext,
1212 ) -> Result<Self> {
1213 let position = message
1214 .position
1215 .and_then(deserialize_anchor)
1216 .ok_or_else(|| anyhow!("invalid position"))?;
1217 buffer
1218 .update(&mut cx, |buffer, _| {
1219 buffer.wait_for_version(deserialize_version(&message.version))
1220 })?
1221 .await?;
1222 Ok(Self {
1223 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1224 })
1225 }
1226
1227 fn response_to_proto(
1228 response: Self::Response,
1229 _: &mut Project,
1230 _: PeerId,
1231 _: &clock::Global,
1232 _: &mut AppContext,
1233 ) -> proto::GetHoverResponse {
1234 if let Some(response) = response {
1235 let (start, end) = if let Some(range) = response.range {
1236 (
1237 Some(language2::proto::serialize_anchor(&range.start)),
1238 Some(language2::proto::serialize_anchor(&range.end)),
1239 )
1240 } else {
1241 (None, None)
1242 };
1243
1244 let contents = response
1245 .contents
1246 .into_iter()
1247 .map(|block| proto::HoverBlock {
1248 text: block.text,
1249 is_markdown: block.kind == HoverBlockKind::Markdown,
1250 language: if let HoverBlockKind::Code { language } = block.kind {
1251 Some(language)
1252 } else {
1253 None
1254 },
1255 })
1256 .collect();
1257
1258 proto::GetHoverResponse {
1259 start,
1260 end,
1261 contents,
1262 }
1263 } else {
1264 proto::GetHoverResponse {
1265 start: None,
1266 end: None,
1267 contents: Vec::new(),
1268 }
1269 }
1270 }
1271
1272 async fn response_from_proto(
1273 self,
1274 message: proto::GetHoverResponse,
1275 _: Handle<Project>,
1276 buffer: Handle<Buffer>,
1277 mut cx: AsyncAppContext,
1278 ) -> Result<Self::Response> {
1279 let contents: Vec<_> = message
1280 .contents
1281 .into_iter()
1282 .map(|block| HoverBlock {
1283 text: block.text,
1284 kind: if let Some(language) = block.language {
1285 HoverBlockKind::Code { language }
1286 } else if block.is_markdown {
1287 HoverBlockKind::Markdown
1288 } else {
1289 HoverBlockKind::PlainText
1290 },
1291 })
1292 .collect();
1293 if contents.is_empty() {
1294 return Ok(None);
1295 }
1296
1297 let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
1298 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1299 language2::proto::deserialize_anchor(start)
1300 .and_then(|start| language2::proto::deserialize_anchor(end).map(|end| start..end))
1301 } else {
1302 None
1303 };
1304
1305 Ok(Some(Hover {
1306 contents,
1307 range,
1308 language,
1309 }))
1310 }
1311
1312 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 {
1313 message.buffer_id
1314 }
1315}
1316
1317#[async_trait]
1318impl LspCommand for GetCompletions {
1319 type Response = Vec<Completion>;
1320 type LspRequest = lsp2::request::Completion;
1321 type ProtoRequest = proto::GetCompletions;
1322
1323 fn to_lsp(
1324 &self,
1325 path: &Path,
1326 _: &Buffer,
1327 _: &Arc<LanguageServer>,
1328 _: &AppContext,
1329 ) -> lsp2::CompletionParams {
1330 lsp2::CompletionParams {
1331 text_document_position: lsp2::TextDocumentPositionParams::new(
1332 lsp2::TextDocumentIdentifier::new(lsp2::Url::from_file_path(path).unwrap()),
1333 point_to_lsp(self.position),
1334 ),
1335 context: Default::default(),
1336 work_done_progress_params: Default::default(),
1337 partial_result_params: Default::default(),
1338 }
1339 }
1340
1341 async fn response_from_lsp(
1342 self,
1343 completions: Option<lsp2::CompletionResponse>,
1344 _: Handle<Project>,
1345 buffer: Handle<Buffer>,
1346 server_id: LanguageServerId,
1347 mut cx: AsyncAppContext,
1348 ) -> Result<Vec<Completion>> {
1349 let mut response_list = None;
1350 let completions = if let Some(completions) = completions {
1351 match completions {
1352 lsp2::CompletionResponse::Array(completions) => completions,
1353
1354 lsp2::CompletionResponse::List(mut list) => {
1355 let items = std::mem::take(&mut list.items);
1356 response_list = Some(list);
1357 items
1358 }
1359 }
1360 } else {
1361 Default::default()
1362 };
1363
1364 let completions = buffer.update(&mut cx, |buffer, _| {
1365 let language = buffer.language().cloned();
1366 let snapshot = buffer.snapshot();
1367 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1368
1369 let mut range_for_token = None;
1370 completions
1371 .into_iter()
1372 .filter_map(move |mut lsp_completion| {
1373 let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() {
1374 // If the language server provides a range to overwrite, then
1375 // check that the range is valid.
1376 Some(lsp2::CompletionTextEdit::Edit(edit)) => {
1377 let range = range_from_lsp(edit.range);
1378 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1379 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1380 if start != range.start.0 || end != range.end.0 {
1381 log::info!("completion out of expected range");
1382 return None;
1383 }
1384 (
1385 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1386 edit.new_text.clone(),
1387 )
1388 }
1389
1390 // If the language server does not provide a range, then infer
1391 // the range based on the syntax tree.
1392 None => {
1393 if self.position != clipped_position {
1394 log::info!("completion out of expected range");
1395 return None;
1396 }
1397
1398 let default_edit_range = response_list
1399 .as_ref()
1400 .and_then(|list| list.item_defaults.as_ref())
1401 .and_then(|defaults| defaults.edit_range.as_ref())
1402 .and_then(|range| match range {
1403 CompletionListItemDefaultsEditRange::Range(r) => Some(r),
1404 _ => None,
1405 });
1406
1407 let range = if let Some(range) = default_edit_range {
1408 let range = range_from_lsp(range.clone());
1409 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1410 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1411 if start != range.start.0 || end != range.end.0 {
1412 log::info!("completion out of expected range");
1413 return None;
1414 }
1415
1416 snapshot.anchor_before(start)..snapshot.anchor_after(end)
1417 } else {
1418 range_for_token
1419 .get_or_insert_with(|| {
1420 let offset = self.position.to_offset(&snapshot);
1421 let (range, kind) = snapshot.surrounding_word(offset);
1422 let range = if kind == Some(CharKind::Word) {
1423 range
1424 } else {
1425 offset..offset
1426 };
1427
1428 snapshot.anchor_before(range.start)
1429 ..snapshot.anchor_after(range.end)
1430 })
1431 .clone()
1432 };
1433
1434 let text = lsp_completion
1435 .insert_text
1436 .as_ref()
1437 .unwrap_or(&lsp_completion.label)
1438 .clone();
1439 (range, text)
1440 }
1441
1442 Some(lsp2::CompletionTextEdit::InsertAndReplace(_)) => {
1443 log::info!("unsupported insert/replace completion");
1444 return None;
1445 }
1446 };
1447
1448 let language = language.clone();
1449 LineEnding::normalize(&mut new_text);
1450 Some(async move {
1451 let mut label = None;
1452 if let Some(language) = language {
1453 language.process_completion(&mut lsp_completion).await;
1454 label = language.label_for_completion(&lsp_completion).await;
1455 }
1456 Completion {
1457 old_range,
1458 new_text,
1459 label: label.unwrap_or_else(|| {
1460 language2::CodeLabel::plain(
1461 lsp_completion.label.clone(),
1462 lsp_completion.filter_text.as_deref(),
1463 )
1464 }),
1465 server_id,
1466 lsp_completion,
1467 }
1468 })
1469 })
1470 })?;
1471
1472 Ok(future::join_all(completions).await)
1473 }
1474
1475 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1476 let anchor = buffer.anchor_after(self.position);
1477 proto::GetCompletions {
1478 project_id,
1479 buffer_id: buffer.remote_id(),
1480 position: Some(language2::proto::serialize_anchor(&anchor)),
1481 version: serialize_version(&buffer.version()),
1482 }
1483 }
1484
1485 async fn from_proto(
1486 message: proto::GetCompletions,
1487 _: Handle<Project>,
1488 buffer: Handle<Buffer>,
1489 mut cx: AsyncAppContext,
1490 ) -> Result<Self> {
1491 let version = deserialize_version(&message.version);
1492 buffer
1493 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))?
1494 .await?;
1495 let position = message
1496 .position
1497 .and_then(language2::proto::deserialize_anchor)
1498 .map(|p| {
1499 buffer.update(&mut cx, |buffer, _| {
1500 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1501 })
1502 })
1503 .ok_or_else(|| anyhow!("invalid position"))??;
1504 Ok(Self { position })
1505 }
1506
1507 fn response_to_proto(
1508 completions: Vec<Completion>,
1509 _: &mut Project,
1510 _: PeerId,
1511 buffer_version: &clock::Global,
1512 _: &mut AppContext,
1513 ) -> proto::GetCompletionsResponse {
1514 proto::GetCompletionsResponse {
1515 completions: completions
1516 .iter()
1517 .map(language2::proto::serialize_completion)
1518 .collect(),
1519 version: serialize_version(&buffer_version),
1520 }
1521 }
1522
1523 async fn response_from_proto(
1524 self,
1525 message: proto::GetCompletionsResponse,
1526 _: Handle<Project>,
1527 buffer: Handle<Buffer>,
1528 mut cx: AsyncAppContext,
1529 ) -> Result<Vec<Completion>> {
1530 buffer
1531 .update(&mut cx, |buffer, _| {
1532 buffer.wait_for_version(deserialize_version(&message.version))
1533 })?
1534 .await?;
1535
1536 let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
1537 let completions = message.completions.into_iter().map(|completion| {
1538 language2::proto::deserialize_completion(completion, language.clone())
1539 });
1540 future::try_join_all(completions).await
1541 }
1542
1543 fn buffer_id_from_proto(message: &proto::GetCompletions) -> u64 {
1544 message.buffer_id
1545 }
1546}
1547
1548#[async_trait]
1549impl LspCommand for GetCodeActions {
1550 type Response = Vec<CodeAction>;
1551 type LspRequest = lsp2::request::CodeActionRequest;
1552 type ProtoRequest = proto::GetCodeActions;
1553
1554 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1555 match &capabilities.code_action_provider {
1556 None => false,
1557 Some(lsp2::CodeActionProviderCapability::Simple(false)) => false,
1558 _ => true,
1559 }
1560 }
1561
1562 fn to_lsp(
1563 &self,
1564 path: &Path,
1565 buffer: &Buffer,
1566 language_server: &Arc<LanguageServer>,
1567 _: &AppContext,
1568 ) -> lsp2::CodeActionParams {
1569 let relevant_diagnostics = buffer
1570 .snapshot()
1571 .diagnostics_in_range::<_, usize>(self.range.clone(), false)
1572 .map(|entry| entry.to_lsp_diagnostic_stub())
1573 .collect();
1574 lsp2::CodeActionParams {
1575 text_document: lsp2::TextDocumentIdentifier::new(
1576 lsp2::Url::from_file_path(path).unwrap(),
1577 ),
1578 range: range_to_lsp(self.range.to_point_utf16(buffer)),
1579 work_done_progress_params: Default::default(),
1580 partial_result_params: Default::default(),
1581 context: lsp2::CodeActionContext {
1582 diagnostics: relevant_diagnostics,
1583 only: language_server.code_action_kinds(),
1584 ..lsp2::CodeActionContext::default()
1585 },
1586 }
1587 }
1588
1589 async fn response_from_lsp(
1590 self,
1591 actions: Option<lsp2::CodeActionResponse>,
1592 _: Handle<Project>,
1593 _: Handle<Buffer>,
1594 server_id: LanguageServerId,
1595 _: AsyncAppContext,
1596 ) -> Result<Vec<CodeAction>> {
1597 Ok(actions
1598 .unwrap_or_default()
1599 .into_iter()
1600 .filter_map(|entry| {
1601 if let lsp2::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1602 Some(CodeAction {
1603 server_id,
1604 range: self.range.clone(),
1605 lsp_action,
1606 })
1607 } else {
1608 None
1609 }
1610 })
1611 .collect())
1612 }
1613
1614 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
1615 proto::GetCodeActions {
1616 project_id,
1617 buffer_id: buffer.remote_id(),
1618 start: Some(language2::proto::serialize_anchor(&self.range.start)),
1619 end: Some(language2::proto::serialize_anchor(&self.range.end)),
1620 version: serialize_version(&buffer.version()),
1621 }
1622 }
1623
1624 async fn from_proto(
1625 message: proto::GetCodeActions,
1626 _: Handle<Project>,
1627 buffer: Handle<Buffer>,
1628 mut cx: AsyncAppContext,
1629 ) -> Result<Self> {
1630 let start = message
1631 .start
1632 .and_then(language2::proto::deserialize_anchor)
1633 .ok_or_else(|| anyhow!("invalid start"))?;
1634 let end = message
1635 .end
1636 .and_then(language2::proto::deserialize_anchor)
1637 .ok_or_else(|| anyhow!("invalid end"))?;
1638 buffer
1639 .update(&mut cx, |buffer, _| {
1640 buffer.wait_for_version(deserialize_version(&message.version))
1641 })?
1642 .await?;
1643
1644 Ok(Self { range: start..end })
1645 }
1646
1647 fn response_to_proto(
1648 code_actions: Vec<CodeAction>,
1649 _: &mut Project,
1650 _: PeerId,
1651 buffer_version: &clock::Global,
1652 _: &mut AppContext,
1653 ) -> proto::GetCodeActionsResponse {
1654 proto::GetCodeActionsResponse {
1655 actions: code_actions
1656 .iter()
1657 .map(language2::proto::serialize_code_action)
1658 .collect(),
1659 version: serialize_version(&buffer_version),
1660 }
1661 }
1662
1663 async fn response_from_proto(
1664 self,
1665 message: proto::GetCodeActionsResponse,
1666 _: Handle<Project>,
1667 buffer: Handle<Buffer>,
1668 mut cx: AsyncAppContext,
1669 ) -> Result<Vec<CodeAction>> {
1670 buffer
1671 .update(&mut cx, |buffer, _| {
1672 buffer.wait_for_version(deserialize_version(&message.version))
1673 })?
1674 .await?;
1675 message
1676 .actions
1677 .into_iter()
1678 .map(language2::proto::deserialize_code_action)
1679 .collect()
1680 }
1681
1682 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> u64 {
1683 message.buffer_id
1684 }
1685}
1686
1687#[async_trait]
1688impl LspCommand for OnTypeFormatting {
1689 type Response = Option<Transaction>;
1690 type LspRequest = lsp2::request::OnTypeFormatting;
1691 type ProtoRequest = proto::OnTypeFormatting;
1692
1693 fn check_capabilities(&self, server_capabilities: &lsp2::ServerCapabilities) -> bool {
1694 let Some(on_type_formatting_options) =
1695 &server_capabilities.document_on_type_formatting_provider
1696 else {
1697 return false;
1698 };
1699 on_type_formatting_options
1700 .first_trigger_character
1701 .contains(&self.trigger)
1702 || on_type_formatting_options
1703 .more_trigger_character
1704 .iter()
1705 .flatten()
1706 .any(|chars| chars.contains(&self.trigger))
1707 }
1708
1709 fn to_lsp(
1710 &self,
1711 path: &Path,
1712 _: &Buffer,
1713 _: &Arc<LanguageServer>,
1714 _: &AppContext,
1715 ) -> lsp2::DocumentOnTypeFormattingParams {
1716 lsp2::DocumentOnTypeFormattingParams {
1717 text_document_position: lsp2::TextDocumentPositionParams::new(
1718 lsp2::TextDocumentIdentifier::new(lsp2::Url::from_file_path(path).unwrap()),
1719 point_to_lsp(self.position),
1720 ),
1721 ch: self.trigger.clone(),
1722 options: lsp_formatting_options(self.options.tab_size),
1723 }
1724 }
1725
1726 async fn response_from_lsp(
1727 self,
1728 message: Option<Vec<lsp2::TextEdit>>,
1729 project: Handle<Project>,
1730 buffer: Handle<Buffer>,
1731 server_id: LanguageServerId,
1732 mut cx: AsyncAppContext,
1733 ) -> Result<Option<Transaction>> {
1734 if let Some(edits) = message {
1735 let (lsp_adapter, lsp_server) =
1736 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1737 Project::deserialize_edits(
1738 project,
1739 buffer,
1740 edits,
1741 self.push_to_history,
1742 lsp_adapter,
1743 lsp_server,
1744 &mut cx,
1745 )
1746 .await
1747 } else {
1748 Ok(None)
1749 }
1750 }
1751
1752 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1753 proto::OnTypeFormatting {
1754 project_id,
1755 buffer_id: buffer.remote_id(),
1756 position: Some(language2::proto::serialize_anchor(
1757 &buffer.anchor_before(self.position),
1758 )),
1759 trigger: self.trigger.clone(),
1760 version: serialize_version(&buffer.version()),
1761 }
1762 }
1763
1764 async fn from_proto(
1765 message: proto::OnTypeFormatting,
1766 _: Handle<Project>,
1767 buffer: Handle<Buffer>,
1768 mut cx: AsyncAppContext,
1769 ) -> Result<Self> {
1770 let position = message
1771 .position
1772 .and_then(deserialize_anchor)
1773 .ok_or_else(|| anyhow!("invalid position"))?;
1774 buffer
1775 .update(&mut cx, |buffer, _| {
1776 buffer.wait_for_version(deserialize_version(&message.version))
1777 })?
1778 .await?;
1779
1780 let tab_size = buffer.update(&mut cx, |buffer, cx| {
1781 language_settings(buffer.language(), buffer.file(), cx).tab_size
1782 })?;
1783
1784 Ok(Self {
1785 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1786 trigger: message.trigger.clone(),
1787 options: lsp_formatting_options(tab_size.get()).into(),
1788 push_to_history: false,
1789 })
1790 }
1791
1792 fn response_to_proto(
1793 response: Option<Transaction>,
1794 _: &mut Project,
1795 _: PeerId,
1796 _: &clock::Global,
1797 _: &mut AppContext,
1798 ) -> proto::OnTypeFormattingResponse {
1799 proto::OnTypeFormattingResponse {
1800 transaction: response
1801 .map(|transaction| language2::proto::serialize_transaction(&transaction)),
1802 }
1803 }
1804
1805 async fn response_from_proto(
1806 self,
1807 message: proto::OnTypeFormattingResponse,
1808 _: Handle<Project>,
1809 _: Handle<Buffer>,
1810 _: AsyncAppContext,
1811 ) -> Result<Option<Transaction>> {
1812 let Some(transaction) = message.transaction else {
1813 return Ok(None);
1814 };
1815 Ok(Some(language2::proto::deserialize_transaction(
1816 transaction,
1817 )?))
1818 }
1819
1820 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 {
1821 message.buffer_id
1822 }
1823}
1824
1825impl InlayHints {
1826 pub async fn lsp_to_project_hint(
1827 lsp_hint: lsp2::InlayHint,
1828 buffer_handle: &Handle<Buffer>,
1829 server_id: LanguageServerId,
1830 resolve_state: ResolveState,
1831 force_no_type_left_padding: bool,
1832 cx: &mut AsyncAppContext,
1833 ) -> anyhow::Result<InlayHint> {
1834 let kind = lsp_hint.kind.and_then(|kind| match kind {
1835 lsp2::InlayHintKind::TYPE => Some(InlayHintKind::Type),
1836 lsp2::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
1837 _ => None,
1838 });
1839
1840 let position = buffer_handle.update(cx, |buffer, _| {
1841 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
1842 if kind == Some(InlayHintKind::Parameter) {
1843 buffer.anchor_before(position)
1844 } else {
1845 buffer.anchor_after(position)
1846 }
1847 })?;
1848 let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
1849 .await
1850 .context("lsp to project inlay hint conversion")?;
1851 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
1852 false
1853 } else {
1854 lsp_hint.padding_left.unwrap_or(false)
1855 };
1856
1857 Ok(InlayHint {
1858 position,
1859 padding_left,
1860 padding_right: lsp_hint.padding_right.unwrap_or(false),
1861 label,
1862 kind,
1863 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
1864 lsp2::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
1865 lsp2::InlayHintTooltip::MarkupContent(markup_content) => {
1866 InlayHintTooltip::MarkupContent(MarkupContent {
1867 kind: match markup_content.kind {
1868 lsp2::MarkupKind::PlainText => HoverBlockKind::PlainText,
1869 lsp2::MarkupKind::Markdown => HoverBlockKind::Markdown,
1870 },
1871 value: markup_content.value,
1872 })
1873 }
1874 }),
1875 resolve_state,
1876 })
1877 }
1878
1879 async fn lsp_inlay_label_to_project(
1880 lsp_label: lsp2::InlayHintLabel,
1881 server_id: LanguageServerId,
1882 ) -> anyhow::Result<InlayHintLabel> {
1883 let label = match lsp_label {
1884 lsp2::InlayHintLabel::String(s) => InlayHintLabel::String(s),
1885 lsp2::InlayHintLabel::LabelParts(lsp_parts) => {
1886 let mut parts = Vec::with_capacity(lsp_parts.len());
1887 for lsp_part in lsp_parts {
1888 parts.push(InlayHintLabelPart {
1889 value: lsp_part.value,
1890 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
1891 lsp2::InlayHintLabelPartTooltip::String(s) => {
1892 InlayHintLabelPartTooltip::String(s)
1893 }
1894 lsp2::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
1895 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
1896 kind: match markup_content.kind {
1897 lsp2::MarkupKind::PlainText => HoverBlockKind::PlainText,
1898 lsp2::MarkupKind::Markdown => HoverBlockKind::Markdown,
1899 },
1900 value: markup_content.value,
1901 })
1902 }
1903 }),
1904 location: Some(server_id).zip(lsp_part.location),
1905 });
1906 }
1907 InlayHintLabel::LabelParts(parts)
1908 }
1909 };
1910
1911 Ok(label)
1912 }
1913
1914 pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
1915 let (state, lsp_resolve_state) = match response_hint.resolve_state {
1916 ResolveState::Resolved => (0, None),
1917 ResolveState::CanResolve(server_id, resolve_data) => (
1918 1,
1919 resolve_data
1920 .map(|json_data| {
1921 serde_json::to_string(&json_data)
1922 .expect("failed to serialize resolve json data")
1923 })
1924 .map(|value| proto::resolve_state::LspResolveState {
1925 server_id: server_id.0 as u64,
1926 value,
1927 }),
1928 ),
1929 ResolveState::Resolving => (2, None),
1930 };
1931 let resolve_state = Some(proto::ResolveState {
1932 state,
1933 lsp_resolve_state,
1934 });
1935 proto::InlayHint {
1936 position: Some(language2::proto::serialize_anchor(&response_hint.position)),
1937 padding_left: response_hint.padding_left,
1938 padding_right: response_hint.padding_right,
1939 label: Some(proto::InlayHintLabel {
1940 label: Some(match response_hint.label {
1941 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
1942 InlayHintLabel::LabelParts(label_parts) => {
1943 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
1944 parts: label_parts.into_iter().map(|label_part| {
1945 let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
1946 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 });
1947 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 });
1948 proto::InlayHintLabelPart {
1949 value: label_part.value,
1950 tooltip: label_part.tooltip.map(|tooltip| {
1951 let proto_tooltip = match tooltip {
1952 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
1953 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
1954 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1955 value: markup_content.value,
1956 }),
1957 };
1958 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
1959 }),
1960 location_url,
1961 location_range_start,
1962 location_range_end,
1963 language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
1964 }}).collect()
1965 })
1966 }
1967 }),
1968 }),
1969 kind: response_hint.kind.map(|kind| kind.name().to_string()),
1970 tooltip: response_hint.tooltip.map(|response_tooltip| {
1971 let proto_tooltip = match response_tooltip {
1972 InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
1973 InlayHintTooltip::MarkupContent(markup_content) => {
1974 proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
1975 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1976 value: markup_content.value,
1977 })
1978 }
1979 };
1980 proto::InlayHintTooltip {
1981 content: Some(proto_tooltip),
1982 }
1983 }),
1984 resolve_state,
1985 }
1986 }
1987
1988 pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
1989 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
1990 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
1991 });
1992 let resolve_state_data = resolve_state
1993 .lsp_resolve_state.as_ref()
1994 .map(|lsp_resolve_state| {
1995 serde_json::from_str::<Option<lsp2::LSPAny>>(&lsp_resolve_state.value)
1996 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
1997 .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
1998 })
1999 .transpose()?;
2000 let resolve_state = match resolve_state.state {
2001 0 => ResolveState::Resolved,
2002 1 => {
2003 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2004 format!(
2005 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2006 )
2007 })?;
2008 ResolveState::CanResolve(server_id, lsp_resolve_state)
2009 }
2010 2 => ResolveState::Resolving,
2011 invalid => {
2012 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2013 }
2014 };
2015 Ok(InlayHint {
2016 position: message_hint
2017 .position
2018 .and_then(language2::proto::deserialize_anchor)
2019 .context("invalid position")?,
2020 label: match message_hint
2021 .label
2022 .and_then(|label| label.label)
2023 .context("missing label")?
2024 {
2025 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2026 proto::inlay_hint_label::Label::LabelParts(parts) => {
2027 let mut label_parts = Vec::new();
2028 for part in parts.parts {
2029 label_parts.push(InlayHintLabelPart {
2030 value: part.value,
2031 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2032 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2033 InlayHintLabelPartTooltip::String(s)
2034 }
2035 Some(
2036 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2037 markup_content,
2038 ),
2039 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2040 kind: if markup_content.is_markdown {
2041 HoverBlockKind::Markdown
2042 } else {
2043 HoverBlockKind::PlainText
2044 },
2045 value: markup_content.value,
2046 }),
2047 None => InlayHintLabelPartTooltip::String(String::new()),
2048 }),
2049 location: {
2050 match part
2051 .location_url
2052 .zip(
2053 part.location_range_start.and_then(|start| {
2054 Some(start..part.location_range_end?)
2055 }),
2056 )
2057 .zip(part.language_server_id)
2058 {
2059 Some(((uri, range), server_id)) => Some((
2060 LanguageServerId(server_id as usize),
2061 lsp2::Location {
2062 uri: lsp2::Url::parse(&uri)
2063 .context("invalid uri in hint part {part:?}")?,
2064 range: lsp2::Range::new(
2065 point_to_lsp(PointUtf16::new(
2066 range.start.row,
2067 range.start.column,
2068 )),
2069 point_to_lsp(PointUtf16::new(
2070 range.end.row,
2071 range.end.column,
2072 )),
2073 ),
2074 },
2075 )),
2076 None => None,
2077 }
2078 },
2079 });
2080 }
2081
2082 InlayHintLabel::LabelParts(label_parts)
2083 }
2084 },
2085 padding_left: message_hint.padding_left,
2086 padding_right: message_hint.padding_right,
2087 kind: message_hint
2088 .kind
2089 .as_deref()
2090 .and_then(InlayHintKind::from_name),
2091 tooltip: message_hint.tooltip.and_then(|tooltip| {
2092 Some(match tooltip.content? {
2093 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2094 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2095 InlayHintTooltip::MarkupContent(MarkupContent {
2096 kind: if markup_content.is_markdown {
2097 HoverBlockKind::Markdown
2098 } else {
2099 HoverBlockKind::PlainText
2100 },
2101 value: markup_content.value,
2102 })
2103 }
2104 })
2105 }),
2106 resolve_state,
2107 })
2108 }
2109
2110 pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp2::InlayHint {
2111 lsp2::InlayHint {
2112 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2113 kind: hint.kind.map(|kind| match kind {
2114 InlayHintKind::Type => lsp2::InlayHintKind::TYPE,
2115 InlayHintKind::Parameter => lsp2::InlayHintKind::PARAMETER,
2116 }),
2117 text_edits: None,
2118 tooltip: hint.tooltip.and_then(|tooltip| {
2119 Some(match tooltip {
2120 InlayHintTooltip::String(s) => lsp2::InlayHintTooltip::String(s),
2121 InlayHintTooltip::MarkupContent(markup_content) => {
2122 lsp2::InlayHintTooltip::MarkupContent(lsp2::MarkupContent {
2123 kind: match markup_content.kind {
2124 HoverBlockKind::PlainText => lsp2::MarkupKind::PlainText,
2125 HoverBlockKind::Markdown => lsp2::MarkupKind::Markdown,
2126 HoverBlockKind::Code { .. } => return None,
2127 },
2128 value: markup_content.value,
2129 })
2130 }
2131 })
2132 }),
2133 label: match hint.label {
2134 InlayHintLabel::String(s) => lsp2::InlayHintLabel::String(s),
2135 InlayHintLabel::LabelParts(label_parts) => lsp2::InlayHintLabel::LabelParts(
2136 label_parts
2137 .into_iter()
2138 .map(|part| lsp2::InlayHintLabelPart {
2139 value: part.value,
2140 tooltip: part.tooltip.and_then(|tooltip| {
2141 Some(match tooltip {
2142 InlayHintLabelPartTooltip::String(s) => {
2143 lsp2::InlayHintLabelPartTooltip::String(s)
2144 }
2145 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2146 lsp2::InlayHintLabelPartTooltip::MarkupContent(
2147 lsp2::MarkupContent {
2148 kind: match markup_content.kind {
2149 HoverBlockKind::PlainText => {
2150 lsp2::MarkupKind::PlainText
2151 }
2152 HoverBlockKind::Markdown => {
2153 lsp2::MarkupKind::Markdown
2154 }
2155 HoverBlockKind::Code { .. } => return None,
2156 },
2157 value: markup_content.value,
2158 },
2159 )
2160 }
2161 })
2162 }),
2163 location: part.location.map(|(_, location)| location),
2164 command: None,
2165 })
2166 .collect(),
2167 ),
2168 },
2169 padding_left: Some(hint.padding_left),
2170 padding_right: Some(hint.padding_right),
2171 data: match hint.resolve_state {
2172 ResolveState::CanResolve(_, data) => data,
2173 ResolveState::Resolving | ResolveState::Resolved => None,
2174 },
2175 }
2176 }
2177
2178 pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2179 capabilities
2180 .inlay_hint_provider
2181 .as_ref()
2182 .and_then(|options| match options {
2183 OneOf::Left(_is_supported) => None,
2184 OneOf::Right(capabilities) => match capabilities {
2185 lsp2::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2186 lsp2::InlayHintServerCapabilities::RegistrationOptions(o) => {
2187 o.inlay_hint_options.resolve_provider
2188 }
2189 },
2190 })
2191 .unwrap_or(false)
2192 }
2193}
2194
2195#[async_trait]
2196impl LspCommand for InlayHints {
2197 type Response = Vec<InlayHint>;
2198 type LspRequest = lsp2::InlayHintRequest;
2199 type ProtoRequest = proto::InlayHints;
2200
2201 fn check_capabilities(&self, server_capabilities: &lsp2::ServerCapabilities) -> bool {
2202 let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else {
2203 return false;
2204 };
2205 match inlay_hint_provider {
2206 lsp2::OneOf::Left(enabled) => *enabled,
2207 lsp2::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2208 lsp2::InlayHintServerCapabilities::Options(_) => true,
2209 lsp2::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2210 },
2211 }
2212 }
2213
2214 fn to_lsp(
2215 &self,
2216 path: &Path,
2217 buffer: &Buffer,
2218 _: &Arc<LanguageServer>,
2219 _: &AppContext,
2220 ) -> lsp2::InlayHintParams {
2221 lsp2::InlayHintParams {
2222 text_document: lsp2::TextDocumentIdentifier {
2223 uri: lsp2::Url::from_file_path(path).unwrap(),
2224 },
2225 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2226 work_done_progress_params: Default::default(),
2227 }
2228 }
2229
2230 async fn response_from_lsp(
2231 self,
2232 message: Option<Vec<lsp2::InlayHint>>,
2233 project: Handle<Project>,
2234 buffer: Handle<Buffer>,
2235 server_id: LanguageServerId,
2236 mut cx: AsyncAppContext,
2237 ) -> anyhow::Result<Vec<InlayHint>> {
2238 let (lsp_adapter, lsp_server) =
2239 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2240 // `typescript-language-server` adds padding to the left for type hints, turning
2241 // `const foo: boolean` into `const foo : boolean` which looks odd.
2242 // `rust-analyzer` does not have the padding for this case, and we have to accomodate both.
2243 //
2244 // We could trim the whole string, but being pessimistic on par with the situation above,
2245 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2246 // Hence let's use a heuristic first to handle the most awkward case and look for more.
2247 let force_no_type_left_padding =
2248 lsp_adapter.name.0.as_ref() == "typescript-language-server";
2249
2250 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2251 let resolve_state = if InlayHints::can_resolve_inlays(lsp_server.capabilities()) {
2252 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2253 } else {
2254 ResolveState::Resolved
2255 };
2256
2257 let buffer = buffer.clone();
2258 cx.spawn(move |mut cx| async move {
2259 InlayHints::lsp_to_project_hint(
2260 lsp_hint,
2261 &buffer,
2262 server_id,
2263 resolve_state,
2264 force_no_type_left_padding,
2265 &mut cx,
2266 )
2267 .await
2268 })
2269 });
2270 future::join_all(hints)
2271 .await
2272 .into_iter()
2273 .collect::<anyhow::Result<_>>()
2274 .context("lsp to project inlay hints conversion")
2275 }
2276
2277 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2278 proto::InlayHints {
2279 project_id,
2280 buffer_id: buffer.remote_id(),
2281 start: Some(language2::proto::serialize_anchor(&self.range.start)),
2282 end: Some(language2::proto::serialize_anchor(&self.range.end)),
2283 version: serialize_version(&buffer.version()),
2284 }
2285 }
2286
2287 async fn from_proto(
2288 message: proto::InlayHints,
2289 _: Handle<Project>,
2290 buffer: Handle<Buffer>,
2291 mut cx: AsyncAppContext,
2292 ) -> Result<Self> {
2293 let start = message
2294 .start
2295 .and_then(language2::proto::deserialize_anchor)
2296 .context("invalid start")?;
2297 let end = message
2298 .end
2299 .and_then(language2::proto::deserialize_anchor)
2300 .context("invalid end")?;
2301 buffer
2302 .update(&mut cx, |buffer, _| {
2303 buffer.wait_for_version(deserialize_version(&message.version))
2304 })?
2305 .await?;
2306
2307 Ok(Self { range: start..end })
2308 }
2309
2310 fn response_to_proto(
2311 response: Vec<InlayHint>,
2312 _: &mut Project,
2313 _: PeerId,
2314 buffer_version: &clock::Global,
2315 _: &mut AppContext,
2316 ) -> proto::InlayHintsResponse {
2317 proto::InlayHintsResponse {
2318 hints: response
2319 .into_iter()
2320 .map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
2321 .collect(),
2322 version: serialize_version(buffer_version),
2323 }
2324 }
2325
2326 async fn response_from_proto(
2327 self,
2328 message: proto::InlayHintsResponse,
2329 _: Handle<Project>,
2330 buffer: Handle<Buffer>,
2331 mut cx: AsyncAppContext,
2332 ) -> anyhow::Result<Vec<InlayHint>> {
2333 buffer
2334 .update(&mut cx, |buffer, _| {
2335 buffer.wait_for_version(deserialize_version(&message.version))
2336 })?
2337 .await?;
2338
2339 let mut hints = Vec::new();
2340 for message_hint in message.hints {
2341 hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2342 }
2343
2344 Ok(hints)
2345 }
2346
2347 fn buffer_id_from_proto(message: &proto::InlayHints) -> u64 {
2348 message.buffer_id
2349 }
2350}