lsp_command.rs

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