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