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