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