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