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