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