1use crate::{
2 DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel,
3 InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Item, Location, LocationLink,
4 MarkupContent, Project, ProjectTransaction, ResolveState,
5};
6use anyhow::{anyhow, Context, Result};
7use async_trait::async_trait;
8use client::proto::{self, PeerId};
9use fs::LineEnding;
10use futures::future;
11use gpui::{AppContext, AsyncAppContext, ModelHandle};
12use language::{
13 language_settings::{language_settings, InlayHintKind},
14 point_from_lsp, point_to_lsp,
15 proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
16 range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind,
17 CodeAction, Completion, LanguageServerName, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16,
18 Transaction, Unclipped,
19};
20use lsp::{DocumentHighlightKind, LanguageServer, LanguageServerId, ServerCapabilities};
21use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
22
23pub fn lsp_formatting_options(tab_size: u32) -> lsp::FormattingOptions {
24 lsp::FormattingOptions {
25 tab_size,
26 insert_spaces: true,
27 insert_final_newline: Some(true),
28 ..lsp::FormattingOptions::default()
29 }
30}
31
32#[async_trait(?Send)]
33pub(crate) trait LspCommand: 'static + Sized {
34 type Response: 'static + Default + Send;
35 type LspRequest: 'static + Send + lsp::request::Request;
36 type ProtoRequest: 'static + Send + proto::RequestMessage;
37
38 fn check_capabilities(&self, _: &lsp::ServerCapabilities) -> bool {
39 true
40 }
41
42 fn to_lsp(
43 &self,
44 path: &Path,
45 buffer: &Buffer,
46 language_server: &Arc<LanguageServer>,
47 cx: &AppContext,
48 ) -> <Self::LspRequest as lsp::request::Request>::Params;
49
50 async fn response_from_lsp(
51 self,
52 message: <Self::LspRequest as lsp::request::Request>::Result,
53 project: ModelHandle<Project>,
54 buffer: ModelHandle<Buffer>,
55 server_id: LanguageServerId,
56 cx: AsyncAppContext,
57 ) -> Result<Self::Response>;
58
59 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
60
61 async fn from_proto(
62 message: Self::ProtoRequest,
63 project: ModelHandle<Project>,
64 buffer: ModelHandle<Buffer>,
65 cx: AsyncAppContext,
66 ) -> Result<Self>;
67
68 fn response_to_proto(
69 response: Self::Response,
70 project: &mut Project,
71 peer_id: PeerId,
72 buffer_version: &clock::Global,
73 cx: &mut AppContext,
74 ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
75
76 async fn response_from_proto(
77 self,
78 message: <Self::ProtoRequest as proto::RequestMessage>::Response,
79 project: ModelHandle<Project>,
80 buffer: ModelHandle<Buffer>,
81 cx: AsyncAppContext,
82 ) -> Result<Self::Response>;
83
84 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64;
85}
86
87pub(crate) struct PrepareRename {
88 pub position: PointUtf16,
89}
90
91pub(crate) struct PerformRename {
92 pub position: PointUtf16,
93 pub new_name: String,
94 pub push_to_history: bool,
95}
96
97pub(crate) struct GetDefinition {
98 pub position: PointUtf16,
99}
100
101pub(crate) struct GetTypeDefinition {
102 pub position: PointUtf16,
103}
104
105pub(crate) struct GetReferences {
106 pub position: PointUtf16,
107}
108
109pub(crate) struct GetDocumentHighlights {
110 pub position: PointUtf16,
111}
112
113pub(crate) struct GetHover {
114 pub position: PointUtf16,
115}
116
117pub(crate) struct GetCompletions {
118 pub position: PointUtf16,
119}
120
121pub(crate) struct GetCodeActions {
122 pub range: Range<Anchor>,
123}
124
125pub(crate) struct OnTypeFormatting {
126 pub position: PointUtf16,
127 pub trigger: String,
128 pub options: FormattingOptions,
129 pub push_to_history: bool,
130}
131
132pub(crate) struct InlayHints {
133 pub range: Range<Anchor>,
134}
135
136pub(crate) struct FormattingOptions {
137 tab_size: u32,
138}
139
140impl From<lsp::FormattingOptions> for FormattingOptions {
141 fn from(value: lsp::FormattingOptions) -> Self {
142 Self {
143 tab_size: value.tab_size,
144 }
145 }
146}
147
148#[async_trait(?Send)]
149impl LspCommand for PrepareRename {
150 type Response = Option<Range<Anchor>>;
151 type LspRequest = lsp::request::PrepareRenameRequest;
152 type ProtoRequest = proto::PrepareRename;
153
154 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
155 if let Some(lsp::OneOf::Right(rename)) = &capabilities.rename_provider {
156 rename.prepare_provider == Some(true)
157 } else {
158 false
159 }
160 }
161
162 fn to_lsp(
163 &self,
164 path: &Path,
165 _: &Buffer,
166 _: &Arc<LanguageServer>,
167 _: &AppContext,
168 ) -> lsp::TextDocumentPositionParams {
169 lsp::TextDocumentPositionParams {
170 text_document: lsp::TextDocumentIdentifier {
171 uri: lsp::Url::from_file_path(path).unwrap(),
172 },
173 position: point_to_lsp(self.position),
174 }
175 }
176
177 async fn response_from_lsp(
178 self,
179 message: Option<lsp::PrepareRenameResponse>,
180 _: ModelHandle<Project>,
181 buffer: ModelHandle<Buffer>,
182 _: LanguageServerId,
183 cx: AsyncAppContext,
184 ) -> Result<Option<Range<Anchor>>> {
185 buffer.read_with(&cx, |buffer, _| {
186 if let Some(
187 lsp::PrepareRenameResponse::Range(range)
188 | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
189 ) = message
190 {
191 let Range { start, end } = range_from_lsp(range);
192 if buffer.clip_point_utf16(start, Bias::Left) == start.0
193 && buffer.clip_point_utf16(end, Bias::Left) == end.0
194 {
195 return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
196 }
197 }
198 Ok(None)
199 })
200 }
201
202 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
203 proto::PrepareRename {
204 project_id,
205 buffer_id: buffer.remote_id(),
206 position: Some(language::proto::serialize_anchor(
207 &buffer.anchor_before(self.position),
208 )),
209 version: serialize_version(&buffer.version()),
210 }
211 }
212
213 async fn from_proto(
214 message: proto::PrepareRename,
215 _: ModelHandle<Project>,
216 buffer: ModelHandle<Buffer>,
217 mut cx: AsyncAppContext,
218 ) -> Result<Self> {
219 let position = message
220 .position
221 .and_then(deserialize_anchor)
222 .ok_or_else(|| anyhow!("invalid position"))?;
223 buffer
224 .update(&mut cx, |buffer, _| {
225 buffer.wait_for_version(deserialize_version(&message.version))
226 })
227 .await?;
228
229 Ok(Self {
230 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
231 })
232 }
233
234 fn response_to_proto(
235 range: Option<Range<Anchor>>,
236 _: &mut Project,
237 _: PeerId,
238 buffer_version: &clock::Global,
239 _: &mut AppContext,
240 ) -> proto::PrepareRenameResponse {
241 proto::PrepareRenameResponse {
242 can_rename: range.is_some(),
243 start: range
244 .as_ref()
245 .map(|range| language::proto::serialize_anchor(&range.start)),
246 end: range
247 .as_ref()
248 .map(|range| language::proto::serialize_anchor(&range.end)),
249 version: serialize_version(buffer_version),
250 }
251 }
252
253 async fn response_from_proto(
254 self,
255 message: proto::PrepareRenameResponse,
256 _: ModelHandle<Project>,
257 buffer: ModelHandle<Buffer>,
258 mut cx: AsyncAppContext,
259 ) -> Result<Option<Range<Anchor>>> {
260 if message.can_rename {
261 buffer
262 .update(&mut cx, |buffer, _| {
263 buffer.wait_for_version(deserialize_version(&message.version))
264 })
265 .await?;
266 let start = message.start.and_then(deserialize_anchor);
267 let end = message.end.and_then(deserialize_anchor);
268 Ok(start.zip(end).map(|(start, end)| start..end))
269 } else {
270 Ok(None)
271 }
272 }
273
274 fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
275 message.buffer_id
276 }
277}
278
279#[async_trait(?Send)]
280impl LspCommand for PerformRename {
281 type Response = ProjectTransaction;
282 type LspRequest = lsp::request::Rename;
283 type ProtoRequest = proto::PerformRename;
284
285 fn to_lsp(
286 &self,
287 path: &Path,
288 _: &Buffer,
289 _: &Arc<LanguageServer>,
290 _: &AppContext,
291 ) -> lsp::RenameParams {
292 lsp::RenameParams {
293 text_document_position: lsp::TextDocumentPositionParams {
294 text_document: lsp::TextDocumentIdentifier {
295 uri: lsp::Url::from_file_path(path).unwrap(),
296 },
297 position: point_to_lsp(self.position),
298 },
299 new_name: self.new_name.clone(),
300 work_done_progress_params: Default::default(),
301 }
302 }
303
304 async fn response_from_lsp(
305 self,
306 message: Option<lsp::WorkspaceEdit>,
307 project: ModelHandle<Project>,
308 buffer: ModelHandle<Buffer>,
309 server_id: LanguageServerId,
310 mut cx: AsyncAppContext,
311 ) -> Result<ProjectTransaction> {
312 if let Some(edit) = message {
313 let (lsp_adapter, lsp_server) =
314 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
315 Project::deserialize_workspace_edit(
316 project,
317 edit,
318 self.push_to_history,
319 lsp_adapter,
320 lsp_server,
321 &mut cx,
322 )
323 .await
324 } else {
325 Ok(ProjectTransaction::default())
326 }
327 }
328
329 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
330 proto::PerformRename {
331 project_id,
332 buffer_id: buffer.remote_id(),
333 position: Some(language::proto::serialize_anchor(
334 &buffer.anchor_before(self.position),
335 )),
336 new_name: self.new_name.clone(),
337 version: serialize_version(&buffer.version()),
338 }
339 }
340
341 async fn from_proto(
342 message: proto::PerformRename,
343 _: ModelHandle<Project>,
344 buffer: ModelHandle<Buffer>,
345 mut cx: AsyncAppContext,
346 ) -> Result<Self> {
347 let position = message
348 .position
349 .and_then(deserialize_anchor)
350 .ok_or_else(|| anyhow!("invalid position"))?;
351 buffer
352 .update(&mut cx, |buffer, _| {
353 buffer.wait_for_version(deserialize_version(&message.version))
354 })
355 .await?;
356 Ok(Self {
357 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
358 new_name: message.new_name,
359 push_to_history: false,
360 })
361 }
362
363 fn response_to_proto(
364 response: ProjectTransaction,
365 project: &mut Project,
366 peer_id: PeerId,
367 _: &clock::Global,
368 cx: &mut AppContext,
369 ) -> proto::PerformRenameResponse {
370 let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
371 proto::PerformRenameResponse {
372 transaction: Some(transaction),
373 }
374 }
375
376 async fn response_from_proto(
377 self,
378 message: proto::PerformRenameResponse,
379 project: ModelHandle<Project>,
380 _: ModelHandle<Buffer>,
381 mut cx: AsyncAppContext,
382 ) -> Result<ProjectTransaction> {
383 let message = message
384 .transaction
385 .ok_or_else(|| anyhow!("missing transaction"))?;
386 project
387 .update(&mut cx, |project, cx| {
388 project.deserialize_project_transaction(message, self.push_to_history, cx)
389 })
390 .await
391 }
392
393 fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
394 message.buffer_id
395 }
396}
397
398#[async_trait(?Send)]
399impl LspCommand for GetDefinition {
400 type Response = Vec<LocationLink>;
401 type LspRequest = lsp::request::GotoDefinition;
402 type ProtoRequest = proto::GetDefinition;
403
404 fn to_lsp(
405 &self,
406 path: &Path,
407 _: &Buffer,
408 _: &Arc<LanguageServer>,
409 _: &AppContext,
410 ) -> lsp::GotoDefinitionParams {
411 lsp::GotoDefinitionParams {
412 text_document_position_params: lsp::TextDocumentPositionParams {
413 text_document: lsp::TextDocumentIdentifier {
414 uri: lsp::Url::from_file_path(path).unwrap(),
415 },
416 position: point_to_lsp(self.position),
417 },
418 work_done_progress_params: Default::default(),
419 partial_result_params: Default::default(),
420 }
421 }
422
423 async fn response_from_lsp(
424 self,
425 message: Option<lsp::GotoDefinitionResponse>,
426 project: ModelHandle<Project>,
427 buffer: ModelHandle<Buffer>,
428 server_id: LanguageServerId,
429 cx: AsyncAppContext,
430 ) -> Result<Vec<LocationLink>> {
431 location_links_from_lsp(message, project, buffer, server_id, cx).await
432 }
433
434 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
435 proto::GetDefinition {
436 project_id,
437 buffer_id: buffer.remote_id(),
438 position: Some(language::proto::serialize_anchor(
439 &buffer.anchor_before(self.position),
440 )),
441 version: serialize_version(&buffer.version()),
442 }
443 }
444
445 async fn from_proto(
446 message: proto::GetDefinition,
447 _: ModelHandle<Project>,
448 buffer: ModelHandle<Buffer>,
449 mut cx: AsyncAppContext,
450 ) -> Result<Self> {
451 let position = message
452 .position
453 .and_then(deserialize_anchor)
454 .ok_or_else(|| anyhow!("invalid position"))?;
455 buffer
456 .update(&mut cx, |buffer, _| {
457 buffer.wait_for_version(deserialize_version(&message.version))
458 })
459 .await?;
460 Ok(Self {
461 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
462 })
463 }
464
465 fn response_to_proto(
466 response: Vec<LocationLink>,
467 project: &mut Project,
468 peer_id: PeerId,
469 _: &clock::Global,
470 cx: &mut AppContext,
471 ) -> proto::GetDefinitionResponse {
472 let links = location_links_to_proto(response, project, peer_id, cx);
473 proto::GetDefinitionResponse { links }
474 }
475
476 async fn response_from_proto(
477 self,
478 message: proto::GetDefinitionResponse,
479 project: ModelHandle<Project>,
480 _: ModelHandle<Buffer>,
481 cx: AsyncAppContext,
482 ) -> Result<Vec<LocationLink>> {
483 location_links_from_proto(message.links, project, cx).await
484 }
485
486 fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
487 message.buffer_id
488 }
489}
490
491#[async_trait(?Send)]
492impl LspCommand for GetTypeDefinition {
493 type Response = Vec<LocationLink>;
494 type LspRequest = lsp::request::GotoTypeDefinition;
495 type ProtoRequest = proto::GetTypeDefinition;
496
497 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
498 match &capabilities.type_definition_provider {
499 None => false,
500 Some(lsp::TypeDefinitionProviderCapability::Simple(false)) => false,
501 _ => true,
502 }
503 }
504
505 fn to_lsp(
506 &self,
507 path: &Path,
508 _: &Buffer,
509 _: &Arc<LanguageServer>,
510 _: &AppContext,
511 ) -> lsp::GotoTypeDefinitionParams {
512 lsp::GotoTypeDefinitionParams {
513 text_document_position_params: lsp::TextDocumentPositionParams {
514 text_document: lsp::TextDocumentIdentifier {
515 uri: lsp::Url::from_file_path(path).unwrap(),
516 },
517 position: point_to_lsp(self.position),
518 },
519 work_done_progress_params: Default::default(),
520 partial_result_params: Default::default(),
521 }
522 }
523
524 async fn response_from_lsp(
525 self,
526 message: Option<lsp::GotoTypeDefinitionResponse>,
527 project: ModelHandle<Project>,
528 buffer: ModelHandle<Buffer>,
529 server_id: LanguageServerId,
530 cx: AsyncAppContext,
531 ) -> Result<Vec<LocationLink>> {
532 location_links_from_lsp(message, project, buffer, server_id, cx).await
533 }
534
535 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition {
536 proto::GetTypeDefinition {
537 project_id,
538 buffer_id: buffer.remote_id(),
539 position: Some(language::proto::serialize_anchor(
540 &buffer.anchor_before(self.position),
541 )),
542 version: serialize_version(&buffer.version()),
543 }
544 }
545
546 async fn from_proto(
547 message: proto::GetTypeDefinition,
548 _: ModelHandle<Project>,
549 buffer: ModelHandle<Buffer>,
550 mut cx: AsyncAppContext,
551 ) -> Result<Self> {
552 let position = message
553 .position
554 .and_then(deserialize_anchor)
555 .ok_or_else(|| anyhow!("invalid position"))?;
556 buffer
557 .update(&mut cx, |buffer, _| {
558 buffer.wait_for_version(deserialize_version(&message.version))
559 })
560 .await?;
561 Ok(Self {
562 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
563 })
564 }
565
566 fn response_to_proto(
567 response: Vec<LocationLink>,
568 project: &mut Project,
569 peer_id: PeerId,
570 _: &clock::Global,
571 cx: &mut AppContext,
572 ) -> proto::GetTypeDefinitionResponse {
573 let links = location_links_to_proto(response, project, peer_id, cx);
574 proto::GetTypeDefinitionResponse { links }
575 }
576
577 async fn response_from_proto(
578 self,
579 message: proto::GetTypeDefinitionResponse,
580 project: ModelHandle<Project>,
581 _: ModelHandle<Buffer>,
582 cx: AsyncAppContext,
583 ) -> Result<Vec<LocationLink>> {
584 location_links_from_proto(message.links, project, cx).await
585 }
586
587 fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> u64 {
588 message.buffer_id
589 }
590}
591
592fn language_server_for_buffer(
593 project: &ModelHandle<Project>,
594 buffer: &ModelHandle<Buffer>,
595 server_id: LanguageServerId,
596 cx: &mut AsyncAppContext,
597) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
598 project
599 .read_with(cx, |project, cx| {
600 project
601 .language_server_for_buffer(buffer.read(cx), server_id, cx)
602 .map(|(adapter, server)| (adapter.clone(), server.clone()))
603 })
604 .ok_or_else(|| anyhow!("no language server found for buffer"))
605}
606
607async fn location_links_from_proto(
608 proto_links: Vec<proto::LocationLink>,
609 project: ModelHandle<Project>,
610 mut cx: AsyncAppContext,
611) -> Result<Vec<LocationLink>> {
612 let mut links = Vec::new();
613
614 for link in proto_links {
615 let origin = match link.origin {
616 Some(origin) => {
617 let buffer = project
618 .update(&mut cx, |this, cx| {
619 this.wait_for_remote_buffer(origin.buffer_id, cx)
620 })
621 .await?;
622 let start = origin
623 .start
624 .and_then(deserialize_anchor)
625 .ok_or_else(|| anyhow!("missing origin start"))?;
626 let end = origin
627 .end
628 .and_then(deserialize_anchor)
629 .ok_or_else(|| anyhow!("missing origin end"))?;
630 buffer
631 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
632 .await?;
633 Some(Location {
634 buffer,
635 range: start..end,
636 })
637 }
638 None => None,
639 };
640
641 let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
642 let buffer = project
643 .update(&mut cx, |this, cx| {
644 this.wait_for_remote_buffer(target.buffer_id, cx)
645 })
646 .await?;
647 let start = target
648 .start
649 .and_then(deserialize_anchor)
650 .ok_or_else(|| anyhow!("missing target start"))?;
651 let end = target
652 .end
653 .and_then(deserialize_anchor)
654 .ok_or_else(|| anyhow!("missing target end"))?;
655 buffer
656 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
657 .await?;
658 let target = Location {
659 buffer,
660 range: start..end,
661 };
662
663 links.push(LocationLink { origin, target })
664 }
665
666 Ok(links)
667}
668
669async fn location_links_from_lsp(
670 message: Option<lsp::GotoDefinitionResponse>,
671 project: ModelHandle<Project>,
672 buffer: ModelHandle<Buffer>,
673 server_id: LanguageServerId,
674 mut cx: AsyncAppContext,
675) -> Result<Vec<LocationLink>> {
676 let message = match message {
677 Some(message) => message,
678 None => return Ok(Vec::new()),
679 };
680
681 let mut unresolved_links = Vec::new();
682 match message {
683 lsp::GotoDefinitionResponse::Scalar(loc) => {
684 unresolved_links.push((None, loc.uri, loc.range));
685 }
686
687 lsp::GotoDefinitionResponse::Array(locs) => {
688 unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
689 }
690
691 lsp::GotoDefinitionResponse::Link(links) => {
692 unresolved_links.extend(links.into_iter().map(|l| {
693 (
694 l.origin_selection_range,
695 l.target_uri,
696 l.target_selection_range,
697 )
698 }));
699 }
700 }
701
702 let (lsp_adapter, language_server) =
703 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
704 let mut definitions = Vec::new();
705 for (origin_range, target_uri, target_range) in unresolved_links {
706 let target_buffer_handle = project
707 .update(&mut cx, |this, cx| {
708 this.open_local_buffer_via_lsp(
709 target_uri,
710 language_server.server_id(),
711 lsp_adapter.name.clone(),
712 cx,
713 )
714 })
715 .await?;
716
717 cx.read(|cx| {
718 let origin_location = origin_range.map(|origin_range| {
719 let origin_buffer = buffer.read(cx);
720 let origin_start =
721 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
722 let origin_end =
723 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
724 Location {
725 buffer: buffer.clone(),
726 range: origin_buffer.anchor_after(origin_start)
727 ..origin_buffer.anchor_before(origin_end),
728 }
729 });
730
731 let target_buffer = target_buffer_handle.read(cx);
732 let target_start =
733 target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
734 let target_end =
735 target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
736 let target_location = Location {
737 buffer: target_buffer_handle,
738 range: target_buffer.anchor_after(target_start)
739 ..target_buffer.anchor_before(target_end),
740 };
741
742 definitions.push(LocationLink {
743 origin: origin_location,
744 target: target_location,
745 })
746 });
747 }
748 Ok(definitions)
749}
750
751fn location_links_to_proto(
752 links: Vec<LocationLink>,
753 project: &mut Project,
754 peer_id: PeerId,
755 cx: &mut AppContext,
756) -> Vec<proto::LocationLink> {
757 links
758 .into_iter()
759 .map(|definition| {
760 let origin = definition.origin.map(|origin| {
761 let buffer_id = project.create_buffer_for_peer(&origin.buffer, peer_id, cx);
762 proto::Location {
763 start: Some(serialize_anchor(&origin.range.start)),
764 end: Some(serialize_anchor(&origin.range.end)),
765 buffer_id,
766 }
767 });
768
769 let buffer_id = project.create_buffer_for_peer(&definition.target.buffer, peer_id, cx);
770 let target = proto::Location {
771 start: Some(serialize_anchor(&definition.target.range.start)),
772 end: Some(serialize_anchor(&definition.target.range.end)),
773 buffer_id,
774 };
775
776 proto::LocationLink {
777 origin,
778 target: Some(target),
779 }
780 })
781 .collect()
782}
783
784#[async_trait(?Send)]
785impl LspCommand for GetReferences {
786 type Response = Vec<Location>;
787 type LspRequest = lsp::request::References;
788 type ProtoRequest = proto::GetReferences;
789
790 fn to_lsp(
791 &self,
792 path: &Path,
793 _: &Buffer,
794 _: &Arc<LanguageServer>,
795 _: &AppContext,
796 ) -> lsp::ReferenceParams {
797 lsp::ReferenceParams {
798 text_document_position: lsp::TextDocumentPositionParams {
799 text_document: lsp::TextDocumentIdentifier {
800 uri: lsp::Url::from_file_path(path).unwrap(),
801 },
802 position: point_to_lsp(self.position),
803 },
804 work_done_progress_params: Default::default(),
805 partial_result_params: Default::default(),
806 context: lsp::ReferenceContext {
807 include_declaration: true,
808 },
809 }
810 }
811
812 async fn response_from_lsp(
813 self,
814 locations: Option<Vec<lsp::Location>>,
815 project: ModelHandle<Project>,
816 buffer: ModelHandle<Buffer>,
817 server_id: LanguageServerId,
818 mut cx: AsyncAppContext,
819 ) -> Result<Vec<Location>> {
820 let mut references = Vec::new();
821 let (lsp_adapter, language_server) =
822 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
823
824 if let Some(locations) = locations {
825 for lsp_location in locations {
826 let target_buffer_handle = project
827 .update(&mut cx, |this, cx| {
828 this.open_local_buffer_via_lsp(
829 lsp_location.uri,
830 language_server.server_id(),
831 lsp_adapter.name.clone(),
832 cx,
833 )
834 })
835 .await?;
836
837 cx.read(|cx| {
838 let target_buffer = target_buffer_handle.read(cx);
839 let target_start = target_buffer
840 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
841 let target_end = target_buffer
842 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
843 references.push(Location {
844 buffer: target_buffer_handle,
845 range: target_buffer.anchor_after(target_start)
846 ..target_buffer.anchor_before(target_end),
847 });
848 });
849 }
850 }
851
852 Ok(references)
853 }
854
855 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
856 proto::GetReferences {
857 project_id,
858 buffer_id: buffer.remote_id(),
859 position: Some(language::proto::serialize_anchor(
860 &buffer.anchor_before(self.position),
861 )),
862 version: serialize_version(&buffer.version()),
863 }
864 }
865
866 async fn from_proto(
867 message: proto::GetReferences,
868 _: ModelHandle<Project>,
869 buffer: ModelHandle<Buffer>,
870 mut cx: AsyncAppContext,
871 ) -> Result<Self> {
872 let position = message
873 .position
874 .and_then(deserialize_anchor)
875 .ok_or_else(|| anyhow!("invalid position"))?;
876 buffer
877 .update(&mut cx, |buffer, _| {
878 buffer.wait_for_version(deserialize_version(&message.version))
879 })
880 .await?;
881 Ok(Self {
882 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
883 })
884 }
885
886 fn response_to_proto(
887 response: Vec<Location>,
888 project: &mut Project,
889 peer_id: PeerId,
890 _: &clock::Global,
891 cx: &mut AppContext,
892 ) -> proto::GetReferencesResponse {
893 let locations = response
894 .into_iter()
895 .map(|definition| {
896 let buffer_id = project.create_buffer_for_peer(&definition.buffer, peer_id, cx);
897 proto::Location {
898 start: Some(serialize_anchor(&definition.range.start)),
899 end: Some(serialize_anchor(&definition.range.end)),
900 buffer_id,
901 }
902 })
903 .collect();
904 proto::GetReferencesResponse { locations }
905 }
906
907 async fn response_from_proto(
908 self,
909 message: proto::GetReferencesResponse,
910 project: ModelHandle<Project>,
911 _: ModelHandle<Buffer>,
912 mut cx: AsyncAppContext,
913 ) -> Result<Vec<Location>> {
914 let mut locations = Vec::new();
915 for location in message.locations {
916 let target_buffer = project
917 .update(&mut cx, |this, cx| {
918 this.wait_for_remote_buffer(location.buffer_id, cx)
919 })
920 .await?;
921 let start = location
922 .start
923 .and_then(deserialize_anchor)
924 .ok_or_else(|| anyhow!("missing target start"))?;
925 let end = location
926 .end
927 .and_then(deserialize_anchor)
928 .ok_or_else(|| anyhow!("missing target end"))?;
929 target_buffer
930 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
931 .await?;
932 locations.push(Location {
933 buffer: target_buffer,
934 range: start..end,
935 })
936 }
937 Ok(locations)
938 }
939
940 fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
941 message.buffer_id
942 }
943}
944
945#[async_trait(?Send)]
946impl LspCommand for GetDocumentHighlights {
947 type Response = Vec<DocumentHighlight>;
948 type LspRequest = lsp::request::DocumentHighlightRequest;
949 type ProtoRequest = proto::GetDocumentHighlights;
950
951 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
952 capabilities.document_highlight_provider.is_some()
953 }
954
955 fn to_lsp(
956 &self,
957 path: &Path,
958 _: &Buffer,
959 _: &Arc<LanguageServer>,
960 _: &AppContext,
961 ) -> lsp::DocumentHighlightParams {
962 lsp::DocumentHighlightParams {
963 text_document_position_params: lsp::TextDocumentPositionParams {
964 text_document: lsp::TextDocumentIdentifier {
965 uri: lsp::Url::from_file_path(path).unwrap(),
966 },
967 position: point_to_lsp(self.position),
968 },
969 work_done_progress_params: Default::default(),
970 partial_result_params: Default::default(),
971 }
972 }
973
974 async fn response_from_lsp(
975 self,
976 lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
977 _: ModelHandle<Project>,
978 buffer: ModelHandle<Buffer>,
979 _: LanguageServerId,
980 cx: AsyncAppContext,
981 ) -> Result<Vec<DocumentHighlight>> {
982 buffer.read_with(&cx, |buffer, _| {
983 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
984 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
985 Ok(lsp_highlights
986 .into_iter()
987 .map(|lsp_highlight| {
988 let start = buffer
989 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
990 let end = buffer
991 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
992 DocumentHighlight {
993 range: buffer.anchor_after(start)..buffer.anchor_before(end),
994 kind: lsp_highlight
995 .kind
996 .unwrap_or(lsp::DocumentHighlightKind::READ),
997 }
998 })
999 .collect())
1000 })
1001 }
1002
1003 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
1004 proto::GetDocumentHighlights {
1005 project_id,
1006 buffer_id: buffer.remote_id(),
1007 position: Some(language::proto::serialize_anchor(
1008 &buffer.anchor_before(self.position),
1009 )),
1010 version: serialize_version(&buffer.version()),
1011 }
1012 }
1013
1014 async fn from_proto(
1015 message: proto::GetDocumentHighlights,
1016 _: ModelHandle<Project>,
1017 buffer: ModelHandle<Buffer>,
1018 mut cx: AsyncAppContext,
1019 ) -> Result<Self> {
1020 let position = message
1021 .position
1022 .and_then(deserialize_anchor)
1023 .ok_or_else(|| anyhow!("invalid position"))?;
1024 buffer
1025 .update(&mut cx, |buffer, _| {
1026 buffer.wait_for_version(deserialize_version(&message.version))
1027 })
1028 .await?;
1029 Ok(Self {
1030 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1031 })
1032 }
1033
1034 fn response_to_proto(
1035 response: Vec<DocumentHighlight>,
1036 _: &mut Project,
1037 _: PeerId,
1038 _: &clock::Global,
1039 _: &mut AppContext,
1040 ) -> proto::GetDocumentHighlightsResponse {
1041 let highlights = response
1042 .into_iter()
1043 .map(|highlight| proto::DocumentHighlight {
1044 start: Some(serialize_anchor(&highlight.range.start)),
1045 end: Some(serialize_anchor(&highlight.range.end)),
1046 kind: match highlight.kind {
1047 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
1048 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
1049 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
1050 _ => proto::document_highlight::Kind::Text.into(),
1051 },
1052 })
1053 .collect();
1054 proto::GetDocumentHighlightsResponse { highlights }
1055 }
1056
1057 async fn response_from_proto(
1058 self,
1059 message: proto::GetDocumentHighlightsResponse,
1060 _: ModelHandle<Project>,
1061 buffer: ModelHandle<Buffer>,
1062 mut cx: AsyncAppContext,
1063 ) -> Result<Vec<DocumentHighlight>> {
1064 let mut highlights = Vec::new();
1065 for highlight in message.highlights {
1066 let start = highlight
1067 .start
1068 .and_then(deserialize_anchor)
1069 .ok_or_else(|| anyhow!("missing target start"))?;
1070 let end = highlight
1071 .end
1072 .and_then(deserialize_anchor)
1073 .ok_or_else(|| anyhow!("missing target end"))?;
1074 buffer
1075 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
1076 .await?;
1077 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
1078 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
1079 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
1080 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
1081 None => DocumentHighlightKind::TEXT,
1082 };
1083 highlights.push(DocumentHighlight {
1084 range: start..end,
1085 kind,
1086 });
1087 }
1088 Ok(highlights)
1089 }
1090
1091 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
1092 message.buffer_id
1093 }
1094}
1095
1096#[async_trait(?Send)]
1097impl LspCommand for GetHover {
1098 type Response = Option<Hover>;
1099 type LspRequest = lsp::request::HoverRequest;
1100 type ProtoRequest = proto::GetHover;
1101
1102 fn to_lsp(
1103 &self,
1104 path: &Path,
1105 _: &Buffer,
1106 _: &Arc<LanguageServer>,
1107 _: &AppContext,
1108 ) -> lsp::HoverParams {
1109 lsp::HoverParams {
1110 text_document_position_params: lsp::TextDocumentPositionParams {
1111 text_document: lsp::TextDocumentIdentifier {
1112 uri: lsp::Url::from_file_path(path).unwrap(),
1113 },
1114 position: point_to_lsp(self.position),
1115 },
1116 work_done_progress_params: Default::default(),
1117 }
1118 }
1119
1120 async fn response_from_lsp(
1121 self,
1122 message: Option<lsp::Hover>,
1123 _: ModelHandle<Project>,
1124 buffer: ModelHandle<Buffer>,
1125 _: LanguageServerId,
1126 cx: AsyncAppContext,
1127 ) -> Result<Self::Response> {
1128 Ok(message.and_then(|hover| {
1129 let (language, range) = cx.read(|cx| {
1130 let buffer = buffer.read(cx);
1131 (
1132 buffer.language().cloned(),
1133 hover.range.map(|range| {
1134 let token_start =
1135 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1136 let token_end =
1137 buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1138 buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1139 }),
1140 )
1141 });
1142
1143 fn hover_blocks_from_marked_string(
1144 marked_string: lsp::MarkedString,
1145 ) -> Option<HoverBlock> {
1146 let block = match marked_string {
1147 lsp::MarkedString::String(content) => HoverBlock {
1148 text: content,
1149 kind: HoverBlockKind::Markdown,
1150 },
1151 lsp::MarkedString::LanguageString(lsp::LanguageString { language, value }) => {
1152 HoverBlock {
1153 text: value,
1154 kind: HoverBlockKind::Code { language },
1155 }
1156 }
1157 };
1158 if block.text.is_empty() {
1159 None
1160 } else {
1161 Some(block)
1162 }
1163 }
1164
1165 let contents = cx.read(|_| match hover.contents {
1166 lsp::HoverContents::Scalar(marked_string) => {
1167 hover_blocks_from_marked_string(marked_string)
1168 .into_iter()
1169 .collect()
1170 }
1171 lsp::HoverContents::Array(marked_strings) => marked_strings
1172 .into_iter()
1173 .filter_map(hover_blocks_from_marked_string)
1174 .collect(),
1175 lsp::HoverContents::Markup(markup_content) => vec![HoverBlock {
1176 text: markup_content.value,
1177 kind: if markup_content.kind == lsp::MarkupKind::Markdown {
1178 HoverBlockKind::Markdown
1179 } else {
1180 HoverBlockKind::PlainText
1181 },
1182 }],
1183 });
1184
1185 Some(Hover {
1186 contents,
1187 range,
1188 language,
1189 })
1190 }))
1191 }
1192
1193 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1194 proto::GetHover {
1195 project_id,
1196 buffer_id: buffer.remote_id(),
1197 position: Some(language::proto::serialize_anchor(
1198 &buffer.anchor_before(self.position),
1199 )),
1200 version: serialize_version(&buffer.version),
1201 }
1202 }
1203
1204 async fn from_proto(
1205 message: Self::ProtoRequest,
1206 _: ModelHandle<Project>,
1207 buffer: ModelHandle<Buffer>,
1208 mut cx: AsyncAppContext,
1209 ) -> Result<Self> {
1210 let position = message
1211 .position
1212 .and_then(deserialize_anchor)
1213 .ok_or_else(|| anyhow!("invalid position"))?;
1214 buffer
1215 .update(&mut cx, |buffer, _| {
1216 buffer.wait_for_version(deserialize_version(&message.version))
1217 })
1218 .await?;
1219 Ok(Self {
1220 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1221 })
1222 }
1223
1224 fn response_to_proto(
1225 response: Self::Response,
1226 _: &mut Project,
1227 _: PeerId,
1228 _: &clock::Global,
1229 _: &mut AppContext,
1230 ) -> proto::GetHoverResponse {
1231 if let Some(response) = response {
1232 let (start, end) = if let Some(range) = response.range {
1233 (
1234 Some(language::proto::serialize_anchor(&range.start)),
1235 Some(language::proto::serialize_anchor(&range.end)),
1236 )
1237 } else {
1238 (None, None)
1239 };
1240
1241 let contents = response
1242 .contents
1243 .into_iter()
1244 .map(|block| proto::HoverBlock {
1245 text: block.text,
1246 is_markdown: block.kind == HoverBlockKind::Markdown,
1247 language: if let HoverBlockKind::Code { language } = block.kind {
1248 Some(language)
1249 } else {
1250 None
1251 },
1252 })
1253 .collect();
1254
1255 proto::GetHoverResponse {
1256 start,
1257 end,
1258 contents,
1259 }
1260 } else {
1261 proto::GetHoverResponse {
1262 start: None,
1263 end: None,
1264 contents: Vec::new(),
1265 }
1266 }
1267 }
1268
1269 async fn response_from_proto(
1270 self,
1271 message: proto::GetHoverResponse,
1272 _: ModelHandle<Project>,
1273 buffer: ModelHandle<Buffer>,
1274 cx: AsyncAppContext,
1275 ) -> Result<Self::Response> {
1276 let contents: Vec<_> = message
1277 .contents
1278 .into_iter()
1279 .map(|block| HoverBlock {
1280 text: block.text,
1281 kind: if let Some(language) = block.language {
1282 HoverBlockKind::Code { language }
1283 } else if block.is_markdown {
1284 HoverBlockKind::Markdown
1285 } else {
1286 HoverBlockKind::PlainText
1287 },
1288 })
1289 .collect();
1290 if contents.is_empty() {
1291 return Ok(None);
1292 }
1293
1294 let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned());
1295 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1296 language::proto::deserialize_anchor(start)
1297 .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
1298 } else {
1299 None
1300 };
1301
1302 Ok(Some(Hover {
1303 contents,
1304 range,
1305 language,
1306 }))
1307 }
1308
1309 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 {
1310 message.buffer_id
1311 }
1312}
1313
1314#[async_trait(?Send)]
1315impl LspCommand for GetCompletions {
1316 type Response = Vec<Completion>;
1317 type LspRequest = lsp::request::Completion;
1318 type ProtoRequest = proto::GetCompletions;
1319
1320 fn to_lsp(
1321 &self,
1322 path: &Path,
1323 _: &Buffer,
1324 _: &Arc<LanguageServer>,
1325 _: &AppContext,
1326 ) -> lsp::CompletionParams {
1327 lsp::CompletionParams {
1328 text_document_position: lsp::TextDocumentPositionParams::new(
1329 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1330 point_to_lsp(self.position),
1331 ),
1332 context: Default::default(),
1333 work_done_progress_params: Default::default(),
1334 partial_result_params: Default::default(),
1335 }
1336 }
1337
1338 async fn response_from_lsp(
1339 self,
1340 completions: Option<lsp::CompletionResponse>,
1341 _: ModelHandle<Project>,
1342 buffer: ModelHandle<Buffer>,
1343 _: LanguageServerId,
1344 cx: AsyncAppContext,
1345 ) -> Result<Vec<Completion>> {
1346 let completions = if let Some(completions) = completions {
1347 match completions {
1348 lsp::CompletionResponse::Array(completions) => completions,
1349 lsp::CompletionResponse::List(list) => list.items,
1350 }
1351 } else {
1352 Default::default()
1353 };
1354
1355 let completions = buffer.read_with(&cx, |buffer, _| {
1356 let language = buffer.language().cloned();
1357 let snapshot = buffer.snapshot();
1358 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1359 let mut range_for_token = None;
1360 completions
1361 .into_iter()
1362 .filter_map(move |mut lsp_completion| {
1363 let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() {
1364 // If the language server provides a range to overwrite, then
1365 // check that the range is valid.
1366 Some(lsp::CompletionTextEdit::Edit(edit)) => {
1367 let range = range_from_lsp(edit.range);
1368 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1369 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1370 if start != range.start.0 || end != range.end.0 {
1371 log::info!("completion out of expected range");
1372 return None;
1373 }
1374 (
1375 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1376 edit.new_text.clone(),
1377 )
1378 }
1379 // If the language server does not provide a range, then infer
1380 // the range based on the syntax tree.
1381 None => {
1382 if self.position != clipped_position {
1383 log::info!("completion out of expected range");
1384 return None;
1385 }
1386 let Range { start, end } = range_for_token
1387 .get_or_insert_with(|| {
1388 let offset = self.position.to_offset(&snapshot);
1389 let (range, kind) = snapshot.surrounding_word(offset);
1390 if kind == Some(CharKind::Word) {
1391 range
1392 } else {
1393 offset..offset
1394 }
1395 })
1396 .clone();
1397 let text = lsp_completion
1398 .insert_text
1399 .as_ref()
1400 .unwrap_or(&lsp_completion.label)
1401 .clone();
1402 (
1403 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1404 text,
1405 )
1406 }
1407 Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => {
1408 log::info!("unsupported insert/replace completion");
1409 return None;
1410 }
1411 };
1412
1413 let language = language.clone();
1414 LineEnding::normalize(&mut new_text);
1415 Some(async move {
1416 let mut label = None;
1417 if let Some(language) = language {
1418 language.process_completion(&mut lsp_completion).await;
1419 label = language.label_for_completion(&lsp_completion).await;
1420 }
1421 Completion {
1422 old_range,
1423 new_text,
1424 label: label.unwrap_or_else(|| {
1425 language::CodeLabel::plain(
1426 lsp_completion.label.clone(),
1427 lsp_completion.filter_text.as_deref(),
1428 )
1429 }),
1430 lsp_completion,
1431 }
1432 })
1433 })
1434 });
1435
1436 Ok(future::join_all(completions).await)
1437 }
1438
1439 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1440 let anchor = buffer.anchor_after(self.position);
1441 proto::GetCompletions {
1442 project_id,
1443 buffer_id: buffer.remote_id(),
1444 position: Some(language::proto::serialize_anchor(&anchor)),
1445 version: serialize_version(&buffer.version()),
1446 }
1447 }
1448
1449 async fn from_proto(
1450 message: proto::GetCompletions,
1451 _: ModelHandle<Project>,
1452 buffer: ModelHandle<Buffer>,
1453 mut cx: AsyncAppContext,
1454 ) -> Result<Self> {
1455 let version = deserialize_version(&message.version);
1456 buffer
1457 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))
1458 .await?;
1459 let position = message
1460 .position
1461 .and_then(language::proto::deserialize_anchor)
1462 .map(|p| {
1463 buffer.read_with(&cx, |buffer, _| {
1464 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1465 })
1466 })
1467 .ok_or_else(|| anyhow!("invalid position"))?;
1468 Ok(Self { position })
1469 }
1470
1471 fn response_to_proto(
1472 completions: Vec<Completion>,
1473 _: &mut Project,
1474 _: PeerId,
1475 buffer_version: &clock::Global,
1476 _: &mut AppContext,
1477 ) -> proto::GetCompletionsResponse {
1478 proto::GetCompletionsResponse {
1479 completions: completions
1480 .iter()
1481 .map(language::proto::serialize_completion)
1482 .collect(),
1483 version: serialize_version(&buffer_version),
1484 }
1485 }
1486
1487 async fn response_from_proto(
1488 self,
1489 message: proto::GetCompletionsResponse,
1490 _: ModelHandle<Project>,
1491 buffer: ModelHandle<Buffer>,
1492 mut cx: AsyncAppContext,
1493 ) -> Result<Vec<Completion>> {
1494 buffer
1495 .update(&mut cx, |buffer, _| {
1496 buffer.wait_for_version(deserialize_version(&message.version))
1497 })
1498 .await?;
1499
1500 let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned());
1501 let completions = message.completions.into_iter().map(|completion| {
1502 language::proto::deserialize_completion(completion, language.clone())
1503 });
1504 future::try_join_all(completions).await
1505 }
1506
1507 fn buffer_id_from_proto(message: &proto::GetCompletions) -> u64 {
1508 message.buffer_id
1509 }
1510}
1511
1512#[async_trait(?Send)]
1513impl LspCommand for GetCodeActions {
1514 type Response = Vec<CodeAction>;
1515 type LspRequest = lsp::request::CodeActionRequest;
1516 type ProtoRequest = proto::GetCodeActions;
1517
1518 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1519 match &capabilities.code_action_provider {
1520 None => false,
1521 Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
1522 _ => true,
1523 }
1524 }
1525
1526 fn to_lsp(
1527 &self,
1528 path: &Path,
1529 buffer: &Buffer,
1530 language_server: &Arc<LanguageServer>,
1531 _: &AppContext,
1532 ) -> lsp::CodeActionParams {
1533 let relevant_diagnostics = buffer
1534 .snapshot()
1535 .diagnostics_in_range::<_, usize>(self.range.clone(), false)
1536 .map(|entry| entry.to_lsp_diagnostic_stub())
1537 .collect();
1538 lsp::CodeActionParams {
1539 text_document: lsp::TextDocumentIdentifier::new(
1540 lsp::Url::from_file_path(path).unwrap(),
1541 ),
1542 range: range_to_lsp(self.range.to_point_utf16(buffer)),
1543 work_done_progress_params: Default::default(),
1544 partial_result_params: Default::default(),
1545 context: lsp::CodeActionContext {
1546 diagnostics: relevant_diagnostics,
1547 only: language_server.code_action_kinds(),
1548 ..lsp::CodeActionContext::default()
1549 },
1550 }
1551 }
1552
1553 async fn response_from_lsp(
1554 self,
1555 actions: Option<lsp::CodeActionResponse>,
1556 _: ModelHandle<Project>,
1557 _: ModelHandle<Buffer>,
1558 server_id: LanguageServerId,
1559 _: AsyncAppContext,
1560 ) -> Result<Vec<CodeAction>> {
1561 Ok(actions
1562 .unwrap_or_default()
1563 .into_iter()
1564 .filter_map(|entry| {
1565 if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1566 Some(CodeAction {
1567 server_id,
1568 range: self.range.clone(),
1569 lsp_action,
1570 })
1571 } else {
1572 None
1573 }
1574 })
1575 .collect())
1576 }
1577
1578 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
1579 proto::GetCodeActions {
1580 project_id,
1581 buffer_id: buffer.remote_id(),
1582 start: Some(language::proto::serialize_anchor(&self.range.start)),
1583 end: Some(language::proto::serialize_anchor(&self.range.end)),
1584 version: serialize_version(&buffer.version()),
1585 }
1586 }
1587
1588 async fn from_proto(
1589 message: proto::GetCodeActions,
1590 _: ModelHandle<Project>,
1591 buffer: ModelHandle<Buffer>,
1592 mut cx: AsyncAppContext,
1593 ) -> Result<Self> {
1594 let start = message
1595 .start
1596 .and_then(language::proto::deserialize_anchor)
1597 .ok_or_else(|| anyhow!("invalid start"))?;
1598 let end = message
1599 .end
1600 .and_then(language::proto::deserialize_anchor)
1601 .ok_or_else(|| anyhow!("invalid end"))?;
1602 buffer
1603 .update(&mut cx, |buffer, _| {
1604 buffer.wait_for_version(deserialize_version(&message.version))
1605 })
1606 .await?;
1607
1608 Ok(Self { range: start..end })
1609 }
1610
1611 fn response_to_proto(
1612 code_actions: Vec<CodeAction>,
1613 _: &mut Project,
1614 _: PeerId,
1615 buffer_version: &clock::Global,
1616 _: &mut AppContext,
1617 ) -> proto::GetCodeActionsResponse {
1618 proto::GetCodeActionsResponse {
1619 actions: code_actions
1620 .iter()
1621 .map(language::proto::serialize_code_action)
1622 .collect(),
1623 version: serialize_version(&buffer_version),
1624 }
1625 }
1626
1627 async fn response_from_proto(
1628 self,
1629 message: proto::GetCodeActionsResponse,
1630 _: ModelHandle<Project>,
1631 buffer: ModelHandle<Buffer>,
1632 mut cx: AsyncAppContext,
1633 ) -> Result<Vec<CodeAction>> {
1634 buffer
1635 .update(&mut cx, |buffer, _| {
1636 buffer.wait_for_version(deserialize_version(&message.version))
1637 })
1638 .await?;
1639 message
1640 .actions
1641 .into_iter()
1642 .map(language::proto::deserialize_code_action)
1643 .collect()
1644 }
1645
1646 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> u64 {
1647 message.buffer_id
1648 }
1649}
1650
1651#[async_trait(?Send)]
1652impl LspCommand for OnTypeFormatting {
1653 type Response = Option<Transaction>;
1654 type LspRequest = lsp::request::OnTypeFormatting;
1655 type ProtoRequest = proto::OnTypeFormatting;
1656
1657 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
1658 let Some(on_type_formatting_options) = &server_capabilities.document_on_type_formatting_provider else { return false };
1659 on_type_formatting_options
1660 .first_trigger_character
1661 .contains(&self.trigger)
1662 || on_type_formatting_options
1663 .more_trigger_character
1664 .iter()
1665 .flatten()
1666 .any(|chars| chars.contains(&self.trigger))
1667 }
1668
1669 fn to_lsp(
1670 &self,
1671 path: &Path,
1672 _: &Buffer,
1673 _: &Arc<LanguageServer>,
1674 _: &AppContext,
1675 ) -> lsp::DocumentOnTypeFormattingParams {
1676 lsp::DocumentOnTypeFormattingParams {
1677 text_document_position: lsp::TextDocumentPositionParams::new(
1678 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1679 point_to_lsp(self.position),
1680 ),
1681 ch: self.trigger.clone(),
1682 options: lsp_formatting_options(self.options.tab_size),
1683 }
1684 }
1685
1686 async fn response_from_lsp(
1687 self,
1688 message: Option<Vec<lsp::TextEdit>>,
1689 project: ModelHandle<Project>,
1690 buffer: ModelHandle<Buffer>,
1691 server_id: LanguageServerId,
1692 mut cx: AsyncAppContext,
1693 ) -> Result<Option<Transaction>> {
1694 if let Some(edits) = message {
1695 let (lsp_adapter, lsp_server) =
1696 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1697 Project::deserialize_edits(
1698 project,
1699 buffer,
1700 edits,
1701 self.push_to_history,
1702 lsp_adapter,
1703 lsp_server,
1704 &mut cx,
1705 )
1706 .await
1707 } else {
1708 Ok(None)
1709 }
1710 }
1711
1712 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1713 proto::OnTypeFormatting {
1714 project_id,
1715 buffer_id: buffer.remote_id(),
1716 position: Some(language::proto::serialize_anchor(
1717 &buffer.anchor_before(self.position),
1718 )),
1719 trigger: self.trigger.clone(),
1720 version: serialize_version(&buffer.version()),
1721 }
1722 }
1723
1724 async fn from_proto(
1725 message: proto::OnTypeFormatting,
1726 _: ModelHandle<Project>,
1727 buffer: ModelHandle<Buffer>,
1728 mut cx: AsyncAppContext,
1729 ) -> Result<Self> {
1730 let position = message
1731 .position
1732 .and_then(deserialize_anchor)
1733 .ok_or_else(|| anyhow!("invalid position"))?;
1734 buffer
1735 .update(&mut cx, |buffer, _| {
1736 buffer.wait_for_version(deserialize_version(&message.version))
1737 })
1738 .await?;
1739
1740 let tab_size = buffer.read_with(&cx, |buffer, cx| {
1741 language_settings(buffer.language(), buffer.file(), cx).tab_size
1742 });
1743
1744 Ok(Self {
1745 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1746 trigger: message.trigger.clone(),
1747 options: lsp_formatting_options(tab_size.get()).into(),
1748 push_to_history: false,
1749 })
1750 }
1751
1752 fn response_to_proto(
1753 response: Option<Transaction>,
1754 _: &mut Project,
1755 _: PeerId,
1756 _: &clock::Global,
1757 _: &mut AppContext,
1758 ) -> proto::OnTypeFormattingResponse {
1759 proto::OnTypeFormattingResponse {
1760 transaction: response
1761 .map(|transaction| language::proto::serialize_transaction(&transaction)),
1762 }
1763 }
1764
1765 async fn response_from_proto(
1766 self,
1767 message: proto::OnTypeFormattingResponse,
1768 _: ModelHandle<Project>,
1769 _: ModelHandle<Buffer>,
1770 _: AsyncAppContext,
1771 ) -> Result<Option<Transaction>> {
1772 let Some(transaction) = message.transaction else { return Ok(None) };
1773 Ok(Some(language::proto::deserialize_transaction(transaction)?))
1774 }
1775
1776 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 {
1777 message.buffer_id
1778 }
1779}
1780
1781impl InlayHints {
1782 pub async fn lsp_to_project_hint(
1783 lsp_hint: lsp::InlayHint,
1784 project: &ModelHandle<Project>,
1785 buffer_handle: &ModelHandle<Buffer>,
1786 server_id: LanguageServerId,
1787 resolve_state: ResolveState,
1788 force_no_type_left_padding: bool,
1789 cx: &mut AsyncAppContext,
1790 ) -> anyhow::Result<InlayHint> {
1791 let kind = lsp_hint.kind.and_then(|kind| match kind {
1792 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
1793 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
1794 _ => None,
1795 });
1796
1797 let position = cx.update(|cx| {
1798 let buffer = buffer_handle.read(cx);
1799 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
1800 if kind == Some(InlayHintKind::Parameter) {
1801 buffer.anchor_before(position)
1802 } else {
1803 buffer.anchor_after(position)
1804 }
1805 });
1806 let label = Self::lsp_inlay_label_to_project(
1807 &buffer_handle,
1808 project,
1809 server_id,
1810 lsp_hint.label,
1811 cx,
1812 )
1813 .await
1814 .context("lsp to project inlay hint conversion")?;
1815 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
1816 false
1817 } else {
1818 lsp_hint.padding_left.unwrap_or(false)
1819 };
1820
1821 Ok(InlayHint {
1822 position,
1823 padding_left,
1824 padding_right: lsp_hint.padding_right.unwrap_or(false),
1825 label,
1826 kind,
1827 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
1828 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
1829 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
1830 InlayHintTooltip::MarkupContent(MarkupContent {
1831 kind: match markup_content.kind {
1832 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
1833 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
1834 },
1835 value: markup_content.value,
1836 })
1837 }
1838 }),
1839 resolve_state,
1840 })
1841 }
1842
1843 async fn lsp_inlay_label_to_project(
1844 buffer: &ModelHandle<Buffer>,
1845 project: &ModelHandle<Project>,
1846 server_id: LanguageServerId,
1847 lsp_label: lsp::InlayHintLabel,
1848 cx: &mut AsyncAppContext,
1849 ) -> anyhow::Result<InlayHintLabel> {
1850 let label = match lsp_label {
1851 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
1852 lsp::InlayHintLabel::LabelParts(lsp_parts) => {
1853 let mut parts_data = Vec::with_capacity(lsp_parts.len());
1854 buffer.update(cx, |buffer, cx| {
1855 for lsp_part in lsp_parts {
1856 let location_buffer_task = match &lsp_part.location {
1857 Some(lsp_location) => {
1858 let location_buffer_task = project.update(cx, |project, cx| {
1859 let language_server_name = project
1860 .language_server_for_buffer(buffer, server_id, cx)
1861 .map(|(_, lsp_adapter)| {
1862 LanguageServerName(Arc::from(lsp_adapter.name()))
1863 });
1864 language_server_name.map(|language_server_name| {
1865 project.open_local_buffer_via_lsp(
1866 lsp_location.uri.clone(),
1867 server_id,
1868 language_server_name,
1869 cx,
1870 )
1871 })
1872 });
1873 Some(lsp_location.clone()).zip(location_buffer_task)
1874 }
1875 None => None,
1876 };
1877
1878 parts_data.push((lsp_part, location_buffer_task));
1879 }
1880 });
1881
1882 let mut parts = Vec::with_capacity(parts_data.len());
1883 for (lsp_part, location_buffer_task) in parts_data {
1884 let location = match location_buffer_task {
1885 Some((lsp_location, target_buffer_handle_task)) => {
1886 let target_buffer_handle = target_buffer_handle_task
1887 .await
1888 .context("resolving location for label part buffer")?;
1889 let range = cx.read(|cx| {
1890 let target_buffer = target_buffer_handle.read(cx);
1891 let target_start = target_buffer.clip_point_utf16(
1892 point_from_lsp(lsp_location.range.start),
1893 Bias::Left,
1894 );
1895 let target_end = target_buffer.clip_point_utf16(
1896 point_from_lsp(lsp_location.range.end),
1897 Bias::Left,
1898 );
1899 target_buffer.anchor_after(target_start)
1900 ..target_buffer.anchor_before(target_end)
1901 });
1902 Some(Location {
1903 buffer: target_buffer_handle,
1904 range,
1905 })
1906 }
1907 None => None,
1908 };
1909
1910 parts.push(InlayHintLabelPart {
1911 value: lsp_part.value,
1912 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
1913 lsp::InlayHintLabelPartTooltip::String(s) => {
1914 InlayHintLabelPartTooltip::String(s)
1915 }
1916 lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
1917 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
1918 kind: match markup_content.kind {
1919 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
1920 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
1921 },
1922 value: markup_content.value,
1923 })
1924 }
1925 }),
1926 location,
1927 });
1928 }
1929 InlayHintLabel::LabelParts(parts)
1930 }
1931 };
1932
1933 Ok(label)
1934 }
1935
1936 pub fn project_to_proto_hint(response_hint: InlayHint, cx: &AppContext) -> proto::InlayHint {
1937 let (state, lsp_resolve_state) = match response_hint.resolve_state {
1938 ResolveState::CanResolve(server_id, resolve_data) => (
1939 0,
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::Resolved => (1, None),
1951 ResolveState::Resolving => (2, None),
1952 };
1953 let resolve_state = Some(proto::ResolveState {
1954 state,
1955 lsp_resolve_state,
1956 });
1957 proto::InlayHint {
1958 position: Some(language::proto::serialize_anchor(&response_hint.position)),
1959 padding_left: response_hint.padding_left,
1960 padding_right: response_hint.padding_right,
1961 label: Some(proto::InlayHintLabel {
1962 label: Some(match response_hint.label {
1963 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
1964 InlayHintLabel::LabelParts(label_parts) => {
1965 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
1966 parts: label_parts.into_iter().map(|label_part| proto::InlayHintLabelPart {
1967 value: label_part.value,
1968 tooltip: label_part.tooltip.map(|tooltip| {
1969 let proto_tooltip = match tooltip {
1970 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
1971 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
1972 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1973 value: markup_content.value,
1974 }),
1975 };
1976 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
1977 }),
1978 location: label_part.location.map(|location| proto::Location {
1979 start: Some(serialize_anchor(&location.range.start)),
1980 end: Some(serialize_anchor(&location.range.end)),
1981 buffer_id: location.buffer.read(cx).remote_id(),
1982 }),
1983 }).collect()
1984 })
1985 }
1986 }),
1987 }),
1988 kind: response_hint.kind.map(|kind| kind.name().to_string()),
1989 tooltip: response_hint.tooltip.map(|response_tooltip| {
1990 let proto_tooltip = match response_tooltip {
1991 InlayHintTooltip::String(s) => {
1992 proto::inlay_hint_tooltip::Content::Value(s)
1993 }
1994 InlayHintTooltip::MarkupContent(markup_content) => {
1995 proto::inlay_hint_tooltip::Content::MarkupContent(
1996 proto::MarkupContent {
1997 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1998 value: markup_content.value,
1999 },
2000 )
2001 }
2002 };
2003 proto::InlayHintTooltip {
2004 content: Some(proto_tooltip),
2005 }
2006 }),
2007 resolve_state,
2008 }
2009 }
2010
2011 pub async fn proto_to_project_hint(
2012 message_hint: proto::InlayHint,
2013 project: &ModelHandle<Project>,
2014 cx: &mut AsyncAppContext,
2015 ) -> anyhow::Result<InlayHint> {
2016 let buffer_id = message_hint
2017 .position
2018 .as_ref()
2019 .and_then(|location| location.buffer_id)
2020 .context("missing buffer id")?;
2021 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
2022 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
2023 });
2024 let resolve_state_data = resolve_state
2025 .lsp_resolve_state.as_ref()
2026 .map(|lsp_resolve_state| {
2027 serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
2028 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
2029 .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
2030 })
2031 .transpose()?;
2032 let resolve_state = match resolve_state.state {
2033 0 => ResolveState::Resolved,
2034 1 => {
2035 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2036 format!(
2037 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2038 )
2039 })?;
2040 ResolveState::CanResolve(server_id, lsp_resolve_state)
2041 }
2042 2 => ResolveState::Resolving,
2043 invalid => {
2044 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2045 }
2046 };
2047 Ok(InlayHint {
2048 position: message_hint
2049 .position
2050 .and_then(language::proto::deserialize_anchor)
2051 .context("invalid position")?,
2052 label: match message_hint
2053 .label
2054 .and_then(|label| label.label)
2055 .context("missing label")?
2056 {
2057 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2058 proto::inlay_hint_label::Label::LabelParts(parts) => {
2059 let mut label_parts = Vec::new();
2060 for part in parts.parts {
2061 let buffer = project
2062 .update(cx, |this, cx| this.wait_for_remote_buffer(buffer_id, cx))
2063 .await?;
2064 label_parts.push(InlayHintLabelPart {
2065 value: part.value,
2066 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2067 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2068 InlayHintLabelPartTooltip::String(s)
2069 }
2070 Some(
2071 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2072 markup_content,
2073 ),
2074 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2075 kind: if markup_content.is_markdown {
2076 HoverBlockKind::Markdown
2077 } else {
2078 HoverBlockKind::PlainText
2079 },
2080 value: markup_content.value,
2081 }),
2082 None => InlayHintLabelPartTooltip::String(String::new()),
2083 }),
2084 location: match part.location {
2085 Some(location) => Some(Location {
2086 range: location
2087 .start
2088 .and_then(language::proto::deserialize_anchor)
2089 .context("invalid start")?
2090 ..location
2091 .end
2092 .and_then(language::proto::deserialize_anchor)
2093 .context("invalid end")?,
2094 buffer,
2095 }),
2096 None => None,
2097 },
2098 });
2099 }
2100
2101 InlayHintLabel::LabelParts(label_parts)
2102 }
2103 },
2104 padding_left: message_hint.padding_left,
2105 padding_right: message_hint.padding_right,
2106 kind: message_hint
2107 .kind
2108 .as_deref()
2109 .and_then(InlayHintKind::from_name),
2110 tooltip: message_hint.tooltip.and_then(|tooltip| {
2111 Some(match tooltip.content? {
2112 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2113 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2114 InlayHintTooltip::MarkupContent(MarkupContent {
2115 kind: if markup_content.is_markdown {
2116 HoverBlockKind::Markdown
2117 } else {
2118 HoverBlockKind::PlainText
2119 },
2120 value: markup_content.value,
2121 })
2122 }
2123 })
2124 }),
2125 resolve_state,
2126 })
2127 }
2128
2129 // TODO kb instead, store all LSP data inside the project::InlayHint?
2130 pub fn project_to_lsp_hint(
2131 hint: InlayHint,
2132 project: &ModelHandle<Project>,
2133 snapshot: &BufferSnapshot,
2134 cx: &AsyncAppContext,
2135 ) -> lsp::InlayHint {
2136 lsp::InlayHint {
2137 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2138 kind: hint.kind.map(|kind| match kind {
2139 InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2140 InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2141 }),
2142 text_edits: None,
2143 tooltip: hint.tooltip.and_then(|tooltip| {
2144 Some(match tooltip {
2145 InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2146 InlayHintTooltip::MarkupContent(markup_content) => {
2147 lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2148 kind: match markup_content.kind {
2149 HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2150 HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2151 HoverBlockKind::Code { .. } => return None,
2152 },
2153 value: markup_content.value,
2154 })
2155 }
2156 })
2157 }),
2158 label: match hint.label {
2159 InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2160 InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2161 label_parts
2162 .into_iter()
2163 .map(|part| lsp::InlayHintLabelPart {
2164 value: part.value,
2165 tooltip: part.tooltip.and_then(|tooltip| {
2166 Some(match tooltip {
2167 InlayHintLabelPartTooltip::String(s) => {
2168 lsp::InlayHintLabelPartTooltip::String(s)
2169 }
2170 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2171 lsp::InlayHintLabelPartTooltip::MarkupContent(
2172 lsp::MarkupContent {
2173 kind: match markup_content.kind {
2174 HoverBlockKind::PlainText => {
2175 lsp::MarkupKind::PlainText
2176 }
2177 HoverBlockKind::Markdown => {
2178 lsp::MarkupKind::Markdown
2179 }
2180 HoverBlockKind::Code { .. } => return None,
2181 },
2182 value: markup_content.value,
2183 },
2184 )
2185 }
2186 })
2187 }),
2188 location: part.location.and_then(|location| {
2189 let (path, location_snapshot) = cx.read(|cx| {
2190 let buffer = location.buffer.read(cx);
2191 let project_path = buffer.project_path(cx)?;
2192 let location_snapshot = buffer.snapshot();
2193 let path = project.read(cx).absolute_path(&project_path, cx);
2194 path.zip(Some(location_snapshot))
2195 })?;
2196 Some(lsp::Location::new(
2197 lsp::Url::from_file_path(path).unwrap(),
2198 range_to_lsp(
2199 location.range.start.to_point_utf16(&location_snapshot)
2200 ..location.range.end.to_point_utf16(&location_snapshot),
2201 ),
2202 ))
2203 }),
2204 command: None,
2205 })
2206 .collect(),
2207 ),
2208 },
2209 padding_left: Some(hint.padding_left),
2210 padding_right: Some(hint.padding_right),
2211 data: match hint.resolve_state {
2212 ResolveState::CanResolve(_, data) => data,
2213 ResolveState::Resolving | ResolveState::Resolved => None,
2214 },
2215 }
2216 }
2217}
2218
2219#[async_trait(?Send)]
2220impl LspCommand for InlayHints {
2221 type Response = Vec<InlayHint>;
2222 type LspRequest = lsp::InlayHintRequest;
2223 type ProtoRequest = proto::InlayHints;
2224
2225 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
2226 let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else { return false };
2227 match inlay_hint_provider {
2228 lsp::OneOf::Left(enabled) => *enabled,
2229 lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2230 lsp::InlayHintServerCapabilities::Options(_) => true,
2231 lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2232 },
2233 }
2234 }
2235
2236 fn to_lsp(
2237 &self,
2238 path: &Path,
2239 buffer: &Buffer,
2240 _: &Arc<LanguageServer>,
2241 _: &AppContext,
2242 ) -> lsp::InlayHintParams {
2243 lsp::InlayHintParams {
2244 text_document: lsp::TextDocumentIdentifier {
2245 uri: lsp::Url::from_file_path(path).unwrap(),
2246 },
2247 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2248 work_done_progress_params: Default::default(),
2249 }
2250 }
2251
2252 async fn response_from_lsp(
2253 self,
2254 message: Option<Vec<lsp::InlayHint>>,
2255 project: ModelHandle<Project>,
2256 buffer: ModelHandle<Buffer>,
2257 server_id: LanguageServerId,
2258 mut cx: AsyncAppContext,
2259 ) -> anyhow::Result<Vec<InlayHint>> {
2260 let (lsp_adapter, lsp_server) =
2261 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2262 // `typescript-language-server` adds padding to the left for type hints, turning
2263 // `const foo: boolean` into `const foo : boolean` which looks odd.
2264 // `rust-analyzer` does not have the padding for this case, and we have to accomodate both.
2265 //
2266 // We could trim the whole string, but being pessimistic on par with the situation above,
2267 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2268 // Hence let's use a heuristic first to handle the most awkward case and look for more.
2269 let force_no_type_left_padding =
2270 lsp_adapter.name.0.as_ref() == "typescript-language-server";
2271
2272 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2273 let resolve_state = match lsp_server.capabilities().inlay_hint_provider {
2274 Some(lsp::OneOf::Right(lsp::InlayHintServerCapabilities::Options(
2275 lsp::InlayHintOptions {
2276 resolve_provider: Some(true),
2277 ..
2278 },
2279 ))) => ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone()),
2280 _ => ResolveState::Resolved,
2281 };
2282
2283 let project = project.clone();
2284 let buffer = buffer.clone();
2285 cx.spawn(|mut cx| async move {
2286 InlayHints::lsp_to_project_hint(
2287 lsp_hint,
2288 &project,
2289 &buffer,
2290 server_id,
2291 resolve_state,
2292 force_no_type_left_padding,
2293 &mut cx,
2294 )
2295 .await
2296 })
2297 });
2298 future::join_all(hints)
2299 .await
2300 .into_iter()
2301 .collect::<anyhow::Result<_>>()
2302 .context("lsp to project inlay hints conversion")
2303 }
2304
2305 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2306 proto::InlayHints {
2307 project_id,
2308 buffer_id: buffer.remote_id(),
2309 start: Some(language::proto::serialize_anchor(&self.range.start)),
2310 end: Some(language::proto::serialize_anchor(&self.range.end)),
2311 version: serialize_version(&buffer.version()),
2312 }
2313 }
2314
2315 async fn from_proto(
2316 message: proto::InlayHints,
2317 _: ModelHandle<Project>,
2318 buffer: ModelHandle<Buffer>,
2319 mut cx: AsyncAppContext,
2320 ) -> Result<Self> {
2321 let start = message
2322 .start
2323 .and_then(language::proto::deserialize_anchor)
2324 .context("invalid start")?;
2325 let end = message
2326 .end
2327 .and_then(language::proto::deserialize_anchor)
2328 .context("invalid end")?;
2329 buffer
2330 .update(&mut cx, |buffer, _| {
2331 buffer.wait_for_version(deserialize_version(&message.version))
2332 })
2333 .await?;
2334
2335 Ok(Self { range: start..end })
2336 }
2337
2338 fn response_to_proto(
2339 response: Vec<InlayHint>,
2340 _: &mut Project,
2341 _: PeerId,
2342 buffer_version: &clock::Global,
2343 cx: &mut AppContext,
2344 ) -> proto::InlayHintsResponse {
2345 proto::InlayHintsResponse {
2346 hints: response
2347 .into_iter()
2348 .map(|response_hint| InlayHints::project_to_proto_hint(response_hint, cx))
2349 .collect(),
2350 version: serialize_version(buffer_version),
2351 }
2352 }
2353
2354 async fn response_from_proto(
2355 self,
2356 message: proto::InlayHintsResponse,
2357 project: ModelHandle<Project>,
2358 buffer: ModelHandle<Buffer>,
2359 mut cx: AsyncAppContext,
2360 ) -> anyhow::Result<Vec<InlayHint>> {
2361 buffer
2362 .update(&mut cx, |buffer, _| {
2363 buffer.wait_for_version(deserialize_version(&message.version))
2364 })
2365 .await?;
2366
2367 let mut hints = Vec::new();
2368 for message_hint in message.hints {
2369 hints.push(InlayHints::proto_to_project_hint(message_hint, &project, &mut cx).await?);
2370 }
2371
2372 Ok(hints)
2373 }
2374
2375 fn buffer_id_from_proto(message: &proto::InlayHints) -> u64 {
2376 message.buffer_id
2377 }
2378}