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