lsp_command.rs

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