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::{self, 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, CachedLspAdapter, PointUtf16, ToPointUtf16,
  12};
  13use lsp::{DocumentHighlightKind, LanguageServer, ServerCapabilities};
  14use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
  15use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
  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: &mut 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 GetTypeDefinition {
  79    pub position: PointUtf16,
  80}
  81
  82pub(crate) struct GetReferences {
  83    pub position: PointUtf16,
  84}
  85
  86pub(crate) struct GetDocumentHighlights {
  87    pub position: PointUtf16,
  88}
  89
  90pub(crate) struct GetHover {
  91    pub position: PointUtf16,
  92}
  93
  94#[async_trait(?Send)]
  95impl LspCommand for PrepareRename {
  96    type Response = Option<Range<Anchor>>;
  97    type LspRequest = lsp::request::PrepareRenameRequest;
  98    type ProtoRequest = proto::PrepareRename;
  99
 100    fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
 101        if let Some(lsp::OneOf::Right(rename)) = &capabilities.rename_provider {
 102            rename.prepare_provider == Some(true)
 103        } else {
 104            false
 105        }
 106    }
 107
 108    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::TextDocumentPositionParams {
 109        lsp::TextDocumentPositionParams {
 110            text_document: lsp::TextDocumentIdentifier {
 111                uri: lsp::Url::from_file_path(path).unwrap(),
 112            },
 113            position: point_to_lsp(self.position),
 114        }
 115    }
 116
 117    async fn response_from_lsp(
 118        self,
 119        message: Option<lsp::PrepareRenameResponse>,
 120        _: ModelHandle<Project>,
 121        buffer: ModelHandle<Buffer>,
 122        cx: AsyncAppContext,
 123    ) -> Result<Option<Range<Anchor>>> {
 124        buffer.read_with(&cx, |buffer, _| {
 125            if let Some(
 126                lsp::PrepareRenameResponse::Range(range)
 127                | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
 128            ) = message
 129            {
 130                let Range { start, end } = range_from_lsp(range);
 131                if buffer.clip_point_utf16(start, Bias::Left) == start.0
 132                    && buffer.clip_point_utf16(end, Bias::Left) == end.0
 133                {
 134                    return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
 135                }
 136            }
 137            Ok(None)
 138        })
 139    }
 140
 141    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
 142        proto::PrepareRename {
 143            project_id,
 144            buffer_id: buffer.remote_id(),
 145            position: Some(language::proto::serialize_anchor(
 146                &buffer.anchor_before(self.position),
 147            )),
 148            version: serialize_version(&buffer.version()),
 149        }
 150    }
 151
 152    async fn from_proto(
 153        message: proto::PrepareRename,
 154        _: ModelHandle<Project>,
 155        buffer: ModelHandle<Buffer>,
 156        mut cx: AsyncAppContext,
 157    ) -> Result<Self> {
 158        let position = message
 159            .position
 160            .and_then(deserialize_anchor)
 161            .ok_or_else(|| anyhow!("invalid position"))?;
 162        buffer
 163            .update(&mut cx, |buffer, _| {
 164                buffer.wait_for_version(deserialize_version(&message.version))
 165            })
 166            .await?;
 167
 168        Ok(Self {
 169            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
 170        })
 171    }
 172
 173    fn response_to_proto(
 174        range: Option<Range<Anchor>>,
 175        _: &mut Project,
 176        _: PeerId,
 177        buffer_version: &clock::Global,
 178        _: &mut AppContext,
 179    ) -> proto::PrepareRenameResponse {
 180        proto::PrepareRenameResponse {
 181            can_rename: range.is_some(),
 182            start: range
 183                .as_ref()
 184                .map(|range| language::proto::serialize_anchor(&range.start)),
 185            end: range
 186                .as_ref()
 187                .map(|range| language::proto::serialize_anchor(&range.end)),
 188            version: serialize_version(buffer_version),
 189        }
 190    }
 191
 192    async fn response_from_proto(
 193        self,
 194        message: proto::PrepareRenameResponse,
 195        _: ModelHandle<Project>,
 196        buffer: ModelHandle<Buffer>,
 197        mut cx: AsyncAppContext,
 198    ) -> Result<Option<Range<Anchor>>> {
 199        if message.can_rename {
 200            buffer
 201                .update(&mut cx, |buffer, _| {
 202                    buffer.wait_for_version(deserialize_version(&message.version))
 203                })
 204                .await?;
 205            let start = message.start.and_then(deserialize_anchor);
 206            let end = message.end.and_then(deserialize_anchor);
 207            Ok(start.zip(end).map(|(start, end)| start..end))
 208        } else {
 209            Ok(None)
 210        }
 211    }
 212
 213    fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
 214        message.buffer_id
 215    }
 216}
 217
 218#[async_trait(?Send)]
 219impl LspCommand for PerformRename {
 220    type Response = ProjectTransaction;
 221    type LspRequest = lsp::request::Rename;
 222    type ProtoRequest = proto::PerformRename;
 223
 224    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::RenameParams {
 225        lsp::RenameParams {
 226            text_document_position: lsp::TextDocumentPositionParams {
 227                text_document: lsp::TextDocumentIdentifier {
 228                    uri: lsp::Url::from_file_path(path).unwrap(),
 229                },
 230                position: point_to_lsp(self.position),
 231            },
 232            new_name: self.new_name.clone(),
 233            work_done_progress_params: Default::default(),
 234        }
 235    }
 236
 237    async fn response_from_lsp(
 238        self,
 239        message: Option<lsp::WorkspaceEdit>,
 240        project: ModelHandle<Project>,
 241        buffer: ModelHandle<Buffer>,
 242        mut cx: AsyncAppContext,
 243    ) -> Result<ProjectTransaction> {
 244        if let Some(edit) = message {
 245            let (lsp_adapter, lsp_server) = language_server_for_buffer(&project, &buffer, &mut cx)?;
 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: &mut 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<LocationLink>;
 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        cx: AsyncAppContext,
 354    ) -> Result<Vec<LocationLink>> {
 355        location_links_from_lsp(message, project, buffer, cx).await
 356    }
 357
 358    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
 359        proto::GetDefinition {
 360            project_id,
 361            buffer_id: buffer.remote_id(),
 362            position: Some(language::proto::serialize_anchor(
 363                &buffer.anchor_before(self.position),
 364            )),
 365            version: serialize_version(&buffer.version()),
 366        }
 367    }
 368
 369    async fn from_proto(
 370        message: proto::GetDefinition,
 371        _: ModelHandle<Project>,
 372        buffer: ModelHandle<Buffer>,
 373        mut cx: AsyncAppContext,
 374    ) -> Result<Self> {
 375        let position = message
 376            .position
 377            .and_then(deserialize_anchor)
 378            .ok_or_else(|| anyhow!("invalid position"))?;
 379        buffer
 380            .update(&mut cx, |buffer, _| {
 381                buffer.wait_for_version(deserialize_version(&message.version))
 382            })
 383            .await?;
 384        Ok(Self {
 385            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
 386        })
 387    }
 388
 389    fn response_to_proto(
 390        response: Vec<LocationLink>,
 391        project: &mut Project,
 392        peer_id: PeerId,
 393        _: &clock::Global,
 394        cx: &mut AppContext,
 395    ) -> proto::GetDefinitionResponse {
 396        let links = location_links_to_proto(response, project, peer_id, cx);
 397        proto::GetDefinitionResponse { links }
 398    }
 399
 400    async fn response_from_proto(
 401        self,
 402        message: proto::GetDefinitionResponse,
 403        project: ModelHandle<Project>,
 404        _: ModelHandle<Buffer>,
 405        cx: AsyncAppContext,
 406    ) -> Result<Vec<LocationLink>> {
 407        location_links_from_proto(message.links, project, cx).await
 408    }
 409
 410    fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
 411        message.buffer_id
 412    }
 413}
 414
 415#[async_trait(?Send)]
 416impl LspCommand for GetTypeDefinition {
 417    type Response = Vec<LocationLink>;
 418    type LspRequest = lsp::request::GotoTypeDefinition;
 419    type ProtoRequest = proto::GetTypeDefinition;
 420
 421    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoTypeDefinitionParams {
 422        lsp::GotoTypeDefinitionParams {
 423            text_document_position_params: lsp::TextDocumentPositionParams {
 424                text_document: lsp::TextDocumentIdentifier {
 425                    uri: lsp::Url::from_file_path(path).unwrap(),
 426                },
 427                position: point_to_lsp(self.position),
 428            },
 429            work_done_progress_params: Default::default(),
 430            partial_result_params: Default::default(),
 431        }
 432    }
 433
 434    async fn response_from_lsp(
 435        self,
 436        message: Option<lsp::GotoTypeDefinitionResponse>,
 437        project: ModelHandle<Project>,
 438        buffer: ModelHandle<Buffer>,
 439        cx: AsyncAppContext,
 440    ) -> Result<Vec<LocationLink>> {
 441        location_links_from_lsp(message, project, buffer, cx).await
 442    }
 443
 444    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition {
 445        proto::GetTypeDefinition {
 446            project_id,
 447            buffer_id: buffer.remote_id(),
 448            position: Some(language::proto::serialize_anchor(
 449                &buffer.anchor_before(self.position),
 450            )),
 451            version: serialize_version(&buffer.version()),
 452        }
 453    }
 454
 455    async fn from_proto(
 456        message: proto::GetTypeDefinition,
 457        _: ModelHandle<Project>,
 458        buffer: ModelHandle<Buffer>,
 459        mut cx: AsyncAppContext,
 460    ) -> Result<Self> {
 461        let position = message
 462            .position
 463            .and_then(deserialize_anchor)
 464            .ok_or_else(|| anyhow!("invalid position"))?;
 465        buffer
 466            .update(&mut cx, |buffer, _| {
 467                buffer.wait_for_version(deserialize_version(&message.version))
 468            })
 469            .await?;
 470        Ok(Self {
 471            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
 472        })
 473    }
 474
 475    fn response_to_proto(
 476        response: Vec<LocationLink>,
 477        project: &mut Project,
 478        peer_id: PeerId,
 479        _: &clock::Global,
 480        cx: &mut AppContext,
 481    ) -> proto::GetTypeDefinitionResponse {
 482        let links = location_links_to_proto(response, project, peer_id, cx);
 483        proto::GetTypeDefinitionResponse { links }
 484    }
 485
 486    async fn response_from_proto(
 487        self,
 488        message: proto::GetTypeDefinitionResponse,
 489        project: ModelHandle<Project>,
 490        _: ModelHandle<Buffer>,
 491        cx: AsyncAppContext,
 492    ) -> Result<Vec<LocationLink>> {
 493        location_links_from_proto(message.links, project, cx).await
 494    }
 495
 496    fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> u64 {
 497        message.buffer_id
 498    }
 499}
 500
 501fn language_server_for_buffer(
 502    project: &ModelHandle<Project>,
 503    buffer: &ModelHandle<Buffer>,
 504    cx: &mut AsyncAppContext,
 505) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
 506    project
 507        .read_with(cx, |project, cx| {
 508            project
 509                .language_server_for_buffer(buffer.read(cx), cx)
 510                .map(|(adapter, server)| (adapter.clone(), server.clone()))
 511        })
 512        .ok_or_else(|| anyhow!("no language server found for buffer"))
 513}
 514
 515async fn location_links_from_proto(
 516    proto_links: Vec<proto::LocationLink>,
 517    project: ModelHandle<Project>,
 518    mut cx: AsyncAppContext,
 519) -> Result<Vec<LocationLink>> {
 520    let mut links = Vec::new();
 521
 522    for link in proto_links {
 523        let origin = match link.origin {
 524            Some(origin) => {
 525                let buffer = project
 526                    .update(&mut cx, |this, cx| {
 527                        this.wait_for_remote_buffer(origin.buffer_id, cx)
 528                    })
 529                    .await?;
 530                let start = origin
 531                    .start
 532                    .and_then(deserialize_anchor)
 533                    .ok_or_else(|| anyhow!("missing origin start"))?;
 534                let end = origin
 535                    .end
 536                    .and_then(deserialize_anchor)
 537                    .ok_or_else(|| anyhow!("missing origin end"))?;
 538                buffer
 539                    .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
 540                    .await?;
 541                Some(Location {
 542                    buffer,
 543                    range: start..end,
 544                })
 545            }
 546            None => None,
 547        };
 548
 549        let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
 550        let buffer = project
 551            .update(&mut cx, |this, cx| {
 552                this.wait_for_remote_buffer(target.buffer_id, cx)
 553            })
 554            .await?;
 555        let start = target
 556            .start
 557            .and_then(deserialize_anchor)
 558            .ok_or_else(|| anyhow!("missing target start"))?;
 559        let end = target
 560            .end
 561            .and_then(deserialize_anchor)
 562            .ok_or_else(|| anyhow!("missing target end"))?;
 563        buffer
 564            .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
 565            .await?;
 566        let target = Location {
 567            buffer,
 568            range: start..end,
 569        };
 570
 571        links.push(LocationLink { origin, target })
 572    }
 573
 574    Ok(links)
 575}
 576
 577async fn location_links_from_lsp(
 578    message: Option<lsp::GotoDefinitionResponse>,
 579    project: ModelHandle<Project>,
 580    buffer: ModelHandle<Buffer>,
 581    mut cx: AsyncAppContext,
 582) -> Result<Vec<LocationLink>> {
 583    let message = match message {
 584        Some(message) => message,
 585        None => return Ok(Vec::new()),
 586    };
 587
 588    let mut unresolved_links = Vec::new();
 589    match message {
 590        lsp::GotoDefinitionResponse::Scalar(loc) => {
 591            unresolved_links.push((None, loc.uri, loc.range));
 592        }
 593
 594        lsp::GotoDefinitionResponse::Array(locs) => {
 595            unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
 596        }
 597
 598        lsp::GotoDefinitionResponse::Link(links) => {
 599            unresolved_links.extend(links.into_iter().map(|l| {
 600                (
 601                    l.origin_selection_range,
 602                    l.target_uri,
 603                    l.target_selection_range,
 604                )
 605            }));
 606        }
 607    }
 608
 609    let (lsp_adapter, language_server) = language_server_for_buffer(&project, &buffer, &mut cx)?;
 610    let mut definitions = Vec::new();
 611    for (origin_range, target_uri, target_range) in unresolved_links {
 612        let target_buffer_handle = project
 613            .update(&mut cx, |this, cx| {
 614                this.open_local_buffer_via_lsp(
 615                    target_uri,
 616                    language_server.server_id(),
 617                    lsp_adapter.name.clone(),
 618                    cx,
 619                )
 620            })
 621            .await?;
 622
 623        cx.read(|cx| {
 624            let origin_location = origin_range.map(|origin_range| {
 625                let origin_buffer = buffer.read(cx);
 626                let origin_start =
 627                    origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
 628                let origin_end =
 629                    origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
 630                Location {
 631                    buffer: buffer.clone(),
 632                    range: origin_buffer.anchor_after(origin_start)
 633                        ..origin_buffer.anchor_before(origin_end),
 634                }
 635            });
 636
 637            let target_buffer = target_buffer_handle.read(cx);
 638            let target_start =
 639                target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
 640            let target_end =
 641                target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
 642            let target_location = Location {
 643                buffer: target_buffer_handle,
 644                range: target_buffer.anchor_after(target_start)
 645                    ..target_buffer.anchor_before(target_end),
 646            };
 647
 648            definitions.push(LocationLink {
 649                origin: origin_location,
 650                target: target_location,
 651            })
 652        });
 653    }
 654    Ok(definitions)
 655}
 656
 657fn location_links_to_proto(
 658    links: Vec<LocationLink>,
 659    project: &mut Project,
 660    peer_id: PeerId,
 661    cx: &mut AppContext,
 662) -> Vec<proto::LocationLink> {
 663    links
 664        .into_iter()
 665        .map(|definition| {
 666            let origin = definition.origin.map(|origin| {
 667                let buffer_id = project.create_buffer_for_peer(&origin.buffer, peer_id, cx);
 668                proto::Location {
 669                    start: Some(serialize_anchor(&origin.range.start)),
 670                    end: Some(serialize_anchor(&origin.range.end)),
 671                    buffer_id,
 672                }
 673            });
 674
 675            let buffer_id = project.create_buffer_for_peer(&definition.target.buffer, peer_id, cx);
 676            let target = proto::Location {
 677                start: Some(serialize_anchor(&definition.target.range.start)),
 678                end: Some(serialize_anchor(&definition.target.range.end)),
 679                buffer_id,
 680            };
 681
 682            proto::LocationLink {
 683                origin,
 684                target: Some(target),
 685            }
 686        })
 687        .collect()
 688}
 689
 690#[async_trait(?Send)]
 691impl LspCommand for GetReferences {
 692    type Response = Vec<Location>;
 693    type LspRequest = lsp::request::References;
 694    type ProtoRequest = proto::GetReferences;
 695
 696    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::ReferenceParams {
 697        lsp::ReferenceParams {
 698            text_document_position: lsp::TextDocumentPositionParams {
 699                text_document: lsp::TextDocumentIdentifier {
 700                    uri: lsp::Url::from_file_path(path).unwrap(),
 701                },
 702                position: point_to_lsp(self.position),
 703            },
 704            work_done_progress_params: Default::default(),
 705            partial_result_params: Default::default(),
 706            context: lsp::ReferenceContext {
 707                include_declaration: true,
 708            },
 709        }
 710    }
 711
 712    async fn response_from_lsp(
 713        self,
 714        locations: Option<Vec<lsp::Location>>,
 715        project: ModelHandle<Project>,
 716        buffer: ModelHandle<Buffer>,
 717        mut cx: AsyncAppContext,
 718    ) -> Result<Vec<Location>> {
 719        let mut references = Vec::new();
 720        let (lsp_adapter, language_server) =
 721            language_server_for_buffer(&project, &buffer, &mut cx)?;
 722
 723        if let Some(locations) = locations {
 724            for lsp_location in locations {
 725                let target_buffer_handle = project
 726                    .update(&mut cx, |this, cx| {
 727                        this.open_local_buffer_via_lsp(
 728                            lsp_location.uri,
 729                            language_server.server_id(),
 730                            lsp_adapter.name.clone(),
 731                            cx,
 732                        )
 733                    })
 734                    .await?;
 735
 736                cx.read(|cx| {
 737                    let target_buffer = target_buffer_handle.read(cx);
 738                    let target_start = target_buffer
 739                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
 740                    let target_end = target_buffer
 741                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
 742                    references.push(Location {
 743                        buffer: target_buffer_handle,
 744                        range: target_buffer.anchor_after(target_start)
 745                            ..target_buffer.anchor_before(target_end),
 746                    });
 747                });
 748            }
 749        }
 750
 751        Ok(references)
 752    }
 753
 754    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
 755        proto::GetReferences {
 756            project_id,
 757            buffer_id: buffer.remote_id(),
 758            position: Some(language::proto::serialize_anchor(
 759                &buffer.anchor_before(self.position),
 760            )),
 761            version: serialize_version(&buffer.version()),
 762        }
 763    }
 764
 765    async fn from_proto(
 766        message: proto::GetReferences,
 767        _: ModelHandle<Project>,
 768        buffer: ModelHandle<Buffer>,
 769        mut cx: AsyncAppContext,
 770    ) -> Result<Self> {
 771        let position = message
 772            .position
 773            .and_then(deserialize_anchor)
 774            .ok_or_else(|| anyhow!("invalid position"))?;
 775        buffer
 776            .update(&mut cx, |buffer, _| {
 777                buffer.wait_for_version(deserialize_version(&message.version))
 778            })
 779            .await?;
 780        Ok(Self {
 781            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
 782        })
 783    }
 784
 785    fn response_to_proto(
 786        response: Vec<Location>,
 787        project: &mut Project,
 788        peer_id: PeerId,
 789        _: &clock::Global,
 790        cx: &mut AppContext,
 791    ) -> proto::GetReferencesResponse {
 792        let locations = response
 793            .into_iter()
 794            .map(|definition| {
 795                let buffer_id = project.create_buffer_for_peer(&definition.buffer, peer_id, cx);
 796                proto::Location {
 797                    start: Some(serialize_anchor(&definition.range.start)),
 798                    end: Some(serialize_anchor(&definition.range.end)),
 799                    buffer_id,
 800                }
 801            })
 802            .collect();
 803        proto::GetReferencesResponse { locations }
 804    }
 805
 806    async fn response_from_proto(
 807        self,
 808        message: proto::GetReferencesResponse,
 809        project: ModelHandle<Project>,
 810        _: ModelHandle<Buffer>,
 811        mut cx: AsyncAppContext,
 812    ) -> Result<Vec<Location>> {
 813        let mut locations = Vec::new();
 814        for location in message.locations {
 815            let target_buffer = project
 816                .update(&mut cx, |this, cx| {
 817                    this.wait_for_remote_buffer(location.buffer_id, cx)
 818                })
 819                .await?;
 820            let start = location
 821                .start
 822                .and_then(deserialize_anchor)
 823                .ok_or_else(|| anyhow!("missing target start"))?;
 824            let end = location
 825                .end
 826                .and_then(deserialize_anchor)
 827                .ok_or_else(|| anyhow!("missing target end"))?;
 828            target_buffer
 829                .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
 830                .await?;
 831            locations.push(Location {
 832                buffer: target_buffer,
 833                range: start..end,
 834            })
 835        }
 836        Ok(locations)
 837    }
 838
 839    fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
 840        message.buffer_id
 841    }
 842}
 843
 844#[async_trait(?Send)]
 845impl LspCommand for GetDocumentHighlights {
 846    type Response = Vec<DocumentHighlight>;
 847    type LspRequest = lsp::request::DocumentHighlightRequest;
 848    type ProtoRequest = proto::GetDocumentHighlights;
 849
 850    fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
 851        capabilities.document_highlight_provider.is_some()
 852    }
 853
 854    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::DocumentHighlightParams {
 855        lsp::DocumentHighlightParams {
 856            text_document_position_params: lsp::TextDocumentPositionParams {
 857                text_document: lsp::TextDocumentIdentifier {
 858                    uri: lsp::Url::from_file_path(path).unwrap(),
 859                },
 860                position: point_to_lsp(self.position),
 861            },
 862            work_done_progress_params: Default::default(),
 863            partial_result_params: Default::default(),
 864        }
 865    }
 866
 867    async fn response_from_lsp(
 868        self,
 869        lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
 870        _: ModelHandle<Project>,
 871        buffer: ModelHandle<Buffer>,
 872        cx: AsyncAppContext,
 873    ) -> Result<Vec<DocumentHighlight>> {
 874        buffer.read_with(&cx, |buffer, _| {
 875            let mut lsp_highlights = lsp_highlights.unwrap_or_default();
 876            lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
 877            Ok(lsp_highlights
 878                .into_iter()
 879                .map(|lsp_highlight| {
 880                    let start = buffer
 881                        .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
 882                    let end = buffer
 883                        .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
 884                    DocumentHighlight {
 885                        range: buffer.anchor_after(start)..buffer.anchor_before(end),
 886                        kind: lsp_highlight
 887                            .kind
 888                            .unwrap_or(lsp::DocumentHighlightKind::READ),
 889                    }
 890                })
 891                .collect())
 892        })
 893    }
 894
 895    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
 896        proto::GetDocumentHighlights {
 897            project_id,
 898            buffer_id: buffer.remote_id(),
 899            position: Some(language::proto::serialize_anchor(
 900                &buffer.anchor_before(self.position),
 901            )),
 902            version: serialize_version(&buffer.version()),
 903        }
 904    }
 905
 906    async fn from_proto(
 907        message: proto::GetDocumentHighlights,
 908        _: ModelHandle<Project>,
 909        buffer: ModelHandle<Buffer>,
 910        mut cx: AsyncAppContext,
 911    ) -> Result<Self> {
 912        let position = message
 913            .position
 914            .and_then(deserialize_anchor)
 915            .ok_or_else(|| anyhow!("invalid position"))?;
 916        buffer
 917            .update(&mut cx, |buffer, _| {
 918                buffer.wait_for_version(deserialize_version(&message.version))
 919            })
 920            .await?;
 921        Ok(Self {
 922            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
 923        })
 924    }
 925
 926    fn response_to_proto(
 927        response: Vec<DocumentHighlight>,
 928        _: &mut Project,
 929        _: PeerId,
 930        _: &clock::Global,
 931        _: &mut AppContext,
 932    ) -> proto::GetDocumentHighlightsResponse {
 933        let highlights = response
 934            .into_iter()
 935            .map(|highlight| proto::DocumentHighlight {
 936                start: Some(serialize_anchor(&highlight.range.start)),
 937                end: Some(serialize_anchor(&highlight.range.end)),
 938                kind: match highlight.kind {
 939                    DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
 940                    DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
 941                    DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
 942                    _ => proto::document_highlight::Kind::Text.into(),
 943                },
 944            })
 945            .collect();
 946        proto::GetDocumentHighlightsResponse { highlights }
 947    }
 948
 949    async fn response_from_proto(
 950        self,
 951        message: proto::GetDocumentHighlightsResponse,
 952        _: ModelHandle<Project>,
 953        buffer: ModelHandle<Buffer>,
 954        mut cx: AsyncAppContext,
 955    ) -> Result<Vec<DocumentHighlight>> {
 956        let mut highlights = Vec::new();
 957        for highlight in message.highlights {
 958            let start = highlight
 959                .start
 960                .and_then(deserialize_anchor)
 961                .ok_or_else(|| anyhow!("missing target start"))?;
 962            let end = highlight
 963                .end
 964                .and_then(deserialize_anchor)
 965                .ok_or_else(|| anyhow!("missing target end"))?;
 966            buffer
 967                .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
 968                .await?;
 969            let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
 970                Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
 971                Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
 972                Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
 973                None => DocumentHighlightKind::TEXT,
 974            };
 975            highlights.push(DocumentHighlight {
 976                range: start..end,
 977                kind,
 978            });
 979        }
 980        Ok(highlights)
 981    }
 982
 983    fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
 984        message.buffer_id
 985    }
 986}
 987
 988#[async_trait(?Send)]
 989impl LspCommand for GetHover {
 990    type Response = Option<Hover>;
 991    type LspRequest = lsp::request::HoverRequest;
 992    type ProtoRequest = proto::GetHover;
 993
 994    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::HoverParams {
 995        lsp::HoverParams {
 996            text_document_position_params: lsp::TextDocumentPositionParams {
 997                text_document: lsp::TextDocumentIdentifier {
 998                    uri: lsp::Url::from_file_path(path).unwrap(),
 999                },
1000                position: point_to_lsp(self.position),
1001            },
1002            work_done_progress_params: Default::default(),
1003        }
1004    }
1005
1006    async fn response_from_lsp(
1007        self,
1008        message: Option<lsp::Hover>,
1009        _: ModelHandle<Project>,
1010        buffer: ModelHandle<Buffer>,
1011        cx: AsyncAppContext,
1012    ) -> Result<Self::Response> {
1013        Ok(message.and_then(|hover| {
1014            let range = hover.range.map(|range| {
1015                cx.read(|cx| {
1016                    let buffer = buffer.read(cx);
1017                    let token_start =
1018                        buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1019                    let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1020                    buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1021                })
1022            });
1023
1024            let contents = cx.read(|_| match hover.contents {
1025                lsp::HoverContents::Scalar(marked_string) => {
1026                    HoverBlock::try_new(marked_string).map(|contents| vec![contents])
1027                }
1028                lsp::HoverContents::Array(marked_strings) => {
1029                    let content: Vec<HoverBlock> = marked_strings
1030                        .into_iter()
1031                        .filter_map(HoverBlock::try_new)
1032                        .collect();
1033                    if content.is_empty() {
1034                        None
1035                    } else {
1036                        Some(content)
1037                    }
1038                }
1039                lsp::HoverContents::Markup(markup_content) => {
1040                    let mut contents = Vec::new();
1041                    let mut language = None;
1042                    let mut current_text = String::new();
1043                    for event in Parser::new_ext(&markup_content.value, Options::all()) {
1044                        match event {
1045                            Event::SoftBreak => {
1046                                current_text.push(' ');
1047                            }
1048                            Event::Text(text) | Event::Code(text) => {
1049                                current_text.push_str(&text.to_string());
1050                            }
1051                            Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(new_language))) => {
1052                                if !current_text.is_empty() {
1053                                    let text = std::mem::take(&mut current_text).trim().to_string();
1054                                    contents.push(HoverBlock { text, language });
1055                                }
1056
1057                                language = if new_language.is_empty() {
1058                                    None
1059                                } else {
1060                                    Some(new_language.to_string())
1061                                };
1062                            }
1063                            Event::End(Tag::CodeBlock(_))
1064                            | Event::End(Tag::Paragraph)
1065                            | Event::End(Tag::Heading(_, _, _))
1066                            | Event::End(Tag::BlockQuote)
1067                            | Event::HardBreak => {
1068                                if !current_text.is_empty() {
1069                                    let text = std::mem::take(&mut current_text).trim().to_string();
1070                                    contents.push(HoverBlock { text, language });
1071                                }
1072                                language = None;
1073                            }
1074                            _ => {}
1075                        }
1076                    }
1077
1078                    if !current_text.trim().is_empty() {
1079                        contents.push(HoverBlock {
1080                            text: current_text,
1081                            language,
1082                        });
1083                    }
1084
1085                    if contents.is_empty() {
1086                        None
1087                    } else {
1088                        Some(contents)
1089                    }
1090                }
1091            });
1092
1093            contents.map(|contents| Hover { contents, range })
1094        }))
1095    }
1096
1097    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1098        proto::GetHover {
1099            project_id,
1100            buffer_id: buffer.remote_id(),
1101            position: Some(language::proto::serialize_anchor(
1102                &buffer.anchor_before(self.position),
1103            )),
1104            version: serialize_version(&buffer.version),
1105        }
1106    }
1107
1108    async fn from_proto(
1109        message: Self::ProtoRequest,
1110        _: ModelHandle<Project>,
1111        buffer: ModelHandle<Buffer>,
1112        mut cx: AsyncAppContext,
1113    ) -> Result<Self> {
1114        let position = message
1115            .position
1116            .and_then(deserialize_anchor)
1117            .ok_or_else(|| anyhow!("invalid position"))?;
1118        buffer
1119            .update(&mut cx, |buffer, _| {
1120                buffer.wait_for_version(deserialize_version(&message.version))
1121            })
1122            .await?;
1123        Ok(Self {
1124            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1125        })
1126    }
1127
1128    fn response_to_proto(
1129        response: Self::Response,
1130        _: &mut Project,
1131        _: PeerId,
1132        _: &clock::Global,
1133        _: &mut AppContext,
1134    ) -> proto::GetHoverResponse {
1135        if let Some(response) = response {
1136            let (start, end) = if let Some(range) = response.range {
1137                (
1138                    Some(language::proto::serialize_anchor(&range.start)),
1139                    Some(language::proto::serialize_anchor(&range.end)),
1140                )
1141            } else {
1142                (None, None)
1143            };
1144
1145            let contents = response
1146                .contents
1147                .into_iter()
1148                .map(|block| proto::HoverBlock {
1149                    text: block.text,
1150                    language: block.language,
1151                })
1152                .collect();
1153
1154            proto::GetHoverResponse {
1155                start,
1156                end,
1157                contents,
1158            }
1159        } else {
1160            proto::GetHoverResponse {
1161                start: None,
1162                end: None,
1163                contents: Vec::new(),
1164            }
1165        }
1166    }
1167
1168    async fn response_from_proto(
1169        self,
1170        message: proto::GetHoverResponse,
1171        _: ModelHandle<Project>,
1172        _: ModelHandle<Buffer>,
1173        _: AsyncAppContext,
1174    ) -> Result<Self::Response> {
1175        let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1176            language::proto::deserialize_anchor(start)
1177                .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
1178        } else {
1179            None
1180        };
1181
1182        let contents: Vec<_> = message
1183            .contents
1184            .into_iter()
1185            .map(|block| HoverBlock {
1186                text: block.text,
1187                language: block.language,
1188            })
1189            .collect();
1190
1191        Ok(if contents.is_empty() {
1192            None
1193        } else {
1194            Some(Hover { contents, range })
1195        })
1196    }
1197
1198    fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 {
1199        message.buffer_id
1200    }
1201}