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_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}