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