lsp_command.rs

   1use crate::{
   2    CodeAction, CoreCompletion, DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint,
   3    InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location,
   4    LocationLink, MarkupContent, Project, ProjectTransaction, ResolveState,
   5};
   6use anyhow::{anyhow, Context, Result};
   7use async_trait::async_trait;
   8use client::proto::{self, PeerId};
   9use futures::future;
  10use gpui::{AppContext, AsyncAppContext, Model};
  11use language::{
  12    language_settings::{language_settings, InlayHintKind},
  13    point_from_lsp, point_to_lsp,
  14    proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
  15    range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind,
  16    OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped,
  17};
  18use lsp::{
  19    CompletionListItemDefaultsEditRange, DocumentHighlightKind, LanguageServer, LanguageServerId,
  20    OneOf, ServerCapabilities,
  21};
  22use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
  23use text::{BufferId, LineEnding};
  24
  25pub fn lsp_formatting_options(tab_size: u32) -> lsp::FormattingOptions {
  26    lsp::FormattingOptions {
  27        tab_size,
  28        insert_spaces: true,
  29        insert_final_newline: Some(true),
  30        ..lsp::FormattingOptions::default()
  31    }
  32}
  33
  34#[async_trait(?Send)]
  35pub trait LspCommand: 'static + Sized + Send {
  36    type Response: 'static + Default + Send;
  37    type LspRequest: 'static + Send + lsp::request::Request;
  38    type ProtoRequest: 'static + Send + proto::RequestMessage;
  39
  40    fn check_capabilities(&self, _: &lsp::ServerCapabilities) -> bool {
  41        true
  42    }
  43
  44    fn status(&self) -> Option<String> {
  45        None
  46    }
  47
  48    fn to_lsp(
  49        &self,
  50        path: &Path,
  51        buffer: &Buffer,
  52        language_server: &Arc<LanguageServer>,
  53        cx: &AppContext,
  54    ) -> <Self::LspRequest as lsp::request::Request>::Params;
  55
  56    async fn response_from_lsp(
  57        self,
  58        message: <Self::LspRequest as lsp::request::Request>::Result,
  59        project: Model<Project>,
  60        buffer: Model<Buffer>,
  61        server_id: LanguageServerId,
  62        cx: AsyncAppContext,
  63    ) -> Result<Self::Response>;
  64
  65    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
  66
  67    async fn from_proto(
  68        message: Self::ProtoRequest,
  69        project: Model<Project>,
  70        buffer: Model<Buffer>,
  71        cx: AsyncAppContext,
  72    ) -> Result<Self>;
  73
  74    fn response_to_proto(
  75        response: Self::Response,
  76        project: &mut Project,
  77        peer_id: PeerId,
  78        buffer_version: &clock::Global,
  79        cx: &mut AppContext,
  80    ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
  81
  82    async fn response_from_proto(
  83        self,
  84        message: <Self::ProtoRequest as proto::RequestMessage>::Response,
  85        project: Model<Project>,
  86        buffer: Model<Buffer>,
  87        cx: AsyncAppContext,
  88    ) -> Result<Self::Response>;
  89
  90    fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId>;
  91}
  92
  93pub(crate) struct PrepareRename {
  94    pub position: PointUtf16,
  95}
  96
  97pub(crate) struct PerformRename {
  98    pub position: PointUtf16,
  99    pub new_name: String,
 100    pub push_to_history: bool,
 101}
 102
 103pub struct GetDefinition {
 104    pub position: PointUtf16,
 105}
 106
 107pub(crate) struct GetTypeDefinition {
 108    pub position: PointUtf16,
 109}
 110
 111pub(crate) struct GetImplementation {
 112    pub position: PointUtf16,
 113}
 114
 115pub(crate) struct GetReferences {
 116    pub position: PointUtf16,
 117}
 118
 119pub(crate) struct GetDocumentHighlights {
 120    pub position: PointUtf16,
 121}
 122
 123#[derive(Clone)]
 124pub(crate) struct GetHover {
 125    pub position: PointUtf16,
 126}
 127
 128pub(crate) struct GetCompletions {
 129    pub position: PointUtf16,
 130}
 131
 132#[derive(Clone)]
 133pub(crate) struct GetCodeActions {
 134    pub range: Range<Anchor>,
 135    pub kinds: Option<Vec<lsp::CodeActionKind>>,
 136}
 137
 138pub(crate) struct OnTypeFormatting {
 139    pub position: PointUtf16,
 140    pub trigger: String,
 141    pub options: FormattingOptions,
 142    pub push_to_history: bool,
 143}
 144
 145pub(crate) struct InlayHints {
 146    pub range: Range<Anchor>,
 147}
 148
 149pub(crate) struct FormattingOptions {
 150    tab_size: u32,
 151}
 152
 153impl From<lsp::FormattingOptions> for FormattingOptions {
 154    fn from(value: lsp::FormattingOptions) -> Self {
 155        Self {
 156            tab_size: value.tab_size,
 157        }
 158    }
 159}
 160
 161#[async_trait(?Send)]
 162impl LspCommand for PrepareRename {
 163    type Response = Option<Range<Anchor>>;
 164    type LspRequest = lsp::request::PrepareRenameRequest;
 165    type ProtoRequest = proto::PrepareRename;
 166
 167    fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
 168        if let Some(lsp::OneOf::Right(rename)) = &capabilities.rename_provider {
 169            rename.prepare_provider == Some(true)
 170        } else {
 171            false
 172        }
 173    }
 174
 175    fn to_lsp(
 176        &self,
 177        path: &Path,
 178        _: &Buffer,
 179        _: &Arc<LanguageServer>,
 180        _: &AppContext,
 181    ) -> lsp::TextDocumentPositionParams {
 182        lsp::TextDocumentPositionParams {
 183            text_document: lsp::TextDocumentIdentifier {
 184                uri: lsp::Url::from_file_path(path).unwrap(),
 185            },
 186            position: point_to_lsp(self.position),
 187        }
 188    }
 189
 190    async fn response_from_lsp(
 191        self,
 192        message: Option<lsp::PrepareRenameResponse>,
 193        _: Model<Project>,
 194        buffer: Model<Buffer>,
 195        _: LanguageServerId,
 196        mut cx: AsyncAppContext,
 197    ) -> Result<Option<Range<Anchor>>> {
 198        buffer.update(&mut cx, |buffer, _| {
 199            if let Some(
 200                lsp::PrepareRenameResponse::Range(range)
 201                | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
 202            ) = message
 203            {
 204                let Range { start, end } = range_from_lsp(range);
 205                if buffer.clip_point_utf16(start, Bias::Left) == start.0
 206                    && buffer.clip_point_utf16(end, Bias::Left) == end.0
 207                {
 208                    return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
 209                }
 210            }
 211            Ok(None)
 212        })?
 213    }
 214
 215    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
 216        proto::PrepareRename {
 217            project_id,
 218            buffer_id: buffer.remote_id().into(),
 219            position: Some(language::proto::serialize_anchor(
 220                &buffer.anchor_before(self.position),
 221            )),
 222            version: serialize_version(&buffer.version()),
 223        }
 224    }
 225
 226    async fn from_proto(
 227        message: proto::PrepareRename,
 228        _: Model<Project>,
 229        buffer: Model<Buffer>,
 230        mut cx: AsyncAppContext,
 231    ) -> Result<Self> {
 232        let position = message
 233            .position
 234            .and_then(deserialize_anchor)
 235            .ok_or_else(|| anyhow!("invalid position"))?;
 236        buffer
 237            .update(&mut cx, |buffer, _| {
 238                buffer.wait_for_version(deserialize_version(&message.version))
 239            })?
 240            .await?;
 241
 242        Ok(Self {
 243            position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
 244        })
 245    }
 246
 247    fn response_to_proto(
 248        range: Option<Range<Anchor>>,
 249        _: &mut Project,
 250        _: PeerId,
 251        buffer_version: &clock::Global,
 252        _: &mut AppContext,
 253    ) -> proto::PrepareRenameResponse {
 254        proto::PrepareRenameResponse {
 255            can_rename: range.is_some(),
 256            start: range
 257                .as_ref()
 258                .map(|range| language::proto::serialize_anchor(&range.start)),
 259            end: range
 260                .as_ref()
 261                .map(|range| language::proto::serialize_anchor(&range.end)),
 262            version: serialize_version(buffer_version),
 263        }
 264    }
 265
 266    async fn response_from_proto(
 267        self,
 268        message: proto::PrepareRenameResponse,
 269        _: Model<Project>,
 270        buffer: Model<Buffer>,
 271        mut cx: AsyncAppContext,
 272    ) -> Result<Option<Range<Anchor>>> {
 273        if message.can_rename {
 274            buffer
 275                .update(&mut cx, |buffer, _| {
 276                    buffer.wait_for_version(deserialize_version(&message.version))
 277                })?
 278                .await?;
 279            let start = message.start.and_then(deserialize_anchor);
 280            let end = message.end.and_then(deserialize_anchor);
 281            Ok(start.zip(end).map(|(start, end)| start..end))
 282        } else {
 283            Ok(None)
 284        }
 285    }
 286
 287    fn buffer_id_from_proto(message: &proto::PrepareRename) -> Result<BufferId> {
 288        BufferId::new(message.buffer_id)
 289    }
 290}
 291
 292#[async_trait(?Send)]
 293impl LspCommand for PerformRename {
 294    type Response = ProjectTransaction;
 295    type LspRequest = lsp::request::Rename;
 296    type ProtoRequest = proto::PerformRename;
 297
 298    fn to_lsp(
 299        &self,
 300        path: &Path,
 301        _: &Buffer,
 302        _: &Arc<LanguageServer>,
 303        _: &AppContext,
 304    ) -> lsp::RenameParams {
 305        lsp::RenameParams {
 306            text_document_position: lsp::TextDocumentPositionParams {
 307                text_document: lsp::TextDocumentIdentifier {
 308                    uri: lsp::Url::from_file_path(path).unwrap(),
 309                },
 310                position: point_to_lsp(self.position),
 311            },
 312            new_name: self.new_name.clone(),
 313            work_done_progress_params: Default::default(),
 314        }
 315    }
 316
 317    async fn response_from_lsp(
 318        self,
 319        message: Option<lsp::WorkspaceEdit>,
 320        project: Model<Project>,
 321        buffer: Model<Buffer>,
 322        server_id: LanguageServerId,
 323        mut cx: AsyncAppContext,
 324    ) -> Result<ProjectTransaction> {
 325        if let Some(edit) = message {
 326            let (lsp_adapter, lsp_server) =
 327                language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
 328            Project::deserialize_workspace_edit(
 329                project,
 330                edit,
 331                self.push_to_history,
 332                lsp_adapter,
 333                lsp_server,
 334                &mut cx,
 335            )
 336            .await
 337        } else {
 338            Ok(ProjectTransaction::default())
 339        }
 340    }
 341
 342    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
 343        proto::PerformRename {
 344            project_id,
 345            buffer_id: buffer.remote_id().into(),
 346            position: Some(language::proto::serialize_anchor(
 347                &buffer.anchor_before(self.position),
 348            )),
 349            new_name: self.new_name.clone(),
 350            version: serialize_version(&buffer.version()),
 351        }
 352    }
 353
 354    async fn from_proto(
 355        message: proto::PerformRename,
 356        _: Model<Project>,
 357        buffer: Model<Buffer>,
 358        mut cx: AsyncAppContext,
 359    ) -> Result<Self> {
 360        let position = message
 361            .position
 362            .and_then(deserialize_anchor)
 363            .ok_or_else(|| anyhow!("invalid position"))?;
 364        buffer
 365            .update(&mut cx, |buffer, _| {
 366                buffer.wait_for_version(deserialize_version(&message.version))
 367            })?
 368            .await?;
 369        Ok(Self {
 370            position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
 371            new_name: message.new_name,
 372            push_to_history: false,
 373        })
 374    }
 375
 376    fn response_to_proto(
 377        response: ProjectTransaction,
 378        project: &mut Project,
 379        peer_id: PeerId,
 380        _: &clock::Global,
 381        cx: &mut AppContext,
 382    ) -> proto::PerformRenameResponse {
 383        let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
 384        proto::PerformRenameResponse {
 385            transaction: Some(transaction),
 386        }
 387    }
 388
 389    async fn response_from_proto(
 390        self,
 391        message: proto::PerformRenameResponse,
 392        project: Model<Project>,
 393        _: Model<Buffer>,
 394        mut cx: AsyncAppContext,
 395    ) -> Result<ProjectTransaction> {
 396        let message = message
 397            .transaction
 398            .ok_or_else(|| anyhow!("missing transaction"))?;
 399        project
 400            .update(&mut cx, |project, cx| {
 401                project.deserialize_project_transaction(message, self.push_to_history, cx)
 402            })?
 403            .await
 404    }
 405
 406    fn buffer_id_from_proto(message: &proto::PerformRename) -> Result<BufferId> {
 407        BufferId::new(message.buffer_id)
 408    }
 409}
 410
 411#[async_trait(?Send)]
 412impl LspCommand for GetDefinition {
 413    type Response = Vec<LocationLink>;
 414    type LspRequest = lsp::request::GotoDefinition;
 415    type ProtoRequest = proto::GetDefinition;
 416
 417    fn to_lsp(
 418        &self,
 419        path: &Path,
 420        _: &Buffer,
 421        _: &Arc<LanguageServer>,
 422        _: &AppContext,
 423    ) -> lsp::GotoDefinitionParams {
 424        lsp::GotoDefinitionParams {
 425            text_document_position_params: lsp::TextDocumentPositionParams {
 426                text_document: lsp::TextDocumentIdentifier {
 427                    uri: lsp::Url::from_file_path(path).unwrap(),
 428                },
 429                position: point_to_lsp(self.position),
 430            },
 431            work_done_progress_params: Default::default(),
 432            partial_result_params: Default::default(),
 433        }
 434    }
 435
 436    async fn response_from_lsp(
 437        self,
 438        message: Option<lsp::GotoDefinitionResponse>,
 439        project: Model<Project>,
 440        buffer: Model<Buffer>,
 441        server_id: LanguageServerId,
 442        cx: AsyncAppContext,
 443    ) -> Result<Vec<LocationLink>> {
 444        location_links_from_lsp(message, project, buffer, server_id, cx).await
 445    }
 446
 447    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
 448        proto::GetDefinition {
 449            project_id,
 450            buffer_id: buffer.remote_id().into(),
 451            position: Some(language::proto::serialize_anchor(
 452                &buffer.anchor_before(self.position),
 453            )),
 454            version: serialize_version(&buffer.version()),
 455        }
 456    }
 457
 458    async fn from_proto(
 459        message: proto::GetDefinition,
 460        _: Model<Project>,
 461        buffer: Model<Buffer>,
 462        mut cx: AsyncAppContext,
 463    ) -> Result<Self> {
 464        let position = message
 465            .position
 466            .and_then(deserialize_anchor)
 467            .ok_or_else(|| anyhow!("invalid position"))?;
 468        buffer
 469            .update(&mut cx, |buffer, _| {
 470                buffer.wait_for_version(deserialize_version(&message.version))
 471            })?
 472            .await?;
 473        Ok(Self {
 474            position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
 475        })
 476    }
 477
 478    fn response_to_proto(
 479        response: Vec<LocationLink>,
 480        project: &mut Project,
 481        peer_id: PeerId,
 482        _: &clock::Global,
 483        cx: &mut AppContext,
 484    ) -> proto::GetDefinitionResponse {
 485        let links = location_links_to_proto(response, project, peer_id, cx);
 486        proto::GetDefinitionResponse { links }
 487    }
 488
 489    async fn response_from_proto(
 490        self,
 491        message: proto::GetDefinitionResponse,
 492        project: Model<Project>,
 493        _: Model<Buffer>,
 494        cx: AsyncAppContext,
 495    ) -> Result<Vec<LocationLink>> {
 496        location_links_from_proto(message.links, project, cx).await
 497    }
 498
 499    fn buffer_id_from_proto(message: &proto::GetDefinition) -> Result<BufferId> {
 500        BufferId::new(message.buffer_id)
 501    }
 502}
 503
 504#[async_trait(?Send)]
 505impl LspCommand for GetImplementation {
 506    type Response = Vec<LocationLink>;
 507    type LspRequest = lsp::request::GotoImplementation;
 508    type ProtoRequest = proto::GetImplementation;
 509
 510    fn to_lsp(
 511        &self,
 512        path: &Path,
 513        _: &Buffer,
 514        _: &Arc<LanguageServer>,
 515        _: &AppContext,
 516    ) -> lsp::GotoImplementationParams {
 517        lsp::GotoImplementationParams {
 518            text_document_position_params: lsp::TextDocumentPositionParams {
 519                text_document: lsp::TextDocumentIdentifier {
 520                    uri: lsp::Url::from_file_path(path).unwrap(),
 521                },
 522                position: point_to_lsp(self.position),
 523            },
 524            work_done_progress_params: Default::default(),
 525            partial_result_params: Default::default(),
 526        }
 527    }
 528
 529    async fn response_from_lsp(
 530        self,
 531        message: Option<lsp::GotoImplementationResponse>,
 532        project: Model<Project>,
 533        buffer: Model<Buffer>,
 534        server_id: LanguageServerId,
 535        cx: AsyncAppContext,
 536    ) -> Result<Vec<LocationLink>> {
 537        location_links_from_lsp(message, project, buffer, server_id, cx).await
 538    }
 539
 540    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetImplementation {
 541        proto::GetImplementation {
 542            project_id,
 543            buffer_id: buffer.remote_id().into(),
 544            position: Some(language::proto::serialize_anchor(
 545                &buffer.anchor_before(self.position),
 546            )),
 547            version: serialize_version(&buffer.version()),
 548        }
 549    }
 550
 551    async fn from_proto(
 552        message: proto::GetImplementation,
 553        _: Model<Project>,
 554        buffer: Model<Buffer>,
 555        mut cx: AsyncAppContext,
 556    ) -> Result<Self> {
 557        let position = message
 558            .position
 559            .and_then(deserialize_anchor)
 560            .ok_or_else(|| anyhow!("invalid position"))?;
 561        buffer
 562            .update(&mut cx, |buffer, _| {
 563                buffer.wait_for_version(deserialize_version(&message.version))
 564            })?
 565            .await?;
 566        Ok(Self {
 567            position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
 568        })
 569    }
 570
 571    fn response_to_proto(
 572        response: Vec<LocationLink>,
 573        project: &mut Project,
 574        peer_id: PeerId,
 575        _: &clock::Global,
 576        cx: &mut AppContext,
 577    ) -> proto::GetImplementationResponse {
 578        let links = location_links_to_proto(response, project, peer_id, cx);
 579        proto::GetImplementationResponse { links }
 580    }
 581
 582    async fn response_from_proto(
 583        self,
 584        message: proto::GetImplementationResponse,
 585        project: Model<Project>,
 586        _: Model<Buffer>,
 587        cx: AsyncAppContext,
 588    ) -> Result<Vec<LocationLink>> {
 589        location_links_from_proto(message.links, project, cx).await
 590    }
 591
 592    fn buffer_id_from_proto(message: &proto::GetImplementation) -> Result<BufferId> {
 593        BufferId::new(message.buffer_id)
 594    }
 595}
 596
 597#[async_trait(?Send)]
 598impl LspCommand for GetTypeDefinition {
 599    type Response = Vec<LocationLink>;
 600    type LspRequest = lsp::request::GotoTypeDefinition;
 601    type ProtoRequest = proto::GetTypeDefinition;
 602
 603    fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
 604        match &capabilities.type_definition_provider {
 605            None => false,
 606            Some(lsp::TypeDefinitionProviderCapability::Simple(false)) => false,
 607            _ => true,
 608        }
 609    }
 610
 611    fn to_lsp(
 612        &self,
 613        path: &Path,
 614        _: &Buffer,
 615        _: &Arc<LanguageServer>,
 616        _: &AppContext,
 617    ) -> lsp::GotoTypeDefinitionParams {
 618        lsp::GotoTypeDefinitionParams {
 619            text_document_position_params: lsp::TextDocumentPositionParams {
 620                text_document: lsp::TextDocumentIdentifier {
 621                    uri: lsp::Url::from_file_path(path).unwrap(),
 622                },
 623                position: point_to_lsp(self.position),
 624            },
 625            work_done_progress_params: Default::default(),
 626            partial_result_params: Default::default(),
 627        }
 628    }
 629
 630    async fn response_from_lsp(
 631        self,
 632        message: Option<lsp::GotoTypeDefinitionResponse>,
 633        project: Model<Project>,
 634        buffer: Model<Buffer>,
 635        server_id: LanguageServerId,
 636        cx: AsyncAppContext,
 637    ) -> Result<Vec<LocationLink>> {
 638        location_links_from_lsp(message, project, buffer, server_id, cx).await
 639    }
 640
 641    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition {
 642        proto::GetTypeDefinition {
 643            project_id,
 644            buffer_id: buffer.remote_id().into(),
 645            position: Some(language::proto::serialize_anchor(
 646                &buffer.anchor_before(self.position),
 647            )),
 648            version: serialize_version(&buffer.version()),
 649        }
 650    }
 651
 652    async fn from_proto(
 653        message: proto::GetTypeDefinition,
 654        _: Model<Project>,
 655        buffer: Model<Buffer>,
 656        mut cx: AsyncAppContext,
 657    ) -> Result<Self> {
 658        let position = message
 659            .position
 660            .and_then(deserialize_anchor)
 661            .ok_or_else(|| anyhow!("invalid position"))?;
 662        buffer
 663            .update(&mut cx, |buffer, _| {
 664                buffer.wait_for_version(deserialize_version(&message.version))
 665            })?
 666            .await?;
 667        Ok(Self {
 668            position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
 669        })
 670    }
 671
 672    fn response_to_proto(
 673        response: Vec<LocationLink>,
 674        project: &mut Project,
 675        peer_id: PeerId,
 676        _: &clock::Global,
 677        cx: &mut AppContext,
 678    ) -> proto::GetTypeDefinitionResponse {
 679        let links = location_links_to_proto(response, project, peer_id, cx);
 680        proto::GetTypeDefinitionResponse { links }
 681    }
 682
 683    async fn response_from_proto(
 684        self,
 685        message: proto::GetTypeDefinitionResponse,
 686        project: Model<Project>,
 687        _: Model<Buffer>,
 688        cx: AsyncAppContext,
 689    ) -> Result<Vec<LocationLink>> {
 690        location_links_from_proto(message.links, project, cx).await
 691    }
 692
 693    fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> Result<BufferId> {
 694        BufferId::new(message.buffer_id)
 695    }
 696}
 697
 698fn language_server_for_buffer(
 699    project: &Model<Project>,
 700    buffer: &Model<Buffer>,
 701    server_id: LanguageServerId,
 702    cx: &mut AsyncAppContext,
 703) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
 704    project
 705        .update(cx, |project, cx| {
 706            project
 707                .language_server_for_buffer(buffer.read(cx), server_id, cx)
 708                .map(|(adapter, server)| (adapter.clone(), server.clone()))
 709        })?
 710        .ok_or_else(|| anyhow!("no language server found for buffer"))
 711}
 712
 713async fn location_links_from_proto(
 714    proto_links: Vec<proto::LocationLink>,
 715    project: Model<Project>,
 716    mut cx: AsyncAppContext,
 717) -> Result<Vec<LocationLink>> {
 718    let mut links = Vec::new();
 719
 720    for link in proto_links {
 721        let origin = match link.origin {
 722            Some(origin) => {
 723                let buffer_id = BufferId::new(origin.buffer_id)?;
 724                let buffer = project
 725                    .update(&mut cx, |this, cx| {
 726                        this.wait_for_remote_buffer(buffer_id, cx)
 727                    })?
 728                    .await?;
 729                let start = origin
 730                    .start
 731                    .and_then(deserialize_anchor)
 732                    .ok_or_else(|| anyhow!("missing origin start"))?;
 733                let end = origin
 734                    .end
 735                    .and_then(deserialize_anchor)
 736                    .ok_or_else(|| anyhow!("missing origin end"))?;
 737                buffer
 738                    .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
 739                    .await?;
 740                Some(Location {
 741                    buffer,
 742                    range: start..end,
 743                })
 744            }
 745            None => None,
 746        };
 747
 748        let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
 749        let buffer_id = BufferId::new(target.buffer_id)?;
 750        let buffer = project
 751            .update(&mut cx, |this, cx| {
 752                this.wait_for_remote_buffer(buffer_id, cx)
 753            })?
 754            .await?;
 755        let start = target
 756            .start
 757            .and_then(deserialize_anchor)
 758            .ok_or_else(|| anyhow!("missing target start"))?;
 759        let end = target
 760            .end
 761            .and_then(deserialize_anchor)
 762            .ok_or_else(|| anyhow!("missing target end"))?;
 763        buffer
 764            .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
 765            .await?;
 766        let target = Location {
 767            buffer,
 768            range: start..end,
 769        };
 770
 771        links.push(LocationLink { origin, target })
 772    }
 773
 774    Ok(links)
 775}
 776
 777async fn location_links_from_lsp(
 778    message: Option<lsp::GotoDefinitionResponse>,
 779    project: Model<Project>,
 780    buffer: Model<Buffer>,
 781    server_id: LanguageServerId,
 782    mut cx: AsyncAppContext,
 783) -> Result<Vec<LocationLink>> {
 784    let message = match message {
 785        Some(message) => message,
 786        None => return Ok(Vec::new()),
 787    };
 788
 789    let mut unresolved_links = Vec::new();
 790    match message {
 791        lsp::GotoDefinitionResponse::Scalar(loc) => {
 792            unresolved_links.push((None, loc.uri, loc.range));
 793        }
 794
 795        lsp::GotoDefinitionResponse::Array(locs) => {
 796            unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
 797        }
 798
 799        lsp::GotoDefinitionResponse::Link(links) => {
 800            unresolved_links.extend(links.into_iter().map(|l| {
 801                (
 802                    l.origin_selection_range,
 803                    l.target_uri,
 804                    l.target_selection_range,
 805                )
 806            }));
 807        }
 808    }
 809
 810    let (lsp_adapter, language_server) =
 811        language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
 812    let mut definitions = Vec::new();
 813    for (origin_range, target_uri, target_range) in unresolved_links {
 814        let target_buffer_handle = project
 815            .update(&mut cx, |this, cx| {
 816                this.open_local_buffer_via_lsp(
 817                    target_uri,
 818                    language_server.server_id(),
 819                    lsp_adapter.name.clone(),
 820                    cx,
 821                )
 822            })?
 823            .await?;
 824
 825        cx.update(|cx| {
 826            let origin_location = origin_range.map(|origin_range| {
 827                let origin_buffer = buffer.read(cx);
 828                let origin_start =
 829                    origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
 830                let origin_end =
 831                    origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
 832                Location {
 833                    buffer: buffer.clone(),
 834                    range: origin_buffer.anchor_after(origin_start)
 835                        ..origin_buffer.anchor_before(origin_end),
 836                }
 837            });
 838
 839            let target_buffer = target_buffer_handle.read(cx);
 840            let target_start =
 841                target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
 842            let target_end =
 843                target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
 844            let target_location = Location {
 845                buffer: target_buffer_handle,
 846                range: target_buffer.anchor_after(target_start)
 847                    ..target_buffer.anchor_before(target_end),
 848            };
 849
 850            definitions.push(LocationLink {
 851                origin: origin_location,
 852                target: target_location,
 853            })
 854        })?;
 855    }
 856    Ok(definitions)
 857}
 858
 859fn location_links_to_proto(
 860    links: Vec<LocationLink>,
 861    project: &mut Project,
 862    peer_id: PeerId,
 863    cx: &mut AppContext,
 864) -> Vec<proto::LocationLink> {
 865    links
 866        .into_iter()
 867        .map(|definition| {
 868            let origin = definition.origin.map(|origin| {
 869                let buffer_id = project
 870                    .create_buffer_for_peer(&origin.buffer, peer_id, cx)
 871                    .into();
 872                proto::Location {
 873                    start: Some(serialize_anchor(&origin.range.start)),
 874                    end: Some(serialize_anchor(&origin.range.end)),
 875                    buffer_id,
 876                }
 877            });
 878
 879            let buffer_id = project
 880                .create_buffer_for_peer(&definition.target.buffer, peer_id, cx)
 881                .into();
 882            let target = proto::Location {
 883                start: Some(serialize_anchor(&definition.target.range.start)),
 884                end: Some(serialize_anchor(&definition.target.range.end)),
 885                buffer_id,
 886            };
 887
 888            proto::LocationLink {
 889                origin,
 890                target: Some(target),
 891            }
 892        })
 893        .collect()
 894}
 895
 896#[async_trait(?Send)]
 897impl LspCommand for GetReferences {
 898    type Response = Vec<Location>;
 899    type LspRequest = lsp::request::References;
 900    type ProtoRequest = proto::GetReferences;
 901
 902    fn status(&self) -> Option<String> {
 903        return Some("Finding references...".to_owned());
 904    }
 905
 906    fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
 907        match &capabilities.references_provider {
 908            Some(OneOf::Left(has_support)) => *has_support,
 909            Some(OneOf::Right(_)) => true,
 910            None => false,
 911        }
 912    }
 913
 914    fn to_lsp(
 915        &self,
 916        path: &Path,
 917        _: &Buffer,
 918        _: &Arc<LanguageServer>,
 919        _: &AppContext,
 920    ) -> lsp::ReferenceParams {
 921        lsp::ReferenceParams {
 922            text_document_position: lsp::TextDocumentPositionParams {
 923                text_document: lsp::TextDocumentIdentifier {
 924                    uri: lsp::Url::from_file_path(path).unwrap(),
 925                },
 926                position: point_to_lsp(self.position),
 927            },
 928            work_done_progress_params: Default::default(),
 929            partial_result_params: Default::default(),
 930            context: lsp::ReferenceContext {
 931                include_declaration: true,
 932            },
 933        }
 934    }
 935
 936    async fn response_from_lsp(
 937        self,
 938        locations: Option<Vec<lsp::Location>>,
 939        project: Model<Project>,
 940        buffer: Model<Buffer>,
 941        server_id: LanguageServerId,
 942        mut cx: AsyncAppContext,
 943    ) -> Result<Vec<Location>> {
 944        let mut references = Vec::new();
 945        let (lsp_adapter, language_server) =
 946            language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
 947
 948        if let Some(locations) = locations {
 949            for lsp_location in locations {
 950                let target_buffer_handle = project
 951                    .update(&mut cx, |this, cx| {
 952                        this.open_local_buffer_via_lsp(
 953                            lsp_location.uri,
 954                            language_server.server_id(),
 955                            lsp_adapter.name.clone(),
 956                            cx,
 957                        )
 958                    })?
 959                    .await?;
 960
 961                target_buffer_handle
 962                    .clone()
 963                    .update(&mut cx, |target_buffer, _| {
 964                        let target_start = target_buffer
 965                            .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
 966                        let target_end = target_buffer
 967                            .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
 968                        references.push(Location {
 969                            buffer: target_buffer_handle,
 970                            range: target_buffer.anchor_after(target_start)
 971                                ..target_buffer.anchor_before(target_end),
 972                        });
 973                    })?;
 974            }
 975        }
 976
 977        Ok(references)
 978    }
 979
 980    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
 981        proto::GetReferences {
 982            project_id,
 983            buffer_id: buffer.remote_id().into(),
 984            position: Some(language::proto::serialize_anchor(
 985                &buffer.anchor_before(self.position),
 986            )),
 987            version: serialize_version(&buffer.version()),
 988        }
 989    }
 990
 991    async fn from_proto(
 992        message: proto::GetReferences,
 993        _: Model<Project>,
 994        buffer: Model<Buffer>,
 995        mut cx: AsyncAppContext,
 996    ) -> Result<Self> {
 997        let position = message
 998            .position
 999            .and_then(deserialize_anchor)
1000            .ok_or_else(|| anyhow!("invalid position"))?;
1001        buffer
1002            .update(&mut cx, |buffer, _| {
1003                buffer.wait_for_version(deserialize_version(&message.version))
1004            })?
1005            .await?;
1006        Ok(Self {
1007            position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1008        })
1009    }
1010
1011    fn response_to_proto(
1012        response: Vec<Location>,
1013        project: &mut Project,
1014        peer_id: PeerId,
1015        _: &clock::Global,
1016        cx: &mut AppContext,
1017    ) -> proto::GetReferencesResponse {
1018        let locations = response
1019            .into_iter()
1020            .map(|definition| {
1021                let buffer_id = project.create_buffer_for_peer(&definition.buffer, peer_id, cx);
1022                proto::Location {
1023                    start: Some(serialize_anchor(&definition.range.start)),
1024                    end: Some(serialize_anchor(&definition.range.end)),
1025                    buffer_id: buffer_id.into(),
1026                }
1027            })
1028            .collect();
1029        proto::GetReferencesResponse { locations }
1030    }
1031
1032    async fn response_from_proto(
1033        self,
1034        message: proto::GetReferencesResponse,
1035        project: Model<Project>,
1036        _: Model<Buffer>,
1037        mut cx: AsyncAppContext,
1038    ) -> Result<Vec<Location>> {
1039        let mut locations = Vec::new();
1040        for location in message.locations {
1041            let buffer_id = BufferId::new(location.buffer_id)?;
1042            let target_buffer = project
1043                .update(&mut cx, |this, cx| {
1044                    this.wait_for_remote_buffer(buffer_id, cx)
1045                })?
1046                .await?;
1047            let start = location
1048                .start
1049                .and_then(deserialize_anchor)
1050                .ok_or_else(|| anyhow!("missing target start"))?;
1051            let end = location
1052                .end
1053                .and_then(deserialize_anchor)
1054                .ok_or_else(|| anyhow!("missing target end"))?;
1055            target_buffer
1056                .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
1057                .await?;
1058            locations.push(Location {
1059                buffer: target_buffer,
1060                range: start..end,
1061            })
1062        }
1063        Ok(locations)
1064    }
1065
1066    fn buffer_id_from_proto(message: &proto::GetReferences) -> Result<BufferId> {
1067        BufferId::new(message.buffer_id)
1068    }
1069}
1070
1071#[async_trait(?Send)]
1072impl LspCommand for GetDocumentHighlights {
1073    type Response = Vec<DocumentHighlight>;
1074    type LspRequest = lsp::request::DocumentHighlightRequest;
1075    type ProtoRequest = proto::GetDocumentHighlights;
1076
1077    fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1078        capabilities.document_highlight_provider.is_some()
1079    }
1080
1081    fn to_lsp(
1082        &self,
1083        path: &Path,
1084        _: &Buffer,
1085        _: &Arc<LanguageServer>,
1086        _: &AppContext,
1087    ) -> lsp::DocumentHighlightParams {
1088        lsp::DocumentHighlightParams {
1089            text_document_position_params: lsp::TextDocumentPositionParams {
1090                text_document: lsp::TextDocumentIdentifier {
1091                    uri: lsp::Url::from_file_path(path).unwrap(),
1092                },
1093                position: point_to_lsp(self.position),
1094            },
1095            work_done_progress_params: Default::default(),
1096            partial_result_params: Default::default(),
1097        }
1098    }
1099
1100    async fn response_from_lsp(
1101        self,
1102        lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
1103        _: Model<Project>,
1104        buffer: Model<Buffer>,
1105        _: LanguageServerId,
1106        mut cx: AsyncAppContext,
1107    ) -> Result<Vec<DocumentHighlight>> {
1108        buffer.update(&mut cx, |buffer, _| {
1109            let mut lsp_highlights = lsp_highlights.unwrap_or_default();
1110            lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
1111            lsp_highlights
1112                .into_iter()
1113                .map(|lsp_highlight| {
1114                    let start = buffer
1115                        .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
1116                    let end = buffer
1117                        .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
1118                    DocumentHighlight {
1119                        range: buffer.anchor_after(start)..buffer.anchor_before(end),
1120                        kind: lsp_highlight
1121                            .kind
1122                            .unwrap_or(lsp::DocumentHighlightKind::READ),
1123                    }
1124                })
1125                .collect()
1126        })
1127    }
1128
1129    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
1130        proto::GetDocumentHighlights {
1131            project_id,
1132            buffer_id: buffer.remote_id().into(),
1133            position: Some(language::proto::serialize_anchor(
1134                &buffer.anchor_before(self.position),
1135            )),
1136            version: serialize_version(&buffer.version()),
1137        }
1138    }
1139
1140    async fn from_proto(
1141        message: proto::GetDocumentHighlights,
1142        _: Model<Project>,
1143        buffer: Model<Buffer>,
1144        mut cx: AsyncAppContext,
1145    ) -> Result<Self> {
1146        let position = message
1147            .position
1148            .and_then(deserialize_anchor)
1149            .ok_or_else(|| anyhow!("invalid position"))?;
1150        buffer
1151            .update(&mut cx, |buffer, _| {
1152                buffer.wait_for_version(deserialize_version(&message.version))
1153            })?
1154            .await?;
1155        Ok(Self {
1156            position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1157        })
1158    }
1159
1160    fn response_to_proto(
1161        response: Vec<DocumentHighlight>,
1162        _: &mut Project,
1163        _: PeerId,
1164        _: &clock::Global,
1165        _: &mut AppContext,
1166    ) -> proto::GetDocumentHighlightsResponse {
1167        let highlights = response
1168            .into_iter()
1169            .map(|highlight| proto::DocumentHighlight {
1170                start: Some(serialize_anchor(&highlight.range.start)),
1171                end: Some(serialize_anchor(&highlight.range.end)),
1172                kind: match highlight.kind {
1173                    DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
1174                    DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
1175                    DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
1176                    _ => proto::document_highlight::Kind::Text.into(),
1177                },
1178            })
1179            .collect();
1180        proto::GetDocumentHighlightsResponse { highlights }
1181    }
1182
1183    async fn response_from_proto(
1184        self,
1185        message: proto::GetDocumentHighlightsResponse,
1186        _: Model<Project>,
1187        buffer: Model<Buffer>,
1188        mut cx: AsyncAppContext,
1189    ) -> Result<Vec<DocumentHighlight>> {
1190        let mut highlights = Vec::new();
1191        for highlight in message.highlights {
1192            let start = highlight
1193                .start
1194                .and_then(deserialize_anchor)
1195                .ok_or_else(|| anyhow!("missing target start"))?;
1196            let end = highlight
1197                .end
1198                .and_then(deserialize_anchor)
1199                .ok_or_else(|| anyhow!("missing target end"))?;
1200            buffer
1201                .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
1202                .await?;
1203            let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
1204                Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
1205                Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
1206                Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
1207                None => DocumentHighlightKind::TEXT,
1208            };
1209            highlights.push(DocumentHighlight {
1210                range: start..end,
1211                kind,
1212            });
1213        }
1214        Ok(highlights)
1215    }
1216
1217    fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> Result<BufferId> {
1218        BufferId::new(message.buffer_id)
1219    }
1220}
1221
1222#[async_trait(?Send)]
1223impl LspCommand for GetHover {
1224    type Response = Option<Hover>;
1225    type LspRequest = lsp::request::HoverRequest;
1226    type ProtoRequest = proto::GetHover;
1227
1228    fn to_lsp(
1229        &self,
1230        path: &Path,
1231        _: &Buffer,
1232        _: &Arc<LanguageServer>,
1233        _: &AppContext,
1234    ) -> lsp::HoverParams {
1235        lsp::HoverParams {
1236            text_document_position_params: lsp::TextDocumentPositionParams {
1237                text_document: lsp::TextDocumentIdentifier {
1238                    uri: lsp::Url::from_file_path(path).unwrap(),
1239                },
1240                position: point_to_lsp(self.position),
1241            },
1242            work_done_progress_params: Default::default(),
1243        }
1244    }
1245
1246    async fn response_from_lsp(
1247        self,
1248        message: Option<lsp::Hover>,
1249        _: Model<Project>,
1250        buffer: Model<Buffer>,
1251        _: LanguageServerId,
1252        mut cx: AsyncAppContext,
1253    ) -> Result<Self::Response> {
1254        let Some(hover) = message else {
1255            return Ok(None);
1256        };
1257
1258        let (language, range) = buffer.update(&mut cx, |buffer, _| {
1259            (
1260                buffer.language().cloned(),
1261                hover.range.map(|range| {
1262                    let token_start =
1263                        buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1264                    let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1265                    buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1266                }),
1267            )
1268        })?;
1269
1270        fn hover_blocks_from_marked_string(marked_string: lsp::MarkedString) -> Option<HoverBlock> {
1271            let block = match marked_string {
1272                lsp::MarkedString::String(content) => HoverBlock {
1273                    text: content,
1274                    kind: HoverBlockKind::Markdown,
1275                },
1276                lsp::MarkedString::LanguageString(lsp::LanguageString { language, value }) => {
1277                    HoverBlock {
1278                        text: value,
1279                        kind: HoverBlockKind::Code { language },
1280                    }
1281                }
1282            };
1283            if block.text.is_empty() {
1284                None
1285            } else {
1286                Some(block)
1287            }
1288        }
1289
1290        let contents = match hover.contents {
1291            lsp::HoverContents::Scalar(marked_string) => {
1292                hover_blocks_from_marked_string(marked_string)
1293                    .into_iter()
1294                    .collect()
1295            }
1296            lsp::HoverContents::Array(marked_strings) => marked_strings
1297                .into_iter()
1298                .filter_map(hover_blocks_from_marked_string)
1299                .collect(),
1300            lsp::HoverContents::Markup(markup_content) => vec![HoverBlock {
1301                text: markup_content.value,
1302                kind: if markup_content.kind == lsp::MarkupKind::Markdown {
1303                    HoverBlockKind::Markdown
1304                } else {
1305                    HoverBlockKind::PlainText
1306                },
1307            }],
1308        };
1309
1310        Ok(Some(Hover {
1311            contents,
1312            range,
1313            language,
1314        }))
1315    }
1316
1317    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1318        proto::GetHover {
1319            project_id,
1320            buffer_id: buffer.remote_id().into(),
1321            position: Some(language::proto::serialize_anchor(
1322                &buffer.anchor_before(self.position),
1323            )),
1324            version: serialize_version(&buffer.version),
1325        }
1326    }
1327
1328    async fn from_proto(
1329        message: Self::ProtoRequest,
1330        _: Model<Project>,
1331        buffer: Model<Buffer>,
1332        mut cx: AsyncAppContext,
1333    ) -> Result<Self> {
1334        let position = message
1335            .position
1336            .and_then(deserialize_anchor)
1337            .ok_or_else(|| anyhow!("invalid position"))?;
1338        buffer
1339            .update(&mut cx, |buffer, _| {
1340                buffer.wait_for_version(deserialize_version(&message.version))
1341            })?
1342            .await?;
1343        Ok(Self {
1344            position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1345        })
1346    }
1347
1348    fn response_to_proto(
1349        response: Self::Response,
1350        _: &mut Project,
1351        _: PeerId,
1352        _: &clock::Global,
1353        _: &mut AppContext,
1354    ) -> proto::GetHoverResponse {
1355        if let Some(response) = response {
1356            let (start, end) = if let Some(range) = response.range {
1357                (
1358                    Some(language::proto::serialize_anchor(&range.start)),
1359                    Some(language::proto::serialize_anchor(&range.end)),
1360                )
1361            } else {
1362                (None, None)
1363            };
1364
1365            let contents = response
1366                .contents
1367                .into_iter()
1368                .map(|block| proto::HoverBlock {
1369                    text: block.text,
1370                    is_markdown: block.kind == HoverBlockKind::Markdown,
1371                    language: if let HoverBlockKind::Code { language } = block.kind {
1372                        Some(language)
1373                    } else {
1374                        None
1375                    },
1376                })
1377                .collect();
1378
1379            proto::GetHoverResponse {
1380                start,
1381                end,
1382                contents,
1383            }
1384        } else {
1385            proto::GetHoverResponse {
1386                start: None,
1387                end: None,
1388                contents: Vec::new(),
1389            }
1390        }
1391    }
1392
1393    async fn response_from_proto(
1394        self,
1395        message: proto::GetHoverResponse,
1396        _: Model<Project>,
1397        buffer: Model<Buffer>,
1398        mut cx: AsyncAppContext,
1399    ) -> Result<Self::Response> {
1400        let contents: Vec<_> = message
1401            .contents
1402            .into_iter()
1403            .map(|block| HoverBlock {
1404                text: block.text,
1405                kind: if let Some(language) = block.language {
1406                    HoverBlockKind::Code { language }
1407                } else if block.is_markdown {
1408                    HoverBlockKind::Markdown
1409                } else {
1410                    HoverBlockKind::PlainText
1411                },
1412            })
1413            .collect();
1414        if contents.is_empty() {
1415            return Ok(None);
1416        }
1417
1418        let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
1419        let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1420            language::proto::deserialize_anchor(start)
1421                .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
1422        } else {
1423            None
1424        };
1425        if let Some(range) = range.as_ref() {
1426            buffer
1427                .update(&mut cx, |buffer, _| {
1428                    buffer.wait_for_anchors([range.start, range.end])
1429                })?
1430                .await?;
1431        }
1432
1433        Ok(Some(Hover {
1434            contents,
1435            range,
1436            language,
1437        }))
1438    }
1439
1440    fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
1441        BufferId::new(message.buffer_id)
1442    }
1443}
1444
1445#[async_trait(?Send)]
1446impl LspCommand for GetCompletions {
1447    type Response = Vec<CoreCompletion>;
1448    type LspRequest = lsp::request::Completion;
1449    type ProtoRequest = proto::GetCompletions;
1450
1451    fn to_lsp(
1452        &self,
1453        path: &Path,
1454        _: &Buffer,
1455        _: &Arc<LanguageServer>,
1456        _: &AppContext,
1457    ) -> lsp::CompletionParams {
1458        lsp::CompletionParams {
1459            text_document_position: lsp::TextDocumentPositionParams::new(
1460                lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1461                point_to_lsp(self.position),
1462            ),
1463            context: Default::default(),
1464            work_done_progress_params: Default::default(),
1465            partial_result_params: Default::default(),
1466        }
1467    }
1468
1469    async fn response_from_lsp(
1470        self,
1471        completions: Option<lsp::CompletionResponse>,
1472        project: Model<Project>,
1473        buffer: Model<Buffer>,
1474        server_id: LanguageServerId,
1475        mut cx: AsyncAppContext,
1476    ) -> Result<Self::Response> {
1477        let mut response_list = None;
1478        let mut completions = if let Some(completions) = completions {
1479            match completions {
1480                lsp::CompletionResponse::Array(completions) => completions,
1481
1482                lsp::CompletionResponse::List(mut list) => {
1483                    let items = std::mem::take(&mut list.items);
1484                    response_list = Some(list);
1485                    items
1486                }
1487            }
1488        } else {
1489            Default::default()
1490        };
1491
1492        let language_server_adapter = project
1493            .update(&mut cx, |project, _cx| {
1494                project.language_server_adapter_for_id(server_id)
1495            })?
1496            .ok_or_else(|| anyhow!("no such language server"))?;
1497
1498        let item_defaults = response_list
1499            .as_ref()
1500            .and_then(|list| list.item_defaults.as_ref());
1501
1502        if let Some(item_defaults) = item_defaults {
1503            let default_data = item_defaults.data.as_ref();
1504            let default_commit_characters = item_defaults.commit_characters.as_ref();
1505            let default_insert_text_mode = item_defaults.insert_text_mode.as_ref();
1506
1507            if default_data.is_some()
1508                || default_commit_characters.is_some()
1509                || default_insert_text_mode.is_some()
1510            {
1511                for item in completions.iter_mut() {
1512                    if let Some(data) = default_data {
1513                        item.data = Some(data.clone())
1514                    }
1515                    if let Some(characters) = default_commit_characters {
1516                        item.commit_characters = Some(characters.clone())
1517                    }
1518                    if let Some(text_mode) = default_insert_text_mode {
1519                        item.insert_text_mode = Some(*text_mode)
1520                    }
1521                }
1522            }
1523        }
1524
1525        let mut completion_edits = Vec::new();
1526        buffer.update(&mut cx, |buffer, _cx| {
1527            let snapshot = buffer.snapshot();
1528            let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1529
1530            let mut range_for_token = None;
1531            completions.retain_mut(|lsp_completion| {
1532                let edit = match lsp_completion.text_edit.as_ref() {
1533                    // If the language server provides a range to overwrite, then
1534                    // check that the range is valid.
1535                    Some(completion_text_edit) => {
1536                        match parse_completion_text_edit(completion_text_edit, &snapshot) {
1537                            Some(edit) => edit,
1538                            None => return false,
1539                        }
1540                    }
1541
1542                    // If the language server does not provide a range, then infer
1543                    // the range based on the syntax tree.
1544                    None => {
1545                        if self.position != clipped_position {
1546                            log::info!("completion out of expected range");
1547                            return false;
1548                        }
1549
1550                        let default_edit_range = response_list
1551                            .as_ref()
1552                            .and_then(|list| list.item_defaults.as_ref())
1553                            .and_then(|defaults| defaults.edit_range.as_ref())
1554                            .and_then(|range| match range {
1555                                CompletionListItemDefaultsEditRange::Range(r) => Some(r),
1556                                _ => None,
1557                            });
1558
1559                        let range = if let Some(range) = default_edit_range {
1560                            let range = range_from_lsp(*range);
1561                            let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1562                            let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1563                            if start != range.start.0 || end != range.end.0 {
1564                                log::info!("completion out of expected range");
1565                                return false;
1566                            }
1567
1568                            snapshot.anchor_before(start)..snapshot.anchor_after(end)
1569                        } else {
1570                            range_for_token
1571                                .get_or_insert_with(|| {
1572                                    let offset = self.position.to_offset(&snapshot);
1573                                    let (range, kind) = snapshot.surrounding_word(offset);
1574                                    let range = if kind == Some(CharKind::Word) {
1575                                        range
1576                                    } else {
1577                                        offset..offset
1578                                    };
1579
1580                                    snapshot.anchor_before(range.start)
1581                                        ..snapshot.anchor_after(range.end)
1582                                })
1583                                .clone()
1584                        };
1585
1586                        let text = lsp_completion
1587                            .insert_text
1588                            .as_ref()
1589                            .unwrap_or(&lsp_completion.label)
1590                            .clone();
1591                        (range, text)
1592                    }
1593                };
1594
1595                completion_edits.push(edit);
1596                true
1597            });
1598        })?;
1599
1600        language_server_adapter
1601            .process_completions(&mut completions)
1602            .await;
1603
1604        Ok(completions
1605            .into_iter()
1606            .zip(completion_edits)
1607            .map(|(lsp_completion, (old_range, mut new_text))| {
1608                LineEnding::normalize(&mut new_text);
1609                CoreCompletion {
1610                    old_range,
1611                    new_text,
1612                    server_id,
1613                    lsp_completion,
1614                }
1615            })
1616            .collect())
1617    }
1618
1619    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1620        let anchor = buffer.anchor_after(self.position);
1621        proto::GetCompletions {
1622            project_id,
1623            buffer_id: buffer.remote_id().into(),
1624            position: Some(language::proto::serialize_anchor(&anchor)),
1625            version: serialize_version(&buffer.version()),
1626        }
1627    }
1628
1629    async fn from_proto(
1630        message: proto::GetCompletions,
1631        _: Model<Project>,
1632        buffer: Model<Buffer>,
1633        mut cx: AsyncAppContext,
1634    ) -> Result<Self> {
1635        let version = deserialize_version(&message.version);
1636        buffer
1637            .update(&mut cx, |buffer, _| buffer.wait_for_version(version))?
1638            .await?;
1639        let position = message
1640            .position
1641            .and_then(language::proto::deserialize_anchor)
1642            .map(|p| {
1643                buffer.update(&mut cx, |buffer, _| {
1644                    buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1645                })
1646            })
1647            .ok_or_else(|| anyhow!("invalid position"))??;
1648        Ok(Self { position })
1649    }
1650
1651    fn response_to_proto(
1652        completions: Vec<CoreCompletion>,
1653        _: &mut Project,
1654        _: PeerId,
1655        buffer_version: &clock::Global,
1656        _: &mut AppContext,
1657    ) -> proto::GetCompletionsResponse {
1658        proto::GetCompletionsResponse {
1659            completions: completions
1660                .iter()
1661                .map(Project::serialize_completion)
1662                .collect(),
1663            version: serialize_version(buffer_version),
1664        }
1665    }
1666
1667    async fn response_from_proto(
1668        self,
1669        message: proto::GetCompletionsResponse,
1670        _project: Model<Project>,
1671        buffer: Model<Buffer>,
1672        mut cx: AsyncAppContext,
1673    ) -> Result<Self::Response> {
1674        buffer
1675            .update(&mut cx, |buffer, _| {
1676                buffer.wait_for_version(deserialize_version(&message.version))
1677            })?
1678            .await?;
1679
1680        message
1681            .completions
1682            .into_iter()
1683            .map(Project::deserialize_completion)
1684            .collect()
1685    }
1686
1687    fn buffer_id_from_proto(message: &proto::GetCompletions) -> Result<BufferId> {
1688        BufferId::new(message.buffer_id)
1689    }
1690}
1691
1692pub(crate) fn parse_completion_text_edit(
1693    edit: &lsp::CompletionTextEdit,
1694    snapshot: &BufferSnapshot,
1695) -> Option<(Range<Anchor>, String)> {
1696    match edit {
1697        lsp::CompletionTextEdit::Edit(edit) => {
1698            let range = range_from_lsp(edit.range);
1699            let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1700            let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1701            if start != range.start.0 || end != range.end.0 {
1702                log::info!("completion out of expected range");
1703                None
1704            } else {
1705                Some((
1706                    snapshot.anchor_before(start)..snapshot.anchor_after(end),
1707                    edit.new_text.clone(),
1708                ))
1709            }
1710        }
1711
1712        lsp::CompletionTextEdit::InsertAndReplace(edit) => {
1713            let range = range_from_lsp(edit.insert);
1714
1715            let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1716            let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1717            if start != range.start.0 || end != range.end.0 {
1718                log::info!("completion out of expected range");
1719                None
1720            } else {
1721                Some((
1722                    snapshot.anchor_before(start)..snapshot.anchor_after(end),
1723                    edit.new_text.clone(),
1724                ))
1725            }
1726        }
1727    }
1728}
1729
1730#[async_trait(?Send)]
1731impl LspCommand for GetCodeActions {
1732    type Response = Vec<CodeAction>;
1733    type LspRequest = lsp::request::CodeActionRequest;
1734    type ProtoRequest = proto::GetCodeActions;
1735
1736    fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1737        match &capabilities.code_action_provider {
1738            None => false,
1739            Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
1740            _ => true,
1741        }
1742    }
1743
1744    fn to_lsp(
1745        &self,
1746        path: &Path,
1747        buffer: &Buffer,
1748        language_server: &Arc<LanguageServer>,
1749        _: &AppContext,
1750    ) -> lsp::CodeActionParams {
1751        let relevant_diagnostics = buffer
1752            .snapshot()
1753            .diagnostics_in_range::<_, language::PointUtf16>(self.range.clone(), false)
1754            .map(|entry| entry.to_lsp_diagnostic_stub())
1755            .collect::<Vec<_>>();
1756
1757        lsp::CodeActionParams {
1758            text_document: lsp::TextDocumentIdentifier::new(
1759                lsp::Url::from_file_path(path).unwrap(),
1760            ),
1761            range: range_to_lsp(self.range.to_point_utf16(buffer)),
1762            work_done_progress_params: Default::default(),
1763            partial_result_params: Default::default(),
1764            context: lsp::CodeActionContext {
1765                diagnostics: relevant_diagnostics,
1766                only: self
1767                    .kinds
1768                    .clone()
1769                    .or_else(|| language_server.code_action_kinds()),
1770                ..lsp::CodeActionContext::default()
1771            },
1772        }
1773    }
1774
1775    async fn response_from_lsp(
1776        self,
1777        actions: Option<lsp::CodeActionResponse>,
1778        _: Model<Project>,
1779        _: Model<Buffer>,
1780        server_id: LanguageServerId,
1781        _: AsyncAppContext,
1782    ) -> Result<Vec<CodeAction>> {
1783        Ok(actions
1784            .unwrap_or_default()
1785            .into_iter()
1786            .filter_map(|entry| {
1787                if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1788                    Some(CodeAction {
1789                        server_id,
1790                        range: self.range.clone(),
1791                        lsp_action,
1792                    })
1793                } else {
1794                    None
1795                }
1796            })
1797            .collect())
1798    }
1799
1800    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
1801        proto::GetCodeActions {
1802            project_id,
1803            buffer_id: buffer.remote_id().into(),
1804            start: Some(language::proto::serialize_anchor(&self.range.start)),
1805            end: Some(language::proto::serialize_anchor(&self.range.end)),
1806            version: serialize_version(&buffer.version()),
1807        }
1808    }
1809
1810    async fn from_proto(
1811        message: proto::GetCodeActions,
1812        _: Model<Project>,
1813        buffer: Model<Buffer>,
1814        mut cx: AsyncAppContext,
1815    ) -> Result<Self> {
1816        let start = message
1817            .start
1818            .and_then(language::proto::deserialize_anchor)
1819            .ok_or_else(|| anyhow!("invalid start"))?;
1820        let end = message
1821            .end
1822            .and_then(language::proto::deserialize_anchor)
1823            .ok_or_else(|| anyhow!("invalid end"))?;
1824        buffer
1825            .update(&mut cx, |buffer, _| {
1826                buffer.wait_for_version(deserialize_version(&message.version))
1827            })?
1828            .await?;
1829
1830        Ok(Self {
1831            range: start..end,
1832            kinds: None,
1833        })
1834    }
1835
1836    fn response_to_proto(
1837        code_actions: Vec<CodeAction>,
1838        _: &mut Project,
1839        _: PeerId,
1840        buffer_version: &clock::Global,
1841        _: &mut AppContext,
1842    ) -> proto::GetCodeActionsResponse {
1843        proto::GetCodeActionsResponse {
1844            actions: code_actions
1845                .iter()
1846                .map(Project::serialize_code_action)
1847                .collect(),
1848            version: serialize_version(buffer_version),
1849        }
1850    }
1851
1852    async fn response_from_proto(
1853        self,
1854        message: proto::GetCodeActionsResponse,
1855        _: Model<Project>,
1856        buffer: Model<Buffer>,
1857        mut cx: AsyncAppContext,
1858    ) -> Result<Vec<CodeAction>> {
1859        buffer
1860            .update(&mut cx, |buffer, _| {
1861                buffer.wait_for_version(deserialize_version(&message.version))
1862            })?
1863            .await?;
1864        message
1865            .actions
1866            .into_iter()
1867            .map(Project::deserialize_code_action)
1868            .collect()
1869    }
1870
1871    fn buffer_id_from_proto(message: &proto::GetCodeActions) -> Result<BufferId> {
1872        BufferId::new(message.buffer_id)
1873    }
1874}
1875
1876impl GetCodeActions {
1877    pub fn can_resolve_actions(capabilities: &ServerCapabilities) -> bool {
1878        capabilities
1879            .code_action_provider
1880            .as_ref()
1881            .and_then(|options| match options {
1882                lsp::CodeActionProviderCapability::Simple(_is_supported) => None,
1883                lsp::CodeActionProviderCapability::Options(options) => options.resolve_provider,
1884            })
1885            .unwrap_or(false)
1886    }
1887
1888    pub fn supports_code_actions(capabilities: &ServerCapabilities) -> bool {
1889        capabilities
1890            .code_action_provider
1891            .as_ref()
1892            .map(|options| match options {
1893                lsp::CodeActionProviderCapability::Simple(is_supported) => *is_supported,
1894                lsp::CodeActionProviderCapability::Options(_) => true,
1895            })
1896            .unwrap_or(false)
1897    }
1898}
1899
1900#[async_trait(?Send)]
1901impl LspCommand for OnTypeFormatting {
1902    type Response = Option<Transaction>;
1903    type LspRequest = lsp::request::OnTypeFormatting;
1904    type ProtoRequest = proto::OnTypeFormatting;
1905
1906    fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
1907        let Some(on_type_formatting_options) =
1908            &server_capabilities.document_on_type_formatting_provider
1909        else {
1910            return false;
1911        };
1912        on_type_formatting_options
1913            .first_trigger_character
1914            .contains(&self.trigger)
1915            || on_type_formatting_options
1916                .more_trigger_character
1917                .iter()
1918                .flatten()
1919                .any(|chars| chars.contains(&self.trigger))
1920    }
1921
1922    fn to_lsp(
1923        &self,
1924        path: &Path,
1925        _: &Buffer,
1926        _: &Arc<LanguageServer>,
1927        _: &AppContext,
1928    ) -> lsp::DocumentOnTypeFormattingParams {
1929        lsp::DocumentOnTypeFormattingParams {
1930            text_document_position: lsp::TextDocumentPositionParams::new(
1931                lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1932                point_to_lsp(self.position),
1933            ),
1934            ch: self.trigger.clone(),
1935            options: lsp_formatting_options(self.options.tab_size),
1936        }
1937    }
1938
1939    async fn response_from_lsp(
1940        self,
1941        message: Option<Vec<lsp::TextEdit>>,
1942        project: Model<Project>,
1943        buffer: Model<Buffer>,
1944        server_id: LanguageServerId,
1945        mut cx: AsyncAppContext,
1946    ) -> Result<Option<Transaction>> {
1947        if let Some(edits) = message {
1948            let (lsp_adapter, lsp_server) =
1949                language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1950            Project::deserialize_edits(
1951                project,
1952                buffer,
1953                edits,
1954                self.push_to_history,
1955                lsp_adapter,
1956                lsp_server,
1957                &mut cx,
1958            )
1959            .await
1960        } else {
1961            Ok(None)
1962        }
1963    }
1964
1965    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1966        proto::OnTypeFormatting {
1967            project_id,
1968            buffer_id: buffer.remote_id().into(),
1969            position: Some(language::proto::serialize_anchor(
1970                &buffer.anchor_before(self.position),
1971            )),
1972            trigger: self.trigger.clone(),
1973            version: serialize_version(&buffer.version()),
1974        }
1975    }
1976
1977    async fn from_proto(
1978        message: proto::OnTypeFormatting,
1979        _: Model<Project>,
1980        buffer: Model<Buffer>,
1981        mut cx: AsyncAppContext,
1982    ) -> Result<Self> {
1983        let position = message
1984            .position
1985            .and_then(deserialize_anchor)
1986            .ok_or_else(|| anyhow!("invalid position"))?;
1987        buffer
1988            .update(&mut cx, |buffer, _| {
1989                buffer.wait_for_version(deserialize_version(&message.version))
1990            })?
1991            .await?;
1992
1993        let tab_size = buffer.update(&mut cx, |buffer, cx| {
1994            language_settings(buffer.language(), buffer.file(), cx).tab_size
1995        })?;
1996
1997        Ok(Self {
1998            position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1999            trigger: message.trigger.clone(),
2000            options: lsp_formatting_options(tab_size.get()).into(),
2001            push_to_history: false,
2002        })
2003    }
2004
2005    fn response_to_proto(
2006        response: Option<Transaction>,
2007        _: &mut Project,
2008        _: PeerId,
2009        _: &clock::Global,
2010        _: &mut AppContext,
2011    ) -> proto::OnTypeFormattingResponse {
2012        proto::OnTypeFormattingResponse {
2013            transaction: response
2014                .map(|transaction| language::proto::serialize_transaction(&transaction)),
2015        }
2016    }
2017
2018    async fn response_from_proto(
2019        self,
2020        message: proto::OnTypeFormattingResponse,
2021        _: Model<Project>,
2022        _: Model<Buffer>,
2023        _: AsyncAppContext,
2024    ) -> Result<Option<Transaction>> {
2025        let Some(transaction) = message.transaction else {
2026            return Ok(None);
2027        };
2028        Ok(Some(language::proto::deserialize_transaction(transaction)?))
2029    }
2030
2031    fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> Result<BufferId> {
2032        BufferId::new(message.buffer_id)
2033    }
2034}
2035
2036impl InlayHints {
2037    pub async fn lsp_to_project_hint(
2038        lsp_hint: lsp::InlayHint,
2039        buffer_handle: &Model<Buffer>,
2040        server_id: LanguageServerId,
2041        resolve_state: ResolveState,
2042        force_no_type_left_padding: bool,
2043        cx: &mut AsyncAppContext,
2044    ) -> anyhow::Result<InlayHint> {
2045        let kind = lsp_hint.kind.and_then(|kind| match kind {
2046            lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
2047            lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
2048            _ => None,
2049        });
2050
2051        let position = buffer_handle.update(cx, |buffer, _| {
2052            let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
2053            if kind == Some(InlayHintKind::Parameter) {
2054                buffer.anchor_before(position)
2055            } else {
2056                buffer.anchor_after(position)
2057            }
2058        })?;
2059        let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
2060            .await
2061            .context("lsp to project inlay hint conversion")?;
2062        let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
2063            false
2064        } else {
2065            lsp_hint.padding_left.unwrap_or(false)
2066        };
2067
2068        Ok(InlayHint {
2069            position,
2070            padding_left,
2071            padding_right: lsp_hint.padding_right.unwrap_or(false),
2072            label,
2073            kind,
2074            tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
2075                lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
2076                lsp::InlayHintTooltip::MarkupContent(markup_content) => {
2077                    InlayHintTooltip::MarkupContent(MarkupContent {
2078                        kind: match markup_content.kind {
2079                            lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2080                            lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2081                        },
2082                        value: markup_content.value,
2083                    })
2084                }
2085            }),
2086            resolve_state,
2087        })
2088    }
2089
2090    async fn lsp_inlay_label_to_project(
2091        lsp_label: lsp::InlayHintLabel,
2092        server_id: LanguageServerId,
2093    ) -> anyhow::Result<InlayHintLabel> {
2094        let label = match lsp_label {
2095            lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
2096            lsp::InlayHintLabel::LabelParts(lsp_parts) => {
2097                let mut parts = Vec::with_capacity(lsp_parts.len());
2098                for lsp_part in lsp_parts {
2099                    parts.push(InlayHintLabelPart {
2100                        value: lsp_part.value,
2101                        tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
2102                            lsp::InlayHintLabelPartTooltip::String(s) => {
2103                                InlayHintLabelPartTooltip::String(s)
2104                            }
2105                            lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2106                                InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2107                                    kind: match markup_content.kind {
2108                                        lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2109                                        lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2110                                    },
2111                                    value: markup_content.value,
2112                                })
2113                            }
2114                        }),
2115                        location: Some(server_id).zip(lsp_part.location),
2116                    });
2117                }
2118                InlayHintLabel::LabelParts(parts)
2119            }
2120        };
2121
2122        Ok(label)
2123    }
2124
2125    pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
2126        let (state, lsp_resolve_state) = match response_hint.resolve_state {
2127            ResolveState::Resolved => (0, None),
2128            ResolveState::CanResolve(server_id, resolve_data) => (
2129                1,
2130                resolve_data
2131                    .map(|json_data| {
2132                        serde_json::to_string(&json_data)
2133                            .expect("failed to serialize resolve json data")
2134                    })
2135                    .map(|value| proto::resolve_state::LspResolveState {
2136                        server_id: server_id.0 as u64,
2137                        value,
2138                    }),
2139            ),
2140            ResolveState::Resolving => (2, None),
2141        };
2142        let resolve_state = Some(proto::ResolveState {
2143            state,
2144            lsp_resolve_state,
2145        });
2146        proto::InlayHint {
2147            position: Some(language::proto::serialize_anchor(&response_hint.position)),
2148            padding_left: response_hint.padding_left,
2149            padding_right: response_hint.padding_right,
2150            label: Some(proto::InlayHintLabel {
2151                label: Some(match response_hint.label {
2152                    InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
2153                    InlayHintLabel::LabelParts(label_parts) => {
2154                        proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
2155                            parts: label_parts.into_iter().map(|label_part| {
2156                                let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
2157                                let location_range_start = label_part.location.as_ref().map(|(_, location)| point_from_lsp(location.range.start).0).map(|point| proto::PointUtf16 { row: point.row, column: point.column });
2158                                let location_range_end = label_part.location.as_ref().map(|(_, location)| point_from_lsp(location.range.end).0).map(|point| proto::PointUtf16 { row: point.row, column: point.column });
2159                                proto::InlayHintLabelPart {
2160                                value: label_part.value,
2161                                tooltip: label_part.tooltip.map(|tooltip| {
2162                                    let proto_tooltip = match tooltip {
2163                                        InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
2164                                        InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
2165                                            is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2166                                            value: markup_content.value,
2167                                        }),
2168                                    };
2169                                    proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
2170                                }),
2171                                location_url,
2172                                location_range_start,
2173                                location_range_end,
2174                                language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
2175                            }}).collect()
2176                        })
2177                    }
2178                }),
2179            }),
2180            kind: response_hint.kind.map(|kind| kind.name().to_string()),
2181            tooltip: response_hint.tooltip.map(|response_tooltip| {
2182                let proto_tooltip = match response_tooltip {
2183                    InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
2184                    InlayHintTooltip::MarkupContent(markup_content) => {
2185                        proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
2186                            is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2187                            value: markup_content.value,
2188                        })
2189                    }
2190                };
2191                proto::InlayHintTooltip {
2192                    content: Some(proto_tooltip),
2193                }
2194            }),
2195            resolve_state,
2196        }
2197    }
2198
2199    pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
2200        let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
2201            panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
2202        });
2203        let resolve_state_data = resolve_state
2204            .lsp_resolve_state.as_ref()
2205            .map(|lsp_resolve_state| {
2206                serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
2207                    .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
2208                    .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
2209            })
2210            .transpose()?;
2211        let resolve_state = match resolve_state.state {
2212            0 => ResolveState::Resolved,
2213            1 => {
2214                let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2215                    format!(
2216                        "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2217                    )
2218                })?;
2219                ResolveState::CanResolve(server_id, lsp_resolve_state)
2220            }
2221            2 => ResolveState::Resolving,
2222            invalid => {
2223                anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2224            }
2225        };
2226        Ok(InlayHint {
2227            position: message_hint
2228                .position
2229                .and_then(language::proto::deserialize_anchor)
2230                .context("invalid position")?,
2231            label: match message_hint
2232                .label
2233                .and_then(|label| label.label)
2234                .context("missing label")?
2235            {
2236                proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2237                proto::inlay_hint_label::Label::LabelParts(parts) => {
2238                    let mut label_parts = Vec::new();
2239                    for part in parts.parts {
2240                        label_parts.push(InlayHintLabelPart {
2241                            value: part.value,
2242                            tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2243                                Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2244                                    InlayHintLabelPartTooltip::String(s)
2245                                }
2246                                Some(
2247                                    proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2248                                        markup_content,
2249                                    ),
2250                                ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2251                                    kind: if markup_content.is_markdown {
2252                                        HoverBlockKind::Markdown
2253                                    } else {
2254                                        HoverBlockKind::PlainText
2255                                    },
2256                                    value: markup_content.value,
2257                                }),
2258                                None => InlayHintLabelPartTooltip::String(String::new()),
2259                            }),
2260                            location: {
2261                                match part
2262                                    .location_url
2263                                    .zip(
2264                                        part.location_range_start.and_then(|start| {
2265                                            Some(start..part.location_range_end?)
2266                                        }),
2267                                    )
2268                                    .zip(part.language_server_id)
2269                                {
2270                                    Some(((uri, range), server_id)) => Some((
2271                                        LanguageServerId(server_id as usize),
2272                                        lsp::Location {
2273                                            uri: lsp::Url::parse(&uri)
2274                                                .context("invalid uri in hint part {part:?}")?,
2275                                            range: lsp::Range::new(
2276                                                point_to_lsp(PointUtf16::new(
2277                                                    range.start.row,
2278                                                    range.start.column,
2279                                                )),
2280                                                point_to_lsp(PointUtf16::new(
2281                                                    range.end.row,
2282                                                    range.end.column,
2283                                                )),
2284                                            ),
2285                                        },
2286                                    )),
2287                                    None => None,
2288                                }
2289                            },
2290                        });
2291                    }
2292
2293                    InlayHintLabel::LabelParts(label_parts)
2294                }
2295            },
2296            padding_left: message_hint.padding_left,
2297            padding_right: message_hint.padding_right,
2298            kind: message_hint
2299                .kind
2300                .as_deref()
2301                .and_then(InlayHintKind::from_name),
2302            tooltip: message_hint.tooltip.and_then(|tooltip| {
2303                Some(match tooltip.content? {
2304                    proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2305                    proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2306                        InlayHintTooltip::MarkupContent(MarkupContent {
2307                            kind: if markup_content.is_markdown {
2308                                HoverBlockKind::Markdown
2309                            } else {
2310                                HoverBlockKind::PlainText
2311                            },
2312                            value: markup_content.value,
2313                        })
2314                    }
2315                })
2316            }),
2317            resolve_state,
2318        })
2319    }
2320
2321    pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
2322        lsp::InlayHint {
2323            position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2324            kind: hint.kind.map(|kind| match kind {
2325                InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2326                InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2327            }),
2328            text_edits: None,
2329            tooltip: hint.tooltip.and_then(|tooltip| {
2330                Some(match tooltip {
2331                    InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2332                    InlayHintTooltip::MarkupContent(markup_content) => {
2333                        lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2334                            kind: match markup_content.kind {
2335                                HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2336                                HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2337                                HoverBlockKind::Code { .. } => return None,
2338                            },
2339                            value: markup_content.value,
2340                        })
2341                    }
2342                })
2343            }),
2344            label: match hint.label {
2345                InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2346                InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2347                    label_parts
2348                        .into_iter()
2349                        .map(|part| lsp::InlayHintLabelPart {
2350                            value: part.value,
2351                            tooltip: part.tooltip.and_then(|tooltip| {
2352                                Some(match tooltip {
2353                                    InlayHintLabelPartTooltip::String(s) => {
2354                                        lsp::InlayHintLabelPartTooltip::String(s)
2355                                    }
2356                                    InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2357                                        lsp::InlayHintLabelPartTooltip::MarkupContent(
2358                                            lsp::MarkupContent {
2359                                                kind: match markup_content.kind {
2360                                                    HoverBlockKind::PlainText => {
2361                                                        lsp::MarkupKind::PlainText
2362                                                    }
2363                                                    HoverBlockKind::Markdown => {
2364                                                        lsp::MarkupKind::Markdown
2365                                                    }
2366                                                    HoverBlockKind::Code { .. } => return None,
2367                                                },
2368                                                value: markup_content.value,
2369                                            },
2370                                        )
2371                                    }
2372                                })
2373                            }),
2374                            location: part.location.map(|(_, location)| location),
2375                            command: None,
2376                        })
2377                        .collect(),
2378                ),
2379            },
2380            padding_left: Some(hint.padding_left),
2381            padding_right: Some(hint.padding_right),
2382            data: match hint.resolve_state {
2383                ResolveState::CanResolve(_, data) => data,
2384                ResolveState::Resolving | ResolveState::Resolved => None,
2385            },
2386        }
2387    }
2388
2389    pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2390        capabilities
2391            .inlay_hint_provider
2392            .as_ref()
2393            .and_then(|options| match options {
2394                OneOf::Left(_is_supported) => None,
2395                OneOf::Right(capabilities) => match capabilities {
2396                    lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2397                    lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
2398                        o.inlay_hint_options.resolve_provider
2399                    }
2400                },
2401            })
2402            .unwrap_or(false)
2403    }
2404}
2405
2406#[async_trait(?Send)]
2407impl LspCommand for InlayHints {
2408    type Response = Vec<InlayHint>;
2409    type LspRequest = lsp::InlayHintRequest;
2410    type ProtoRequest = proto::InlayHints;
2411
2412    fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
2413        let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else {
2414            return false;
2415        };
2416        match inlay_hint_provider {
2417            lsp::OneOf::Left(enabled) => *enabled,
2418            lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2419                lsp::InlayHintServerCapabilities::Options(_) => true,
2420                lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2421            },
2422        }
2423    }
2424
2425    fn to_lsp(
2426        &self,
2427        path: &Path,
2428        buffer: &Buffer,
2429        _: &Arc<LanguageServer>,
2430        _: &AppContext,
2431    ) -> lsp::InlayHintParams {
2432        lsp::InlayHintParams {
2433            text_document: lsp::TextDocumentIdentifier {
2434                uri: lsp::Url::from_file_path(path).unwrap(),
2435            },
2436            range: range_to_lsp(self.range.to_point_utf16(buffer)),
2437            work_done_progress_params: Default::default(),
2438        }
2439    }
2440
2441    async fn response_from_lsp(
2442        self,
2443        message: Option<Vec<lsp::InlayHint>>,
2444        project: Model<Project>,
2445        buffer: Model<Buffer>,
2446        server_id: LanguageServerId,
2447        mut cx: AsyncAppContext,
2448    ) -> anyhow::Result<Vec<InlayHint>> {
2449        let (lsp_adapter, lsp_server) =
2450            language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2451        // `typescript-language-server` adds padding to the left for type hints, turning
2452        // `const foo: boolean` into `const foo : boolean` which looks odd.
2453        // `rust-analyzer` does not have the padding for this case, and we have to accommodate both.
2454        //
2455        // We could trim the whole string, but being pessimistic on par with the situation above,
2456        // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2457        // Hence let's use a heuristic first to handle the most awkward case and look for more.
2458        let force_no_type_left_padding =
2459            lsp_adapter.name.0.as_ref() == "typescript-language-server";
2460
2461        let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2462            let resolve_state = if InlayHints::can_resolve_inlays(lsp_server.capabilities()) {
2463                ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2464            } else {
2465                ResolveState::Resolved
2466            };
2467
2468            let buffer = buffer.clone();
2469            cx.spawn(move |mut cx| async move {
2470                InlayHints::lsp_to_project_hint(
2471                    lsp_hint,
2472                    &buffer,
2473                    server_id,
2474                    resolve_state,
2475                    force_no_type_left_padding,
2476                    &mut cx,
2477                )
2478                .await
2479            })
2480        });
2481        future::join_all(hints)
2482            .await
2483            .into_iter()
2484            .collect::<anyhow::Result<_>>()
2485            .context("lsp to project inlay hints conversion")
2486    }
2487
2488    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2489        proto::InlayHints {
2490            project_id,
2491            buffer_id: buffer.remote_id().into(),
2492            start: Some(language::proto::serialize_anchor(&self.range.start)),
2493            end: Some(language::proto::serialize_anchor(&self.range.end)),
2494            version: serialize_version(&buffer.version()),
2495        }
2496    }
2497
2498    async fn from_proto(
2499        message: proto::InlayHints,
2500        _: Model<Project>,
2501        buffer: Model<Buffer>,
2502        mut cx: AsyncAppContext,
2503    ) -> Result<Self> {
2504        let start = message
2505            .start
2506            .and_then(language::proto::deserialize_anchor)
2507            .context("invalid start")?;
2508        let end = message
2509            .end
2510            .and_then(language::proto::deserialize_anchor)
2511            .context("invalid end")?;
2512        buffer
2513            .update(&mut cx, |buffer, _| {
2514                buffer.wait_for_version(deserialize_version(&message.version))
2515            })?
2516            .await?;
2517
2518        Ok(Self { range: start..end })
2519    }
2520
2521    fn response_to_proto(
2522        response: Vec<InlayHint>,
2523        _: &mut Project,
2524        _: PeerId,
2525        buffer_version: &clock::Global,
2526        _: &mut AppContext,
2527    ) -> proto::InlayHintsResponse {
2528        proto::InlayHintsResponse {
2529            hints: response
2530                .into_iter()
2531                .map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
2532                .collect(),
2533            version: serialize_version(buffer_version),
2534        }
2535    }
2536
2537    async fn response_from_proto(
2538        self,
2539        message: proto::InlayHintsResponse,
2540        _: Model<Project>,
2541        buffer: Model<Buffer>,
2542        mut cx: AsyncAppContext,
2543    ) -> anyhow::Result<Vec<InlayHint>> {
2544        buffer
2545            .update(&mut cx, |buffer, _| {
2546                buffer.wait_for_version(deserialize_version(&message.version))
2547            })?
2548            .await?;
2549
2550        let mut hints = Vec::new();
2551        for message_hint in message.hints {
2552            hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2553        }
2554
2555        Ok(hints)
2556    }
2557
2558    fn buffer_id_from_proto(message: &proto::InlayHints) -> Result<BufferId> {
2559        BufferId::new(message.buffer_id)
2560    }
2561}