lsp_command.rs

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