lsp_command.rs

  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}