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