lsp_command.rs

  1use crate::{DocumentHighlight, Hover, HoverContents, 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
 83pub(crate) struct GetHover {
 84    pub position: PointUtf16,
 85}
 86
 87#[async_trait(?Send)]
 88impl LspCommand for PrepareRename {
 89    type Response = Option<Range<Anchor>>;
 90    type LspRequest = lsp::request::PrepareRenameRequest;
 91    type ProtoRequest = proto::PrepareRename;
 92
 93    fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
 94        if let Some(lsp::OneOf::Right(rename)) = &capabilities.rename_provider {
 95            rename.prepare_provider == Some(true)
 96        } else {
 97            false
 98        }
 99    }
100
101    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::TextDocumentPositionParams {
102        lsp::TextDocumentPositionParams {
103            text_document: lsp::TextDocumentIdentifier {
104                uri: lsp::Url::from_file_path(path).unwrap(),
105            },
106            position: point_to_lsp(self.position),
107        }
108    }
109
110    async fn response_from_lsp(
111        self,
112        message: Option<lsp::PrepareRenameResponse>,
113        _: ModelHandle<Project>,
114        buffer: ModelHandle<Buffer>,
115        cx: AsyncAppContext,
116    ) -> Result<Option<Range<Anchor>>> {
117        buffer.read_with(&cx, |buffer, _| {
118            if let Some(
119                lsp::PrepareRenameResponse::Range(range)
120                | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
121            ) = message
122            {
123                let Range { start, end } = range_from_lsp(range);
124                if buffer.clip_point_utf16(start, Bias::Left) == start
125                    && buffer.clip_point_utf16(end, Bias::Left) == end
126                {
127                    return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
128                }
129            }
130            Ok(None)
131        })
132    }
133
134    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
135        proto::PrepareRename {
136            project_id,
137            buffer_id: buffer.remote_id(),
138            position: Some(language::proto::serialize_anchor(
139                &buffer.anchor_before(self.position),
140            )),
141            version: serialize_version(&buffer.version()),
142        }
143    }
144
145    async fn from_proto(
146        message: proto::PrepareRename,
147        _: ModelHandle<Project>,
148        buffer: ModelHandle<Buffer>,
149        mut cx: AsyncAppContext,
150    ) -> Result<Self> {
151        let position = message
152            .position
153            .and_then(deserialize_anchor)
154            .ok_or_else(|| anyhow!("invalid position"))?;
155        buffer
156            .update(&mut cx, |buffer, _| {
157                buffer.wait_for_version(deserialize_version(message.version))
158            })
159            .await;
160
161        Ok(Self {
162            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
163        })
164    }
165
166    fn response_to_proto(
167        range: Option<Range<Anchor>>,
168        _: &mut Project,
169        _: PeerId,
170        buffer_version: &clock::Global,
171        _: &AppContext,
172    ) -> proto::PrepareRenameResponse {
173        proto::PrepareRenameResponse {
174            can_rename: range.is_some(),
175            start: range
176                .as_ref()
177                .map(|range| language::proto::serialize_anchor(&range.start)),
178            end: range
179                .as_ref()
180                .map(|range| language::proto::serialize_anchor(&range.end)),
181            version: serialize_version(buffer_version),
182        }
183    }
184
185    async fn response_from_proto(
186        self,
187        message: proto::PrepareRenameResponse,
188        _: ModelHandle<Project>,
189        buffer: ModelHandle<Buffer>,
190        mut cx: AsyncAppContext,
191    ) -> Result<Option<Range<Anchor>>> {
192        if message.can_rename {
193            buffer
194                .update(&mut cx, |buffer, _| {
195                    buffer.wait_for_version(deserialize_version(message.version))
196                })
197                .await;
198            let start = message.start.and_then(deserialize_anchor);
199            let end = message.end.and_then(deserialize_anchor);
200            Ok(start.zip(end).map(|(start, end)| start..end))
201        } else {
202            Ok(None)
203        }
204    }
205
206    fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
207        message.buffer_id
208    }
209}
210
211#[async_trait(?Send)]
212impl LspCommand for PerformRename {
213    type Response = ProjectTransaction;
214    type LspRequest = lsp::request::Rename;
215    type ProtoRequest = proto::PerformRename;
216
217    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::RenameParams {
218        lsp::RenameParams {
219            text_document_position: lsp::TextDocumentPositionParams {
220                text_document: lsp::TextDocumentIdentifier {
221                    uri: lsp::Url::from_file_path(path).unwrap(),
222                },
223                position: point_to_lsp(self.position),
224            },
225            new_name: self.new_name.clone(),
226            work_done_progress_params: Default::default(),
227        }
228    }
229
230    async fn response_from_lsp(
231        self,
232        message: Option<lsp::WorkspaceEdit>,
233        project: ModelHandle<Project>,
234        buffer: ModelHandle<Buffer>,
235        mut cx: AsyncAppContext,
236    ) -> Result<ProjectTransaction> {
237        if let Some(edit) = message {
238            let (lsp_adapter, lsp_server) = project
239                .read_with(&cx, |project, cx| {
240                    project
241                        .language_server_for_buffer(buffer.read(cx), cx)
242                        .cloned()
243                })
244                .ok_or_else(|| anyhow!("no language server found for buffer"))?;
245            Project::deserialize_workspace_edit(
246                project,
247                edit,
248                self.push_to_history,
249                lsp_adapter,
250                lsp_server,
251                &mut cx,
252            )
253            .await
254        } else {
255            Ok(ProjectTransaction::default())
256        }
257    }
258
259    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
260        proto::PerformRename {
261            project_id,
262            buffer_id: buffer.remote_id(),
263            position: Some(language::proto::serialize_anchor(
264                &buffer.anchor_before(self.position),
265            )),
266            new_name: self.new_name.clone(),
267            version: serialize_version(&buffer.version()),
268        }
269    }
270
271    async fn from_proto(
272        message: proto::PerformRename,
273        _: ModelHandle<Project>,
274        buffer: ModelHandle<Buffer>,
275        mut cx: AsyncAppContext,
276    ) -> Result<Self> {
277        let position = message
278            .position
279            .and_then(deserialize_anchor)
280            .ok_or_else(|| anyhow!("invalid position"))?;
281        buffer
282            .update(&mut cx, |buffer, _| {
283                buffer.wait_for_version(deserialize_version(message.version))
284            })
285            .await;
286        Ok(Self {
287            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
288            new_name: message.new_name,
289            push_to_history: false,
290        })
291    }
292
293    fn response_to_proto(
294        response: ProjectTransaction,
295        project: &mut Project,
296        peer_id: PeerId,
297        _: &clock::Global,
298        cx: &AppContext,
299    ) -> proto::PerformRenameResponse {
300        let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
301        proto::PerformRenameResponse {
302            transaction: Some(transaction),
303        }
304    }
305
306    async fn response_from_proto(
307        self,
308        message: proto::PerformRenameResponse,
309        project: ModelHandle<Project>,
310        _: ModelHandle<Buffer>,
311        mut cx: AsyncAppContext,
312    ) -> Result<ProjectTransaction> {
313        let message = message
314            .transaction
315            .ok_or_else(|| anyhow!("missing transaction"))?;
316        project
317            .update(&mut cx, |project, cx| {
318                project.deserialize_project_transaction(message, self.push_to_history, cx)
319            })
320            .await
321    }
322
323    fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
324        message.buffer_id
325    }
326}
327
328#[async_trait(?Send)]
329impl LspCommand for GetDefinition {
330    type Response = Vec<Location>;
331    type LspRequest = lsp::request::GotoDefinition;
332    type ProtoRequest = proto::GetDefinition;
333
334    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoDefinitionParams {
335        lsp::GotoDefinitionParams {
336            text_document_position_params: lsp::TextDocumentPositionParams {
337                text_document: lsp::TextDocumentIdentifier {
338                    uri: lsp::Url::from_file_path(path).unwrap(),
339                },
340                position: point_to_lsp(self.position),
341            },
342            work_done_progress_params: Default::default(),
343            partial_result_params: Default::default(),
344        }
345    }
346
347    async fn response_from_lsp(
348        self,
349        message: Option<lsp::GotoDefinitionResponse>,
350        project: ModelHandle<Project>,
351        buffer: ModelHandle<Buffer>,
352        mut cx: AsyncAppContext,
353    ) -> Result<Vec<Location>> {
354        let mut definitions = Vec::new();
355        let (lsp_adapter, language_server) = project
356            .read_with(&cx, |project, cx| {
357                project
358                    .language_server_for_buffer(buffer.read(cx), cx)
359                    .cloned()
360            })
361            .ok_or_else(|| anyhow!("no language server found for buffer"))?;
362
363        if let Some(message) = message {
364            let mut unresolved_locations = Vec::new();
365            match message {
366                lsp::GotoDefinitionResponse::Scalar(loc) => {
367                    unresolved_locations.push((loc.uri, loc.range));
368                }
369                lsp::GotoDefinitionResponse::Array(locs) => {
370                    unresolved_locations.extend(locs.into_iter().map(|l| (l.uri, l.range)));
371                }
372                lsp::GotoDefinitionResponse::Link(links) => {
373                    unresolved_locations.extend(
374                        links
375                            .into_iter()
376                            .map(|l| (l.target_uri, l.target_selection_range)),
377                    );
378                }
379            }
380
381            for (target_uri, target_range) in unresolved_locations {
382                let target_buffer_handle = project
383                    .update(&mut cx, |this, cx| {
384                        this.open_local_buffer_via_lsp(
385                            target_uri,
386                            lsp_adapter.clone(),
387                            language_server.clone(),
388                            cx,
389                        )
390                    })
391                    .await?;
392
393                cx.read(|cx| {
394                    let target_buffer = target_buffer_handle.read(cx);
395                    let target_start = target_buffer
396                        .clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
397                    let target_end = target_buffer
398                        .clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
399                    definitions.push(Location {
400                        buffer: target_buffer_handle,
401                        range: target_buffer.anchor_after(target_start)
402                            ..target_buffer.anchor_before(target_end),
403                    });
404                });
405            }
406        }
407
408        Ok(definitions)
409    }
410
411    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
412        proto::GetDefinition {
413            project_id,
414            buffer_id: buffer.remote_id(),
415            position: Some(language::proto::serialize_anchor(
416                &buffer.anchor_before(self.position),
417            )),
418            version: serialize_version(&buffer.version()),
419        }
420    }
421
422    async fn from_proto(
423        message: proto::GetDefinition,
424        _: ModelHandle<Project>,
425        buffer: ModelHandle<Buffer>,
426        mut cx: AsyncAppContext,
427    ) -> Result<Self> {
428        let position = message
429            .position
430            .and_then(deserialize_anchor)
431            .ok_or_else(|| anyhow!("invalid position"))?;
432        buffer
433            .update(&mut cx, |buffer, _| {
434                buffer.wait_for_version(deserialize_version(message.version))
435            })
436            .await;
437        Ok(Self {
438            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
439        })
440    }
441
442    fn response_to_proto(
443        response: Vec<Location>,
444        project: &mut Project,
445        peer_id: PeerId,
446        _: &clock::Global,
447        cx: &AppContext,
448    ) -> proto::GetDefinitionResponse {
449        let locations = response
450            .into_iter()
451            .map(|definition| {
452                let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
453                proto::Location {
454                    start: Some(serialize_anchor(&definition.range.start)),
455                    end: Some(serialize_anchor(&definition.range.end)),
456                    buffer: Some(buffer),
457                }
458            })
459            .collect();
460        proto::GetDefinitionResponse { locations }
461    }
462
463    async fn response_from_proto(
464        self,
465        message: proto::GetDefinitionResponse,
466        project: ModelHandle<Project>,
467        _: ModelHandle<Buffer>,
468        mut cx: AsyncAppContext,
469    ) -> Result<Vec<Location>> {
470        let mut locations = Vec::new();
471        for location in message.locations {
472            let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
473            let buffer = project
474                .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
475                .await?;
476            let start = location
477                .start
478                .and_then(deserialize_anchor)
479                .ok_or_else(|| anyhow!("missing target start"))?;
480            let end = location
481                .end
482                .and_then(deserialize_anchor)
483                .ok_or_else(|| anyhow!("missing target end"))?;
484            buffer
485                .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
486                .await;
487            locations.push(Location {
488                buffer,
489                range: start..end,
490            })
491        }
492        Ok(locations)
493    }
494
495    fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
496        message.buffer_id
497    }
498}
499
500#[async_trait(?Send)]
501impl LspCommand for GetReferences {
502    type Response = Vec<Location>;
503    type LspRequest = lsp::request::References;
504    type ProtoRequest = proto::GetReferences;
505
506    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::ReferenceParams {
507        lsp::ReferenceParams {
508            text_document_position: lsp::TextDocumentPositionParams {
509                text_document: lsp::TextDocumentIdentifier {
510                    uri: lsp::Url::from_file_path(path).unwrap(),
511                },
512                position: point_to_lsp(self.position),
513            },
514            work_done_progress_params: Default::default(),
515            partial_result_params: Default::default(),
516            context: lsp::ReferenceContext {
517                include_declaration: true,
518            },
519        }
520    }
521
522    async fn response_from_lsp(
523        self,
524        locations: Option<Vec<lsp::Location>>,
525        project: ModelHandle<Project>,
526        buffer: ModelHandle<Buffer>,
527        mut cx: AsyncAppContext,
528    ) -> Result<Vec<Location>> {
529        let mut references = Vec::new();
530        let (lsp_adapter, language_server) = project
531            .read_with(&cx, |project, cx| {
532                project
533                    .language_server_for_buffer(buffer.read(cx), cx)
534                    .cloned()
535            })
536            .ok_or_else(|| anyhow!("no language server found for buffer"))?;
537
538        if let Some(locations) = locations {
539            for lsp_location in locations {
540                let target_buffer_handle = project
541                    .update(&mut cx, |this, cx| {
542                        this.open_local_buffer_via_lsp(
543                            lsp_location.uri,
544                            lsp_adapter.clone(),
545                            language_server.clone(),
546                            cx,
547                        )
548                    })
549                    .await?;
550
551                cx.read(|cx| {
552                    let target_buffer = target_buffer_handle.read(cx);
553                    let target_start = target_buffer
554                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
555                    let target_end = target_buffer
556                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
557                    references.push(Location {
558                        buffer: target_buffer_handle,
559                        range: target_buffer.anchor_after(target_start)
560                            ..target_buffer.anchor_before(target_end),
561                    });
562                });
563            }
564        }
565
566        Ok(references)
567    }
568
569    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
570        proto::GetReferences {
571            project_id,
572            buffer_id: buffer.remote_id(),
573            position: Some(language::proto::serialize_anchor(
574                &buffer.anchor_before(self.position),
575            )),
576            version: serialize_version(&buffer.version()),
577        }
578    }
579
580    async fn from_proto(
581        message: proto::GetReferences,
582        _: ModelHandle<Project>,
583        buffer: ModelHandle<Buffer>,
584        mut cx: AsyncAppContext,
585    ) -> Result<Self> {
586        let position = message
587            .position
588            .and_then(deserialize_anchor)
589            .ok_or_else(|| anyhow!("invalid position"))?;
590        buffer
591            .update(&mut cx, |buffer, _| {
592                buffer.wait_for_version(deserialize_version(message.version))
593            })
594            .await;
595        Ok(Self {
596            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
597        })
598    }
599
600    fn response_to_proto(
601        response: Vec<Location>,
602        project: &mut Project,
603        peer_id: PeerId,
604        _: &clock::Global,
605        cx: &AppContext,
606    ) -> proto::GetReferencesResponse {
607        let locations = response
608            .into_iter()
609            .map(|definition| {
610                let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
611                proto::Location {
612                    start: Some(serialize_anchor(&definition.range.start)),
613                    end: Some(serialize_anchor(&definition.range.end)),
614                    buffer: Some(buffer),
615                }
616            })
617            .collect();
618        proto::GetReferencesResponse { locations }
619    }
620
621    async fn response_from_proto(
622        self,
623        message: proto::GetReferencesResponse,
624        project: ModelHandle<Project>,
625        _: ModelHandle<Buffer>,
626        mut cx: AsyncAppContext,
627    ) -> Result<Vec<Location>> {
628        let mut locations = Vec::new();
629        for location in message.locations {
630            let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
631            let target_buffer = project
632                .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
633                .await?;
634            let start = location
635                .start
636                .and_then(deserialize_anchor)
637                .ok_or_else(|| anyhow!("missing target start"))?;
638            let end = location
639                .end
640                .and_then(deserialize_anchor)
641                .ok_or_else(|| anyhow!("missing target end"))?;
642            target_buffer
643                .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
644                .await;
645            locations.push(Location {
646                buffer: target_buffer,
647                range: start..end,
648            })
649        }
650        Ok(locations)
651    }
652
653    fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
654        message.buffer_id
655    }
656}
657
658#[async_trait(?Send)]
659impl LspCommand for GetDocumentHighlights {
660    type Response = Vec<DocumentHighlight>;
661    type LspRequest = lsp::request::DocumentHighlightRequest;
662    type ProtoRequest = proto::GetDocumentHighlights;
663
664    fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
665        capabilities.document_highlight_provider.is_some()
666    }
667
668    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::DocumentHighlightParams {
669        lsp::DocumentHighlightParams {
670            text_document_position_params: lsp::TextDocumentPositionParams {
671                text_document: lsp::TextDocumentIdentifier {
672                    uri: lsp::Url::from_file_path(path).unwrap(),
673                },
674                position: point_to_lsp(self.position),
675            },
676            work_done_progress_params: Default::default(),
677            partial_result_params: Default::default(),
678        }
679    }
680
681    async fn response_from_lsp(
682        self,
683        lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
684        _: ModelHandle<Project>,
685        buffer: ModelHandle<Buffer>,
686        cx: AsyncAppContext,
687    ) -> Result<Vec<DocumentHighlight>> {
688        buffer.read_with(&cx, |buffer, _| {
689            let mut lsp_highlights = lsp_highlights.unwrap_or_default();
690            lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
691            Ok(lsp_highlights
692                .into_iter()
693                .map(|lsp_highlight| {
694                    let start = buffer
695                        .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
696                    let end = buffer
697                        .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
698                    DocumentHighlight {
699                        range: buffer.anchor_after(start)..buffer.anchor_before(end),
700                        kind: lsp_highlight
701                            .kind
702                            .unwrap_or(lsp::DocumentHighlightKind::READ),
703                    }
704                })
705                .collect())
706        })
707    }
708
709    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
710        proto::GetDocumentHighlights {
711            project_id,
712            buffer_id: buffer.remote_id(),
713            position: Some(language::proto::serialize_anchor(
714                &buffer.anchor_before(self.position),
715            )),
716            version: serialize_version(&buffer.version()),
717        }
718    }
719
720    async fn from_proto(
721        message: proto::GetDocumentHighlights,
722        _: ModelHandle<Project>,
723        buffer: ModelHandle<Buffer>,
724        mut cx: AsyncAppContext,
725    ) -> Result<Self> {
726        let position = message
727            .position
728            .and_then(deserialize_anchor)
729            .ok_or_else(|| anyhow!("invalid position"))?;
730        buffer
731            .update(&mut cx, |buffer, _| {
732                buffer.wait_for_version(deserialize_version(message.version))
733            })
734            .await;
735        Ok(Self {
736            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
737        })
738    }
739
740    fn response_to_proto(
741        response: Vec<DocumentHighlight>,
742        _: &mut Project,
743        _: PeerId,
744        _: &clock::Global,
745        _: &AppContext,
746    ) -> proto::GetDocumentHighlightsResponse {
747        let highlights = response
748            .into_iter()
749            .map(|highlight| proto::DocumentHighlight {
750                start: Some(serialize_anchor(&highlight.range.start)),
751                end: Some(serialize_anchor(&highlight.range.end)),
752                kind: match highlight.kind {
753                    DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
754                    DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
755                    DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
756                    _ => proto::document_highlight::Kind::Text.into(),
757                },
758            })
759            .collect();
760        proto::GetDocumentHighlightsResponse { highlights }
761    }
762
763    async fn response_from_proto(
764        self,
765        message: proto::GetDocumentHighlightsResponse,
766        _: ModelHandle<Project>,
767        buffer: ModelHandle<Buffer>,
768        mut cx: AsyncAppContext,
769    ) -> Result<Vec<DocumentHighlight>> {
770        let mut highlights = Vec::new();
771        for highlight in message.highlights {
772            let start = highlight
773                .start
774                .and_then(deserialize_anchor)
775                .ok_or_else(|| anyhow!("missing target start"))?;
776            let end = highlight
777                .end
778                .and_then(deserialize_anchor)
779                .ok_or_else(|| anyhow!("missing target end"))?;
780            buffer
781                .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
782                .await;
783            let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
784                Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
785                Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
786                Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
787                None => DocumentHighlightKind::TEXT,
788            };
789            highlights.push(DocumentHighlight {
790                range: start..end,
791                kind,
792            });
793        }
794        Ok(highlights)
795    }
796
797    fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
798        message.buffer_id
799    }
800}
801
802#[async_trait(?Send)]
803impl LspCommand for GetHover {
804    type Response = Option<Hover>;
805    type LspRequest = lsp::request::HoverRequest;
806    type ProtoRequest = proto::GetHover;
807
808    fn to_lsp(&self, path: &Path, cx: &AppContext) -> lsp::HoverParams {
809        lsp::HoverParams {
810            text_document_position_params: lsp::TextDocumentPositionParams {
811                text_document: lsp::TextDocumentIdentifier {
812                    uri: lsp::Url::from_file_path(path).unwrap(),
813                },
814                position: point_to_lsp(self.position),
815            },
816            work_done_progress_params: Default::default(),
817        }
818    }
819
820    async fn response_from_lsp(
821        self,
822        message: Option<lsp::Hover>,
823        project: ModelHandle<Project>,
824        buffer: ModelHandle<Buffer>,
825        mut cx: AsyncAppContext,
826    ) -> Result<Self::Response> {
827        Ok(message.map(|hover| {
828            let range = hover.range.map(|range| {
829                cx.read(|cx| {
830                    let buffer = buffer.read(cx);
831                    let token_start =
832                        buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
833                    let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
834                    buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
835                })
836            });
837
838            fn highlight(lsp_marked_string: lsp::MarkedString, project: &Project) -> HoverContents {
839                match lsp_marked_string {
840                    lsp::MarkedString::LanguageString(lsp::LanguageString { language, value }) => {
841                        if let Some(language) = project.languages().get_language(&language) {
842                            let runs =
843                                language.highlight_text(&value.as_str().into(), 0..value.len());
844                            HoverContents { text: value, runs }
845                        } else {
846                            HoverContents {
847                                text: value,
848                                runs: Vec::new(),
849                            }
850                        }
851                    }
852                    lsp::MarkedString::String(text) => HoverContents {
853                        text,
854                        runs: Vec::new(),
855                    },
856                }
857            }
858
859            let contents = cx.read(|cx| {
860                let project = project.read(cx);
861                match dbg!(hover.contents) {
862                    lsp::HoverContents::Scalar(marked_string) => {
863                        vec![highlight(marked_string, project)]
864                    }
865                    lsp::HoverContents::Array(marked_strings) => marked_strings
866                        .into_iter()
867                        .map(|marked_string| highlight(marked_string, project))
868                        .collect(),
869                    lsp::HoverContents::Markup(markup_content) => {
870                        // TODO: handle markdown
871                        vec![HoverContents {
872                            text: markup_content.value,
873                            runs: Vec::new(),
874                        }]
875                    }
876                }
877            });
878
879            Hover { contents, range }
880        }))
881    }
882
883    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
884        proto::GetHover {
885            project_id,
886            buffer_id: buffer.remote_id(),
887            position: Some(language::proto::serialize_anchor(
888                &buffer.anchor_before(self.position),
889            )),
890            version: serialize_version(&buffer.version),
891        }
892    }
893
894    async fn from_proto(
895        message: Self::ProtoRequest,
896        _: ModelHandle<Project>,
897        buffer: ModelHandle<Buffer>,
898        mut cx: AsyncAppContext,
899    ) -> Result<Self> {
900        let position = message
901            .position
902            .and_then(deserialize_anchor)
903            .ok_or_else(|| anyhow!("invalid position"))?;
904        buffer
905            .update(&mut cx, |buffer, _| {
906                buffer.wait_for_version(deserialize_version(message.version))
907            })
908            .await;
909        Ok(Self {
910            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
911        })
912    }
913
914    fn response_to_proto(
915        response: Self::Response,
916        project: &mut Project,
917        peer_id: PeerId,
918        buffer_version: &clock::Global,
919        cx: &AppContext,
920    ) -> proto::GetHoverResponse {
921        todo!()
922    }
923
924    async fn response_from_proto(
925        self,
926        message: <Self::ProtoRequest as proto::RequestMessage>::Response,
927        project: ModelHandle<Project>,
928        buffer: ModelHandle<Buffer>,
929        cx: AsyncAppContext,
930    ) -> Result<Self::Response> {
931        todo!()
932    }
933
934    fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 {
935        message.buffer_id
936    }
937}