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