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, 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 project: &ModelHandle<Project>,
1791 buffer_handle: &ModelHandle<Buffer>,
1792 server_id: LanguageServerId,
1793 resolve_state: ResolveState,
1794 force_no_type_left_padding: bool,
1795 cx: &mut AsyncAppContext,
1796 ) -> anyhow::Result<InlayHint> {
1797 let kind = lsp_hint.kind.and_then(|kind| match kind {
1798 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
1799 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
1800 _ => None,
1801 });
1802
1803 let position = cx.update(|cx| {
1804 let buffer = buffer_handle.read(cx);
1805 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
1806 if kind == Some(InlayHintKind::Parameter) {
1807 buffer.anchor_before(position)
1808 } else {
1809 buffer.anchor_after(position)
1810 }
1811 });
1812 let label = Self::lsp_inlay_label_to_project(
1813 &buffer_handle,
1814 project,
1815 server_id,
1816 lsp_hint.label,
1817 cx,
1818 )
1819 .await
1820 .context("lsp to project inlay hint conversion")?;
1821 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
1822 false
1823 } else {
1824 lsp_hint.padding_left.unwrap_or(false)
1825 };
1826
1827 Ok(InlayHint {
1828 position,
1829 padding_left,
1830 padding_right: lsp_hint.padding_right.unwrap_or(false),
1831 label,
1832 kind,
1833 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
1834 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
1835 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
1836 InlayHintTooltip::MarkupContent(MarkupContent {
1837 kind: match markup_content.kind {
1838 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
1839 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
1840 },
1841 value: markup_content.value,
1842 })
1843 }
1844 }),
1845 resolve_state,
1846 })
1847 }
1848
1849 async fn lsp_inlay_label_to_project(
1850 buffer: &ModelHandle<Buffer>,
1851 project: &ModelHandle<Project>,
1852 server_id: LanguageServerId,
1853 lsp_label: lsp::InlayHintLabel,
1854 cx: &mut AsyncAppContext,
1855 ) -> anyhow::Result<InlayHintLabel> {
1856 let label = match lsp_label {
1857 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
1858 lsp::InlayHintLabel::LabelParts(lsp_parts) => {
1859 let mut parts_data = Vec::with_capacity(lsp_parts.len());
1860 buffer.update(cx, |buffer, cx| {
1861 for lsp_part in lsp_parts {
1862 let location_buffer_task = match &lsp_part.location {
1863 Some(lsp_location) => {
1864 let location_buffer_task = project.update(cx, |project, cx| {
1865 let language_server_name = project
1866 .language_server_for_buffer(buffer, server_id, cx)
1867 .map(|(_, lsp_adapter)| {
1868 LanguageServerName(Arc::from(lsp_adapter.name()))
1869 });
1870 language_server_name.map(|language_server_name| {
1871 project.open_local_buffer_via_lsp(
1872 lsp_location.uri.clone(),
1873 server_id,
1874 language_server_name,
1875 cx,
1876 )
1877 })
1878 });
1879 Some(lsp_location.clone()).zip(location_buffer_task)
1880 }
1881 None => None,
1882 };
1883
1884 parts_data.push((lsp_part, location_buffer_task));
1885 }
1886 });
1887
1888 let mut parts = Vec::with_capacity(parts_data.len());
1889 for (lsp_part, location_buffer_task) in parts_data {
1890 let location = match location_buffer_task {
1891 Some((lsp_location, target_buffer_handle_task)) => {
1892 let target_buffer_handle = target_buffer_handle_task
1893 .await
1894 .context("resolving location for label part buffer")?;
1895 let range = cx.read(|cx| {
1896 let target_buffer = target_buffer_handle.read(cx);
1897 let target_start = target_buffer.clip_point_utf16(
1898 point_from_lsp(lsp_location.range.start),
1899 Bias::Left,
1900 );
1901 let target_end = target_buffer.clip_point_utf16(
1902 point_from_lsp(lsp_location.range.end),
1903 Bias::Left,
1904 );
1905 target_buffer.anchor_after(target_start)
1906 ..target_buffer.anchor_before(target_end)
1907 });
1908 Some(Location {
1909 buffer: target_buffer_handle,
1910 range,
1911 })
1912 }
1913 None => None,
1914 };
1915
1916 parts.push(InlayHintLabelPart {
1917 value: lsp_part.value,
1918 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
1919 lsp::InlayHintLabelPartTooltip::String(s) => {
1920 InlayHintLabelPartTooltip::String(s)
1921 }
1922 lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
1923 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
1924 kind: match markup_content.kind {
1925 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
1926 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
1927 },
1928 value: markup_content.value,
1929 })
1930 }
1931 }),
1932 location,
1933 });
1934 }
1935 InlayHintLabel::LabelParts(parts)
1936 }
1937 };
1938
1939 Ok(label)
1940 }
1941
1942 pub fn project_to_proto_hint(response_hint: InlayHint, cx: &AppContext) -> proto::InlayHint {
1943 let (state, lsp_resolve_state) = match response_hint.resolve_state {
1944 ResolveState::Resolved => (0, None),
1945 ResolveState::CanResolve(server_id, resolve_data) => (
1946 1,
1947 resolve_data
1948 .map(|json_data| {
1949 serde_json::to_string(&json_data)
1950 .expect("failed to serialize resolve json data")
1951 })
1952 .map(|value| proto::resolve_state::LspResolveState {
1953 server_id: server_id.0 as u64,
1954 value,
1955 }),
1956 ),
1957 ResolveState::Resolving => (2, None),
1958 };
1959 let resolve_state = Some(proto::ResolveState {
1960 state,
1961 lsp_resolve_state,
1962 });
1963 proto::InlayHint {
1964 position: Some(language::proto::serialize_anchor(&response_hint.position)),
1965 padding_left: response_hint.padding_left,
1966 padding_right: response_hint.padding_right,
1967 label: Some(proto::InlayHintLabel {
1968 label: Some(match response_hint.label {
1969 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
1970 InlayHintLabel::LabelParts(label_parts) => {
1971 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
1972 parts: label_parts.into_iter().map(|label_part| proto::InlayHintLabelPart {
1973 value: label_part.value,
1974 tooltip: label_part.tooltip.map(|tooltip| {
1975 let proto_tooltip = match tooltip {
1976 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
1977 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
1978 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1979 value: markup_content.value,
1980 }),
1981 };
1982 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
1983 }),
1984 location: label_part.location.map(|location| proto::Location {
1985 start: Some(serialize_anchor(&location.range.start)),
1986 end: Some(serialize_anchor(&location.range.end)),
1987 buffer_id: location.buffer.read(cx).remote_id(),
1988 }),
1989 }).collect()
1990 })
1991 }
1992 }),
1993 }),
1994 kind: response_hint.kind.map(|kind| kind.name().to_string()),
1995 tooltip: response_hint.tooltip.map(|response_tooltip| {
1996 let proto_tooltip = match response_tooltip {
1997 InlayHintTooltip::String(s) => {
1998 proto::inlay_hint_tooltip::Content::Value(s)
1999 }
2000 InlayHintTooltip::MarkupContent(markup_content) => {
2001 proto::inlay_hint_tooltip::Content::MarkupContent(
2002 proto::MarkupContent {
2003 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2004 value: markup_content.value,
2005 },
2006 )
2007 }
2008 };
2009 proto::InlayHintTooltip {
2010 content: Some(proto_tooltip),
2011 }
2012 }),
2013 resolve_state,
2014 }
2015 }
2016
2017 pub async fn proto_to_project_hint(
2018 message_hint: proto::InlayHint,
2019 project: &ModelHandle<Project>,
2020 cx: &mut AsyncAppContext,
2021 ) -> anyhow::Result<InlayHint> {
2022 let buffer_id = message_hint
2023 .position
2024 .as_ref()
2025 .and_then(|location| location.buffer_id)
2026 .context("missing buffer id")?;
2027 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
2028 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
2029 });
2030 let resolve_state_data = resolve_state
2031 .lsp_resolve_state.as_ref()
2032 .map(|lsp_resolve_state| {
2033 serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
2034 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
2035 .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
2036 })
2037 .transpose()?;
2038 let resolve_state = match resolve_state.state {
2039 0 => ResolveState::Resolved,
2040 1 => {
2041 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2042 format!(
2043 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2044 )
2045 })?;
2046 ResolveState::CanResolve(server_id, lsp_resolve_state)
2047 }
2048 2 => ResolveState::Resolving,
2049 invalid => {
2050 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2051 }
2052 };
2053 Ok(InlayHint {
2054 position: message_hint
2055 .position
2056 .and_then(language::proto::deserialize_anchor)
2057 .context("invalid position")?,
2058 label: match message_hint
2059 .label
2060 .and_then(|label| label.label)
2061 .context("missing label")?
2062 {
2063 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2064 proto::inlay_hint_label::Label::LabelParts(parts) => {
2065 let mut label_parts = Vec::new();
2066 for part in parts.parts {
2067 let buffer = project
2068 .update(cx, |this, cx| this.wait_for_remote_buffer(buffer_id, cx))
2069 .await?;
2070 label_parts.push(InlayHintLabelPart {
2071 value: part.value,
2072 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2073 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2074 InlayHintLabelPartTooltip::String(s)
2075 }
2076 Some(
2077 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2078 markup_content,
2079 ),
2080 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2081 kind: if markup_content.is_markdown {
2082 HoverBlockKind::Markdown
2083 } else {
2084 HoverBlockKind::PlainText
2085 },
2086 value: markup_content.value,
2087 }),
2088 None => InlayHintLabelPartTooltip::String(String::new()),
2089 }),
2090 location: match part.location {
2091 Some(location) => Some(Location {
2092 range: location
2093 .start
2094 .and_then(language::proto::deserialize_anchor)
2095 .context("invalid start")?
2096 ..location
2097 .end
2098 .and_then(language::proto::deserialize_anchor)
2099 .context("invalid end")?,
2100 buffer,
2101 }),
2102 None => None,
2103 },
2104 });
2105 }
2106
2107 InlayHintLabel::LabelParts(label_parts)
2108 }
2109 },
2110 padding_left: message_hint.padding_left,
2111 padding_right: message_hint.padding_right,
2112 kind: message_hint
2113 .kind
2114 .as_deref()
2115 .and_then(InlayHintKind::from_name),
2116 tooltip: message_hint.tooltip.and_then(|tooltip| {
2117 Some(match tooltip.content? {
2118 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2119 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2120 InlayHintTooltip::MarkupContent(MarkupContent {
2121 kind: if markup_content.is_markdown {
2122 HoverBlockKind::Markdown
2123 } else {
2124 HoverBlockKind::PlainText
2125 },
2126 value: markup_content.value,
2127 })
2128 }
2129 })
2130 }),
2131 resolve_state,
2132 })
2133 }
2134
2135 pub fn project_to_lsp_hint(
2136 hint: InlayHint,
2137 project: &ModelHandle<Project>,
2138 snapshot: &BufferSnapshot,
2139 cx: &AsyncAppContext,
2140 ) -> lsp::InlayHint {
2141 lsp::InlayHint {
2142 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2143 kind: hint.kind.map(|kind| match kind {
2144 InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2145 InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2146 }),
2147 text_edits: None,
2148 tooltip: hint.tooltip.and_then(|tooltip| {
2149 Some(match tooltip {
2150 InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2151 InlayHintTooltip::MarkupContent(markup_content) => {
2152 lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2153 kind: match markup_content.kind {
2154 HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2155 HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2156 HoverBlockKind::Code { .. } => return None,
2157 },
2158 value: markup_content.value,
2159 })
2160 }
2161 })
2162 }),
2163 label: match hint.label {
2164 InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2165 InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2166 label_parts
2167 .into_iter()
2168 .map(|part| lsp::InlayHintLabelPart {
2169 value: part.value,
2170 tooltip: part.tooltip.and_then(|tooltip| {
2171 Some(match tooltip {
2172 InlayHintLabelPartTooltip::String(s) => {
2173 lsp::InlayHintLabelPartTooltip::String(s)
2174 }
2175 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2176 lsp::InlayHintLabelPartTooltip::MarkupContent(
2177 lsp::MarkupContent {
2178 kind: match markup_content.kind {
2179 HoverBlockKind::PlainText => {
2180 lsp::MarkupKind::PlainText
2181 }
2182 HoverBlockKind::Markdown => {
2183 lsp::MarkupKind::Markdown
2184 }
2185 HoverBlockKind::Code { .. } => return None,
2186 },
2187 value: markup_content.value,
2188 },
2189 )
2190 }
2191 })
2192 }),
2193 location: part.location.and_then(|location| {
2194 let (path, location_snapshot) = cx.read(|cx| {
2195 let buffer = location.buffer.read(cx);
2196 let project_path = buffer.project_path(cx)?;
2197 let location_snapshot = buffer.snapshot();
2198 let path = project.read(cx).absolute_path(&project_path, cx);
2199 path.zip(Some(location_snapshot))
2200 })?;
2201 Some(lsp::Location::new(
2202 lsp::Url::from_file_path(path).unwrap(),
2203 range_to_lsp(
2204 location.range.start.to_point_utf16(&location_snapshot)
2205 ..location.range.end.to_point_utf16(&location_snapshot),
2206 ),
2207 ))
2208 }),
2209 command: None,
2210 })
2211 .collect(),
2212 ),
2213 },
2214 padding_left: Some(hint.padding_left),
2215 padding_right: Some(hint.padding_right),
2216 data: match hint.resolve_state {
2217 ResolveState::CanResolve(_, data) => data,
2218 ResolveState::Resolving | ResolveState::Resolved => None,
2219 },
2220 }
2221 }
2222
2223 pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2224 capabilities
2225 .inlay_hint_provider
2226 .as_ref()
2227 .and_then(|options| match options {
2228 OneOf::Left(_is_supported) => None,
2229 OneOf::Right(capabilities) => match capabilities {
2230 lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2231 lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
2232 o.inlay_hint_options.resolve_provider
2233 }
2234 },
2235 })
2236 .unwrap_or(false)
2237 }
2238}
2239
2240#[async_trait(?Send)]
2241impl LspCommand for InlayHints {
2242 type Response = Vec<InlayHint>;
2243 type LspRequest = lsp::InlayHintRequest;
2244 type ProtoRequest = proto::InlayHints;
2245
2246 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
2247 let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else {
2248 return false;
2249 };
2250 match inlay_hint_provider {
2251 lsp::OneOf::Left(enabled) => *enabled,
2252 lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2253 lsp::InlayHintServerCapabilities::Options(_) => true,
2254 lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2255 },
2256 }
2257 }
2258
2259 fn to_lsp(
2260 &self,
2261 path: &Path,
2262 buffer: &Buffer,
2263 _: &Arc<LanguageServer>,
2264 _: &AppContext,
2265 ) -> lsp::InlayHintParams {
2266 lsp::InlayHintParams {
2267 text_document: lsp::TextDocumentIdentifier {
2268 uri: lsp::Url::from_file_path(path).unwrap(),
2269 },
2270 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2271 work_done_progress_params: Default::default(),
2272 }
2273 }
2274
2275 async fn response_from_lsp(
2276 self,
2277 message: Option<Vec<lsp::InlayHint>>,
2278 project: ModelHandle<Project>,
2279 buffer: ModelHandle<Buffer>,
2280 server_id: LanguageServerId,
2281 mut cx: AsyncAppContext,
2282 ) -> anyhow::Result<Vec<InlayHint>> {
2283 let (lsp_adapter, lsp_server) =
2284 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2285 // `typescript-language-server` adds padding to the left for type hints, turning
2286 // `const foo: boolean` into `const foo : boolean` which looks odd.
2287 // `rust-analyzer` does not have the padding for this case, and we have to accomodate both.
2288 //
2289 // We could trim the whole string, but being pessimistic on par with the situation above,
2290 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2291 // Hence let's use a heuristic first to handle the most awkward case and look for more.
2292 let force_no_type_left_padding =
2293 lsp_adapter.name.0.as_ref() == "typescript-language-server";
2294
2295 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2296 let resolve_state = if InlayHints::can_resolve_inlays(lsp_server.capabilities()) {
2297 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2298 } else {
2299 ResolveState::Resolved
2300 };
2301
2302 let project = project.clone();
2303 let buffer = buffer.clone();
2304 cx.spawn(|mut cx| async move {
2305 InlayHints::lsp_to_project_hint(
2306 lsp_hint,
2307 &project,
2308 &buffer,
2309 server_id,
2310 resolve_state,
2311 force_no_type_left_padding,
2312 &mut cx,
2313 )
2314 .await
2315 })
2316 });
2317 future::join_all(hints)
2318 .await
2319 .into_iter()
2320 .collect::<anyhow::Result<_>>()
2321 .context("lsp to project inlay hints conversion")
2322 }
2323
2324 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2325 proto::InlayHints {
2326 project_id,
2327 buffer_id: buffer.remote_id(),
2328 start: Some(language::proto::serialize_anchor(&self.range.start)),
2329 end: Some(language::proto::serialize_anchor(&self.range.end)),
2330 version: serialize_version(&buffer.version()),
2331 }
2332 }
2333
2334 async fn from_proto(
2335 message: proto::InlayHints,
2336 _: ModelHandle<Project>,
2337 buffer: ModelHandle<Buffer>,
2338 mut cx: AsyncAppContext,
2339 ) -> Result<Self> {
2340 let start = message
2341 .start
2342 .and_then(language::proto::deserialize_anchor)
2343 .context("invalid start")?;
2344 let end = message
2345 .end
2346 .and_then(language::proto::deserialize_anchor)
2347 .context("invalid end")?;
2348 buffer
2349 .update(&mut cx, |buffer, _| {
2350 buffer.wait_for_version(deserialize_version(&message.version))
2351 })
2352 .await?;
2353
2354 Ok(Self { range: start..end })
2355 }
2356
2357 fn response_to_proto(
2358 response: Vec<InlayHint>,
2359 _: &mut Project,
2360 _: PeerId,
2361 buffer_version: &clock::Global,
2362 cx: &mut AppContext,
2363 ) -> proto::InlayHintsResponse {
2364 proto::InlayHintsResponse {
2365 hints: response
2366 .into_iter()
2367 .map(|response_hint| InlayHints::project_to_proto_hint(response_hint, cx))
2368 .collect(),
2369 version: serialize_version(buffer_version),
2370 }
2371 }
2372
2373 async fn response_from_proto(
2374 self,
2375 message: proto::InlayHintsResponse,
2376 project: ModelHandle<Project>,
2377 buffer: ModelHandle<Buffer>,
2378 mut cx: AsyncAppContext,
2379 ) -> anyhow::Result<Vec<InlayHint>> {
2380 buffer
2381 .update(&mut cx, |buffer, _| {
2382 buffer.wait_for_version(deserialize_version(&message.version))
2383 })
2384 .await?;
2385
2386 let mut hints = Vec::new();
2387 for message_hint in message.hints {
2388 hints.push(InlayHints::proto_to_project_hint(message_hint, &project, &mut cx).await?);
2389 }
2390
2391 Ok(hints)
2392 }
2393
2394 fn buffer_id_from_proto(message: &proto::InlayHints) -> u64 {
2395 message.buffer_id
2396 }
2397}