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 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, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction,
18 Unclipped,
19};
20use lsp::{DocumentHighlightKind, LanguageServer, LanguageServerId, OneOf, 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) =
1659 &server_capabilities.document_on_type_formatting_provider
1660 else {
1661 return false;
1662 };
1663 on_type_formatting_options
1664 .first_trigger_character
1665 .contains(&self.trigger)
1666 || on_type_formatting_options
1667 .more_trigger_character
1668 .iter()
1669 .flatten()
1670 .any(|chars| chars.contains(&self.trigger))
1671 }
1672
1673 fn to_lsp(
1674 &self,
1675 path: &Path,
1676 _: &Buffer,
1677 _: &Arc<LanguageServer>,
1678 _: &AppContext,
1679 ) -> lsp::DocumentOnTypeFormattingParams {
1680 lsp::DocumentOnTypeFormattingParams {
1681 text_document_position: lsp::TextDocumentPositionParams::new(
1682 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1683 point_to_lsp(self.position),
1684 ),
1685 ch: self.trigger.clone(),
1686 options: lsp_formatting_options(self.options.tab_size),
1687 }
1688 }
1689
1690 async fn response_from_lsp(
1691 self,
1692 message: Option<Vec<lsp::TextEdit>>,
1693 project: ModelHandle<Project>,
1694 buffer: ModelHandle<Buffer>,
1695 server_id: LanguageServerId,
1696 mut cx: AsyncAppContext,
1697 ) -> Result<Option<Transaction>> {
1698 if let Some(edits) = message {
1699 let (lsp_adapter, lsp_server) =
1700 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1701 Project::deserialize_edits(
1702 project,
1703 buffer,
1704 edits,
1705 self.push_to_history,
1706 lsp_adapter,
1707 lsp_server,
1708 &mut cx,
1709 )
1710 .await
1711 } else {
1712 Ok(None)
1713 }
1714 }
1715
1716 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1717 proto::OnTypeFormatting {
1718 project_id,
1719 buffer_id: buffer.remote_id(),
1720 position: Some(language::proto::serialize_anchor(
1721 &buffer.anchor_before(self.position),
1722 )),
1723 trigger: self.trigger.clone(),
1724 version: serialize_version(&buffer.version()),
1725 }
1726 }
1727
1728 async fn from_proto(
1729 message: proto::OnTypeFormatting,
1730 _: ModelHandle<Project>,
1731 buffer: ModelHandle<Buffer>,
1732 mut cx: AsyncAppContext,
1733 ) -> Result<Self> {
1734 let position = message
1735 .position
1736 .and_then(deserialize_anchor)
1737 .ok_or_else(|| anyhow!("invalid position"))?;
1738 buffer
1739 .update(&mut cx, |buffer, _| {
1740 buffer.wait_for_version(deserialize_version(&message.version))
1741 })
1742 .await?;
1743
1744 let tab_size = buffer.read_with(&cx, |buffer, cx| {
1745 language_settings(buffer.language(), buffer.file(), cx).tab_size
1746 });
1747
1748 Ok(Self {
1749 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1750 trigger: message.trigger.clone(),
1751 options: lsp_formatting_options(tab_size.get()).into(),
1752 push_to_history: false,
1753 })
1754 }
1755
1756 fn response_to_proto(
1757 response: Option<Transaction>,
1758 _: &mut Project,
1759 _: PeerId,
1760 _: &clock::Global,
1761 _: &mut AppContext,
1762 ) -> proto::OnTypeFormattingResponse {
1763 proto::OnTypeFormattingResponse {
1764 transaction: response
1765 .map(|transaction| language::proto::serialize_transaction(&transaction)),
1766 }
1767 }
1768
1769 async fn response_from_proto(
1770 self,
1771 message: proto::OnTypeFormattingResponse,
1772 _: ModelHandle<Project>,
1773 _: ModelHandle<Buffer>,
1774 _: AsyncAppContext,
1775 ) -> Result<Option<Transaction>> {
1776 let Some(transaction) = message.transaction else {
1777 return Ok(None);
1778 };
1779 Ok(Some(language::proto::deserialize_transaction(transaction)?))
1780 }
1781
1782 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 {
1783 message.buffer_id
1784 }
1785}
1786
1787impl InlayHints {
1788 pub async fn lsp_to_project_hint(
1789 lsp_hint: lsp::InlayHint,
1790 buffer_handle: &ModelHandle<Buffer>,
1791 server_id: LanguageServerId,
1792 resolve_state: ResolveState,
1793 force_no_type_left_padding: bool,
1794 cx: &mut AsyncAppContext,
1795 ) -> anyhow::Result<InlayHint> {
1796 let kind = lsp_hint.kind.and_then(|kind| match kind {
1797 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
1798 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
1799 _ => None,
1800 });
1801
1802 let position = cx.update(|cx| {
1803 let buffer = buffer_handle.read(cx);
1804 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
1805 if kind == Some(InlayHintKind::Parameter) {
1806 buffer.anchor_before(position)
1807 } else {
1808 buffer.anchor_after(position)
1809 }
1810 });
1811 let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
1812 .await
1813 .context("lsp to project inlay hint conversion")?;
1814 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
1815 false
1816 } else {
1817 lsp_hint.padding_left.unwrap_or(false)
1818 };
1819
1820 Ok(InlayHint {
1821 position,
1822 padding_left,
1823 padding_right: lsp_hint.padding_right.unwrap_or(false),
1824 label,
1825 kind,
1826 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
1827 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
1828 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
1829 InlayHintTooltip::MarkupContent(MarkupContent {
1830 kind: match markup_content.kind {
1831 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
1832 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
1833 },
1834 value: markup_content.value,
1835 })
1836 }
1837 }),
1838 resolve_state,
1839 })
1840 }
1841
1842 async fn lsp_inlay_label_to_project(
1843 lsp_label: lsp::InlayHintLabel,
1844 server_id: LanguageServerId,
1845 ) -> anyhow::Result<InlayHintLabel> {
1846 let label = match lsp_label {
1847 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
1848 lsp::InlayHintLabel::LabelParts(lsp_parts) => {
1849 let mut parts = Vec::with_capacity(lsp_parts.len());
1850 for lsp_part in lsp_parts {
1851 parts.push(InlayHintLabelPart {
1852 value: lsp_part.value,
1853 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
1854 lsp::InlayHintLabelPartTooltip::String(s) => {
1855 InlayHintLabelPartTooltip::String(s)
1856 }
1857 lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
1858 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
1859 kind: match markup_content.kind {
1860 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
1861 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
1862 },
1863 value: markup_content.value,
1864 })
1865 }
1866 }),
1867 location: Some(server_id).zip(lsp_part.location),
1868 });
1869 }
1870 InlayHintLabel::LabelParts(parts)
1871 }
1872 };
1873
1874 Ok(label)
1875 }
1876
1877 pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
1878 let (state, lsp_resolve_state) = match response_hint.resolve_state {
1879 ResolveState::Resolved => (0, None),
1880 ResolveState::CanResolve(server_id, resolve_data) => (
1881 1,
1882 resolve_data
1883 .map(|json_data| {
1884 serde_json::to_string(&json_data)
1885 .expect("failed to serialize resolve json data")
1886 })
1887 .map(|value| proto::resolve_state::LspResolveState {
1888 server_id: server_id.0 as u64,
1889 value,
1890 }),
1891 ),
1892 ResolveState::Resolving => (2, None),
1893 };
1894 let resolve_state = Some(proto::ResolveState {
1895 state,
1896 lsp_resolve_state,
1897 });
1898 proto::InlayHint {
1899 position: Some(language::proto::serialize_anchor(&response_hint.position)),
1900 padding_left: response_hint.padding_left,
1901 padding_right: response_hint.padding_right,
1902 label: Some(proto::InlayHintLabel {
1903 label: Some(match response_hint.label {
1904 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
1905 InlayHintLabel::LabelParts(label_parts) => {
1906 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
1907 parts: label_parts.into_iter().map(|label_part| {
1908 let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
1909 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 });
1910 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 });
1911 proto::InlayHintLabelPart {
1912 value: label_part.value,
1913 tooltip: label_part.tooltip.map(|tooltip| {
1914 let proto_tooltip = match tooltip {
1915 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
1916 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
1917 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1918 value: markup_content.value,
1919 }),
1920 };
1921 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
1922 }),
1923 location_url,
1924 location_range_start,
1925 location_range_end,
1926 language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
1927 }}).collect()
1928 })
1929 }
1930 }),
1931 }),
1932 kind: response_hint.kind.map(|kind| kind.name().to_string()),
1933 tooltip: response_hint.tooltip.map(|response_tooltip| {
1934 let proto_tooltip = match response_tooltip {
1935 InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
1936 InlayHintTooltip::MarkupContent(markup_content) => {
1937 proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
1938 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1939 value: markup_content.value,
1940 })
1941 }
1942 };
1943 proto::InlayHintTooltip {
1944 content: Some(proto_tooltip),
1945 }
1946 }),
1947 resolve_state,
1948 }
1949 }
1950
1951 pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
1952 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
1953 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
1954 });
1955 let resolve_state_data = resolve_state
1956 .lsp_resolve_state.as_ref()
1957 .map(|lsp_resolve_state| {
1958 serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
1959 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
1960 .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
1961 })
1962 .transpose()?;
1963 let resolve_state = match resolve_state.state {
1964 0 => ResolveState::Resolved,
1965 1 => {
1966 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
1967 format!(
1968 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
1969 )
1970 })?;
1971 ResolveState::CanResolve(server_id, lsp_resolve_state)
1972 }
1973 2 => ResolveState::Resolving,
1974 invalid => {
1975 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
1976 }
1977 };
1978 Ok(InlayHint {
1979 position: message_hint
1980 .position
1981 .and_then(language::proto::deserialize_anchor)
1982 .context("invalid position")?,
1983 label: match message_hint
1984 .label
1985 .and_then(|label| label.label)
1986 .context("missing label")?
1987 {
1988 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
1989 proto::inlay_hint_label::Label::LabelParts(parts) => {
1990 let mut label_parts = Vec::new();
1991 for part in parts.parts {
1992 label_parts.push(InlayHintLabelPart {
1993 value: part.value,
1994 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
1995 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
1996 InlayHintLabelPartTooltip::String(s)
1997 }
1998 Some(
1999 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2000 markup_content,
2001 ),
2002 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2003 kind: if markup_content.is_markdown {
2004 HoverBlockKind::Markdown
2005 } else {
2006 HoverBlockKind::PlainText
2007 },
2008 value: markup_content.value,
2009 }),
2010 None => InlayHintLabelPartTooltip::String(String::new()),
2011 }),
2012 location: {
2013 match part
2014 .location_url
2015 .zip(
2016 part.location_range_start.and_then(|start| {
2017 Some(start..part.location_range_end?)
2018 }),
2019 )
2020 .zip(part.language_server_id)
2021 {
2022 Some(((uri, range), server_id)) => Some((
2023 LanguageServerId(server_id as usize),
2024 lsp::Location {
2025 uri: lsp::Url::parse(&uri)
2026 .context("invalid uri in hint part {part:?}")?,
2027 range: lsp::Range::new(
2028 point_to_lsp(PointUtf16::new(
2029 range.start.row,
2030 range.start.column,
2031 )),
2032 point_to_lsp(PointUtf16::new(
2033 range.end.row,
2034 range.end.column,
2035 )),
2036 ),
2037 },
2038 )),
2039 None => None,
2040 }
2041 },
2042 });
2043 }
2044
2045 InlayHintLabel::LabelParts(label_parts)
2046 }
2047 },
2048 padding_left: message_hint.padding_left,
2049 padding_right: message_hint.padding_right,
2050 kind: message_hint
2051 .kind
2052 .as_deref()
2053 .and_then(InlayHintKind::from_name),
2054 tooltip: message_hint.tooltip.and_then(|tooltip| {
2055 Some(match tooltip.content? {
2056 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2057 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2058 InlayHintTooltip::MarkupContent(MarkupContent {
2059 kind: if markup_content.is_markdown {
2060 HoverBlockKind::Markdown
2061 } else {
2062 HoverBlockKind::PlainText
2063 },
2064 value: markup_content.value,
2065 })
2066 }
2067 })
2068 }),
2069 resolve_state,
2070 })
2071 }
2072
2073 pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
2074 lsp::InlayHint {
2075 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2076 kind: hint.kind.map(|kind| match kind {
2077 InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2078 InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2079 }),
2080 text_edits: None,
2081 tooltip: hint.tooltip.and_then(|tooltip| {
2082 Some(match tooltip {
2083 InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2084 InlayHintTooltip::MarkupContent(markup_content) => {
2085 lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2086 kind: match markup_content.kind {
2087 HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2088 HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2089 HoverBlockKind::Code { .. } => return None,
2090 },
2091 value: markup_content.value,
2092 })
2093 }
2094 })
2095 }),
2096 label: match hint.label {
2097 InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2098 InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2099 label_parts
2100 .into_iter()
2101 .map(|part| lsp::InlayHintLabelPart {
2102 value: part.value,
2103 tooltip: part.tooltip.and_then(|tooltip| {
2104 Some(match tooltip {
2105 InlayHintLabelPartTooltip::String(s) => {
2106 lsp::InlayHintLabelPartTooltip::String(s)
2107 }
2108 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2109 lsp::InlayHintLabelPartTooltip::MarkupContent(
2110 lsp::MarkupContent {
2111 kind: match markup_content.kind {
2112 HoverBlockKind::PlainText => {
2113 lsp::MarkupKind::PlainText
2114 }
2115 HoverBlockKind::Markdown => {
2116 lsp::MarkupKind::Markdown
2117 }
2118 HoverBlockKind::Code { .. } => return None,
2119 },
2120 value: markup_content.value,
2121 },
2122 )
2123 }
2124 })
2125 }),
2126 location: part.location.map(|(_, location)| location),
2127 command: None,
2128 })
2129 .collect(),
2130 ),
2131 },
2132 padding_left: Some(hint.padding_left),
2133 padding_right: Some(hint.padding_right),
2134 data: match hint.resolve_state {
2135 ResolveState::CanResolve(_, data) => data,
2136 ResolveState::Resolving | ResolveState::Resolved => None,
2137 },
2138 }
2139 }
2140
2141 pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2142 capabilities
2143 .inlay_hint_provider
2144 .as_ref()
2145 .and_then(|options| match options {
2146 OneOf::Left(_is_supported) => None,
2147 OneOf::Right(capabilities) => match capabilities {
2148 lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2149 lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
2150 o.inlay_hint_options.resolve_provider
2151 }
2152 },
2153 })
2154 .unwrap_or(false)
2155 }
2156}
2157
2158#[async_trait(?Send)]
2159impl LspCommand for InlayHints {
2160 type Response = Vec<InlayHint>;
2161 type LspRequest = lsp::InlayHintRequest;
2162 type ProtoRequest = proto::InlayHints;
2163
2164 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
2165 let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else {
2166 return false;
2167 };
2168 match inlay_hint_provider {
2169 lsp::OneOf::Left(enabled) => *enabled,
2170 lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2171 lsp::InlayHintServerCapabilities::Options(_) => true,
2172 lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2173 },
2174 }
2175 }
2176
2177 fn to_lsp(
2178 &self,
2179 path: &Path,
2180 buffer: &Buffer,
2181 _: &Arc<LanguageServer>,
2182 _: &AppContext,
2183 ) -> lsp::InlayHintParams {
2184 lsp::InlayHintParams {
2185 text_document: lsp::TextDocumentIdentifier {
2186 uri: lsp::Url::from_file_path(path).unwrap(),
2187 },
2188 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2189 work_done_progress_params: Default::default(),
2190 }
2191 }
2192
2193 async fn response_from_lsp(
2194 self,
2195 message: Option<Vec<lsp::InlayHint>>,
2196 project: ModelHandle<Project>,
2197 buffer: ModelHandle<Buffer>,
2198 server_id: LanguageServerId,
2199 mut cx: AsyncAppContext,
2200 ) -> anyhow::Result<Vec<InlayHint>> {
2201 let (lsp_adapter, lsp_server) =
2202 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2203 // `typescript-language-server` adds padding to the left for type hints, turning
2204 // `const foo: boolean` into `const foo : boolean` which looks odd.
2205 // `rust-analyzer` does not have the padding for this case, and we have to accomodate both.
2206 //
2207 // We could trim the whole string, but being pessimistic on par with the situation above,
2208 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2209 // Hence let's use a heuristic first to handle the most awkward case and look for more.
2210 let force_no_type_left_padding =
2211 lsp_adapter.name.0.as_ref() == "typescript-language-server";
2212
2213 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2214 let resolve_state = if InlayHints::can_resolve_inlays(lsp_server.capabilities()) {
2215 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2216 } else {
2217 ResolveState::Resolved
2218 };
2219
2220 let buffer = buffer.clone();
2221 cx.spawn(|mut cx| async move {
2222 InlayHints::lsp_to_project_hint(
2223 lsp_hint,
2224 &buffer,
2225 server_id,
2226 resolve_state,
2227 force_no_type_left_padding,
2228 &mut cx,
2229 )
2230 .await
2231 })
2232 });
2233 future::join_all(hints)
2234 .await
2235 .into_iter()
2236 .collect::<anyhow::Result<_>>()
2237 .context("lsp to project inlay hints conversion")
2238 }
2239
2240 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2241 proto::InlayHints {
2242 project_id,
2243 buffer_id: buffer.remote_id(),
2244 start: Some(language::proto::serialize_anchor(&self.range.start)),
2245 end: Some(language::proto::serialize_anchor(&self.range.end)),
2246 version: serialize_version(&buffer.version()),
2247 }
2248 }
2249
2250 async fn from_proto(
2251 message: proto::InlayHints,
2252 _: ModelHandle<Project>,
2253 buffer: ModelHandle<Buffer>,
2254 mut cx: AsyncAppContext,
2255 ) -> Result<Self> {
2256 let start = message
2257 .start
2258 .and_then(language::proto::deserialize_anchor)
2259 .context("invalid start")?;
2260 let end = message
2261 .end
2262 .and_then(language::proto::deserialize_anchor)
2263 .context("invalid end")?;
2264 buffer
2265 .update(&mut cx, |buffer, _| {
2266 buffer.wait_for_version(deserialize_version(&message.version))
2267 })
2268 .await?;
2269
2270 Ok(Self { range: start..end })
2271 }
2272
2273 fn response_to_proto(
2274 response: Vec<InlayHint>,
2275 _: &mut Project,
2276 _: PeerId,
2277 buffer_version: &clock::Global,
2278 _: &mut AppContext,
2279 ) -> proto::InlayHintsResponse {
2280 proto::InlayHintsResponse {
2281 hints: response
2282 .into_iter()
2283 .map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
2284 .collect(),
2285 version: serialize_version(buffer_version),
2286 }
2287 }
2288
2289 async fn response_from_proto(
2290 self,
2291 message: proto::InlayHintsResponse,
2292 _: ModelHandle<Project>,
2293 buffer: ModelHandle<Buffer>,
2294 mut cx: AsyncAppContext,
2295 ) -> anyhow::Result<Vec<InlayHint>> {
2296 buffer
2297 .update(&mut cx, |buffer, _| {
2298 buffer.wait_for_version(deserialize_version(&message.version))
2299 })
2300 .await?;
2301
2302 let mut hints = Vec::new();
2303 for message_hint in message.hints {
2304 hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2305 }
2306
2307 Ok(hints)
2308 }
2309
2310 fn buffer_id_from_proto(message: &proto::InlayHints) -> u64 {
2311 message.buffer_id
2312 }
2313}