lsp_command.rs

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