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 check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1358 match capabilities.server_capabilities.hover_provider {
1359 Some(lsp::HoverProviderCapability::Simple(enabled)) => enabled,
1360 Some(lsp::HoverProviderCapability::Options(_)) => true,
1361 None => false,
1362 }
1363 }
1364
1365 fn to_lsp(
1366 &self,
1367 path: &Path,
1368 _: &Buffer,
1369 _: &Arc<LanguageServer>,
1370 _: &AppContext,
1371 ) -> lsp::HoverParams {
1372 lsp::HoverParams {
1373 text_document_position_params: lsp::TextDocumentPositionParams {
1374 text_document: lsp::TextDocumentIdentifier {
1375 uri: lsp::Url::from_file_path(path).unwrap(),
1376 },
1377 position: point_to_lsp(self.position),
1378 },
1379 work_done_progress_params: Default::default(),
1380 }
1381 }
1382
1383 async fn response_from_lsp(
1384 self,
1385 message: Option<lsp::Hover>,
1386 _: Model<Project>,
1387 buffer: Model<Buffer>,
1388 _: LanguageServerId,
1389 mut cx: AsyncAppContext,
1390 ) -> Result<Self::Response> {
1391 let Some(hover) = message else {
1392 return Ok(None);
1393 };
1394
1395 let (language, range) = buffer.update(&mut cx, |buffer, _| {
1396 (
1397 buffer.language().cloned(),
1398 hover.range.map(|range| {
1399 let token_start =
1400 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1401 let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1402 buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1403 }),
1404 )
1405 })?;
1406
1407 fn hover_blocks_from_marked_string(marked_string: lsp::MarkedString) -> Option<HoverBlock> {
1408 let block = match marked_string {
1409 lsp::MarkedString::String(content) => HoverBlock {
1410 text: content,
1411 kind: HoverBlockKind::Markdown,
1412 },
1413 lsp::MarkedString::LanguageString(lsp::LanguageString { language, value }) => {
1414 HoverBlock {
1415 text: value,
1416 kind: HoverBlockKind::Code { language },
1417 }
1418 }
1419 };
1420 if block.text.is_empty() {
1421 None
1422 } else {
1423 Some(block)
1424 }
1425 }
1426
1427 let contents = match hover.contents {
1428 lsp::HoverContents::Scalar(marked_string) => {
1429 hover_blocks_from_marked_string(marked_string)
1430 .into_iter()
1431 .collect()
1432 }
1433 lsp::HoverContents::Array(marked_strings) => marked_strings
1434 .into_iter()
1435 .filter_map(hover_blocks_from_marked_string)
1436 .collect(),
1437 lsp::HoverContents::Markup(markup_content) => vec![HoverBlock {
1438 text: markup_content.value,
1439 kind: if markup_content.kind == lsp::MarkupKind::Markdown {
1440 HoverBlockKind::Markdown
1441 } else {
1442 HoverBlockKind::PlainText
1443 },
1444 }],
1445 };
1446
1447 Ok(Some(Hover {
1448 contents,
1449 range,
1450 language,
1451 }))
1452 }
1453
1454 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1455 proto::GetHover {
1456 project_id,
1457 buffer_id: buffer.remote_id().into(),
1458 position: Some(language::proto::serialize_anchor(
1459 &buffer.anchor_before(self.position),
1460 )),
1461 version: serialize_version(&buffer.version),
1462 }
1463 }
1464
1465 async fn from_proto(
1466 message: Self::ProtoRequest,
1467 _: Model<Project>,
1468 buffer: Model<Buffer>,
1469 mut cx: AsyncAppContext,
1470 ) -> Result<Self> {
1471 let position = message
1472 .position
1473 .and_then(deserialize_anchor)
1474 .ok_or_else(|| anyhow!("invalid position"))?;
1475 buffer
1476 .update(&mut cx, |buffer, _| {
1477 buffer.wait_for_version(deserialize_version(&message.version))
1478 })?
1479 .await?;
1480 Ok(Self {
1481 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1482 })
1483 }
1484
1485 fn response_to_proto(
1486 response: Self::Response,
1487 _: &mut Project,
1488 _: PeerId,
1489 _: &clock::Global,
1490 _: &mut AppContext,
1491 ) -> proto::GetHoverResponse {
1492 if let Some(response) = response {
1493 let (start, end) = if let Some(range) = response.range {
1494 (
1495 Some(language::proto::serialize_anchor(&range.start)),
1496 Some(language::proto::serialize_anchor(&range.end)),
1497 )
1498 } else {
1499 (None, None)
1500 };
1501
1502 let contents = response
1503 .contents
1504 .into_iter()
1505 .map(|block| proto::HoverBlock {
1506 text: block.text,
1507 is_markdown: block.kind == HoverBlockKind::Markdown,
1508 language: if let HoverBlockKind::Code { language } = block.kind {
1509 Some(language)
1510 } else {
1511 None
1512 },
1513 })
1514 .collect();
1515
1516 proto::GetHoverResponse {
1517 start,
1518 end,
1519 contents,
1520 }
1521 } else {
1522 proto::GetHoverResponse {
1523 start: None,
1524 end: None,
1525 contents: Vec::new(),
1526 }
1527 }
1528 }
1529
1530 async fn response_from_proto(
1531 self,
1532 message: proto::GetHoverResponse,
1533 _: Model<Project>,
1534 buffer: Model<Buffer>,
1535 mut cx: AsyncAppContext,
1536 ) -> Result<Self::Response> {
1537 let contents: Vec<_> = message
1538 .contents
1539 .into_iter()
1540 .map(|block| HoverBlock {
1541 text: block.text,
1542 kind: if let Some(language) = block.language {
1543 HoverBlockKind::Code { language }
1544 } else if block.is_markdown {
1545 HoverBlockKind::Markdown
1546 } else {
1547 HoverBlockKind::PlainText
1548 },
1549 })
1550 .collect();
1551 if contents.is_empty() {
1552 return Ok(None);
1553 }
1554
1555 let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
1556 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1557 language::proto::deserialize_anchor(start)
1558 .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
1559 } else {
1560 None
1561 };
1562 if let Some(range) = range.as_ref() {
1563 buffer
1564 .update(&mut cx, |buffer, _| {
1565 buffer.wait_for_anchors([range.start, range.end])
1566 })?
1567 .await?;
1568 }
1569
1570 Ok(Some(Hover {
1571 contents,
1572 range,
1573 language,
1574 }))
1575 }
1576
1577 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
1578 BufferId::new(message.buffer_id)
1579 }
1580}
1581
1582#[async_trait(?Send)]
1583impl LspCommand for GetCompletions {
1584 type Response = Vec<CoreCompletion>;
1585 type LspRequest = lsp::request::Completion;
1586 type ProtoRequest = proto::GetCompletions;
1587
1588 fn to_lsp(
1589 &self,
1590 path: &Path,
1591 _: &Buffer,
1592 _: &Arc<LanguageServer>,
1593 _: &AppContext,
1594 ) -> lsp::CompletionParams {
1595 lsp::CompletionParams {
1596 text_document_position: lsp::TextDocumentPositionParams::new(
1597 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1598 point_to_lsp(self.position),
1599 ),
1600 context: Some(self.context.clone()),
1601 work_done_progress_params: Default::default(),
1602 partial_result_params: Default::default(),
1603 }
1604 }
1605
1606 async fn response_from_lsp(
1607 self,
1608 completions: Option<lsp::CompletionResponse>,
1609 project: Model<Project>,
1610 buffer: Model<Buffer>,
1611 server_id: LanguageServerId,
1612 mut cx: AsyncAppContext,
1613 ) -> Result<Self::Response> {
1614 let mut response_list = None;
1615 let mut completions = if let Some(completions) = completions {
1616 match completions {
1617 lsp::CompletionResponse::Array(completions) => completions,
1618
1619 lsp::CompletionResponse::List(mut list) => {
1620 let items = std::mem::take(&mut list.items);
1621 response_list = Some(list);
1622 items
1623 }
1624 }
1625 } else {
1626 Default::default()
1627 };
1628
1629 let language_server_adapter = project
1630 .update(&mut cx, |project, _cx| {
1631 project.language_server_adapter_for_id(server_id)
1632 })?
1633 .ok_or_else(|| anyhow!("no such language server"))?;
1634
1635 let item_defaults = response_list
1636 .as_ref()
1637 .and_then(|list| list.item_defaults.as_ref());
1638
1639 if let Some(item_defaults) = item_defaults {
1640 let default_data = item_defaults.data.as_ref();
1641 let default_commit_characters = item_defaults.commit_characters.as_ref();
1642 let default_insert_text_mode = item_defaults.insert_text_mode.as_ref();
1643
1644 if default_data.is_some()
1645 || default_commit_characters.is_some()
1646 || default_insert_text_mode.is_some()
1647 {
1648 for item in completions.iter_mut() {
1649 if let Some(data) = default_data {
1650 item.data = Some(data.clone())
1651 }
1652 if let Some(characters) = default_commit_characters {
1653 item.commit_characters = Some(characters.clone())
1654 }
1655 if let Some(text_mode) = default_insert_text_mode {
1656 item.insert_text_mode = Some(*text_mode)
1657 }
1658 }
1659 }
1660 }
1661
1662 let mut completion_edits = Vec::new();
1663 buffer.update(&mut cx, |buffer, _cx| {
1664 let snapshot = buffer.snapshot();
1665 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1666
1667 let mut range_for_token = None;
1668 completions.retain_mut(|lsp_completion| {
1669 let edit = match lsp_completion.text_edit.as_ref() {
1670 // If the language server provides a range to overwrite, then
1671 // check that the range is valid.
1672 Some(completion_text_edit) => {
1673 match parse_completion_text_edit(completion_text_edit, &snapshot) {
1674 Some(edit) => edit,
1675 None => return false,
1676 }
1677 }
1678
1679 // If the language server does not provide a range, then infer
1680 // the range based on the syntax tree.
1681 None => {
1682 if self.position != clipped_position {
1683 log::info!("completion out of expected range");
1684 return false;
1685 }
1686
1687 let default_edit_range = response_list
1688 .as_ref()
1689 .and_then(|list| list.item_defaults.as_ref())
1690 .and_then(|defaults| defaults.edit_range.as_ref())
1691 .and_then(|range| match range {
1692 CompletionListItemDefaultsEditRange::Range(r) => Some(r),
1693 _ => None,
1694 });
1695
1696 let range = if let Some(range) = default_edit_range {
1697 let range = range_from_lsp(*range);
1698 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1699 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1700 if start != range.start.0 || end != range.end.0 {
1701 log::info!("completion out of expected range");
1702 return false;
1703 }
1704
1705 snapshot.anchor_before(start)..snapshot.anchor_after(end)
1706 } else {
1707 range_for_token
1708 .get_or_insert_with(|| {
1709 let offset = self.position.to_offset(&snapshot);
1710 let (range, kind) = snapshot.surrounding_word(offset);
1711 let range = if kind == Some(CharKind::Word) {
1712 range
1713 } else {
1714 offset..offset
1715 };
1716
1717 snapshot.anchor_before(range.start)
1718 ..snapshot.anchor_after(range.end)
1719 })
1720 .clone()
1721 };
1722
1723 let text = lsp_completion
1724 .insert_text
1725 .as_ref()
1726 .unwrap_or(&lsp_completion.label)
1727 .clone();
1728 (range, text)
1729 }
1730 };
1731
1732 completion_edits.push(edit);
1733 true
1734 });
1735 })?;
1736
1737 language_server_adapter
1738 .process_completions(&mut completions)
1739 .await;
1740
1741 Ok(completions
1742 .into_iter()
1743 .zip(completion_edits)
1744 .map(|(lsp_completion, (old_range, mut new_text))| {
1745 LineEnding::normalize(&mut new_text);
1746 CoreCompletion {
1747 old_range,
1748 new_text,
1749 server_id,
1750 lsp_completion,
1751 }
1752 })
1753 .collect())
1754 }
1755
1756 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1757 let anchor = buffer.anchor_after(self.position);
1758 proto::GetCompletions {
1759 project_id,
1760 buffer_id: buffer.remote_id().into(),
1761 position: Some(language::proto::serialize_anchor(&anchor)),
1762 version: serialize_version(&buffer.version()),
1763 }
1764 }
1765
1766 async fn from_proto(
1767 message: proto::GetCompletions,
1768 _: Model<Project>,
1769 buffer: Model<Buffer>,
1770 mut cx: AsyncAppContext,
1771 ) -> Result<Self> {
1772 let version = deserialize_version(&message.version);
1773 buffer
1774 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))?
1775 .await?;
1776 let position = message
1777 .position
1778 .and_then(language::proto::deserialize_anchor)
1779 .map(|p| {
1780 buffer.update(&mut cx, |buffer, _| {
1781 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1782 })
1783 })
1784 .ok_or_else(|| anyhow!("invalid position"))??;
1785 Ok(Self {
1786 position,
1787 context: CompletionContext {
1788 trigger_kind: CompletionTriggerKind::INVOKED,
1789 trigger_character: None,
1790 },
1791 })
1792 }
1793
1794 fn response_to_proto(
1795 completions: Vec<CoreCompletion>,
1796 _: &mut Project,
1797 _: PeerId,
1798 buffer_version: &clock::Global,
1799 _: &mut AppContext,
1800 ) -> proto::GetCompletionsResponse {
1801 proto::GetCompletionsResponse {
1802 completions: completions
1803 .iter()
1804 .map(Project::serialize_completion)
1805 .collect(),
1806 version: serialize_version(buffer_version),
1807 }
1808 }
1809
1810 async fn response_from_proto(
1811 self,
1812 message: proto::GetCompletionsResponse,
1813 _project: Model<Project>,
1814 buffer: Model<Buffer>,
1815 mut cx: AsyncAppContext,
1816 ) -> Result<Self::Response> {
1817 buffer
1818 .update(&mut cx, |buffer, _| {
1819 buffer.wait_for_version(deserialize_version(&message.version))
1820 })?
1821 .await?;
1822
1823 message
1824 .completions
1825 .into_iter()
1826 .map(Project::deserialize_completion)
1827 .collect()
1828 }
1829
1830 fn buffer_id_from_proto(message: &proto::GetCompletions) -> Result<BufferId> {
1831 BufferId::new(message.buffer_id)
1832 }
1833}
1834
1835pub(crate) fn parse_completion_text_edit(
1836 edit: &lsp::CompletionTextEdit,
1837 snapshot: &BufferSnapshot,
1838) -> Option<(Range<Anchor>, String)> {
1839 match edit {
1840 lsp::CompletionTextEdit::Edit(edit) => {
1841 let range = range_from_lsp(edit.range);
1842 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1843 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1844 if start != range.start.0 || end != range.end.0 {
1845 log::info!("completion out of expected range");
1846 None
1847 } else {
1848 Some((
1849 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1850 edit.new_text.clone(),
1851 ))
1852 }
1853 }
1854
1855 lsp::CompletionTextEdit::InsertAndReplace(edit) => {
1856 let range = range_from_lsp(edit.insert);
1857
1858 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1859 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1860 if start != range.start.0 || end != range.end.0 {
1861 log::info!("completion out of expected range");
1862 None
1863 } else {
1864 Some((
1865 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1866 edit.new_text.clone(),
1867 ))
1868 }
1869 }
1870 }
1871}
1872
1873#[async_trait(?Send)]
1874impl LspCommand for GetCodeActions {
1875 type Response = Vec<CodeAction>;
1876 type LspRequest = lsp::request::CodeActionRequest;
1877 type ProtoRequest = proto::GetCodeActions;
1878
1879 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1880 match &capabilities.server_capabilities.code_action_provider {
1881 None => false,
1882 Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
1883 _ => {
1884 // If we do know that we want specific code actions AND we know that
1885 // the server only supports specific code actions, then we want to filter
1886 // down to the ones that are supported.
1887 if let Some((requested, supported)) = self
1888 .kinds
1889 .as_ref()
1890 .zip(Self::supported_code_action_kinds(capabilities))
1891 {
1892 let server_supported = supported.into_iter().collect::<HashSet<_>>();
1893 requested.iter().any(|kind| server_supported.contains(kind))
1894 } else {
1895 true
1896 }
1897 }
1898 }
1899 }
1900
1901 fn to_lsp(
1902 &self,
1903 path: &Path,
1904 buffer: &Buffer,
1905 language_server: &Arc<LanguageServer>,
1906 _: &AppContext,
1907 ) -> lsp::CodeActionParams {
1908 let relevant_diagnostics = buffer
1909 .snapshot()
1910 .diagnostics_in_range::<_, language::PointUtf16>(self.range.clone(), false)
1911 .map(|entry| entry.to_lsp_diagnostic_stub())
1912 .collect::<Vec<_>>();
1913
1914 let supported =
1915 Self::supported_code_action_kinds(language_server.adapter_server_capabilities());
1916
1917 let only = if let Some(requested) = &self.kinds {
1918 if let Some(supported_kinds) = supported {
1919 let server_supported = supported_kinds.into_iter().collect::<HashSet<_>>();
1920
1921 let filtered = requested
1922 .iter()
1923 .filter(|kind| server_supported.contains(kind))
1924 .cloned()
1925 .collect();
1926 Some(filtered)
1927 } else {
1928 Some(requested.clone())
1929 }
1930 } else {
1931 supported
1932 };
1933
1934 lsp::CodeActionParams {
1935 text_document: lsp::TextDocumentIdentifier::new(
1936 lsp::Url::from_file_path(path).unwrap(),
1937 ),
1938 range: range_to_lsp(self.range.to_point_utf16(buffer)),
1939 work_done_progress_params: Default::default(),
1940 partial_result_params: Default::default(),
1941 context: lsp::CodeActionContext {
1942 diagnostics: relevant_diagnostics,
1943 only,
1944 ..lsp::CodeActionContext::default()
1945 },
1946 }
1947 }
1948
1949 async fn response_from_lsp(
1950 self,
1951 actions: Option<lsp::CodeActionResponse>,
1952 _: Model<Project>,
1953 _: Model<Buffer>,
1954 server_id: LanguageServerId,
1955 _: AsyncAppContext,
1956 ) -> Result<Vec<CodeAction>> {
1957 Ok(actions
1958 .unwrap_or_default()
1959 .into_iter()
1960 .filter_map(|entry| {
1961 if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1962 Some(CodeAction {
1963 server_id,
1964 range: self.range.clone(),
1965 lsp_action,
1966 })
1967 } else {
1968 None
1969 }
1970 })
1971 .collect())
1972 }
1973
1974 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
1975 proto::GetCodeActions {
1976 project_id,
1977 buffer_id: buffer.remote_id().into(),
1978 start: Some(language::proto::serialize_anchor(&self.range.start)),
1979 end: Some(language::proto::serialize_anchor(&self.range.end)),
1980 version: serialize_version(&buffer.version()),
1981 }
1982 }
1983
1984 async fn from_proto(
1985 message: proto::GetCodeActions,
1986 _: Model<Project>,
1987 buffer: Model<Buffer>,
1988 mut cx: AsyncAppContext,
1989 ) -> Result<Self> {
1990 let start = message
1991 .start
1992 .and_then(language::proto::deserialize_anchor)
1993 .ok_or_else(|| anyhow!("invalid start"))?;
1994 let end = message
1995 .end
1996 .and_then(language::proto::deserialize_anchor)
1997 .ok_or_else(|| anyhow!("invalid end"))?;
1998 buffer
1999 .update(&mut cx, |buffer, _| {
2000 buffer.wait_for_version(deserialize_version(&message.version))
2001 })?
2002 .await?;
2003
2004 Ok(Self {
2005 range: start..end,
2006 kinds: None,
2007 })
2008 }
2009
2010 fn response_to_proto(
2011 code_actions: Vec<CodeAction>,
2012 _: &mut Project,
2013 _: PeerId,
2014 buffer_version: &clock::Global,
2015 _: &mut AppContext,
2016 ) -> proto::GetCodeActionsResponse {
2017 proto::GetCodeActionsResponse {
2018 actions: code_actions
2019 .iter()
2020 .map(Project::serialize_code_action)
2021 .collect(),
2022 version: serialize_version(buffer_version),
2023 }
2024 }
2025
2026 async fn response_from_proto(
2027 self,
2028 message: proto::GetCodeActionsResponse,
2029 _: Model<Project>,
2030 buffer: Model<Buffer>,
2031 mut cx: AsyncAppContext,
2032 ) -> Result<Vec<CodeAction>> {
2033 buffer
2034 .update(&mut cx, |buffer, _| {
2035 buffer.wait_for_version(deserialize_version(&message.version))
2036 })?
2037 .await?;
2038 message
2039 .actions
2040 .into_iter()
2041 .map(Project::deserialize_code_action)
2042 .collect()
2043 }
2044
2045 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> Result<BufferId> {
2046 BufferId::new(message.buffer_id)
2047 }
2048}
2049
2050impl GetCodeActions {
2051 fn supported_code_action_kinds(
2052 capabilities: AdapterServerCapabilities,
2053 ) -> Option<Vec<CodeActionKind>> {
2054 match capabilities.server_capabilities.code_action_provider {
2055 Some(lsp::CodeActionProviderCapability::Options(CodeActionOptions {
2056 code_action_kinds: Some(supported_action_kinds),
2057 ..
2058 })) => Some(supported_action_kinds.clone()),
2059 _ => capabilities.code_action_kinds,
2060 }
2061 }
2062
2063 pub fn can_resolve_actions(capabilities: &ServerCapabilities) -> bool {
2064 capabilities
2065 .code_action_provider
2066 .as_ref()
2067 .and_then(|options| match options {
2068 lsp::CodeActionProviderCapability::Simple(_is_supported) => None,
2069 lsp::CodeActionProviderCapability::Options(options) => options.resolve_provider,
2070 })
2071 .unwrap_or(false)
2072 }
2073}
2074
2075#[async_trait(?Send)]
2076impl LspCommand for OnTypeFormatting {
2077 type Response = Option<Transaction>;
2078 type LspRequest = lsp::request::OnTypeFormatting;
2079 type ProtoRequest = proto::OnTypeFormatting;
2080
2081 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2082 let Some(on_type_formatting_options) = &capabilities
2083 .server_capabilities
2084 .document_on_type_formatting_provider
2085 else {
2086 return false;
2087 };
2088 on_type_formatting_options
2089 .first_trigger_character
2090 .contains(&self.trigger)
2091 || on_type_formatting_options
2092 .more_trigger_character
2093 .iter()
2094 .flatten()
2095 .any(|chars| chars.contains(&self.trigger))
2096 }
2097
2098 fn to_lsp(
2099 &self,
2100 path: &Path,
2101 _: &Buffer,
2102 _: &Arc<LanguageServer>,
2103 _: &AppContext,
2104 ) -> lsp::DocumentOnTypeFormattingParams {
2105 lsp::DocumentOnTypeFormattingParams {
2106 text_document_position: lsp::TextDocumentPositionParams::new(
2107 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
2108 point_to_lsp(self.position),
2109 ),
2110 ch: self.trigger.clone(),
2111 options: self.options.clone(),
2112 }
2113 }
2114
2115 async fn response_from_lsp(
2116 self,
2117 message: Option<Vec<lsp::TextEdit>>,
2118 project: Model<Project>,
2119 buffer: Model<Buffer>,
2120 server_id: LanguageServerId,
2121 mut cx: AsyncAppContext,
2122 ) -> Result<Option<Transaction>> {
2123 if let Some(edits) = message {
2124 let (lsp_adapter, lsp_server) =
2125 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2126 Project::deserialize_edits(
2127 project,
2128 buffer,
2129 edits,
2130 self.push_to_history,
2131 lsp_adapter,
2132 lsp_server,
2133 &mut cx,
2134 )
2135 .await
2136 } else {
2137 Ok(None)
2138 }
2139 }
2140
2141 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
2142 proto::OnTypeFormatting {
2143 project_id,
2144 buffer_id: buffer.remote_id().into(),
2145 position: Some(language::proto::serialize_anchor(
2146 &buffer.anchor_before(self.position),
2147 )),
2148 trigger: self.trigger.clone(),
2149 version: serialize_version(&buffer.version()),
2150 }
2151 }
2152
2153 async fn from_proto(
2154 message: proto::OnTypeFormatting,
2155 _: Model<Project>,
2156 buffer: Model<Buffer>,
2157 mut cx: AsyncAppContext,
2158 ) -> Result<Self> {
2159 let position = message
2160 .position
2161 .and_then(deserialize_anchor)
2162 .ok_or_else(|| anyhow!("invalid position"))?;
2163 buffer
2164 .update(&mut cx, |buffer, _| {
2165 buffer.wait_for_version(deserialize_version(&message.version))
2166 })?
2167 .await?;
2168
2169 let options = buffer.update(&mut cx, |buffer, cx| {
2170 lsp_formatting_options(language_settings(buffer.language(), buffer.file(), cx))
2171 })?;
2172
2173 Ok(Self {
2174 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
2175 trigger: message.trigger.clone(),
2176 options,
2177 push_to_history: false,
2178 })
2179 }
2180
2181 fn response_to_proto(
2182 response: Option<Transaction>,
2183 _: &mut Project,
2184 _: PeerId,
2185 _: &clock::Global,
2186 _: &mut AppContext,
2187 ) -> proto::OnTypeFormattingResponse {
2188 proto::OnTypeFormattingResponse {
2189 transaction: response
2190 .map(|transaction| language::proto::serialize_transaction(&transaction)),
2191 }
2192 }
2193
2194 async fn response_from_proto(
2195 self,
2196 message: proto::OnTypeFormattingResponse,
2197 _: Model<Project>,
2198 _: Model<Buffer>,
2199 _: AsyncAppContext,
2200 ) -> Result<Option<Transaction>> {
2201 let Some(transaction) = message.transaction else {
2202 return Ok(None);
2203 };
2204 Ok(Some(language::proto::deserialize_transaction(transaction)?))
2205 }
2206
2207 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> Result<BufferId> {
2208 BufferId::new(message.buffer_id)
2209 }
2210}
2211
2212impl InlayHints {
2213 pub async fn lsp_to_project_hint(
2214 lsp_hint: lsp::InlayHint,
2215 buffer_handle: &Model<Buffer>,
2216 server_id: LanguageServerId,
2217 resolve_state: ResolveState,
2218 force_no_type_left_padding: bool,
2219 cx: &mut AsyncAppContext,
2220 ) -> anyhow::Result<InlayHint> {
2221 let kind = lsp_hint.kind.and_then(|kind| match kind {
2222 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
2223 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
2224 _ => None,
2225 });
2226
2227 let position = buffer_handle.update(cx, |buffer, _| {
2228 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
2229 if kind == Some(InlayHintKind::Parameter) {
2230 buffer.anchor_before(position)
2231 } else {
2232 buffer.anchor_after(position)
2233 }
2234 })?;
2235 let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
2236 .await
2237 .context("lsp to project inlay hint conversion")?;
2238 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
2239 false
2240 } else {
2241 lsp_hint.padding_left.unwrap_or(false)
2242 };
2243
2244 Ok(InlayHint {
2245 position,
2246 padding_left,
2247 padding_right: lsp_hint.padding_right.unwrap_or(false),
2248 label,
2249 kind,
2250 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
2251 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
2252 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
2253 InlayHintTooltip::MarkupContent(MarkupContent {
2254 kind: match markup_content.kind {
2255 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2256 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2257 },
2258 value: markup_content.value,
2259 })
2260 }
2261 }),
2262 resolve_state,
2263 })
2264 }
2265
2266 async fn lsp_inlay_label_to_project(
2267 lsp_label: lsp::InlayHintLabel,
2268 server_id: LanguageServerId,
2269 ) -> anyhow::Result<InlayHintLabel> {
2270 let label = match lsp_label {
2271 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
2272 lsp::InlayHintLabel::LabelParts(lsp_parts) => {
2273 let mut parts = Vec::with_capacity(lsp_parts.len());
2274 for lsp_part in lsp_parts {
2275 parts.push(InlayHintLabelPart {
2276 value: lsp_part.value,
2277 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
2278 lsp::InlayHintLabelPartTooltip::String(s) => {
2279 InlayHintLabelPartTooltip::String(s)
2280 }
2281 lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2282 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2283 kind: match markup_content.kind {
2284 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2285 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2286 },
2287 value: markup_content.value,
2288 })
2289 }
2290 }),
2291 location: Some(server_id).zip(lsp_part.location),
2292 });
2293 }
2294 InlayHintLabel::LabelParts(parts)
2295 }
2296 };
2297
2298 Ok(label)
2299 }
2300
2301 pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
2302 let (state, lsp_resolve_state) = match response_hint.resolve_state {
2303 ResolveState::Resolved => (0, None),
2304 ResolveState::CanResolve(server_id, resolve_data) => (
2305 1,
2306 resolve_data
2307 .map(|json_data| {
2308 serde_json::to_string(&json_data)
2309 .expect("failed to serialize resolve json data")
2310 })
2311 .map(|value| proto::resolve_state::LspResolveState {
2312 server_id: server_id.0 as u64,
2313 value,
2314 }),
2315 ),
2316 ResolveState::Resolving => (2, None),
2317 };
2318 let resolve_state = Some(proto::ResolveState {
2319 state,
2320 lsp_resolve_state,
2321 });
2322 proto::InlayHint {
2323 position: Some(language::proto::serialize_anchor(&response_hint.position)),
2324 padding_left: response_hint.padding_left,
2325 padding_right: response_hint.padding_right,
2326 label: Some(proto::InlayHintLabel {
2327 label: Some(match response_hint.label {
2328 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
2329 InlayHintLabel::LabelParts(label_parts) => {
2330 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
2331 parts: label_parts.into_iter().map(|label_part| {
2332 let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
2333 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 });
2334 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 });
2335 proto::InlayHintLabelPart {
2336 value: label_part.value,
2337 tooltip: label_part.tooltip.map(|tooltip| {
2338 let proto_tooltip = match tooltip {
2339 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
2340 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
2341 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2342 value: markup_content.value,
2343 }),
2344 };
2345 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
2346 }),
2347 location_url,
2348 location_range_start,
2349 location_range_end,
2350 language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
2351 }}).collect()
2352 })
2353 }
2354 }),
2355 }),
2356 kind: response_hint.kind.map(|kind| kind.name().to_string()),
2357 tooltip: response_hint.tooltip.map(|response_tooltip| {
2358 let proto_tooltip = match response_tooltip {
2359 InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
2360 InlayHintTooltip::MarkupContent(markup_content) => {
2361 proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
2362 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2363 value: markup_content.value,
2364 })
2365 }
2366 };
2367 proto::InlayHintTooltip {
2368 content: Some(proto_tooltip),
2369 }
2370 }),
2371 resolve_state,
2372 }
2373 }
2374
2375 pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
2376 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
2377 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
2378 });
2379 let resolve_state_data = resolve_state
2380 .lsp_resolve_state.as_ref()
2381 .map(|lsp_resolve_state| {
2382 serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
2383 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
2384 .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
2385 })
2386 .transpose()?;
2387 let resolve_state = match resolve_state.state {
2388 0 => ResolveState::Resolved,
2389 1 => {
2390 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2391 format!(
2392 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2393 )
2394 })?;
2395 ResolveState::CanResolve(server_id, lsp_resolve_state)
2396 }
2397 2 => ResolveState::Resolving,
2398 invalid => {
2399 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2400 }
2401 };
2402 Ok(InlayHint {
2403 position: message_hint
2404 .position
2405 .and_then(language::proto::deserialize_anchor)
2406 .context("invalid position")?,
2407 label: match message_hint
2408 .label
2409 .and_then(|label| label.label)
2410 .context("missing label")?
2411 {
2412 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2413 proto::inlay_hint_label::Label::LabelParts(parts) => {
2414 let mut label_parts = Vec::new();
2415 for part in parts.parts {
2416 label_parts.push(InlayHintLabelPart {
2417 value: part.value,
2418 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2419 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2420 InlayHintLabelPartTooltip::String(s)
2421 }
2422 Some(
2423 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2424 markup_content,
2425 ),
2426 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2427 kind: if markup_content.is_markdown {
2428 HoverBlockKind::Markdown
2429 } else {
2430 HoverBlockKind::PlainText
2431 },
2432 value: markup_content.value,
2433 }),
2434 None => InlayHintLabelPartTooltip::String(String::new()),
2435 }),
2436 location: {
2437 match part
2438 .location_url
2439 .zip(
2440 part.location_range_start.and_then(|start| {
2441 Some(start..part.location_range_end?)
2442 }),
2443 )
2444 .zip(part.language_server_id)
2445 {
2446 Some(((uri, range), server_id)) => Some((
2447 LanguageServerId(server_id as usize),
2448 lsp::Location {
2449 uri: lsp::Url::parse(&uri)
2450 .context("invalid uri in hint part {part:?}")?,
2451 range: lsp::Range::new(
2452 point_to_lsp(PointUtf16::new(
2453 range.start.row,
2454 range.start.column,
2455 )),
2456 point_to_lsp(PointUtf16::new(
2457 range.end.row,
2458 range.end.column,
2459 )),
2460 ),
2461 },
2462 )),
2463 None => None,
2464 }
2465 },
2466 });
2467 }
2468
2469 InlayHintLabel::LabelParts(label_parts)
2470 }
2471 },
2472 padding_left: message_hint.padding_left,
2473 padding_right: message_hint.padding_right,
2474 kind: message_hint
2475 .kind
2476 .as_deref()
2477 .and_then(InlayHintKind::from_name),
2478 tooltip: message_hint.tooltip.and_then(|tooltip| {
2479 Some(match tooltip.content? {
2480 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2481 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2482 InlayHintTooltip::MarkupContent(MarkupContent {
2483 kind: if markup_content.is_markdown {
2484 HoverBlockKind::Markdown
2485 } else {
2486 HoverBlockKind::PlainText
2487 },
2488 value: markup_content.value,
2489 })
2490 }
2491 })
2492 }),
2493 resolve_state,
2494 })
2495 }
2496
2497 pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
2498 lsp::InlayHint {
2499 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2500 kind: hint.kind.map(|kind| match kind {
2501 InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2502 InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2503 }),
2504 text_edits: None,
2505 tooltip: hint.tooltip.and_then(|tooltip| {
2506 Some(match tooltip {
2507 InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2508 InlayHintTooltip::MarkupContent(markup_content) => {
2509 lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2510 kind: match markup_content.kind {
2511 HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2512 HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2513 HoverBlockKind::Code { .. } => return None,
2514 },
2515 value: markup_content.value,
2516 })
2517 }
2518 })
2519 }),
2520 label: match hint.label {
2521 InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2522 InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2523 label_parts
2524 .into_iter()
2525 .map(|part| lsp::InlayHintLabelPart {
2526 value: part.value,
2527 tooltip: part.tooltip.and_then(|tooltip| {
2528 Some(match tooltip {
2529 InlayHintLabelPartTooltip::String(s) => {
2530 lsp::InlayHintLabelPartTooltip::String(s)
2531 }
2532 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2533 lsp::InlayHintLabelPartTooltip::MarkupContent(
2534 lsp::MarkupContent {
2535 kind: match markup_content.kind {
2536 HoverBlockKind::PlainText => {
2537 lsp::MarkupKind::PlainText
2538 }
2539 HoverBlockKind::Markdown => {
2540 lsp::MarkupKind::Markdown
2541 }
2542 HoverBlockKind::Code { .. } => return None,
2543 },
2544 value: markup_content.value,
2545 },
2546 )
2547 }
2548 })
2549 }),
2550 location: part.location.map(|(_, location)| location),
2551 command: None,
2552 })
2553 .collect(),
2554 ),
2555 },
2556 padding_left: Some(hint.padding_left),
2557 padding_right: Some(hint.padding_right),
2558 data: match hint.resolve_state {
2559 ResolveState::CanResolve(_, data) => data,
2560 ResolveState::Resolving | ResolveState::Resolved => None,
2561 },
2562 }
2563 }
2564
2565 pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2566 capabilities
2567 .inlay_hint_provider
2568 .as_ref()
2569 .and_then(|options| match options {
2570 OneOf::Left(_is_supported) => None,
2571 OneOf::Right(capabilities) => match capabilities {
2572 lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2573 lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
2574 o.inlay_hint_options.resolve_provider
2575 }
2576 },
2577 })
2578 .unwrap_or(false)
2579 }
2580}
2581
2582#[async_trait(?Send)]
2583impl LspCommand for InlayHints {
2584 type Response = Vec<InlayHint>;
2585 type LspRequest = lsp::InlayHintRequest;
2586 type ProtoRequest = proto::InlayHints;
2587
2588 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2589 let Some(inlay_hint_provider) = &capabilities.server_capabilities.inlay_hint_provider
2590 else {
2591 return false;
2592 };
2593 match inlay_hint_provider {
2594 lsp::OneOf::Left(enabled) => *enabled,
2595 lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2596 lsp::InlayHintServerCapabilities::Options(_) => true,
2597 lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2598 },
2599 }
2600 }
2601
2602 fn to_lsp(
2603 &self,
2604 path: &Path,
2605 buffer: &Buffer,
2606 _: &Arc<LanguageServer>,
2607 _: &AppContext,
2608 ) -> lsp::InlayHintParams {
2609 lsp::InlayHintParams {
2610 text_document: lsp::TextDocumentIdentifier {
2611 uri: lsp::Url::from_file_path(path).unwrap(),
2612 },
2613 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2614 work_done_progress_params: Default::default(),
2615 }
2616 }
2617
2618 async fn response_from_lsp(
2619 self,
2620 message: Option<Vec<lsp::InlayHint>>,
2621 project: Model<Project>,
2622 buffer: Model<Buffer>,
2623 server_id: LanguageServerId,
2624 mut cx: AsyncAppContext,
2625 ) -> anyhow::Result<Vec<InlayHint>> {
2626 let (lsp_adapter, lsp_server) =
2627 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2628 // `typescript-language-server` adds padding to the left for type hints, turning
2629 // `const foo: boolean` into `const foo : boolean` which looks odd.
2630 // `rust-analyzer` does not have the padding for this case, and we have to accommodate both.
2631 //
2632 // We could trim the whole string, but being pessimistic on par with the situation above,
2633 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2634 // Hence let's use a heuristic first to handle the most awkward case and look for more.
2635 let force_no_type_left_padding =
2636 lsp_adapter.name.0.as_ref() == "typescript-language-server";
2637
2638 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2639 let resolve_state = if InlayHints::can_resolve_inlays(&lsp_server.capabilities()) {
2640 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2641 } else {
2642 ResolveState::Resolved
2643 };
2644
2645 let buffer = buffer.clone();
2646 cx.spawn(move |mut cx| async move {
2647 InlayHints::lsp_to_project_hint(
2648 lsp_hint,
2649 &buffer,
2650 server_id,
2651 resolve_state,
2652 force_no_type_left_padding,
2653 &mut cx,
2654 )
2655 .await
2656 })
2657 });
2658 future::join_all(hints)
2659 .await
2660 .into_iter()
2661 .collect::<anyhow::Result<_>>()
2662 .context("lsp to project inlay hints conversion")
2663 }
2664
2665 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2666 proto::InlayHints {
2667 project_id,
2668 buffer_id: buffer.remote_id().into(),
2669 start: Some(language::proto::serialize_anchor(&self.range.start)),
2670 end: Some(language::proto::serialize_anchor(&self.range.end)),
2671 version: serialize_version(&buffer.version()),
2672 }
2673 }
2674
2675 async fn from_proto(
2676 message: proto::InlayHints,
2677 _: Model<Project>,
2678 buffer: Model<Buffer>,
2679 mut cx: AsyncAppContext,
2680 ) -> Result<Self> {
2681 let start = message
2682 .start
2683 .and_then(language::proto::deserialize_anchor)
2684 .context("invalid start")?;
2685 let end = message
2686 .end
2687 .and_then(language::proto::deserialize_anchor)
2688 .context("invalid end")?;
2689 buffer
2690 .update(&mut cx, |buffer, _| {
2691 buffer.wait_for_version(deserialize_version(&message.version))
2692 })?
2693 .await?;
2694
2695 Ok(Self { range: start..end })
2696 }
2697
2698 fn response_to_proto(
2699 response: Vec<InlayHint>,
2700 _: &mut Project,
2701 _: PeerId,
2702 buffer_version: &clock::Global,
2703 _: &mut AppContext,
2704 ) -> proto::InlayHintsResponse {
2705 proto::InlayHintsResponse {
2706 hints: response
2707 .into_iter()
2708 .map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
2709 .collect(),
2710 version: serialize_version(buffer_version),
2711 }
2712 }
2713
2714 async fn response_from_proto(
2715 self,
2716 message: proto::InlayHintsResponse,
2717 _: Model<Project>,
2718 buffer: Model<Buffer>,
2719 mut cx: AsyncAppContext,
2720 ) -> anyhow::Result<Vec<InlayHint>> {
2721 buffer
2722 .update(&mut cx, |buffer, _| {
2723 buffer.wait_for_version(deserialize_version(&message.version))
2724 })?
2725 .await?;
2726
2727 let mut hints = Vec::new();
2728 for message_hint in message.hints {
2729 hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2730 }
2731
2732 Ok(hints)
2733 }
2734
2735 fn buffer_id_from_proto(message: &proto::InlayHints) -> Result<BufferId> {
2736 BufferId::new(message.buffer_id)
2737 }
2738}
2739
2740#[async_trait(?Send)]
2741impl LspCommand for LinkedEditingRange {
2742 type Response = Vec<Range<Anchor>>;
2743 type LspRequest = lsp::request::LinkedEditingRange;
2744 type ProtoRequest = proto::LinkedEditingRange;
2745
2746 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2747 let Some(linked_editing_options) = &capabilities
2748 .server_capabilities
2749 .linked_editing_range_provider
2750 else {
2751 return false;
2752 };
2753 if let LinkedEditingRangeServerCapabilities::Simple(false) = linked_editing_options {
2754 return false;
2755 }
2756 return true;
2757 }
2758
2759 fn to_lsp(
2760 &self,
2761 path: &Path,
2762 buffer: &Buffer,
2763 _server: &Arc<LanguageServer>,
2764 _: &AppContext,
2765 ) -> lsp::LinkedEditingRangeParams {
2766 let position = self.position.to_point_utf16(&buffer.snapshot());
2767 lsp::LinkedEditingRangeParams {
2768 text_document_position_params: lsp::TextDocumentPositionParams::new(
2769 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
2770 point_to_lsp(position),
2771 ),
2772 work_done_progress_params: Default::default(),
2773 }
2774 }
2775
2776 async fn response_from_lsp(
2777 self,
2778 message: Option<lsp::LinkedEditingRanges>,
2779 _project: Model<Project>,
2780 buffer: Model<Buffer>,
2781 _server_id: LanguageServerId,
2782 cx: AsyncAppContext,
2783 ) -> Result<Vec<Range<Anchor>>> {
2784 if let Some(lsp::LinkedEditingRanges { mut ranges, .. }) = message {
2785 ranges.sort_by_key(|range| range.start);
2786 let ranges = buffer.read_with(&cx, |buffer, _| {
2787 ranges
2788 .into_iter()
2789 .map(|range| {
2790 let start =
2791 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
2792 let end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
2793 buffer.anchor_before(start)..buffer.anchor_after(end)
2794 })
2795 .collect()
2796 });
2797
2798 ranges
2799 } else {
2800 Ok(vec![])
2801 }
2802 }
2803
2804 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LinkedEditingRange {
2805 proto::LinkedEditingRange {
2806 project_id,
2807 buffer_id: buffer.remote_id().to_proto(),
2808 position: Some(serialize_anchor(&self.position)),
2809 version: serialize_version(&buffer.version()),
2810 }
2811 }
2812
2813 async fn from_proto(
2814 message: proto::LinkedEditingRange,
2815 _project: Model<Project>,
2816 buffer: Model<Buffer>,
2817 mut cx: AsyncAppContext,
2818 ) -> Result<Self> {
2819 let position = message
2820 .position
2821 .ok_or_else(|| anyhow!("invalid position"))?;
2822 buffer
2823 .update(&mut cx, |buffer, _| {
2824 buffer.wait_for_version(deserialize_version(&message.version))
2825 })?
2826 .await?;
2827 let position = deserialize_anchor(position).ok_or_else(|| anyhow!("invalid position"))?;
2828 buffer
2829 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([position]))?
2830 .await?;
2831 Ok(Self { position })
2832 }
2833
2834 fn response_to_proto(
2835 response: Vec<Range<Anchor>>,
2836 _: &mut Project,
2837 _: PeerId,
2838 buffer_version: &clock::Global,
2839 _: &mut AppContext,
2840 ) -> proto::LinkedEditingRangeResponse {
2841 proto::LinkedEditingRangeResponse {
2842 items: response
2843 .into_iter()
2844 .map(|range| proto::AnchorRange {
2845 start: Some(serialize_anchor(&range.start)),
2846 end: Some(serialize_anchor(&range.end)),
2847 })
2848 .collect(),
2849 version: serialize_version(buffer_version),
2850 }
2851 }
2852
2853 async fn response_from_proto(
2854 self,
2855 message: proto::LinkedEditingRangeResponse,
2856 _: Model<Project>,
2857 buffer: Model<Buffer>,
2858 mut cx: AsyncAppContext,
2859 ) -> Result<Vec<Range<Anchor>>> {
2860 buffer
2861 .update(&mut cx, |buffer, _| {
2862 buffer.wait_for_version(deserialize_version(&message.version))
2863 })?
2864 .await?;
2865 let items: Vec<Range<Anchor>> = message
2866 .items
2867 .into_iter()
2868 .filter_map(|range| {
2869 let start = deserialize_anchor(range.start?)?;
2870 let end = deserialize_anchor(range.end?)?;
2871 Some(start..end)
2872 })
2873 .collect();
2874 for range in &items {
2875 buffer
2876 .update(&mut cx, |buffer, _| {
2877 buffer.wait_for_anchors([range.start, range.end])
2878 })?
2879 .await?;
2880 }
2881 Ok(items)
2882 }
2883
2884 fn buffer_id_from_proto(message: &proto::LinkedEditingRange) -> Result<BufferId> {
2885 BufferId::new(message.buffer_id)
2886 }
2887}