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