lsp_command.rs

   1use crate::{
   2    DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel,
   3    InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, 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, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction,
  18    Unclipped,
  19};
  20use lsp::{DocumentHighlightKind, LanguageServer, LanguageServerId, OneOf, 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) =
1659            &server_capabilities.document_on_type_formatting_provider
1660        else {
1661            return false;
1662        };
1663        on_type_formatting_options
1664            .first_trigger_character
1665            .contains(&self.trigger)
1666            || on_type_formatting_options
1667                .more_trigger_character
1668                .iter()
1669                .flatten()
1670                .any(|chars| chars.contains(&self.trigger))
1671    }
1672
1673    fn to_lsp(
1674        &self,
1675        path: &Path,
1676        _: &Buffer,
1677        _: &Arc<LanguageServer>,
1678        _: &AppContext,
1679    ) -> lsp::DocumentOnTypeFormattingParams {
1680        lsp::DocumentOnTypeFormattingParams {
1681            text_document_position: lsp::TextDocumentPositionParams::new(
1682                lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1683                point_to_lsp(self.position),
1684            ),
1685            ch: self.trigger.clone(),
1686            options: lsp_formatting_options(self.options.tab_size),
1687        }
1688    }
1689
1690    async fn response_from_lsp(
1691        self,
1692        message: Option<Vec<lsp::TextEdit>>,
1693        project: ModelHandle<Project>,
1694        buffer: ModelHandle<Buffer>,
1695        server_id: LanguageServerId,
1696        mut cx: AsyncAppContext,
1697    ) -> Result<Option<Transaction>> {
1698        if let Some(edits) = message {
1699            let (lsp_adapter, lsp_server) =
1700                language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1701            Project::deserialize_edits(
1702                project,
1703                buffer,
1704                edits,
1705                self.push_to_history,
1706                lsp_adapter,
1707                lsp_server,
1708                &mut cx,
1709            )
1710            .await
1711        } else {
1712            Ok(None)
1713        }
1714    }
1715
1716    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1717        proto::OnTypeFormatting {
1718            project_id,
1719            buffer_id: buffer.remote_id(),
1720            position: Some(language::proto::serialize_anchor(
1721                &buffer.anchor_before(self.position),
1722            )),
1723            trigger: self.trigger.clone(),
1724            version: serialize_version(&buffer.version()),
1725        }
1726    }
1727
1728    async fn from_proto(
1729        message: proto::OnTypeFormatting,
1730        _: ModelHandle<Project>,
1731        buffer: ModelHandle<Buffer>,
1732        mut cx: AsyncAppContext,
1733    ) -> Result<Self> {
1734        let position = message
1735            .position
1736            .and_then(deserialize_anchor)
1737            .ok_or_else(|| anyhow!("invalid position"))?;
1738        buffer
1739            .update(&mut cx, |buffer, _| {
1740                buffer.wait_for_version(deserialize_version(&message.version))
1741            })
1742            .await?;
1743
1744        let tab_size = buffer.read_with(&cx, |buffer, cx| {
1745            language_settings(buffer.language(), buffer.file(), cx).tab_size
1746        });
1747
1748        Ok(Self {
1749            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1750            trigger: message.trigger.clone(),
1751            options: lsp_formatting_options(tab_size.get()).into(),
1752            push_to_history: false,
1753        })
1754    }
1755
1756    fn response_to_proto(
1757        response: Option<Transaction>,
1758        _: &mut Project,
1759        _: PeerId,
1760        _: &clock::Global,
1761        _: &mut AppContext,
1762    ) -> proto::OnTypeFormattingResponse {
1763        proto::OnTypeFormattingResponse {
1764            transaction: response
1765                .map(|transaction| language::proto::serialize_transaction(&transaction)),
1766        }
1767    }
1768
1769    async fn response_from_proto(
1770        self,
1771        message: proto::OnTypeFormattingResponse,
1772        _: ModelHandle<Project>,
1773        _: ModelHandle<Buffer>,
1774        _: AsyncAppContext,
1775    ) -> Result<Option<Transaction>> {
1776        let Some(transaction) = message.transaction else {
1777            return Ok(None);
1778        };
1779        Ok(Some(language::proto::deserialize_transaction(transaction)?))
1780    }
1781
1782    fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 {
1783        message.buffer_id
1784    }
1785}
1786
1787impl InlayHints {
1788    pub async fn lsp_to_project_hint(
1789        lsp_hint: lsp::InlayHint,
1790        buffer_handle: &ModelHandle<Buffer>,
1791        server_id: LanguageServerId,
1792        resolve_state: ResolveState,
1793        force_no_type_left_padding: bool,
1794        cx: &mut AsyncAppContext,
1795    ) -> anyhow::Result<InlayHint> {
1796        let kind = lsp_hint.kind.and_then(|kind| match kind {
1797            lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
1798            lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
1799            _ => None,
1800        });
1801
1802        let position = cx.update(|cx| {
1803            let buffer = buffer_handle.read(cx);
1804            let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
1805            if kind == Some(InlayHintKind::Parameter) {
1806                buffer.anchor_before(position)
1807            } else {
1808                buffer.anchor_after(position)
1809            }
1810        });
1811        let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
1812            .await
1813            .context("lsp to project inlay hint conversion")?;
1814        let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
1815            false
1816        } else {
1817            lsp_hint.padding_left.unwrap_or(false)
1818        };
1819
1820        Ok(InlayHint {
1821            position,
1822            padding_left,
1823            padding_right: lsp_hint.padding_right.unwrap_or(false),
1824            label,
1825            kind,
1826            tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
1827                lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
1828                lsp::InlayHintTooltip::MarkupContent(markup_content) => {
1829                    InlayHintTooltip::MarkupContent(MarkupContent {
1830                        kind: match markup_content.kind {
1831                            lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
1832                            lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
1833                        },
1834                        value: markup_content.value,
1835                    })
1836                }
1837            }),
1838            resolve_state,
1839        })
1840    }
1841
1842    async fn lsp_inlay_label_to_project(
1843        lsp_label: lsp::InlayHintLabel,
1844        server_id: LanguageServerId,
1845    ) -> anyhow::Result<InlayHintLabel> {
1846        let label = match lsp_label {
1847            lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
1848            lsp::InlayHintLabel::LabelParts(lsp_parts) => {
1849                let mut parts = Vec::with_capacity(lsp_parts.len());
1850                for lsp_part in lsp_parts {
1851                    parts.push(InlayHintLabelPart {
1852                        value: lsp_part.value,
1853                        tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
1854                            lsp::InlayHintLabelPartTooltip::String(s) => {
1855                                InlayHintLabelPartTooltip::String(s)
1856                            }
1857                            lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
1858                                InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
1859                                    kind: match markup_content.kind {
1860                                        lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
1861                                        lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
1862                                    },
1863                                    value: markup_content.value,
1864                                })
1865                            }
1866                        }),
1867                        location: Some(server_id).zip(lsp_part.location),
1868                    });
1869                }
1870                InlayHintLabel::LabelParts(parts)
1871            }
1872        };
1873
1874        Ok(label)
1875    }
1876
1877    pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
1878        let (state, lsp_resolve_state) = match response_hint.resolve_state {
1879            ResolveState::Resolved => (0, None),
1880            ResolveState::CanResolve(server_id, resolve_data) => (
1881                1,
1882                resolve_data
1883                    .map(|json_data| {
1884                        serde_json::to_string(&json_data)
1885                            .expect("failed to serialize resolve json data")
1886                    })
1887                    .map(|value| proto::resolve_state::LspResolveState {
1888                        server_id: server_id.0 as u64,
1889                        value,
1890                    }),
1891            ),
1892            ResolveState::Resolving => (2, None),
1893        };
1894        let resolve_state = Some(proto::ResolveState {
1895            state,
1896            lsp_resolve_state,
1897        });
1898        proto::InlayHint {
1899            position: Some(language::proto::serialize_anchor(&response_hint.position)),
1900            padding_left: response_hint.padding_left,
1901            padding_right: response_hint.padding_right,
1902            label: Some(proto::InlayHintLabel {
1903                label: Some(match response_hint.label {
1904                    InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
1905                    InlayHintLabel::LabelParts(label_parts) => {
1906                        proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
1907                            parts: label_parts.into_iter().map(|label_part| {
1908                                let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
1909                                let location_range_start = label_part.location.as_ref().map(|(_, location)| point_from_lsp(location.range.start).0).map(|point| proto::PointUtf16 { row: point.row, column: point.column });
1910                                let location_range_end = label_part.location.as_ref().map(|(_, location)| point_from_lsp(location.range.end).0).map(|point| proto::PointUtf16 { row: point.row, column: point.column });
1911                                proto::InlayHintLabelPart {
1912                                value: label_part.value,
1913                                tooltip: label_part.tooltip.map(|tooltip| {
1914                                    let proto_tooltip = match tooltip {
1915                                        InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
1916                                        InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
1917                                            is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1918                                            value: markup_content.value,
1919                                        }),
1920                                    };
1921                                    proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
1922                                }),
1923                                location_url,
1924                                location_range_start,
1925                                location_range_end,
1926                                language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
1927                            }}).collect()
1928                        })
1929                    }
1930                }),
1931            }),
1932            kind: response_hint.kind.map(|kind| kind.name().to_string()),
1933            tooltip: response_hint.tooltip.map(|response_tooltip| {
1934                let proto_tooltip = match response_tooltip {
1935                    InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
1936                    InlayHintTooltip::MarkupContent(markup_content) => {
1937                        proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
1938                            is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1939                            value: markup_content.value,
1940                        })
1941                    }
1942                };
1943                proto::InlayHintTooltip {
1944                    content: Some(proto_tooltip),
1945                }
1946            }),
1947            resolve_state,
1948        }
1949    }
1950
1951    pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
1952        let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
1953            panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
1954        });
1955        let resolve_state_data = resolve_state
1956            .lsp_resolve_state.as_ref()
1957            .map(|lsp_resolve_state| {
1958                serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
1959                    .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
1960                    .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
1961            })
1962            .transpose()?;
1963        let resolve_state = match resolve_state.state {
1964            0 => ResolveState::Resolved,
1965            1 => {
1966                let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
1967                    format!(
1968                        "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
1969                    )
1970                })?;
1971                ResolveState::CanResolve(server_id, lsp_resolve_state)
1972            }
1973            2 => ResolveState::Resolving,
1974            invalid => {
1975                anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
1976            }
1977        };
1978        Ok(InlayHint {
1979            position: message_hint
1980                .position
1981                .and_then(language::proto::deserialize_anchor)
1982                .context("invalid position")?,
1983            label: match message_hint
1984                .label
1985                .and_then(|label| label.label)
1986                .context("missing label")?
1987            {
1988                proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
1989                proto::inlay_hint_label::Label::LabelParts(parts) => {
1990                    let mut label_parts = Vec::new();
1991                    for part in parts.parts {
1992                        label_parts.push(InlayHintLabelPart {
1993                            value: part.value,
1994                            tooltip: part.tooltip.map(|tooltip| match tooltip.content {
1995                                Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
1996                                    InlayHintLabelPartTooltip::String(s)
1997                                }
1998                                Some(
1999                                    proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2000                                        markup_content,
2001                                    ),
2002                                ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2003                                    kind: if markup_content.is_markdown {
2004                                        HoverBlockKind::Markdown
2005                                    } else {
2006                                        HoverBlockKind::PlainText
2007                                    },
2008                                    value: markup_content.value,
2009                                }),
2010                                None => InlayHintLabelPartTooltip::String(String::new()),
2011                            }),
2012                            location: {
2013                                match part
2014                                    .location_url
2015                                    .zip(
2016                                        part.location_range_start.and_then(|start| {
2017                                            Some(start..part.location_range_end?)
2018                                        }),
2019                                    )
2020                                    .zip(part.language_server_id)
2021                                {
2022                                    Some(((uri, range), server_id)) => Some((
2023                                        LanguageServerId(server_id as usize),
2024                                        lsp::Location {
2025                                            uri: lsp::Url::parse(&uri)
2026                                                .context("invalid uri in hint part {part:?}")?,
2027                                            range: lsp::Range::new(
2028                                                point_to_lsp(PointUtf16::new(
2029                                                    range.start.row,
2030                                                    range.start.column,
2031                                                )),
2032                                                point_to_lsp(PointUtf16::new(
2033                                                    range.end.row,
2034                                                    range.end.column,
2035                                                )),
2036                                            ),
2037                                        },
2038                                    )),
2039                                    None => None,
2040                                }
2041                            },
2042                        });
2043                    }
2044
2045                    InlayHintLabel::LabelParts(label_parts)
2046                }
2047            },
2048            padding_left: message_hint.padding_left,
2049            padding_right: message_hint.padding_right,
2050            kind: message_hint
2051                .kind
2052                .as_deref()
2053                .and_then(InlayHintKind::from_name),
2054            tooltip: message_hint.tooltip.and_then(|tooltip| {
2055                Some(match tooltip.content? {
2056                    proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2057                    proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2058                        InlayHintTooltip::MarkupContent(MarkupContent {
2059                            kind: if markup_content.is_markdown {
2060                                HoverBlockKind::Markdown
2061                            } else {
2062                                HoverBlockKind::PlainText
2063                            },
2064                            value: markup_content.value,
2065                        })
2066                    }
2067                })
2068            }),
2069            resolve_state,
2070        })
2071    }
2072
2073    pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
2074        lsp::InlayHint {
2075            position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2076            kind: hint.kind.map(|kind| match kind {
2077                InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2078                InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2079            }),
2080            text_edits: None,
2081            tooltip: hint.tooltip.and_then(|tooltip| {
2082                Some(match tooltip {
2083                    InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2084                    InlayHintTooltip::MarkupContent(markup_content) => {
2085                        lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2086                            kind: match markup_content.kind {
2087                                HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2088                                HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2089                                HoverBlockKind::Code { .. } => return None,
2090                            },
2091                            value: markup_content.value,
2092                        })
2093                    }
2094                })
2095            }),
2096            label: match hint.label {
2097                InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2098                InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2099                    label_parts
2100                        .into_iter()
2101                        .map(|part| lsp::InlayHintLabelPart {
2102                            value: part.value,
2103                            tooltip: part.tooltip.and_then(|tooltip| {
2104                                Some(match tooltip {
2105                                    InlayHintLabelPartTooltip::String(s) => {
2106                                        lsp::InlayHintLabelPartTooltip::String(s)
2107                                    }
2108                                    InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2109                                        lsp::InlayHintLabelPartTooltip::MarkupContent(
2110                                            lsp::MarkupContent {
2111                                                kind: match markup_content.kind {
2112                                                    HoverBlockKind::PlainText => {
2113                                                        lsp::MarkupKind::PlainText
2114                                                    }
2115                                                    HoverBlockKind::Markdown => {
2116                                                        lsp::MarkupKind::Markdown
2117                                                    }
2118                                                    HoverBlockKind::Code { .. } => return None,
2119                                                },
2120                                                value: markup_content.value,
2121                                            },
2122                                        )
2123                                    }
2124                                })
2125                            }),
2126                            location: part.location.map(|(_, location)| location),
2127                            command: None,
2128                        })
2129                        .collect(),
2130                ),
2131            },
2132            padding_left: Some(hint.padding_left),
2133            padding_right: Some(hint.padding_right),
2134            data: match hint.resolve_state {
2135                ResolveState::CanResolve(_, data) => data,
2136                ResolveState::Resolving | ResolveState::Resolved => None,
2137            },
2138        }
2139    }
2140
2141    pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2142        capabilities
2143            .inlay_hint_provider
2144            .as_ref()
2145            .and_then(|options| match options {
2146                OneOf::Left(_is_supported) => None,
2147                OneOf::Right(capabilities) => match capabilities {
2148                    lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2149                    lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
2150                        o.inlay_hint_options.resolve_provider
2151                    }
2152                },
2153            })
2154            .unwrap_or(false)
2155    }
2156}
2157
2158#[async_trait(?Send)]
2159impl LspCommand for InlayHints {
2160    type Response = Vec<InlayHint>;
2161    type LspRequest = lsp::InlayHintRequest;
2162    type ProtoRequest = proto::InlayHints;
2163
2164    fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
2165        let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else {
2166            return false;
2167        };
2168        match inlay_hint_provider {
2169            lsp::OneOf::Left(enabled) => *enabled,
2170            lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2171                lsp::InlayHintServerCapabilities::Options(_) => true,
2172                lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2173            },
2174        }
2175    }
2176
2177    fn to_lsp(
2178        &self,
2179        path: &Path,
2180        buffer: &Buffer,
2181        _: &Arc<LanguageServer>,
2182        _: &AppContext,
2183    ) -> lsp::InlayHintParams {
2184        lsp::InlayHintParams {
2185            text_document: lsp::TextDocumentIdentifier {
2186                uri: lsp::Url::from_file_path(path).unwrap(),
2187            },
2188            range: range_to_lsp(self.range.to_point_utf16(buffer)),
2189            work_done_progress_params: Default::default(),
2190        }
2191    }
2192
2193    async fn response_from_lsp(
2194        self,
2195        message: Option<Vec<lsp::InlayHint>>,
2196        project: ModelHandle<Project>,
2197        buffer: ModelHandle<Buffer>,
2198        server_id: LanguageServerId,
2199        mut cx: AsyncAppContext,
2200    ) -> anyhow::Result<Vec<InlayHint>> {
2201        let (lsp_adapter, lsp_server) =
2202            language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2203        // `typescript-language-server` adds padding to the left for type hints, turning
2204        // `const foo: boolean` into `const foo : boolean` which looks odd.
2205        // `rust-analyzer` does not have the padding for this case, and we have to accomodate both.
2206        //
2207        // We could trim the whole string, but being pessimistic on par with the situation above,
2208        // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2209        // Hence let's use a heuristic first to handle the most awkward case and look for more.
2210        let force_no_type_left_padding =
2211            lsp_adapter.name.0.as_ref() == "typescript-language-server";
2212
2213        let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2214            let resolve_state = if InlayHints::can_resolve_inlays(lsp_server.capabilities()) {
2215                ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2216            } else {
2217                ResolveState::Resolved
2218            };
2219
2220            let buffer = buffer.clone();
2221            cx.spawn(|mut cx| async move {
2222                InlayHints::lsp_to_project_hint(
2223                    lsp_hint,
2224                    &buffer,
2225                    server_id,
2226                    resolve_state,
2227                    force_no_type_left_padding,
2228                    &mut cx,
2229                )
2230                .await
2231            })
2232        });
2233        future::join_all(hints)
2234            .await
2235            .into_iter()
2236            .collect::<anyhow::Result<_>>()
2237            .context("lsp to project inlay hints conversion")
2238    }
2239
2240    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2241        proto::InlayHints {
2242            project_id,
2243            buffer_id: buffer.remote_id(),
2244            start: Some(language::proto::serialize_anchor(&self.range.start)),
2245            end: Some(language::proto::serialize_anchor(&self.range.end)),
2246            version: serialize_version(&buffer.version()),
2247        }
2248    }
2249
2250    async fn from_proto(
2251        message: proto::InlayHints,
2252        _: ModelHandle<Project>,
2253        buffer: ModelHandle<Buffer>,
2254        mut cx: AsyncAppContext,
2255    ) -> Result<Self> {
2256        let start = message
2257            .start
2258            .and_then(language::proto::deserialize_anchor)
2259            .context("invalid start")?;
2260        let end = message
2261            .end
2262            .and_then(language::proto::deserialize_anchor)
2263            .context("invalid end")?;
2264        buffer
2265            .update(&mut cx, |buffer, _| {
2266                buffer.wait_for_version(deserialize_version(&message.version))
2267            })
2268            .await?;
2269
2270        Ok(Self { range: start..end })
2271    }
2272
2273    fn response_to_proto(
2274        response: Vec<InlayHint>,
2275        _: &mut Project,
2276        _: PeerId,
2277        buffer_version: &clock::Global,
2278        _: &mut AppContext,
2279    ) -> proto::InlayHintsResponse {
2280        proto::InlayHintsResponse {
2281            hints: response
2282                .into_iter()
2283                .map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
2284                .collect(),
2285            version: serialize_version(buffer_version),
2286        }
2287    }
2288
2289    async fn response_from_proto(
2290        self,
2291        message: proto::InlayHintsResponse,
2292        _: ModelHandle<Project>,
2293        buffer: ModelHandle<Buffer>,
2294        mut cx: AsyncAppContext,
2295    ) -> anyhow::Result<Vec<InlayHint>> {
2296        buffer
2297            .update(&mut cx, |buffer, _| {
2298                buffer.wait_for_version(deserialize_version(&message.version))
2299            })
2300            .await?;
2301
2302        let mut hints = Vec::new();
2303        for message_hint in message.hints {
2304            hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2305        }
2306
2307        Ok(hints)
2308    }
2309
2310    fn buffer_id_from_proto(message: &proto::InlayHints) -> u64 {
2311        message.buffer_id
2312    }
2313}