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