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