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