1use crate::{DocumentHighlight, Location, Project, ProjectTransaction};
2use anyhow::{anyhow, Result};
3use async_trait::async_trait;
4use client::{proto, PeerId};
5use gpui::{AppContext, AsyncAppContext, ModelHandle};
6use language::{
7 point_from_lsp,
8 proto::{deserialize_anchor, serialize_anchor},
9 range_from_lsp, Anchor, Bias, Buffer, PointUtf16, ToLspPosition, ToPointUtf16,
10};
11use lsp::DocumentHighlightKind;
12use std::{cmp::Reverse, ops::Range, path::Path};
13
14#[async_trait(?Send)]
15pub(crate) trait LspCommand: 'static + Sized {
16 type Response: 'static + Default + Send;
17 type LspRequest: 'static + Send + lsp::request::Request;
18 type ProtoRequest: 'static + Send + proto::RequestMessage;
19
20 fn to_lsp(
21 &self,
22 path: &Path,
23 cx: &AppContext,
24 ) -> <Self::LspRequest as lsp::request::Request>::Params;
25 async fn response_from_lsp(
26 self,
27 message: <Self::LspRequest as lsp::request::Request>::Result,
28 project: ModelHandle<Project>,
29 buffer: ModelHandle<Buffer>,
30 cx: AsyncAppContext,
31 ) -> Result<Self::Response>;
32
33 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
34 async fn from_proto(
35 message: Self::ProtoRequest,
36 project: ModelHandle<Project>,
37 buffer: ModelHandle<Buffer>,
38 cx: AsyncAppContext,
39 ) -> Result<Self>;
40 fn response_to_proto(
41 response: Self::Response,
42 project: &mut Project,
43 peer_id: PeerId,
44 buffer_version: &clock::Global,
45 cx: &AppContext,
46 ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
47 async fn response_from_proto(
48 self,
49 message: <Self::ProtoRequest as proto::RequestMessage>::Response,
50 project: ModelHandle<Project>,
51 buffer: ModelHandle<Buffer>,
52 cx: AsyncAppContext,
53 ) -> Result<Self::Response>;
54 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64;
55}
56
57pub(crate) struct PrepareRename {
58 pub position: PointUtf16,
59}
60
61pub(crate) struct PerformRename {
62 pub position: PointUtf16,
63 pub new_name: String,
64 pub push_to_history: bool,
65}
66
67pub(crate) struct GetDefinition {
68 pub position: PointUtf16,
69}
70
71pub(crate) struct GetReferences {
72 pub position: PointUtf16,
73}
74
75pub(crate) struct GetDocumentHighlights {
76 pub position: PointUtf16,
77}
78
79#[async_trait(?Send)]
80impl LspCommand for PrepareRename {
81 type Response = Option<Range<Anchor>>;
82 type LspRequest = lsp::request::PrepareRenameRequest;
83 type ProtoRequest = proto::PrepareRename;
84
85 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::TextDocumentPositionParams {
86 lsp::TextDocumentPositionParams {
87 text_document: lsp::TextDocumentIdentifier {
88 uri: lsp::Url::from_file_path(path).unwrap(),
89 },
90 position: self.position.to_lsp_position(),
91 }
92 }
93
94 async fn response_from_lsp(
95 self,
96 message: Option<lsp::PrepareRenameResponse>,
97 _: ModelHandle<Project>,
98 buffer: ModelHandle<Buffer>,
99 cx: AsyncAppContext,
100 ) -> Result<Option<Range<Anchor>>> {
101 buffer.read_with(&cx, |buffer, _| {
102 if let Some(
103 lsp::PrepareRenameResponse::Range(range)
104 | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
105 ) = message
106 {
107 let Range { start, end } = range_from_lsp(range);
108 if buffer.clip_point_utf16(start, Bias::Left) == start
109 && buffer.clip_point_utf16(end, Bias::Left) == end
110 {
111 return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
112 }
113 }
114 Ok(None)
115 })
116 }
117
118 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
119 proto::PrepareRename {
120 project_id,
121 buffer_id: buffer.remote_id(),
122 position: Some(language::proto::serialize_anchor(
123 &buffer.anchor_before(self.position),
124 )),
125 version: (&buffer.version()).into(),
126 }
127 }
128
129 async fn from_proto(
130 message: proto::PrepareRename,
131 _: ModelHandle<Project>,
132 buffer: ModelHandle<Buffer>,
133 mut cx: AsyncAppContext,
134 ) -> Result<Self> {
135 let position = message
136 .position
137 .and_then(deserialize_anchor)
138 .ok_or_else(|| anyhow!("invalid position"))?;
139 buffer
140 .update(&mut cx, |buffer, _| {
141 buffer.wait_for_version(message.version.into())
142 })
143 .await;
144
145 Ok(Self {
146 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
147 })
148 }
149
150 fn response_to_proto(
151 range: Option<Range<Anchor>>,
152 _: &mut Project,
153 _: PeerId,
154 buffer_version: &clock::Global,
155 _: &AppContext,
156 ) -> proto::PrepareRenameResponse {
157 proto::PrepareRenameResponse {
158 can_rename: range.is_some(),
159 start: range
160 .as_ref()
161 .map(|range| language::proto::serialize_anchor(&range.start)),
162 end: range
163 .as_ref()
164 .map(|range| language::proto::serialize_anchor(&range.end)),
165 version: buffer_version.into(),
166 }
167 }
168
169 async fn response_from_proto(
170 self,
171 message: proto::PrepareRenameResponse,
172 _: ModelHandle<Project>,
173 buffer: ModelHandle<Buffer>,
174 mut cx: AsyncAppContext,
175 ) -> Result<Option<Range<Anchor>>> {
176 if message.can_rename {
177 buffer
178 .update(&mut cx, |buffer, _| {
179 buffer.wait_for_version(message.version.into())
180 })
181 .await;
182 let start = message.start.and_then(deserialize_anchor);
183 let end = message.end.and_then(deserialize_anchor);
184 Ok(start.zip(end).map(|(start, end)| start..end))
185 } else {
186 Ok(None)
187 }
188 }
189
190 fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
191 message.buffer_id
192 }
193}
194
195#[async_trait(?Send)]
196impl LspCommand for PerformRename {
197 type Response = ProjectTransaction;
198 type LspRequest = lsp::request::Rename;
199 type ProtoRequest = proto::PerformRename;
200
201 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::RenameParams {
202 lsp::RenameParams {
203 text_document_position: lsp::TextDocumentPositionParams {
204 text_document: lsp::TextDocumentIdentifier {
205 uri: lsp::Url::from_file_path(path).unwrap(),
206 },
207 position: self.position.to_lsp_position(),
208 },
209 new_name: self.new_name.clone(),
210 work_done_progress_params: Default::default(),
211 }
212 }
213
214 async fn response_from_lsp(
215 self,
216 message: Option<lsp::WorkspaceEdit>,
217 project: ModelHandle<Project>,
218 buffer: ModelHandle<Buffer>,
219 mut cx: AsyncAppContext,
220 ) -> Result<ProjectTransaction> {
221 if let Some(edit) = message {
222 let (language_name, language_server) = buffer.read_with(&cx, |buffer, _| {
223 let language = buffer
224 .language()
225 .ok_or_else(|| anyhow!("buffer's language was removed"))?;
226 let language_server = buffer
227 .language_server()
228 .cloned()
229 .ok_or_else(|| anyhow!("buffer's language server was removed"))?;
230 Ok::<_, anyhow::Error>((language.name().to_string(), language_server))
231 })?;
232 Project::deserialize_workspace_edit(
233 project,
234 edit,
235 self.push_to_history,
236 language_name,
237 language_server,
238 &mut cx,
239 )
240 .await
241 } else {
242 Ok(ProjectTransaction::default())
243 }
244 }
245
246 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
247 proto::PerformRename {
248 project_id,
249 buffer_id: buffer.remote_id(),
250 position: Some(language::proto::serialize_anchor(
251 &buffer.anchor_before(self.position),
252 )),
253 new_name: self.new_name.clone(),
254 version: (&buffer.version()).into(),
255 }
256 }
257
258 async fn from_proto(
259 message: proto::PerformRename,
260 _: ModelHandle<Project>,
261 buffer: ModelHandle<Buffer>,
262 mut cx: AsyncAppContext,
263 ) -> Result<Self> {
264 let position = message
265 .position
266 .and_then(deserialize_anchor)
267 .ok_or_else(|| anyhow!("invalid position"))?;
268 buffer
269 .update(&mut cx, |buffer, _| {
270 buffer.wait_for_version(message.version.into())
271 })
272 .await;
273 Ok(Self {
274 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
275 new_name: message.new_name,
276 push_to_history: false,
277 })
278 }
279
280 fn response_to_proto(
281 response: ProjectTransaction,
282 project: &mut Project,
283 peer_id: PeerId,
284 _: &clock::Global,
285 cx: &AppContext,
286 ) -> proto::PerformRenameResponse {
287 let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
288 proto::PerformRenameResponse {
289 transaction: Some(transaction),
290 }
291 }
292
293 async fn response_from_proto(
294 self,
295 message: proto::PerformRenameResponse,
296 project: ModelHandle<Project>,
297 _: ModelHandle<Buffer>,
298 mut cx: AsyncAppContext,
299 ) -> Result<ProjectTransaction> {
300 let message = message
301 .transaction
302 .ok_or_else(|| anyhow!("missing transaction"))?;
303 project
304 .update(&mut cx, |project, cx| {
305 project.deserialize_project_transaction(message, self.push_to_history, cx)
306 })
307 .await
308 }
309
310 fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
311 message.buffer_id
312 }
313}
314
315#[async_trait(?Send)]
316impl LspCommand for GetDefinition {
317 type Response = Vec<Location>;
318 type LspRequest = lsp::request::GotoDefinition;
319 type ProtoRequest = proto::GetDefinition;
320
321 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoDefinitionParams {
322 lsp::GotoDefinitionParams {
323 text_document_position_params: lsp::TextDocumentPositionParams {
324 text_document: lsp::TextDocumentIdentifier {
325 uri: lsp::Url::from_file_path(path).unwrap(),
326 },
327 position: self.position.to_lsp_position(),
328 },
329 work_done_progress_params: Default::default(),
330 partial_result_params: Default::default(),
331 }
332 }
333
334 async fn response_from_lsp(
335 self,
336 message: Option<lsp::GotoDefinitionResponse>,
337 project: ModelHandle<Project>,
338 buffer: ModelHandle<Buffer>,
339 mut cx: AsyncAppContext,
340 ) -> Result<Vec<Location>> {
341 let mut definitions = Vec::new();
342 let (language, language_server) = buffer
343 .read_with(&cx, |buffer, _| {
344 buffer
345 .language()
346 .cloned()
347 .zip(buffer.language_server().cloned())
348 })
349 .ok_or_else(|| anyhow!("buffer no longer has language server"))?;
350
351 if let Some(message) = message {
352 let mut unresolved_locations = Vec::new();
353 match message {
354 lsp::GotoDefinitionResponse::Scalar(loc) => {
355 unresolved_locations.push((loc.uri, loc.range));
356 }
357 lsp::GotoDefinitionResponse::Array(locs) => {
358 unresolved_locations.extend(locs.into_iter().map(|l| (l.uri, l.range)));
359 }
360 lsp::GotoDefinitionResponse::Link(links) => {
361 unresolved_locations.extend(
362 links
363 .into_iter()
364 .map(|l| (l.target_uri, l.target_selection_range)),
365 );
366 }
367 }
368
369 for (target_uri, target_range) in unresolved_locations {
370 let target_buffer_handle = project
371 .update(&mut cx, |this, cx| {
372 this.open_local_buffer_via_lsp(
373 target_uri,
374 language.name().to_string(),
375 language_server.clone(),
376 cx,
377 )
378 })
379 .await?;
380
381 cx.read(|cx| {
382 let target_buffer = target_buffer_handle.read(cx);
383 let target_start = target_buffer
384 .clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
385 let target_end = target_buffer
386 .clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
387 definitions.push(Location {
388 buffer: target_buffer_handle,
389 range: target_buffer.anchor_after(target_start)
390 ..target_buffer.anchor_before(target_end),
391 });
392 });
393 }
394 }
395
396 Ok(definitions)
397 }
398
399 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
400 proto::GetDefinition {
401 project_id,
402 buffer_id: buffer.remote_id(),
403 position: Some(language::proto::serialize_anchor(
404 &buffer.anchor_before(self.position),
405 )),
406 version: (&buffer.version()).into(),
407 }
408 }
409
410 async fn from_proto(
411 message: proto::GetDefinition,
412 _: ModelHandle<Project>,
413 buffer: ModelHandle<Buffer>,
414 mut cx: AsyncAppContext,
415 ) -> Result<Self> {
416 let position = message
417 .position
418 .and_then(deserialize_anchor)
419 .ok_or_else(|| anyhow!("invalid position"))?;
420 buffer
421 .update(&mut cx, |buffer, _| {
422 buffer.wait_for_version(message.version.into())
423 })
424 .await;
425 Ok(Self {
426 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
427 })
428 }
429
430 fn response_to_proto(
431 response: Vec<Location>,
432 project: &mut Project,
433 peer_id: PeerId,
434 _: &clock::Global,
435 cx: &AppContext,
436 ) -> proto::GetDefinitionResponse {
437 let locations = response
438 .into_iter()
439 .map(|definition| {
440 let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
441 proto::Location {
442 start: Some(serialize_anchor(&definition.range.start)),
443 end: Some(serialize_anchor(&definition.range.end)),
444 buffer: Some(buffer),
445 }
446 })
447 .collect();
448 proto::GetDefinitionResponse { locations }
449 }
450
451 async fn response_from_proto(
452 self,
453 message: proto::GetDefinitionResponse,
454 project: ModelHandle<Project>,
455 _: ModelHandle<Buffer>,
456 mut cx: AsyncAppContext,
457 ) -> Result<Vec<Location>> {
458 let mut locations = Vec::new();
459 for location in message.locations {
460 let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
461 let buffer = project
462 .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
463 .await?;
464 let start = location
465 .start
466 .and_then(deserialize_anchor)
467 .ok_or_else(|| anyhow!("missing target start"))?;
468 let end = location
469 .end
470 .and_then(deserialize_anchor)
471 .ok_or_else(|| anyhow!("missing target end"))?;
472 buffer
473 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
474 .await;
475 locations.push(Location {
476 buffer,
477 range: start..end,
478 })
479 }
480 Ok(locations)
481 }
482
483 fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
484 message.buffer_id
485 }
486}
487
488#[async_trait(?Send)]
489impl LspCommand for GetReferences {
490 type Response = Vec<Location>;
491 type LspRequest = lsp::request::References;
492 type ProtoRequest = proto::GetReferences;
493
494 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::ReferenceParams {
495 lsp::ReferenceParams {
496 text_document_position: lsp::TextDocumentPositionParams {
497 text_document: lsp::TextDocumentIdentifier {
498 uri: lsp::Url::from_file_path(path).unwrap(),
499 },
500 position: self.position.to_lsp_position(),
501 },
502 work_done_progress_params: Default::default(),
503 partial_result_params: Default::default(),
504 context: lsp::ReferenceContext {
505 include_declaration: true,
506 },
507 }
508 }
509
510 async fn response_from_lsp(
511 self,
512 locations: Option<Vec<lsp::Location>>,
513 project: ModelHandle<Project>,
514 buffer: ModelHandle<Buffer>,
515 mut cx: AsyncAppContext,
516 ) -> Result<Vec<Location>> {
517 let mut references = Vec::new();
518 let (language, language_server) = buffer
519 .read_with(&cx, |buffer, _| {
520 buffer
521 .language()
522 .cloned()
523 .zip(buffer.language_server().cloned())
524 })
525 .ok_or_else(|| anyhow!("buffer no longer has language server"))?;
526
527 if let Some(locations) = locations {
528 for lsp_location in locations {
529 let target_buffer_handle = project
530 .update(&mut cx, |this, cx| {
531 this.open_local_buffer_via_lsp(
532 lsp_location.uri,
533 language.name().to_string(),
534 language_server.clone(),
535 cx,
536 )
537 })
538 .await?;
539
540 cx.read(|cx| {
541 let target_buffer = target_buffer_handle.read(cx);
542 let target_start = target_buffer
543 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
544 let target_end = target_buffer
545 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
546 references.push(Location {
547 buffer: target_buffer_handle,
548 range: target_buffer.anchor_after(target_start)
549 ..target_buffer.anchor_before(target_end),
550 });
551 });
552 }
553 }
554
555 Ok(references)
556 }
557
558 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
559 proto::GetReferences {
560 project_id,
561 buffer_id: buffer.remote_id(),
562 position: Some(language::proto::serialize_anchor(
563 &buffer.anchor_before(self.position),
564 )),
565 version: (&buffer.version()).into(),
566 }
567 }
568
569 async fn from_proto(
570 message: proto::GetReferences,
571 _: ModelHandle<Project>,
572 buffer: ModelHandle<Buffer>,
573 mut cx: AsyncAppContext,
574 ) -> Result<Self> {
575 let position = message
576 .position
577 .and_then(deserialize_anchor)
578 .ok_or_else(|| anyhow!("invalid position"))?;
579 buffer
580 .update(&mut cx, |buffer, _| {
581 buffer.wait_for_version(message.version.into())
582 })
583 .await;
584 Ok(Self {
585 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
586 })
587 }
588
589 fn response_to_proto(
590 response: Vec<Location>,
591 project: &mut Project,
592 peer_id: PeerId,
593 _: &clock::Global,
594 cx: &AppContext,
595 ) -> proto::GetReferencesResponse {
596 let locations = response
597 .into_iter()
598 .map(|definition| {
599 let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
600 proto::Location {
601 start: Some(serialize_anchor(&definition.range.start)),
602 end: Some(serialize_anchor(&definition.range.end)),
603 buffer: Some(buffer),
604 }
605 })
606 .collect();
607 proto::GetReferencesResponse { locations }
608 }
609
610 async fn response_from_proto(
611 self,
612 message: proto::GetReferencesResponse,
613 project: ModelHandle<Project>,
614 _: ModelHandle<Buffer>,
615 mut cx: AsyncAppContext,
616 ) -> Result<Vec<Location>> {
617 let mut locations = Vec::new();
618 for location in message.locations {
619 let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
620 let target_buffer = project
621 .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
622 .await?;
623 let start = location
624 .start
625 .and_then(deserialize_anchor)
626 .ok_or_else(|| anyhow!("missing target start"))?;
627 let end = location
628 .end
629 .and_then(deserialize_anchor)
630 .ok_or_else(|| anyhow!("missing target end"))?;
631 target_buffer
632 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
633 .await;
634 locations.push(Location {
635 buffer: target_buffer,
636 range: start..end,
637 })
638 }
639 Ok(locations)
640 }
641
642 fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
643 message.buffer_id
644 }
645}
646
647#[async_trait(?Send)]
648impl LspCommand for GetDocumentHighlights {
649 type Response = Vec<DocumentHighlight>;
650 type LspRequest = lsp::request::DocumentHighlightRequest;
651 type ProtoRequest = proto::GetDocumentHighlights;
652
653 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::DocumentHighlightParams {
654 lsp::DocumentHighlightParams {
655 text_document_position_params: lsp::TextDocumentPositionParams {
656 text_document: lsp::TextDocumentIdentifier {
657 uri: lsp::Url::from_file_path(path).unwrap(),
658 },
659 position: self.position.to_lsp_position(),
660 },
661 work_done_progress_params: Default::default(),
662 partial_result_params: Default::default(),
663 }
664 }
665
666 async fn response_from_lsp(
667 self,
668 lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
669 _: ModelHandle<Project>,
670 buffer: ModelHandle<Buffer>,
671 cx: AsyncAppContext,
672 ) -> Result<Vec<DocumentHighlight>> {
673 buffer.read_with(&cx, |buffer, _| {
674 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
675 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
676 Ok(lsp_highlights
677 .into_iter()
678 .map(|lsp_highlight| {
679 let start = buffer
680 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
681 let end = buffer
682 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
683 DocumentHighlight {
684 range: buffer.anchor_after(start)..buffer.anchor_before(end),
685 kind: lsp_highlight
686 .kind
687 .unwrap_or(lsp::DocumentHighlightKind::READ),
688 }
689 })
690 .collect())
691 })
692 }
693
694 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
695 proto::GetDocumentHighlights {
696 project_id,
697 buffer_id: buffer.remote_id(),
698 position: Some(language::proto::serialize_anchor(
699 &buffer.anchor_before(self.position),
700 )),
701 version: (&buffer.version()).into(),
702 }
703 }
704
705 async fn from_proto(
706 message: proto::GetDocumentHighlights,
707 _: ModelHandle<Project>,
708 buffer: ModelHandle<Buffer>,
709 mut cx: AsyncAppContext,
710 ) -> Result<Self> {
711 let position = message
712 .position
713 .and_then(deserialize_anchor)
714 .ok_or_else(|| anyhow!("invalid position"))?;
715 buffer
716 .update(&mut cx, |buffer, _| {
717 buffer.wait_for_version(message.version.into())
718 })
719 .await;
720 Ok(Self {
721 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
722 })
723 }
724
725 fn response_to_proto(
726 response: Vec<DocumentHighlight>,
727 _: &mut Project,
728 _: PeerId,
729 _: &clock::Global,
730 _: &AppContext,
731 ) -> proto::GetDocumentHighlightsResponse {
732 let highlights = response
733 .into_iter()
734 .map(|highlight| proto::DocumentHighlight {
735 start: Some(serialize_anchor(&highlight.range.start)),
736 end: Some(serialize_anchor(&highlight.range.end)),
737 kind: match highlight.kind {
738 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
739 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
740 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
741 _ => proto::document_highlight::Kind::Text.into(),
742 },
743 })
744 .collect();
745 proto::GetDocumentHighlightsResponse { highlights }
746 }
747
748 async fn response_from_proto(
749 self,
750 message: proto::GetDocumentHighlightsResponse,
751 _: ModelHandle<Project>,
752 buffer: ModelHandle<Buffer>,
753 mut cx: AsyncAppContext,
754 ) -> Result<Vec<DocumentHighlight>> {
755 let mut highlights = Vec::new();
756 for highlight in message.highlights {
757 let start = highlight
758 .start
759 .and_then(deserialize_anchor)
760 .ok_or_else(|| anyhow!("missing target start"))?;
761 let end = highlight
762 .end
763 .and_then(deserialize_anchor)
764 .ok_or_else(|| anyhow!("missing target end"))?;
765 buffer
766 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
767 .await;
768 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
769 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
770 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
771 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
772 None => DocumentHighlightKind::TEXT,
773 };
774 highlights.push(DocumentHighlight {
775 range: start..end,
776 kind,
777 });
778 }
779 Ok(highlights)
780 }
781
782 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
783 message.buffer_id
784 }
785}