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.read_with(&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.read_with(&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.read_with(&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.read_with(&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.read_with(&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 .read_with(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 cx.read(|cx| {
721 let origin_location = origin_range.map(|origin_range| {
722 let origin_buffer = buffer.read(cx);
723 let origin_start =
724 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
725 let origin_end =
726 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
727 Location {
728 buffer: buffer.clone(),
729 range: origin_buffer.anchor_after(origin_start)
730 ..origin_buffer.anchor_before(origin_end),
731 }
732 });
733
734 let target_buffer = target_buffer_handle.read(cx);
735 let target_start =
736 target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
737 let target_end =
738 target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
739 let target_location = Location {
740 buffer: target_buffer_handle,
741 range: target_buffer.anchor_after(target_start)
742 ..target_buffer.anchor_before(target_end),
743 };
744
745 definitions.push(LocationLink {
746 origin: origin_location,
747 target: target_location,
748 })
749 });
750 }
751 Ok(definitions)
752}
753
754fn location_links_to_proto(
755 links: Vec<LocationLink>,
756 project: &mut Project,
757 peer_id: PeerId,
758 cx: &mut AppContext,
759) -> Vec<proto::LocationLink> {
760 links
761 .into_iter()
762 .map(|definition| {
763 let origin = definition.origin.map(|origin| {
764 let buffer_id = project.create_buffer_for_peer(&origin.buffer, peer_id, cx);
765 proto::Location {
766 start: Some(serialize_anchor(&origin.range.start)),
767 end: Some(serialize_anchor(&origin.range.end)),
768 buffer_id,
769 }
770 });
771
772 let buffer_id = project.create_buffer_for_peer(&definition.target.buffer, peer_id, cx);
773 let target = proto::Location {
774 start: Some(serialize_anchor(&definition.target.range.start)),
775 end: Some(serialize_anchor(&definition.target.range.end)),
776 buffer_id,
777 };
778
779 proto::LocationLink {
780 origin,
781 target: Some(target),
782 }
783 })
784 .collect()
785}
786
787#[async_trait(?Send)]
788impl LspCommand for GetReferences {
789 type Response = Vec<Location>;
790 type LspRequest = lsp2::request::References;
791 type ProtoRequest = proto::GetReferences;
792
793 fn to_lsp(
794 &self,
795 path: &Path,
796 _: &Buffer,
797 _: &Arc<LanguageServer>,
798 _: &AppContext,
799 ) -> lsp2::ReferenceParams {
800 lsp2::ReferenceParams {
801 text_document_position: lsp2::TextDocumentPositionParams {
802 text_document: lsp2::TextDocumentIdentifier {
803 uri: lsp2::Url::from_file_path(path).unwrap(),
804 },
805 position: point_to_lsp(self.position),
806 },
807 work_done_progress_params: Default::default(),
808 partial_result_params: Default::default(),
809 context: lsp2::ReferenceContext {
810 include_declaration: true,
811 },
812 }
813 }
814
815 async fn response_from_lsp(
816 self,
817 locations: Option<Vec<lsp2::Location>>,
818 project: Handle<Project>,
819 buffer: Handle<Buffer>,
820 server_id: LanguageServerId,
821 mut cx: AsyncAppContext,
822 ) -> Result<Vec<Location>> {
823 let mut references = Vec::new();
824 let (lsp_adapter, language_server) =
825 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
826
827 if let Some(locations) = locations {
828 for lsp_location in locations {
829 let target_buffer_handle = project
830 .update(&mut cx, |this, cx| {
831 this.open_local_buffer_via_lsp(
832 lsp_location.uri,
833 language_server.server_id(),
834 lsp_adapter.name.clone(),
835 cx,
836 )
837 })
838 .await?;
839
840 cx.read(|cx| {
841 let target_buffer = target_buffer_handle.read(cx);
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.read_with(&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(?Send)]
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 cx: AsyncAppContext,
984 ) -> Result<Vec<DocumentHighlight>> {
985 buffer.read_with(&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 Ok(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.read_with(&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(?Send)]
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 cx: AsyncAppContext,
1130 ) -> Result<Self::Response> {
1131 Ok(message.and_then(|hover| {
1132 let (language, range) = cx.read(|cx| {
1133 let buffer = buffer.read(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 =
1140 buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1141 buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1142 }),
1143 )
1144 });
1145
1146 fn hover_blocks_from_marked_string(
1147 marked_string: lsp2::MarkedString,
1148 ) -> Option<HoverBlock> {
1149 let block = match marked_string {
1150 lsp2::MarkedString::String(content) => HoverBlock {
1151 text: content,
1152 kind: HoverBlockKind::Markdown,
1153 },
1154 lsp2::MarkedString::LanguageString(lsp2::LanguageString {
1155 language,
1156 value,
1157 }) => HoverBlock {
1158 text: value,
1159 kind: HoverBlockKind::Code { language },
1160 },
1161 };
1162 if block.text.is_empty() {
1163 None
1164 } else {
1165 Some(block)
1166 }
1167 }
1168
1169 let contents = cx.read(|_| 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 Some(Hover {
1190 contents,
1191 range,
1192 language,
1193 })
1194 }))
1195 }
1196
1197 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1198 proto::GetHover {
1199 project_id,
1200 buffer_id: buffer.remote_id(),
1201 position: Some(language2::proto::serialize_anchor(
1202 &buffer.anchor_before(self.position),
1203 )),
1204 version: serialize_version(&buffer.version),
1205 }
1206 }
1207
1208 async fn from_proto(
1209 message: Self::ProtoRequest,
1210 _: Handle<Project>,
1211 buffer: Handle<Buffer>,
1212 mut cx: AsyncAppContext,
1213 ) -> Result<Self> {
1214 let position = message
1215 .position
1216 .and_then(deserialize_anchor)
1217 .ok_or_else(|| anyhow!("invalid position"))?;
1218 buffer
1219 .update(&mut cx, |buffer, _| {
1220 buffer.wait_for_version(deserialize_version(&message.version))
1221 })
1222 .await?;
1223 Ok(Self {
1224 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1225 })
1226 }
1227
1228 fn response_to_proto(
1229 response: Self::Response,
1230 _: &mut Project,
1231 _: PeerId,
1232 _: &clock::Global,
1233 _: &mut AppContext,
1234 ) -> proto::GetHoverResponse {
1235 if let Some(response) = response {
1236 let (start, end) = if let Some(range) = response.range {
1237 (
1238 Some(language2::proto::serialize_anchor(&range.start)),
1239 Some(language2::proto::serialize_anchor(&range.end)),
1240 )
1241 } else {
1242 (None, None)
1243 };
1244
1245 let contents = response
1246 .contents
1247 .into_iter()
1248 .map(|block| proto::HoverBlock {
1249 text: block.text,
1250 is_markdown: block.kind == HoverBlockKind::Markdown,
1251 language: if let HoverBlockKind::Code { language } = block.kind {
1252 Some(language)
1253 } else {
1254 None
1255 },
1256 })
1257 .collect();
1258
1259 proto::GetHoverResponse {
1260 start,
1261 end,
1262 contents,
1263 }
1264 } else {
1265 proto::GetHoverResponse {
1266 start: None,
1267 end: None,
1268 contents: Vec::new(),
1269 }
1270 }
1271 }
1272
1273 async fn response_from_proto(
1274 self,
1275 message: proto::GetHoverResponse,
1276 _: Handle<Project>,
1277 buffer: Handle<Buffer>,
1278 cx: AsyncAppContext,
1279 ) -> Result<Self::Response> {
1280 let contents: Vec<_> = message
1281 .contents
1282 .into_iter()
1283 .map(|block| HoverBlock {
1284 text: block.text,
1285 kind: if let Some(language) = block.language {
1286 HoverBlockKind::Code { language }
1287 } else if block.is_markdown {
1288 HoverBlockKind::Markdown
1289 } else {
1290 HoverBlockKind::PlainText
1291 },
1292 })
1293 .collect();
1294 if contents.is_empty() {
1295 return Ok(None);
1296 }
1297
1298 let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned());
1299 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1300 language2::proto::deserialize_anchor(start)
1301 .and_then(|start| language2::proto::deserialize_anchor(end).map(|end| start..end))
1302 } else {
1303 None
1304 };
1305
1306 Ok(Some(Hover {
1307 contents,
1308 range,
1309 language,
1310 }))
1311 }
1312
1313 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 {
1314 message.buffer_id
1315 }
1316}
1317
1318#[async_trait(?Send)]
1319impl LspCommand for GetCompletions {
1320 type Response = Vec<Completion>;
1321 type LspRequest = lsp2::request::Completion;
1322 type ProtoRequest = proto::GetCompletions;
1323
1324 fn to_lsp(
1325 &self,
1326 path: &Path,
1327 _: &Buffer,
1328 _: &Arc<LanguageServer>,
1329 _: &AppContext,
1330 ) -> lsp2::CompletionParams {
1331 lsp2::CompletionParams {
1332 text_document_position: lsp2::TextDocumentPositionParams::new(
1333 lsp2::TextDocumentIdentifier::new(lsp2::Url::from_file_path(path).unwrap()),
1334 point_to_lsp(self.position),
1335 ),
1336 context: Default::default(),
1337 work_done_progress_params: Default::default(),
1338 partial_result_params: Default::default(),
1339 }
1340 }
1341
1342 async fn response_from_lsp(
1343 self,
1344 completions: Option<lsp2::CompletionResponse>,
1345 _: Handle<Project>,
1346 buffer: Handle<Buffer>,
1347 server_id: LanguageServerId,
1348 cx: AsyncAppContext,
1349 ) -> Result<Vec<Completion>> {
1350 let mut response_list = None;
1351 let completions = if let Some(completions) = completions {
1352 match completions {
1353 lsp2::CompletionResponse::Array(completions) => completions,
1354
1355 lsp2::CompletionResponse::List(mut list) => {
1356 let items = std::mem::take(&mut list.items);
1357 response_list = Some(list);
1358 items
1359 }
1360 }
1361 } else {
1362 Default::default()
1363 };
1364
1365 let completions = buffer.read_with(&cx, |buffer, _| {
1366 let language = buffer.language().cloned();
1367 let snapshot = buffer.snapshot();
1368 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1369
1370 let mut range_for_token = None;
1371 completions
1372 .into_iter()
1373 .filter_map(move |mut lsp_completion| {
1374 let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() {
1375 // If the language server provides a range to overwrite, then
1376 // check that the range is valid.
1377 Some(lsp2::CompletionTextEdit::Edit(edit)) => {
1378 let range = range_from_lsp(edit.range);
1379 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1380 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1381 if start != range.start.0 || end != range.end.0 {
1382 log::info!("completion out of expected range");
1383 return None;
1384 }
1385 (
1386 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1387 edit.new_text.clone(),
1388 )
1389 }
1390
1391 // If the language server does not provide a range, then infer
1392 // the range based on the syntax tree.
1393 None => {
1394 if self.position != clipped_position {
1395 log::info!("completion out of expected range");
1396 return None;
1397 }
1398
1399 let default_edit_range = response_list
1400 .as_ref()
1401 .and_then(|list| list.item_defaults.as_ref())
1402 .and_then(|defaults| defaults.edit_range.as_ref())
1403 .and_then(|range| match range {
1404 CompletionListItemDefaultsEditRange::Range(r) => Some(r),
1405 _ => None,
1406 });
1407
1408 let range = if let Some(range) = default_edit_range {
1409 let range = range_from_lsp(range.clone());
1410 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1411 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1412 if start != range.start.0 || end != range.end.0 {
1413 log::info!("completion out of expected range");
1414 return None;
1415 }
1416
1417 snapshot.anchor_before(start)..snapshot.anchor_after(end)
1418 } else {
1419 range_for_token
1420 .get_or_insert_with(|| {
1421 let offset = self.position.to_offset(&snapshot);
1422 let (range, kind) = snapshot.surrounding_word(offset);
1423 let range = if kind == Some(CharKind::Word) {
1424 range
1425 } else {
1426 offset..offset
1427 };
1428
1429 snapshot.anchor_before(range.start)
1430 ..snapshot.anchor_after(range.end)
1431 })
1432 .clone()
1433 };
1434
1435 let text = lsp_completion
1436 .insert_text
1437 .as_ref()
1438 .unwrap_or(&lsp_completion.label)
1439 .clone();
1440 (range, text)
1441 }
1442
1443 Some(lsp2::CompletionTextEdit::InsertAndReplace(_)) => {
1444 log::info!("unsupported insert/replace completion");
1445 return None;
1446 }
1447 };
1448
1449 let language = language.clone();
1450 LineEnding::normalize(&mut new_text);
1451 Some(async move {
1452 let mut label = None;
1453 if let Some(language) = language {
1454 language.process_completion(&mut lsp_completion).await;
1455 label = language.label_for_completion(&lsp_completion).await;
1456 }
1457 Completion {
1458 old_range,
1459 new_text,
1460 label: label.unwrap_or_else(|| {
1461 language2::CodeLabel::plain(
1462 lsp_completion.label.clone(),
1463 lsp_completion.filter_text.as_deref(),
1464 )
1465 }),
1466 server_id,
1467 lsp_completion,
1468 }
1469 })
1470 })
1471 });
1472
1473 Ok(future::join_all(completions).await)
1474 }
1475
1476 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1477 let anchor = buffer.anchor_after(self.position);
1478 proto::GetCompletions {
1479 project_id,
1480 buffer_id: buffer.remote_id(),
1481 position: Some(language2::proto::serialize_anchor(&anchor)),
1482 version: serialize_version(&buffer.version()),
1483 }
1484 }
1485
1486 async fn from_proto(
1487 message: proto::GetCompletions,
1488 _: Handle<Project>,
1489 buffer: Handle<Buffer>,
1490 mut cx: AsyncAppContext,
1491 ) -> Result<Self> {
1492 let version = deserialize_version(&message.version);
1493 buffer
1494 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))
1495 .await?;
1496 let position = message
1497 .position
1498 .and_then(language2::proto::deserialize_anchor)
1499 .map(|p| {
1500 buffer.read_with(&cx, |buffer, _| {
1501 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1502 })
1503 })
1504 .ok_or_else(|| anyhow!("invalid position"))?;
1505 Ok(Self { position })
1506 }
1507
1508 fn response_to_proto(
1509 completions: Vec<Completion>,
1510 _: &mut Project,
1511 _: PeerId,
1512 buffer_version: &clock::Global,
1513 _: &mut AppContext,
1514 ) -> proto::GetCompletionsResponse {
1515 proto::GetCompletionsResponse {
1516 completions: completions
1517 .iter()
1518 .map(language2::proto::serialize_completion)
1519 .collect(),
1520 version: serialize_version(&buffer_version),
1521 }
1522 }
1523
1524 async fn response_from_proto(
1525 self,
1526 message: proto::GetCompletionsResponse,
1527 _: Handle<Project>,
1528 buffer: Handle<Buffer>,
1529 mut cx: AsyncAppContext,
1530 ) -> Result<Vec<Completion>> {
1531 buffer
1532 .update(&mut cx, |buffer, _| {
1533 buffer.wait_for_version(deserialize_version(&message.version))
1534 })
1535 .await?;
1536
1537 let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned());
1538 let completions = message.completions.into_iter().map(|completion| {
1539 language2::proto::deserialize_completion(completion, language.clone())
1540 });
1541 future::try_join_all(completions).await
1542 }
1543
1544 fn buffer_id_from_proto(message: &proto::GetCompletions) -> u64 {
1545 message.buffer_id
1546 }
1547}
1548
1549#[async_trait(?Send)]
1550impl LspCommand for GetCodeActions {
1551 type Response = Vec<CodeAction>;
1552 type LspRequest = lsp2::request::CodeActionRequest;
1553 type ProtoRequest = proto::GetCodeActions;
1554
1555 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1556 match &capabilities.code_action_provider {
1557 None => false,
1558 Some(lsp2::CodeActionProviderCapability::Simple(false)) => false,
1559 _ => true,
1560 }
1561 }
1562
1563 fn to_lsp(
1564 &self,
1565 path: &Path,
1566 buffer: &Buffer,
1567 language_server: &Arc<LanguageServer>,
1568 _: &AppContext,
1569 ) -> lsp2::CodeActionParams {
1570 let relevant_diagnostics = buffer
1571 .snapshot()
1572 .diagnostics_in_range::<_, usize>(self.range.clone(), false)
1573 .map(|entry| entry.to_lsp_diagnostic_stub())
1574 .collect();
1575 lsp2::CodeActionParams {
1576 text_document: lsp2::TextDocumentIdentifier::new(
1577 lsp2::Url::from_file_path(path).unwrap(),
1578 ),
1579 range: range_to_lsp(self.range.to_point_utf16(buffer)),
1580 work_done_progress_params: Default::default(),
1581 partial_result_params: Default::default(),
1582 context: lsp2::CodeActionContext {
1583 diagnostics: relevant_diagnostics,
1584 only: language_server.code_action_kinds(),
1585 ..lsp2::CodeActionContext::default()
1586 },
1587 }
1588 }
1589
1590 async fn response_from_lsp(
1591 self,
1592 actions: Option<lsp2::CodeActionResponse>,
1593 _: Handle<Project>,
1594 _: Handle<Buffer>,
1595 server_id: LanguageServerId,
1596 _: AsyncAppContext,
1597 ) -> Result<Vec<CodeAction>> {
1598 Ok(actions
1599 .unwrap_or_default()
1600 .into_iter()
1601 .filter_map(|entry| {
1602 if let lsp2::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1603 Some(CodeAction {
1604 server_id,
1605 range: self.range.clone(),
1606 lsp_action,
1607 })
1608 } else {
1609 None
1610 }
1611 })
1612 .collect())
1613 }
1614
1615 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
1616 proto::GetCodeActions {
1617 project_id,
1618 buffer_id: buffer.remote_id(),
1619 start: Some(language2::proto::serialize_anchor(&self.range.start)),
1620 end: Some(language2::proto::serialize_anchor(&self.range.end)),
1621 version: serialize_version(&buffer.version()),
1622 }
1623 }
1624
1625 async fn from_proto(
1626 message: proto::GetCodeActions,
1627 _: Handle<Project>,
1628 buffer: Handle<Buffer>,
1629 mut cx: AsyncAppContext,
1630 ) -> Result<Self> {
1631 let start = message
1632 .start
1633 .and_then(language2::proto::deserialize_anchor)
1634 .ok_or_else(|| anyhow!("invalid start"))?;
1635 let end = message
1636 .end
1637 .and_then(language2::proto::deserialize_anchor)
1638 .ok_or_else(|| anyhow!("invalid end"))?;
1639 buffer
1640 .update(&mut cx, |buffer, _| {
1641 buffer.wait_for_version(deserialize_version(&message.version))
1642 })
1643 .await?;
1644
1645 Ok(Self { range: start..end })
1646 }
1647
1648 fn response_to_proto(
1649 code_actions: Vec<CodeAction>,
1650 _: &mut Project,
1651 _: PeerId,
1652 buffer_version: &clock::Global,
1653 _: &mut AppContext,
1654 ) -> proto::GetCodeActionsResponse {
1655 proto::GetCodeActionsResponse {
1656 actions: code_actions
1657 .iter()
1658 .map(language2::proto::serialize_code_action)
1659 .collect(),
1660 version: serialize_version(&buffer_version),
1661 }
1662 }
1663
1664 async fn response_from_proto(
1665 self,
1666 message: proto::GetCodeActionsResponse,
1667 _: Handle<Project>,
1668 buffer: Handle<Buffer>,
1669 mut cx: AsyncAppContext,
1670 ) -> Result<Vec<CodeAction>> {
1671 buffer
1672 .update(&mut cx, |buffer, _| {
1673 buffer.wait_for_version(deserialize_version(&message.version))
1674 })
1675 .await?;
1676 message
1677 .actions
1678 .into_iter()
1679 .map(language2::proto::deserialize_code_action)
1680 .collect()
1681 }
1682
1683 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> u64 {
1684 message.buffer_id
1685 }
1686}
1687
1688#[async_trait(?Send)]
1689impl LspCommand for OnTypeFormatting {
1690 type Response = Option<Transaction>;
1691 type LspRequest = lsp2::request::OnTypeFormatting;
1692 type ProtoRequest = proto::OnTypeFormatting;
1693
1694 fn check_capabilities(&self, server_capabilities: &lsp2::ServerCapabilities) -> bool {
1695 let Some(on_type_formatting_options) =
1696 &server_capabilities.document_on_type_formatting_provider
1697 else {
1698 return false;
1699 };
1700 on_type_formatting_options
1701 .first_trigger_character
1702 .contains(&self.trigger)
1703 || on_type_formatting_options
1704 .more_trigger_character
1705 .iter()
1706 .flatten()
1707 .any(|chars| chars.contains(&self.trigger))
1708 }
1709
1710 fn to_lsp(
1711 &self,
1712 path: &Path,
1713 _: &Buffer,
1714 _: &Arc<LanguageServer>,
1715 _: &AppContext,
1716 ) -> lsp2::DocumentOnTypeFormattingParams {
1717 lsp2::DocumentOnTypeFormattingParams {
1718 text_document_position: lsp2::TextDocumentPositionParams::new(
1719 lsp2::TextDocumentIdentifier::new(lsp2::Url::from_file_path(path).unwrap()),
1720 point_to_lsp(self.position),
1721 ),
1722 ch: self.trigger.clone(),
1723 options: lsp_formatting_options(self.options.tab_size),
1724 }
1725 }
1726
1727 async fn response_from_lsp(
1728 self,
1729 message: Option<Vec<lsp2::TextEdit>>,
1730 project: Handle<Project>,
1731 buffer: Handle<Buffer>,
1732 server_id: LanguageServerId,
1733 mut cx: AsyncAppContext,
1734 ) -> Result<Option<Transaction>> {
1735 if let Some(edits) = message {
1736 let (lsp_adapter, lsp_server) =
1737 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1738 Project::deserialize_edits(
1739 project,
1740 buffer,
1741 edits,
1742 self.push_to_history,
1743 lsp_adapter,
1744 lsp_server,
1745 &mut cx,
1746 )
1747 .await
1748 } else {
1749 Ok(None)
1750 }
1751 }
1752
1753 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1754 proto::OnTypeFormatting {
1755 project_id,
1756 buffer_id: buffer.remote_id(),
1757 position: Some(language2::proto::serialize_anchor(
1758 &buffer.anchor_before(self.position),
1759 )),
1760 trigger: self.trigger.clone(),
1761 version: serialize_version(&buffer.version()),
1762 }
1763 }
1764
1765 async fn from_proto(
1766 message: proto::OnTypeFormatting,
1767 _: Handle<Project>,
1768 buffer: Handle<Buffer>,
1769 mut cx: AsyncAppContext,
1770 ) -> Result<Self> {
1771 let position = message
1772 .position
1773 .and_then(deserialize_anchor)
1774 .ok_or_else(|| anyhow!("invalid position"))?;
1775 buffer
1776 .update(&mut cx, |buffer, _| {
1777 buffer.wait_for_version(deserialize_version(&message.version))
1778 })
1779 .await?;
1780
1781 let tab_size = buffer.read_with(&cx, |buffer, cx| {
1782 language_settings(buffer.language(), buffer.file(), cx).tab_size
1783 });
1784
1785 Ok(Self {
1786 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1787 trigger: message.trigger.clone(),
1788 options: lsp_formatting_options(tab_size.get()).into(),
1789 push_to_history: false,
1790 })
1791 }
1792
1793 fn response_to_proto(
1794 response: Option<Transaction>,
1795 _: &mut Project,
1796 _: PeerId,
1797 _: &clock::Global,
1798 _: &mut AppContext,
1799 ) -> proto::OnTypeFormattingResponse {
1800 proto::OnTypeFormattingResponse {
1801 transaction: response
1802 .map(|transaction| language2::proto::serialize_transaction(&transaction)),
1803 }
1804 }
1805
1806 async fn response_from_proto(
1807 self,
1808 message: proto::OnTypeFormattingResponse,
1809 _: Handle<Project>,
1810 _: Handle<Buffer>,
1811 _: AsyncAppContext,
1812 ) -> Result<Option<Transaction>> {
1813 let Some(transaction) = message.transaction else {
1814 return Ok(None);
1815 };
1816 Ok(Some(language2::proto::deserialize_transaction(
1817 transaction,
1818 )?))
1819 }
1820
1821 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 {
1822 message.buffer_id
1823 }
1824}
1825
1826impl InlayHints {
1827 pub async fn lsp2_to_project_hint(
1828 lsp_hint: lsp2::InlayHint,
1829 buffer_handle: &Handle<Buffer>,
1830 server_id: LanguageServerId,
1831 resolve_state: ResolveState,
1832 force_no_type_left_padding: bool,
1833 cx: &mut AsyncAppContext,
1834 ) -> anyhow::Result<InlayHint> {
1835 let kind = lsp_hint.kind.and_then(|kind| match kind {
1836 lsp2::InlayHintKind::TYPE => Some(InlayHintKind::Type),
1837 lsp2::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
1838 _ => None,
1839 });
1840
1841 let position = cx.update(|cx| {
1842 let buffer = buffer_handle.read(cx);
1843 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
1844 if kind == Some(InlayHintKind::Parameter) {
1845 buffer.anchor_before(position)
1846 } else {
1847 buffer.anchor_after(position)
1848 }
1849 });
1850 let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
1851 .await
1852 .context("lsp to project inlay hint conversion")?;
1853 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
1854 false
1855 } else {
1856 lsp_hint.padding_left.unwrap_or(false)
1857 };
1858
1859 Ok(InlayHint {
1860 position,
1861 padding_left,
1862 padding_right: lsp_hint.padding_right.unwrap_or(false),
1863 label,
1864 kind,
1865 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
1866 lsp2::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
1867 lsp2::InlayHintTooltip::MarkupContent(markup_content) => {
1868 InlayHintTooltip::MarkupContent(MarkupContent {
1869 kind: match markup_content.kind {
1870 lsp2::MarkupKind::PlainText => HoverBlockKind::PlainText,
1871 lsp2::MarkupKind::Markdown => HoverBlockKind::Markdown,
1872 },
1873 value: markup_content.value,
1874 })
1875 }
1876 }),
1877 resolve_state,
1878 })
1879 }
1880
1881 async fn lsp2_inlay_label_to_project(
1882 lsp_label: lsp2::InlayHintLabel,
1883 server_id: LanguageServerId,
1884 ) -> anyhow::Result<InlayHintLabel> {
1885 let label = match lsp_label {
1886 lsp2::InlayHintLabel::String(s) => InlayHintLabel::String(s),
1887 lsp2::InlayHintLabel::LabelParts(lsp_parts) => {
1888 let mut parts = Vec::with_capacity(lsp_parts.len());
1889 for lsp_part in lsp_parts {
1890 parts.push(InlayHintLabelPart {
1891 value: lsp_part.value,
1892 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
1893 lsp2::InlayHintLabelPartTooltip::String(s) => {
1894 InlayHintLabelPartTooltip::String(s)
1895 }
1896 lsp2::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
1897 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
1898 kind: match markup_content.kind {
1899 lsp2::MarkupKind::PlainText => HoverBlockKind::PlainText,
1900 lsp2::MarkupKind::Markdown => HoverBlockKind::Markdown,
1901 },
1902 value: markup_content.value,
1903 })
1904 }
1905 }),
1906 location: Some(server_id).zip(lsp_part.location),
1907 });
1908 }
1909 InlayHintLabel::LabelParts(parts)
1910 }
1911 };
1912
1913 Ok(label)
1914 }
1915
1916 pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
1917 let (state, lsp_resolve_state) = match response_hint.resolve_state {
1918 ResolveState::Resolved => (0, None),
1919 ResolveState::CanResolve(server_id, resolve_data) => (
1920 1,
1921 resolve_data
1922 .map(|json_data| {
1923 serde_json::to_string(&json_data)
1924 .expect("failed to serialize resolve json data")
1925 })
1926 .map(|value| proto::resolve_state::LspResolveState {
1927 server_id: server_id.0 as u64,
1928 value,
1929 }),
1930 ),
1931 ResolveState::Resolving => (2, None),
1932 };
1933 let resolve_state = Some(proto::ResolveState {
1934 state,
1935 lsp_resolve_state,
1936 });
1937 proto::InlayHint {
1938 position: Some(language2::proto::serialize_anchor(&response_hint.position)),
1939 padding_left: response_hint.padding_left,
1940 padding_right: response_hint.padding_right,
1941 label: Some(proto::InlayHintLabel {
1942 label: Some(match response_hint.label {
1943 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
1944 InlayHintLabel::LabelParts(label_parts) => {
1945 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
1946 parts: label_parts.into_iter().map(|label_part| {
1947 let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
1948 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 });
1949 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 });
1950 proto::InlayHintLabelPart {
1951 value: label_part.value,
1952 tooltip: label_part.tooltip.map(|tooltip| {
1953 let proto_tooltip = match tooltip {
1954 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
1955 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
1956 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1957 value: markup_content.value,
1958 }),
1959 };
1960 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
1961 }),
1962 location_url,
1963 location_range_start,
1964 location_range_end,
1965 language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
1966 }}).collect()
1967 })
1968 }
1969 }),
1970 }),
1971 kind: response_hint.kind.map(|kind| kind.name().to_string()),
1972 tooltip: response_hint.tooltip.map(|response_tooltip| {
1973 let proto_tooltip = match response_tooltip {
1974 InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
1975 InlayHintTooltip::MarkupContent(markup_content) => {
1976 proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
1977 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1978 value: markup_content.value,
1979 })
1980 }
1981 };
1982 proto::InlayHintTooltip {
1983 content: Some(proto_tooltip),
1984 }
1985 }),
1986 resolve_state,
1987 }
1988 }
1989
1990 pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
1991 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
1992 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
1993 });
1994 let resolve_state_data = resolve_state
1995 .lsp_resolve_state.as_ref()
1996 .map(|lsp_resolve_state| {
1997 serde_json::from_str::<Option<lsp2::LSPAny>>(&lsp_resolve_state.value)
1998 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
1999 .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
2000 })
2001 .transpose()?;
2002 let resolve_state = match resolve_state.state {
2003 0 => ResolveState::Resolved,
2004 1 => {
2005 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2006 format!(
2007 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2008 )
2009 })?;
2010 ResolveState::CanResolve(server_id, lsp_resolve_state)
2011 }
2012 2 => ResolveState::Resolving,
2013 invalid => {
2014 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2015 }
2016 };
2017 Ok(InlayHint {
2018 position: message_hint
2019 .position
2020 .and_then(language2::proto::deserialize_anchor)
2021 .context("invalid position")?,
2022 label: match message_hint
2023 .label
2024 .and_then(|label| label.label)
2025 .context("missing label")?
2026 {
2027 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2028 proto::inlay_hint_label::Label::LabelParts(parts) => {
2029 let mut label_parts = Vec::new();
2030 for part in parts.parts {
2031 label_parts.push(InlayHintLabelPart {
2032 value: part.value,
2033 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2034 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2035 InlayHintLabelPartTooltip::String(s)
2036 }
2037 Some(
2038 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2039 markup_content,
2040 ),
2041 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2042 kind: if markup_content.is_markdown {
2043 HoverBlockKind::Markdown
2044 } else {
2045 HoverBlockKind::PlainText
2046 },
2047 value: markup_content.value,
2048 }),
2049 None => InlayHintLabelPartTooltip::String(String::new()),
2050 }),
2051 location: {
2052 match part
2053 .location_url
2054 .zip(
2055 part.location_range_start.and_then(|start| {
2056 Some(start..part.location_range_end?)
2057 }),
2058 )
2059 .zip(part.language_server_id)
2060 {
2061 Some(((uri, range), server_id)) => Some((
2062 LanguageServerId(server_id as usize),
2063 lsp2::Location {
2064 uri: lsp2::Url::parse(&uri)
2065 .context("invalid uri in hint part {part:?}")?,
2066 range: lsp2::Range::new(
2067 point_to_lsp(PointUtf16::new(
2068 range.start.row,
2069 range.start.column,
2070 )),
2071 point_to_lsp(PointUtf16::new(
2072 range.end.row,
2073 range.end.column,
2074 )),
2075 ),
2076 },
2077 )),
2078 None => None,
2079 }
2080 },
2081 });
2082 }
2083
2084 InlayHintLabel::LabelParts(label_parts)
2085 }
2086 },
2087 padding_left: message_hint.padding_left,
2088 padding_right: message_hint.padding_right,
2089 kind: message_hint
2090 .kind
2091 .as_deref()
2092 .and_then(InlayHintKind::from_name),
2093 tooltip: message_hint.tooltip.and_then(|tooltip| {
2094 Some(match tooltip.content? {
2095 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2096 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2097 InlayHintTooltip::MarkupContent(MarkupContent {
2098 kind: if markup_content.is_markdown {
2099 HoverBlockKind::Markdown
2100 } else {
2101 HoverBlockKind::PlainText
2102 },
2103 value: markup_content.value,
2104 })
2105 }
2106 })
2107 }),
2108 resolve_state,
2109 })
2110 }
2111
2112 pub fn project_to_lsp2_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp2::InlayHint {
2113 lsp2::InlayHint {
2114 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2115 kind: hint.kind.map(|kind| match kind {
2116 InlayHintKind::Type => lsp2::InlayHintKind::TYPE,
2117 InlayHintKind::Parameter => lsp2::InlayHintKind::PARAMETER,
2118 }),
2119 text_edits: None,
2120 tooltip: hint.tooltip.and_then(|tooltip| {
2121 Some(match tooltip {
2122 InlayHintTooltip::String(s) => lsp2::InlayHintTooltip::String(s),
2123 InlayHintTooltip::MarkupContent(markup_content) => {
2124 lsp2::InlayHintTooltip::MarkupContent(lsp2::MarkupContent {
2125 kind: match markup_content.kind {
2126 HoverBlockKind::PlainText => lsp2::MarkupKind::PlainText,
2127 HoverBlockKind::Markdown => lsp2::MarkupKind::Markdown,
2128 HoverBlockKind::Code { .. } => return None,
2129 },
2130 value: markup_content.value,
2131 })
2132 }
2133 })
2134 }),
2135 label: match hint.label {
2136 InlayHintLabel::String(s) => lsp2::InlayHintLabel::String(s),
2137 InlayHintLabel::LabelParts(label_parts) => lsp2::InlayHintLabel::LabelParts(
2138 label_parts
2139 .into_iter()
2140 .map(|part| lsp2::InlayHintLabelPart {
2141 value: part.value,
2142 tooltip: part.tooltip.and_then(|tooltip| {
2143 Some(match tooltip {
2144 InlayHintLabelPartTooltip::String(s) => {
2145 lsp2::InlayHintLabelPartTooltip::String(s)
2146 }
2147 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2148 lsp2::InlayHintLabelPartTooltip::MarkupContent(
2149 lsp2::MarkupContent {
2150 kind: match markup_content.kind {
2151 HoverBlockKind::PlainText => {
2152 lsp2::MarkupKind::PlainText
2153 }
2154 HoverBlockKind::Markdown => {
2155 lsp2::MarkupKind::Markdown
2156 }
2157 HoverBlockKind::Code { .. } => return None,
2158 },
2159 value: markup_content.value,
2160 },
2161 )
2162 }
2163 })
2164 }),
2165 location: part.location.map(|(_, location)| location),
2166 command: None,
2167 })
2168 .collect(),
2169 ),
2170 },
2171 padding_left: Some(hint.padding_left),
2172 padding_right: Some(hint.padding_right),
2173 data: match hint.resolve_state {
2174 ResolveState::CanResolve(_, data) => data,
2175 ResolveState::Resolving | ResolveState::Resolved => None,
2176 },
2177 }
2178 }
2179
2180 pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2181 capabilities
2182 .inlay_hint_provider
2183 .as_ref()
2184 .and_then(|options| match options {
2185 OneOf::Left(_is_supported) => None,
2186 OneOf::Right(capabilities) => match capabilities {
2187 lsp2::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2188 lsp2::InlayHintServerCapabilities::RegistrationOptions(o) => {
2189 o.inlay_hint_options.resolve_provider
2190 }
2191 },
2192 })
2193 .unwrap_or(false)
2194 }
2195}
2196
2197#[async_trait(?Send)]
2198impl LspCommand for InlayHints {
2199 type Response = Vec<InlayHint>;
2200 type LspRequest = lsp2::InlayHintRequest;
2201 type ProtoRequest = proto::InlayHints;
2202
2203 fn check_capabilities(&self, server_capabilities: &lsp2::ServerCapabilities) -> bool {
2204 let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else {
2205 return false;
2206 };
2207 match inlay_hint_provider {
2208 lsp2::OneOf::Left(enabled) => *enabled,
2209 lsp2::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2210 lsp2::InlayHintServerCapabilities::Options(_) => true,
2211 lsp2::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2212 },
2213 }
2214 }
2215
2216 fn to_lsp(
2217 &self,
2218 path: &Path,
2219 buffer: &Buffer,
2220 _: &Arc<LanguageServer>,
2221 _: &AppContext,
2222 ) -> lsp2::InlayHintParams {
2223 lsp2::InlayHintParams {
2224 text_document: lsp2::TextDocumentIdentifier {
2225 uri: lsp2::Url::from_file_path(path).unwrap(),
2226 },
2227 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2228 work_done_progress_params: Default::default(),
2229 }
2230 }
2231
2232 async fn response_from_lsp(
2233 self,
2234 message: Option<Vec<lsp2::InlayHint>>,
2235 project: Handle<Project>,
2236 buffer: Handle<Buffer>,
2237 server_id: LanguageServerId,
2238 mut cx: AsyncAppContext,
2239 ) -> anyhow::Result<Vec<InlayHint>> {
2240 let (lsp_adapter, lsp_server) =
2241 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2242 // `typescript-language-server` adds padding to the left for type hints, turning
2243 // `const foo: boolean` into `const foo : boolean` which looks odd.
2244 // `rust-analyzer` does not have the padding for this case, and we have to accomodate both.
2245 //
2246 // We could trim the whole string, but being pessimistic on par with the situation above,
2247 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2248 // Hence let's use a heuristic first to handle the most awkward case and look for more.
2249 let force_no_type_left_padding =
2250 lsp_adapter.name.0.as_ref() == "typescript-language-server";
2251
2252 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2253 let resolve_state = if InlayHints::can_resolve_inlays(lsp_server.capabilities()) {
2254 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2255 } else {
2256 ResolveState::Resolved
2257 };
2258
2259 let buffer = buffer.clone();
2260 cx.spawn(|mut cx| async move {
2261 InlayHints::lsp_to_project_hint(
2262 lsp_hint,
2263 &buffer,
2264 server_id,
2265 resolve_state,
2266 force_no_type_left_padding,
2267 &mut cx,
2268 )
2269 .await
2270 })
2271 });
2272 future::join_all(hints)
2273 .await
2274 .into_iter()
2275 .collect::<anyhow::Result<_>>()
2276 .context("lsp to project inlay hints conversion")
2277 }
2278
2279 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2280 proto::InlayHints {
2281 project_id,
2282 buffer_id: buffer.remote_id(),
2283 start: Some(language2::proto::serialize_anchor(&self.range.start)),
2284 end: Some(language2::proto::serialize_anchor(&self.range.end)),
2285 version: serialize_version(&buffer.version()),
2286 }
2287 }
2288
2289 async fn from_proto(
2290 message: proto::InlayHints,
2291 _: Handle<Project>,
2292 buffer: Handle<Buffer>,
2293 mut cx: AsyncAppContext,
2294 ) -> Result<Self> {
2295 let start = message
2296 .start
2297 .and_then(language2::proto::deserialize_anchor)
2298 .context("invalid start")?;
2299 let end = message
2300 .end
2301 .and_then(language2::proto::deserialize_anchor)
2302 .context("invalid end")?;
2303 buffer
2304 .update(&mut cx, |buffer, _| {
2305 buffer.wait_for_version(deserialize_version(&message.version))
2306 })
2307 .await?;
2308
2309 Ok(Self { range: start..end })
2310 }
2311
2312 fn response_to_proto(
2313 response: Vec<InlayHint>,
2314 _: &mut Project,
2315 _: PeerId,
2316 buffer_version: &clock::Global,
2317 _: &mut AppContext,
2318 ) -> proto::InlayHintsResponse {
2319 proto::InlayHintsResponse {
2320 hints: response
2321 .into_iter()
2322 .map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
2323 .collect(),
2324 version: serialize_version(buffer_version),
2325 }
2326 }
2327
2328 async fn response_from_proto(
2329 self,
2330 message: proto::InlayHintsResponse,
2331 _: Handle<Project>,
2332 buffer: Handle<Buffer>,
2333 mut cx: AsyncAppContext,
2334 ) -> anyhow::Result<Vec<InlayHint>> {
2335 buffer
2336 .update(&mut cx, |buffer, _| {
2337 buffer.wait_for_version(deserialize_version(&message.version))
2338 })
2339 .await?;
2340
2341 let mut hints = Vec::new();
2342 for message_hint in message.hints {
2343 hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2344 }
2345
2346 Ok(hints)
2347 }
2348
2349 fn buffer_id_from_proto(message: &proto::InlayHints) -> u64 {
2350 message.buffer_id
2351 }
2352}