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