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, point_to_lsp,
8 proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
9 range_from_lsp, Anchor, Bias, Buffer, PointUtf16, ToPointUtf16,
10};
11use lsp::{DocumentHighlightKind, ServerCapabilities};
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 check_capabilities(&self, _: &lsp::ServerCapabilities) -> bool {
21 true
22 }
23
24 fn to_lsp(
25 &self,
26 path: &Path,
27 cx: &AppContext,
28 ) -> <Self::LspRequest as lsp::request::Request>::Params;
29 async fn response_from_lsp(
30 self,
31 message: <Self::LspRequest as lsp::request::Request>::Result,
32 project: ModelHandle<Project>,
33 buffer: ModelHandle<Buffer>,
34 cx: AsyncAppContext,
35 ) -> Result<Self::Response>;
36
37 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
38 async fn from_proto(
39 message: Self::ProtoRequest,
40 project: ModelHandle<Project>,
41 buffer: ModelHandle<Buffer>,
42 cx: AsyncAppContext,
43 ) -> Result<Self>;
44 fn response_to_proto(
45 response: Self::Response,
46 project: &mut Project,
47 peer_id: PeerId,
48 buffer_version: &clock::Global,
49 cx: &AppContext,
50 ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
51 async fn response_from_proto(
52 self,
53 message: <Self::ProtoRequest as proto::RequestMessage>::Response,
54 project: ModelHandle<Project>,
55 buffer: ModelHandle<Buffer>,
56 cx: AsyncAppContext,
57 ) -> Result<Self::Response>;
58 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64;
59}
60
61pub(crate) struct PrepareRename {
62 pub position: PointUtf16,
63}
64
65pub(crate) struct PerformRename {
66 pub position: PointUtf16,
67 pub new_name: String,
68 pub push_to_history: bool,
69}
70
71pub(crate) struct GetDefinition {
72 pub position: PointUtf16,
73}
74
75pub(crate) struct GetReferences {
76 pub position: PointUtf16,
77}
78
79pub(crate) struct GetDocumentHighlights {
80 pub position: PointUtf16,
81}
82
83#[async_trait(?Send)]
84impl LspCommand for PrepareRename {
85 type Response = Option<Range<Anchor>>;
86 type LspRequest = lsp::request::PrepareRenameRequest;
87 type ProtoRequest = proto::PrepareRename;
88
89 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::TextDocumentPositionParams {
90 lsp::TextDocumentPositionParams {
91 text_document: lsp::TextDocumentIdentifier {
92 uri: lsp::Url::from_file_path(path).unwrap(),
93 },
94 position: point_to_lsp(self.position),
95 }
96 }
97
98 async fn response_from_lsp(
99 self,
100 message: Option<lsp::PrepareRenameResponse>,
101 _: ModelHandle<Project>,
102 buffer: ModelHandle<Buffer>,
103 cx: AsyncAppContext,
104 ) -> Result<Option<Range<Anchor>>> {
105 buffer.read_with(&cx, |buffer, _| {
106 if let Some(
107 lsp::PrepareRenameResponse::Range(range)
108 | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
109 ) = message
110 {
111 let Range { start, end } = range_from_lsp(range);
112 if buffer.clip_point_utf16(start, Bias::Left) == start
113 && buffer.clip_point_utf16(end, Bias::Left) == end
114 {
115 return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
116 }
117 }
118 Ok(None)
119 })
120 }
121
122 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
123 proto::PrepareRename {
124 project_id,
125 buffer_id: buffer.remote_id(),
126 position: Some(language::proto::serialize_anchor(
127 &buffer.anchor_before(self.position),
128 )),
129 version: serialize_version(&buffer.version()),
130 }
131 }
132
133 async fn from_proto(
134 message: proto::PrepareRename,
135 _: ModelHandle<Project>,
136 buffer: ModelHandle<Buffer>,
137 mut cx: AsyncAppContext,
138 ) -> Result<Self> {
139 let position = message
140 .position
141 .and_then(deserialize_anchor)
142 .ok_or_else(|| anyhow!("invalid position"))?;
143 buffer
144 .update(&mut cx, |buffer, _| {
145 buffer.wait_for_version(deserialize_version(message.version))
146 })
147 .await;
148
149 Ok(Self {
150 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
151 })
152 }
153
154 fn response_to_proto(
155 range: Option<Range<Anchor>>,
156 _: &mut Project,
157 _: PeerId,
158 buffer_version: &clock::Global,
159 _: &AppContext,
160 ) -> proto::PrepareRenameResponse {
161 proto::PrepareRenameResponse {
162 can_rename: range.is_some(),
163 start: range
164 .as_ref()
165 .map(|range| language::proto::serialize_anchor(&range.start)),
166 end: range
167 .as_ref()
168 .map(|range| language::proto::serialize_anchor(&range.end)),
169 version: serialize_version(buffer_version),
170 }
171 }
172
173 async fn response_from_proto(
174 self,
175 message: proto::PrepareRenameResponse,
176 _: ModelHandle<Project>,
177 buffer: ModelHandle<Buffer>,
178 mut cx: AsyncAppContext,
179 ) -> Result<Option<Range<Anchor>>> {
180 if message.can_rename {
181 buffer
182 .update(&mut cx, |buffer, _| {
183 buffer.wait_for_version(deserialize_version(message.version))
184 })
185 .await;
186 let start = message.start.and_then(deserialize_anchor);
187 let end = message.end.and_then(deserialize_anchor);
188 Ok(start.zip(end).map(|(start, end)| start..end))
189 } else {
190 Ok(None)
191 }
192 }
193
194 fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
195 message.buffer_id
196 }
197}
198
199#[async_trait(?Send)]
200impl LspCommand for PerformRename {
201 type Response = ProjectTransaction;
202 type LspRequest = lsp::request::Rename;
203 type ProtoRequest = proto::PerformRename;
204
205 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::RenameParams {
206 lsp::RenameParams {
207 text_document_position: lsp::TextDocumentPositionParams {
208 text_document: lsp::TextDocumentIdentifier {
209 uri: lsp::Url::from_file_path(path).unwrap(),
210 },
211 position: point_to_lsp(self.position),
212 },
213 new_name: self.new_name.clone(),
214 work_done_progress_params: Default::default(),
215 }
216 }
217
218 async fn response_from_lsp(
219 self,
220 message: Option<lsp::WorkspaceEdit>,
221 project: ModelHandle<Project>,
222 buffer: ModelHandle<Buffer>,
223 mut cx: AsyncAppContext,
224 ) -> Result<ProjectTransaction> {
225 if let Some(edit) = message {
226 let (lsp_adapter, lsp_server) = project
227 .read_with(&cx, |project, cx| {
228 project
229 .language_server_for_buffer(buffer.read(cx), cx)
230 .cloned()
231 })
232 .ok_or_else(|| anyhow!("no language server found for buffer"))?;
233 Project::deserialize_workspace_edit(
234 project,
235 edit,
236 self.push_to_history,
237 lsp_adapter,
238 lsp_server,
239 &mut cx,
240 )
241 .await
242 } else {
243 Ok(ProjectTransaction::default())
244 }
245 }
246
247 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
248 proto::PerformRename {
249 project_id,
250 buffer_id: buffer.remote_id(),
251 position: Some(language::proto::serialize_anchor(
252 &buffer.anchor_before(self.position),
253 )),
254 new_name: self.new_name.clone(),
255 version: serialize_version(&buffer.version()),
256 }
257 }
258
259 async fn from_proto(
260 message: proto::PerformRename,
261 _: ModelHandle<Project>,
262 buffer: ModelHandle<Buffer>,
263 mut cx: AsyncAppContext,
264 ) -> Result<Self> {
265 let position = message
266 .position
267 .and_then(deserialize_anchor)
268 .ok_or_else(|| anyhow!("invalid position"))?;
269 buffer
270 .update(&mut cx, |buffer, _| {
271 buffer.wait_for_version(deserialize_version(message.version))
272 })
273 .await;
274 Ok(Self {
275 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
276 new_name: message.new_name,
277 push_to_history: false,
278 })
279 }
280
281 fn response_to_proto(
282 response: ProjectTransaction,
283 project: &mut Project,
284 peer_id: PeerId,
285 _: &clock::Global,
286 cx: &AppContext,
287 ) -> proto::PerformRenameResponse {
288 let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
289 proto::PerformRenameResponse {
290 transaction: Some(transaction),
291 }
292 }
293
294 async fn response_from_proto(
295 self,
296 message: proto::PerformRenameResponse,
297 project: ModelHandle<Project>,
298 _: ModelHandle<Buffer>,
299 mut cx: AsyncAppContext,
300 ) -> Result<ProjectTransaction> {
301 let message = message
302 .transaction
303 .ok_or_else(|| anyhow!("missing transaction"))?;
304 project
305 .update(&mut cx, |project, cx| {
306 project.deserialize_project_transaction(message, self.push_to_history, cx)
307 })
308 .await
309 }
310
311 fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
312 message.buffer_id
313 }
314}
315
316#[async_trait(?Send)]
317impl LspCommand for GetDefinition {
318 type Response = Vec<Location>;
319 type LspRequest = lsp::request::GotoDefinition;
320 type ProtoRequest = proto::GetDefinition;
321
322 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoDefinitionParams {
323 lsp::GotoDefinitionParams {
324 text_document_position_params: lsp::TextDocumentPositionParams {
325 text_document: lsp::TextDocumentIdentifier {
326 uri: lsp::Url::from_file_path(path).unwrap(),
327 },
328 position: point_to_lsp(self.position),
329 },
330 work_done_progress_params: Default::default(),
331 partial_result_params: Default::default(),
332 }
333 }
334
335 async fn response_from_lsp(
336 self,
337 message: Option<lsp::GotoDefinitionResponse>,
338 project: ModelHandle<Project>,
339 buffer: ModelHandle<Buffer>,
340 mut cx: AsyncAppContext,
341 ) -> Result<Vec<Location>> {
342 let mut definitions = Vec::new();
343 let (lsp_adapter, language_server) = project
344 .read_with(&cx, |project, cx| {
345 project
346 .language_server_for_buffer(buffer.read(cx), cx)
347 .cloned()
348 })
349 .ok_or_else(|| anyhow!("no language server found for buffer"))?;
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 lsp_adapter.clone(),
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: serialize_version(&buffer.version()),
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(deserialize_version(message.version))
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: point_to_lsp(self.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 (lsp_adapter, language_server) = project
519 .read_with(&cx, |project, cx| {
520 project
521 .language_server_for_buffer(buffer.read(cx), cx)
522 .cloned()
523 })
524 .ok_or_else(|| anyhow!("no language server found for buffer"))?;
525
526 if let Some(locations) = locations {
527 for lsp_location in locations {
528 let target_buffer_handle = project
529 .update(&mut cx, |this, cx| {
530 this.open_local_buffer_via_lsp(
531 lsp_location.uri,
532 lsp_adapter.clone(),
533 language_server.clone(),
534 cx,
535 )
536 })
537 .await?;
538
539 cx.read(|cx| {
540 let target_buffer = target_buffer_handle.read(cx);
541 let target_start = target_buffer
542 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
543 let target_end = target_buffer
544 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
545 references.push(Location {
546 buffer: target_buffer_handle,
547 range: target_buffer.anchor_after(target_start)
548 ..target_buffer.anchor_before(target_end),
549 });
550 });
551 }
552 }
553
554 Ok(references)
555 }
556
557 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
558 proto::GetReferences {
559 project_id,
560 buffer_id: buffer.remote_id(),
561 position: Some(language::proto::serialize_anchor(
562 &buffer.anchor_before(self.position),
563 )),
564 version: serialize_version(&buffer.version()),
565 }
566 }
567
568 async fn from_proto(
569 message: proto::GetReferences,
570 _: ModelHandle<Project>,
571 buffer: ModelHandle<Buffer>,
572 mut cx: AsyncAppContext,
573 ) -> Result<Self> {
574 let position = message
575 .position
576 .and_then(deserialize_anchor)
577 .ok_or_else(|| anyhow!("invalid position"))?;
578 buffer
579 .update(&mut cx, |buffer, _| {
580 buffer.wait_for_version(deserialize_version(message.version))
581 })
582 .await;
583 Ok(Self {
584 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
585 })
586 }
587
588 fn response_to_proto(
589 response: Vec<Location>,
590 project: &mut Project,
591 peer_id: PeerId,
592 _: &clock::Global,
593 cx: &AppContext,
594 ) -> proto::GetReferencesResponse {
595 let locations = response
596 .into_iter()
597 .map(|definition| {
598 let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
599 proto::Location {
600 start: Some(serialize_anchor(&definition.range.start)),
601 end: Some(serialize_anchor(&definition.range.end)),
602 buffer: Some(buffer),
603 }
604 })
605 .collect();
606 proto::GetReferencesResponse { locations }
607 }
608
609 async fn response_from_proto(
610 self,
611 message: proto::GetReferencesResponse,
612 project: ModelHandle<Project>,
613 _: ModelHandle<Buffer>,
614 mut cx: AsyncAppContext,
615 ) -> Result<Vec<Location>> {
616 let mut locations = Vec::new();
617 for location in message.locations {
618 let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
619 let target_buffer = project
620 .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
621 .await?;
622 let start = location
623 .start
624 .and_then(deserialize_anchor)
625 .ok_or_else(|| anyhow!("missing target start"))?;
626 let end = location
627 .end
628 .and_then(deserialize_anchor)
629 .ok_or_else(|| anyhow!("missing target end"))?;
630 target_buffer
631 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
632 .await;
633 locations.push(Location {
634 buffer: target_buffer,
635 range: start..end,
636 })
637 }
638 Ok(locations)
639 }
640
641 fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
642 message.buffer_id
643 }
644}
645
646#[async_trait(?Send)]
647impl LspCommand for GetDocumentHighlights {
648 type Response = Vec<DocumentHighlight>;
649 type LspRequest = lsp::request::DocumentHighlightRequest;
650 type ProtoRequest = proto::GetDocumentHighlights;
651
652 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
653 capabilities.document_highlight_provider.is_some()
654 }
655
656 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::DocumentHighlightParams {
657 lsp::DocumentHighlightParams {
658 text_document_position_params: lsp::TextDocumentPositionParams {
659 text_document: lsp::TextDocumentIdentifier {
660 uri: lsp::Url::from_file_path(path).unwrap(),
661 },
662 position: point_to_lsp(self.position),
663 },
664 work_done_progress_params: Default::default(),
665 partial_result_params: Default::default(),
666 }
667 }
668
669 async fn response_from_lsp(
670 self,
671 lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
672 _: ModelHandle<Project>,
673 buffer: ModelHandle<Buffer>,
674 cx: AsyncAppContext,
675 ) -> Result<Vec<DocumentHighlight>> {
676 buffer.read_with(&cx, |buffer, _| {
677 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
678 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
679 Ok(lsp_highlights
680 .into_iter()
681 .map(|lsp_highlight| {
682 let start = buffer
683 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
684 let end = buffer
685 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
686 DocumentHighlight {
687 range: buffer.anchor_after(start)..buffer.anchor_before(end),
688 kind: lsp_highlight
689 .kind
690 .unwrap_or(lsp::DocumentHighlightKind::READ),
691 }
692 })
693 .collect())
694 })
695 }
696
697 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
698 proto::GetDocumentHighlights {
699 project_id,
700 buffer_id: buffer.remote_id(),
701 position: Some(language::proto::serialize_anchor(
702 &buffer.anchor_before(self.position),
703 )),
704 version: serialize_version(&buffer.version()),
705 }
706 }
707
708 async fn from_proto(
709 message: proto::GetDocumentHighlights,
710 _: ModelHandle<Project>,
711 buffer: ModelHandle<Buffer>,
712 mut cx: AsyncAppContext,
713 ) -> Result<Self> {
714 let position = message
715 .position
716 .and_then(deserialize_anchor)
717 .ok_or_else(|| anyhow!("invalid position"))?;
718 buffer
719 .update(&mut cx, |buffer, _| {
720 buffer.wait_for_version(deserialize_version(message.version))
721 })
722 .await;
723 Ok(Self {
724 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
725 })
726 }
727
728 fn response_to_proto(
729 response: Vec<DocumentHighlight>,
730 _: &mut Project,
731 _: PeerId,
732 _: &clock::Global,
733 _: &AppContext,
734 ) -> proto::GetDocumentHighlightsResponse {
735 let highlights = response
736 .into_iter()
737 .map(|highlight| proto::DocumentHighlight {
738 start: Some(serialize_anchor(&highlight.range.start)),
739 end: Some(serialize_anchor(&highlight.range.end)),
740 kind: match highlight.kind {
741 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
742 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
743 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
744 _ => proto::document_highlight::Kind::Text.into(),
745 },
746 })
747 .collect();
748 proto::GetDocumentHighlightsResponse { highlights }
749 }
750
751 async fn response_from_proto(
752 self,
753 message: proto::GetDocumentHighlightsResponse,
754 _: ModelHandle<Project>,
755 buffer: ModelHandle<Buffer>,
756 mut cx: AsyncAppContext,
757 ) -> Result<Vec<DocumentHighlight>> {
758 let mut highlights = Vec::new();
759 for highlight in message.highlights {
760 let start = highlight
761 .start
762 .and_then(deserialize_anchor)
763 .ok_or_else(|| anyhow!("missing target start"))?;
764 let end = highlight
765 .end
766 .and_then(deserialize_anchor)
767 .ok_or_else(|| anyhow!("missing target end"))?;
768 buffer
769 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
770 .await;
771 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
772 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
773 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
774 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
775 None => DocumentHighlightKind::TEXT,
776 };
777 highlights.push(DocumentHighlight {
778 range: start..end,
779 kind,
780 });
781 }
782 Ok(highlights)
783 }
784
785 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
786 message.buffer_id
787 }
788}