lsp_command.rs

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