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