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