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
1409        Ok(Some(Hover {
1410            contents,
1411            range,
1412            language,
1413        }))
1414    }
1415
1416    fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
1417        BufferId::new(message.buffer_id)
1418    }
1419}
1420
1421#[async_trait(?Send)]
1422impl LspCommand for GetCompletions {
1423    type Response = Vec<Completion>;
1424    type LspRequest = lsp::request::Completion;
1425    type ProtoRequest = proto::GetCompletions;
1426
1427    fn to_lsp(
1428        &self,
1429        path: &Path,
1430        _: &Buffer,
1431        _: &Arc<LanguageServer>,
1432        _: &AppContext,
1433    ) -> lsp::CompletionParams {
1434        lsp::CompletionParams {
1435            text_document_position: lsp::TextDocumentPositionParams::new(
1436                lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1437                point_to_lsp(self.position),
1438            ),
1439            context: Default::default(),
1440            work_done_progress_params: Default::default(),
1441            partial_result_params: Default::default(),
1442        }
1443    }
1444
1445    async fn response_from_lsp(
1446        self,
1447        completions: Option<lsp::CompletionResponse>,
1448        project: Model<Project>,
1449        buffer: Model<Buffer>,
1450        server_id: LanguageServerId,
1451        mut cx: AsyncAppContext,
1452    ) -> Result<Vec<Completion>> {
1453        let mut response_list = None;
1454        let completions = if let Some(completions) = completions {
1455            match completions {
1456                lsp::CompletionResponse::Array(completions) => completions,
1457
1458                lsp::CompletionResponse::List(mut list) => {
1459                    let items = std::mem::take(&mut list.items);
1460                    response_list = Some(list);
1461                    items
1462                }
1463            }
1464        } else {
1465            Default::default()
1466        };
1467
1468        let completions = buffer.update(&mut cx, |buffer, cx| {
1469            let language_registry = project.read(cx).languages().clone();
1470            let language = buffer.language().cloned();
1471            let snapshot = buffer.snapshot();
1472            let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1473
1474            let mut range_for_token = None;
1475            completions
1476                .into_iter()
1477                .filter_map(move |mut lsp_completion| {
1478                    let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() {
1479                        // If the language server provides a range to overwrite, then
1480                        // check that the range is valid.
1481                        Some(lsp::CompletionTextEdit::Edit(edit)) => {
1482                            let range = range_from_lsp(edit.range);
1483                            let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1484                            let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1485                            if start != range.start.0 || end != range.end.0 {
1486                                log::info!("completion out of expected range");
1487                                return None;
1488                            }
1489                            (
1490                                snapshot.anchor_before(start)..snapshot.anchor_after(end),
1491                                edit.new_text.clone(),
1492                            )
1493                        }
1494
1495                        // If the language server does not provide a range, then infer
1496                        // the range based on the syntax tree.
1497                        None => {
1498                            if self.position != clipped_position {
1499                                log::info!("completion out of expected range");
1500                                return None;
1501                            }
1502
1503                            let default_edit_range = response_list
1504                                .as_ref()
1505                                .and_then(|list| list.item_defaults.as_ref())
1506                                .and_then(|defaults| defaults.edit_range.as_ref())
1507                                .and_then(|range| match range {
1508                                    CompletionListItemDefaultsEditRange::Range(r) => Some(r),
1509                                    _ => None,
1510                                });
1511
1512                            let range = if let Some(range) = default_edit_range {
1513                                let range = range_from_lsp(range.clone());
1514                                let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1515                                let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1516                                if start != range.start.0 || end != range.end.0 {
1517                                    log::info!("completion out of expected range");
1518                                    return None;
1519                                }
1520
1521                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
1522                            } else {
1523                                range_for_token
1524                                    .get_or_insert_with(|| {
1525                                        let offset = self.position.to_offset(&snapshot);
1526                                        let (range, kind) = snapshot.surrounding_word(offset);
1527                                        let range = if kind == Some(CharKind::Word) {
1528                                            range
1529                                        } else {
1530                                            offset..offset
1531                                        };
1532
1533                                        snapshot.anchor_before(range.start)
1534                                            ..snapshot.anchor_after(range.end)
1535                                    })
1536                                    .clone()
1537                            };
1538
1539                            let text = lsp_completion
1540                                .insert_text
1541                                .as_ref()
1542                                .unwrap_or(&lsp_completion.label)
1543                                .clone();
1544                            (range, text)
1545                        }
1546
1547                        Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => {
1548                            log::info!("unsupported insert/replace completion");
1549                            return None;
1550                        }
1551                    };
1552
1553                    let language_registry = language_registry.clone();
1554                    let language = language.clone();
1555                    LineEnding::normalize(&mut new_text);
1556                    Some(async move {
1557                        let mut label = None;
1558                        if let Some(language) = language.as_ref() {
1559                            language.process_completion(&mut lsp_completion).await;
1560                            label = language.label_for_completion(&lsp_completion).await;
1561                        }
1562
1563                        let documentation = if let Some(lsp_docs) = &lsp_completion.documentation {
1564                            Some(
1565                                prepare_completion_documentation(
1566                                    lsp_docs,
1567                                    &language_registry,
1568                                    language.clone(),
1569                                )
1570                                .await,
1571                            )
1572                        } else {
1573                            None
1574                        };
1575
1576                        Completion {
1577                            old_range,
1578                            new_text,
1579                            label: label.unwrap_or_else(|| {
1580                                language::CodeLabel::plain(
1581                                    lsp_completion.label.clone(),
1582                                    lsp_completion.filter_text.as_deref(),
1583                                )
1584                            }),
1585                            documentation,
1586                            server_id,
1587                            lsp_completion,
1588                        }
1589                    })
1590                })
1591        })?;
1592
1593        Ok(future::join_all(completions).await)
1594    }
1595
1596    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1597        let anchor = buffer.anchor_after(self.position);
1598        proto::GetCompletions {
1599            project_id,
1600            buffer_id: buffer.remote_id().into(),
1601            position: Some(language::proto::serialize_anchor(&anchor)),
1602            version: serialize_version(&buffer.version()),
1603        }
1604    }
1605
1606    async fn from_proto(
1607        message: proto::GetCompletions,
1608        _: Model<Project>,
1609        buffer: Model<Buffer>,
1610        mut cx: AsyncAppContext,
1611    ) -> Result<Self> {
1612        let version = deserialize_version(&message.version);
1613        buffer
1614            .update(&mut cx, |buffer, _| buffer.wait_for_version(version))?
1615            .await?;
1616        let position = message
1617            .position
1618            .and_then(language::proto::deserialize_anchor)
1619            .map(|p| {
1620                buffer.update(&mut cx, |buffer, _| {
1621                    buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1622                })
1623            })
1624            .ok_or_else(|| anyhow!("invalid position"))??;
1625        Ok(Self { position })
1626    }
1627
1628    fn response_to_proto(
1629        completions: Vec<Completion>,
1630        _: &mut Project,
1631        _: PeerId,
1632        buffer_version: &clock::Global,
1633        _: &mut AppContext,
1634    ) -> proto::GetCompletionsResponse {
1635        proto::GetCompletionsResponse {
1636            completions: completions
1637                .iter()
1638                .map(language::proto::serialize_completion)
1639                .collect(),
1640            version: serialize_version(buffer_version),
1641        }
1642    }
1643
1644    async fn response_from_proto(
1645        self,
1646        message: proto::GetCompletionsResponse,
1647        _: Model<Project>,
1648        buffer: Model<Buffer>,
1649        mut cx: AsyncAppContext,
1650    ) -> Result<Vec<Completion>> {
1651        buffer
1652            .update(&mut cx, |buffer, _| {
1653                buffer.wait_for_version(deserialize_version(&message.version))
1654            })?
1655            .await?;
1656
1657        let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
1658        let completions = message.completions.into_iter().map(|completion| {
1659            language::proto::deserialize_completion(completion, language.clone())
1660        });
1661        future::try_join_all(completions).await
1662    }
1663
1664    fn buffer_id_from_proto(message: &proto::GetCompletions) -> Result<BufferId> {
1665        BufferId::new(message.buffer_id)
1666    }
1667}
1668
1669#[async_trait(?Send)]
1670impl LspCommand for GetCodeActions {
1671    type Response = Vec<CodeAction>;
1672    type LspRequest = lsp::request::CodeActionRequest;
1673    type ProtoRequest = proto::GetCodeActions;
1674
1675    fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1676        match &capabilities.code_action_provider {
1677            None => false,
1678            Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
1679            _ => true,
1680        }
1681    }
1682
1683    fn to_lsp(
1684        &self,
1685        path: &Path,
1686        buffer: &Buffer,
1687        language_server: &Arc<LanguageServer>,
1688        _: &AppContext,
1689    ) -> lsp::CodeActionParams {
1690        let relevant_diagnostics = buffer
1691            .snapshot()
1692            .diagnostics_in_range::<_, usize>(self.range.clone(), false)
1693            .map(|entry| entry.to_lsp_diagnostic_stub())
1694            .collect();
1695        lsp::CodeActionParams {
1696            text_document: lsp::TextDocumentIdentifier::new(
1697                lsp::Url::from_file_path(path).unwrap(),
1698            ),
1699            range: range_to_lsp(self.range.to_point_utf16(buffer)),
1700            work_done_progress_params: Default::default(),
1701            partial_result_params: Default::default(),
1702            context: lsp::CodeActionContext {
1703                diagnostics: relevant_diagnostics,
1704                only: self
1705                    .kinds
1706                    .clone()
1707                    .or_else(|| language_server.code_action_kinds()),
1708                ..lsp::CodeActionContext::default()
1709            },
1710        }
1711    }
1712
1713    async fn response_from_lsp(
1714        self,
1715        actions: Option<lsp::CodeActionResponse>,
1716        _: Model<Project>,
1717        _: Model<Buffer>,
1718        server_id: LanguageServerId,
1719        _: AsyncAppContext,
1720    ) -> Result<Vec<CodeAction>> {
1721        Ok(actions
1722            .unwrap_or_default()
1723            .into_iter()
1724            .filter_map(|entry| {
1725                if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1726                    Some(CodeAction {
1727                        server_id,
1728                        range: self.range.clone(),
1729                        lsp_action,
1730                    })
1731                } else {
1732                    None
1733                }
1734            })
1735            .collect())
1736    }
1737
1738    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
1739        proto::GetCodeActions {
1740            project_id,
1741            buffer_id: buffer.remote_id().into(),
1742            start: Some(language::proto::serialize_anchor(&self.range.start)),
1743            end: Some(language::proto::serialize_anchor(&self.range.end)),
1744            version: serialize_version(&buffer.version()),
1745        }
1746    }
1747
1748    async fn from_proto(
1749        message: proto::GetCodeActions,
1750        _: Model<Project>,
1751        buffer: Model<Buffer>,
1752        mut cx: AsyncAppContext,
1753    ) -> Result<Self> {
1754        let start = message
1755            .start
1756            .and_then(language::proto::deserialize_anchor)
1757            .ok_or_else(|| anyhow!("invalid start"))?;
1758        let end = message
1759            .end
1760            .and_then(language::proto::deserialize_anchor)
1761            .ok_or_else(|| anyhow!("invalid end"))?;
1762        buffer
1763            .update(&mut cx, |buffer, _| {
1764                buffer.wait_for_version(deserialize_version(&message.version))
1765            })?
1766            .await?;
1767
1768        Ok(Self {
1769            range: start..end,
1770            kinds: None,
1771        })
1772    }
1773
1774    fn response_to_proto(
1775        code_actions: Vec<CodeAction>,
1776        _: &mut Project,
1777        _: PeerId,
1778        buffer_version: &clock::Global,
1779        _: &mut AppContext,
1780    ) -> proto::GetCodeActionsResponse {
1781        proto::GetCodeActionsResponse {
1782            actions: code_actions
1783                .iter()
1784                .map(language::proto::serialize_code_action)
1785                .collect(),
1786            version: serialize_version(buffer_version),
1787        }
1788    }
1789
1790    async fn response_from_proto(
1791        self,
1792        message: proto::GetCodeActionsResponse,
1793        _: Model<Project>,
1794        buffer: Model<Buffer>,
1795        mut cx: AsyncAppContext,
1796    ) -> Result<Vec<CodeAction>> {
1797        buffer
1798            .update(&mut cx, |buffer, _| {
1799                buffer.wait_for_version(deserialize_version(&message.version))
1800            })?
1801            .await?;
1802        message
1803            .actions
1804            .into_iter()
1805            .map(language::proto::deserialize_code_action)
1806            .collect()
1807    }
1808
1809    fn buffer_id_from_proto(message: &proto::GetCodeActions) -> Result<BufferId> {
1810        BufferId::new(message.buffer_id)
1811    }
1812}
1813
1814#[async_trait(?Send)]
1815impl LspCommand for OnTypeFormatting {
1816    type Response = Option<Transaction>;
1817    type LspRequest = lsp::request::OnTypeFormatting;
1818    type ProtoRequest = proto::OnTypeFormatting;
1819
1820    fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
1821        let Some(on_type_formatting_options) =
1822            &server_capabilities.document_on_type_formatting_provider
1823        else {
1824            return false;
1825        };
1826        on_type_formatting_options
1827            .first_trigger_character
1828            .contains(&self.trigger)
1829            || on_type_formatting_options
1830                .more_trigger_character
1831                .iter()
1832                .flatten()
1833                .any(|chars| chars.contains(&self.trigger))
1834    }
1835
1836    fn to_lsp(
1837        &self,
1838        path: &Path,
1839        _: &Buffer,
1840        _: &Arc<LanguageServer>,
1841        _: &AppContext,
1842    ) -> lsp::DocumentOnTypeFormattingParams {
1843        lsp::DocumentOnTypeFormattingParams {
1844            text_document_position: lsp::TextDocumentPositionParams::new(
1845                lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1846                point_to_lsp(self.position),
1847            ),
1848            ch: self.trigger.clone(),
1849            options: lsp_formatting_options(self.options.tab_size),
1850        }
1851    }
1852
1853    async fn response_from_lsp(
1854        self,
1855        message: Option<Vec<lsp::TextEdit>>,
1856        project: Model<Project>,
1857        buffer: Model<Buffer>,
1858        server_id: LanguageServerId,
1859        mut cx: AsyncAppContext,
1860    ) -> Result<Option<Transaction>> {
1861        if let Some(edits) = message {
1862            let (lsp_adapter, lsp_server) =
1863                language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1864            Project::deserialize_edits(
1865                project,
1866                buffer,
1867                edits,
1868                self.push_to_history,
1869                lsp_adapter,
1870                lsp_server,
1871                &mut cx,
1872            )
1873            .await
1874        } else {
1875            Ok(None)
1876        }
1877    }
1878
1879    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1880        proto::OnTypeFormatting {
1881            project_id,
1882            buffer_id: buffer.remote_id().into(),
1883            position: Some(language::proto::serialize_anchor(
1884                &buffer.anchor_before(self.position),
1885            )),
1886            trigger: self.trigger.clone(),
1887            version: serialize_version(&buffer.version()),
1888        }
1889    }
1890
1891    async fn from_proto(
1892        message: proto::OnTypeFormatting,
1893        _: Model<Project>,
1894        buffer: Model<Buffer>,
1895        mut cx: AsyncAppContext,
1896    ) -> Result<Self> {
1897        let position = message
1898            .position
1899            .and_then(deserialize_anchor)
1900            .ok_or_else(|| anyhow!("invalid position"))?;
1901        buffer
1902            .update(&mut cx, |buffer, _| {
1903                buffer.wait_for_version(deserialize_version(&message.version))
1904            })?
1905            .await?;
1906
1907        let tab_size = buffer.update(&mut cx, |buffer, cx| {
1908            language_settings(buffer.language(), buffer.file(), cx).tab_size
1909        })?;
1910
1911        Ok(Self {
1912            position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1913            trigger: message.trigger.clone(),
1914            options: lsp_formatting_options(tab_size.get()).into(),
1915            push_to_history: false,
1916        })
1917    }
1918
1919    fn response_to_proto(
1920        response: Option<Transaction>,
1921        _: &mut Project,
1922        _: PeerId,
1923        _: &clock::Global,
1924        _: &mut AppContext,
1925    ) -> proto::OnTypeFormattingResponse {
1926        proto::OnTypeFormattingResponse {
1927            transaction: response
1928                .map(|transaction| language::proto::serialize_transaction(&transaction)),
1929        }
1930    }
1931
1932    async fn response_from_proto(
1933        self,
1934        message: proto::OnTypeFormattingResponse,
1935        _: Model<Project>,
1936        _: Model<Buffer>,
1937        _: AsyncAppContext,
1938    ) -> Result<Option<Transaction>> {
1939        let Some(transaction) = message.transaction else {
1940            return Ok(None);
1941        };
1942        Ok(Some(language::proto::deserialize_transaction(transaction)?))
1943    }
1944
1945    fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> Result<BufferId> {
1946        BufferId::new(message.buffer_id)
1947    }
1948}
1949
1950impl InlayHints {
1951    pub async fn lsp_to_project_hint(
1952        lsp_hint: lsp::InlayHint,
1953        buffer_handle: &Model<Buffer>,
1954        server_id: LanguageServerId,
1955        resolve_state: ResolveState,
1956        force_no_type_left_padding: bool,
1957        cx: &mut AsyncAppContext,
1958    ) -> anyhow::Result<InlayHint> {
1959        let kind = lsp_hint.kind.and_then(|kind| match kind {
1960            lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
1961            lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
1962            _ => None,
1963        });
1964
1965        let position = buffer_handle.update(cx, |buffer, _| {
1966            let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
1967            if kind == Some(InlayHintKind::Parameter) {
1968                buffer.anchor_before(position)
1969            } else {
1970                buffer.anchor_after(position)
1971            }
1972        })?;
1973        let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
1974            .await
1975            .context("lsp to project inlay hint conversion")?;
1976        let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
1977            false
1978        } else {
1979            lsp_hint.padding_left.unwrap_or(false)
1980        };
1981
1982        Ok(InlayHint {
1983            position,
1984            padding_left,
1985            padding_right: lsp_hint.padding_right.unwrap_or(false),
1986            label,
1987            kind,
1988            tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
1989                lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
1990                lsp::InlayHintTooltip::MarkupContent(markup_content) => {
1991                    InlayHintTooltip::MarkupContent(MarkupContent {
1992                        kind: match markup_content.kind {
1993                            lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
1994                            lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
1995                        },
1996                        value: markup_content.value,
1997                    })
1998                }
1999            }),
2000            resolve_state,
2001        })
2002    }
2003
2004    async fn lsp_inlay_label_to_project(
2005        lsp_label: lsp::InlayHintLabel,
2006        server_id: LanguageServerId,
2007    ) -> anyhow::Result<InlayHintLabel> {
2008        let label = match lsp_label {
2009            lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
2010            lsp::InlayHintLabel::LabelParts(lsp_parts) => {
2011                let mut parts = Vec::with_capacity(lsp_parts.len());
2012                for lsp_part in lsp_parts {
2013                    parts.push(InlayHintLabelPart {
2014                        value: lsp_part.value,
2015                        tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
2016                            lsp::InlayHintLabelPartTooltip::String(s) => {
2017                                InlayHintLabelPartTooltip::String(s)
2018                            }
2019                            lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2020                                InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2021                                    kind: match markup_content.kind {
2022                                        lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2023                                        lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2024                                    },
2025                                    value: markup_content.value,
2026                                })
2027                            }
2028                        }),
2029                        location: Some(server_id).zip(lsp_part.location),
2030                    });
2031                }
2032                InlayHintLabel::LabelParts(parts)
2033            }
2034        };
2035
2036        Ok(label)
2037    }
2038
2039    pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
2040        let (state, lsp_resolve_state) = match response_hint.resolve_state {
2041            ResolveState::Resolved => (0, None),
2042            ResolveState::CanResolve(server_id, resolve_data) => (
2043                1,
2044                resolve_data
2045                    .map(|json_data| {
2046                        serde_json::to_string(&json_data)
2047                            .expect("failed to serialize resolve json data")
2048                    })
2049                    .map(|value| proto::resolve_state::LspResolveState {
2050                        server_id: server_id.0 as u64,
2051                        value,
2052                    }),
2053            ),
2054            ResolveState::Resolving => (2, None),
2055        };
2056        let resolve_state = Some(proto::ResolveState {
2057            state,
2058            lsp_resolve_state,
2059        });
2060        proto::InlayHint {
2061            position: Some(language::proto::serialize_anchor(&response_hint.position)),
2062            padding_left: response_hint.padding_left,
2063            padding_right: response_hint.padding_right,
2064            label: Some(proto::InlayHintLabel {
2065                label: Some(match response_hint.label {
2066                    InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
2067                    InlayHintLabel::LabelParts(label_parts) => {
2068                        proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
2069                            parts: label_parts.into_iter().map(|label_part| {
2070                                let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
2071                                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 });
2072                                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 });
2073                                proto::InlayHintLabelPart {
2074                                value: label_part.value,
2075                                tooltip: label_part.tooltip.map(|tooltip| {
2076                                    let proto_tooltip = match tooltip {
2077                                        InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
2078                                        InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
2079                                            is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2080                                            value: markup_content.value,
2081                                        }),
2082                                    };
2083                                    proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
2084                                }),
2085                                location_url,
2086                                location_range_start,
2087                                location_range_end,
2088                                language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
2089                            }}).collect()
2090                        })
2091                    }
2092                }),
2093            }),
2094            kind: response_hint.kind.map(|kind| kind.name().to_string()),
2095            tooltip: response_hint.tooltip.map(|response_tooltip| {
2096                let proto_tooltip = match response_tooltip {
2097                    InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
2098                    InlayHintTooltip::MarkupContent(markup_content) => {
2099                        proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
2100                            is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2101                            value: markup_content.value,
2102                        })
2103                    }
2104                };
2105                proto::InlayHintTooltip {
2106                    content: Some(proto_tooltip),
2107                }
2108            }),
2109            resolve_state,
2110        }
2111    }
2112
2113    pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
2114        let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
2115            panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
2116        });
2117        let resolve_state_data = resolve_state
2118            .lsp_resolve_state.as_ref()
2119            .map(|lsp_resolve_state| {
2120                serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
2121                    .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
2122                    .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
2123            })
2124            .transpose()?;
2125        let resolve_state = match resolve_state.state {
2126            0 => ResolveState::Resolved,
2127            1 => {
2128                let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2129                    format!(
2130                        "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2131                    )
2132                })?;
2133                ResolveState::CanResolve(server_id, lsp_resolve_state)
2134            }
2135            2 => ResolveState::Resolving,
2136            invalid => {
2137                anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2138            }
2139        };
2140        Ok(InlayHint {
2141            position: message_hint
2142                .position
2143                .and_then(language::proto::deserialize_anchor)
2144                .context("invalid position")?,
2145            label: match message_hint
2146                .label
2147                .and_then(|label| label.label)
2148                .context("missing label")?
2149            {
2150                proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2151                proto::inlay_hint_label::Label::LabelParts(parts) => {
2152                    let mut label_parts = Vec::new();
2153                    for part in parts.parts {
2154                        label_parts.push(InlayHintLabelPart {
2155                            value: part.value,
2156                            tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2157                                Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2158                                    InlayHintLabelPartTooltip::String(s)
2159                                }
2160                                Some(
2161                                    proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2162                                        markup_content,
2163                                    ),
2164                                ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2165                                    kind: if markup_content.is_markdown {
2166                                        HoverBlockKind::Markdown
2167                                    } else {
2168                                        HoverBlockKind::PlainText
2169                                    },
2170                                    value: markup_content.value,
2171                                }),
2172                                None => InlayHintLabelPartTooltip::String(String::new()),
2173                            }),
2174                            location: {
2175                                match part
2176                                    .location_url
2177                                    .zip(
2178                                        part.location_range_start.and_then(|start| {
2179                                            Some(start..part.location_range_end?)
2180                                        }),
2181                                    )
2182                                    .zip(part.language_server_id)
2183                                {
2184                                    Some(((uri, range), server_id)) => Some((
2185                                        LanguageServerId(server_id as usize),
2186                                        lsp::Location {
2187                                            uri: lsp::Url::parse(&uri)
2188                                                .context("invalid uri in hint part {part:?}")?,
2189                                            range: lsp::Range::new(
2190                                                point_to_lsp(PointUtf16::new(
2191                                                    range.start.row,
2192                                                    range.start.column,
2193                                                )),
2194                                                point_to_lsp(PointUtf16::new(
2195                                                    range.end.row,
2196                                                    range.end.column,
2197                                                )),
2198                                            ),
2199                                        },
2200                                    )),
2201                                    None => None,
2202                                }
2203                            },
2204                        });
2205                    }
2206
2207                    InlayHintLabel::LabelParts(label_parts)
2208                }
2209            },
2210            padding_left: message_hint.padding_left,
2211            padding_right: message_hint.padding_right,
2212            kind: message_hint
2213                .kind
2214                .as_deref()
2215                .and_then(InlayHintKind::from_name),
2216            tooltip: message_hint.tooltip.and_then(|tooltip| {
2217                Some(match tooltip.content? {
2218                    proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2219                    proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2220                        InlayHintTooltip::MarkupContent(MarkupContent {
2221                            kind: if markup_content.is_markdown {
2222                                HoverBlockKind::Markdown
2223                            } else {
2224                                HoverBlockKind::PlainText
2225                            },
2226                            value: markup_content.value,
2227                        })
2228                    }
2229                })
2230            }),
2231            resolve_state,
2232        })
2233    }
2234
2235    pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
2236        lsp::InlayHint {
2237            position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2238            kind: hint.kind.map(|kind| match kind {
2239                InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2240                InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2241            }),
2242            text_edits: None,
2243            tooltip: hint.tooltip.and_then(|tooltip| {
2244                Some(match tooltip {
2245                    InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2246                    InlayHintTooltip::MarkupContent(markup_content) => {
2247                        lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2248                            kind: match markup_content.kind {
2249                                HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2250                                HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2251                                HoverBlockKind::Code { .. } => return None,
2252                            },
2253                            value: markup_content.value,
2254                        })
2255                    }
2256                })
2257            }),
2258            label: match hint.label {
2259                InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2260                InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2261                    label_parts
2262                        .into_iter()
2263                        .map(|part| lsp::InlayHintLabelPart {
2264                            value: part.value,
2265                            tooltip: part.tooltip.and_then(|tooltip| {
2266                                Some(match tooltip {
2267                                    InlayHintLabelPartTooltip::String(s) => {
2268                                        lsp::InlayHintLabelPartTooltip::String(s)
2269                                    }
2270                                    InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2271                                        lsp::InlayHintLabelPartTooltip::MarkupContent(
2272                                            lsp::MarkupContent {
2273                                                kind: match markup_content.kind {
2274                                                    HoverBlockKind::PlainText => {
2275                                                        lsp::MarkupKind::PlainText
2276                                                    }
2277                                                    HoverBlockKind::Markdown => {
2278                                                        lsp::MarkupKind::Markdown
2279                                                    }
2280                                                    HoverBlockKind::Code { .. } => return None,
2281                                                },
2282                                                value: markup_content.value,
2283                                            },
2284                                        )
2285                                    }
2286                                })
2287                            }),
2288                            location: part.location.map(|(_, location)| location),
2289                            command: None,
2290                        })
2291                        .collect(),
2292                ),
2293            },
2294            padding_left: Some(hint.padding_left),
2295            padding_right: Some(hint.padding_right),
2296            data: match hint.resolve_state {
2297                ResolveState::CanResolve(_, data) => data,
2298                ResolveState::Resolving | ResolveState::Resolved => None,
2299            },
2300        }
2301    }
2302
2303    pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2304        capabilities
2305            .inlay_hint_provider
2306            .as_ref()
2307            .and_then(|options| match options {
2308                OneOf::Left(_is_supported) => None,
2309                OneOf::Right(capabilities) => match capabilities {
2310                    lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2311                    lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
2312                        o.inlay_hint_options.resolve_provider
2313                    }
2314                },
2315            })
2316            .unwrap_or(false)
2317    }
2318}
2319
2320#[async_trait(?Send)]
2321impl LspCommand for InlayHints {
2322    type Response = Vec<InlayHint>;
2323    type LspRequest = lsp::InlayHintRequest;
2324    type ProtoRequest = proto::InlayHints;
2325
2326    fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
2327        let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else {
2328            return false;
2329        };
2330        match inlay_hint_provider {
2331            lsp::OneOf::Left(enabled) => *enabled,
2332            lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2333                lsp::InlayHintServerCapabilities::Options(_) => true,
2334                lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2335            },
2336        }
2337    }
2338
2339    fn to_lsp(
2340        &self,
2341        path: &Path,
2342        buffer: &Buffer,
2343        _: &Arc<LanguageServer>,
2344        _: &AppContext,
2345    ) -> lsp::InlayHintParams {
2346        lsp::InlayHintParams {
2347            text_document: lsp::TextDocumentIdentifier {
2348                uri: lsp::Url::from_file_path(path).unwrap(),
2349            },
2350            range: range_to_lsp(self.range.to_point_utf16(buffer)),
2351            work_done_progress_params: Default::default(),
2352        }
2353    }
2354
2355    async fn response_from_lsp(
2356        self,
2357        message: Option<Vec<lsp::InlayHint>>,
2358        project: Model<Project>,
2359        buffer: Model<Buffer>,
2360        server_id: LanguageServerId,
2361        mut cx: AsyncAppContext,
2362    ) -> anyhow::Result<Vec<InlayHint>> {
2363        let (lsp_adapter, lsp_server) =
2364            language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2365        // `typescript-language-server` adds padding to the left for type hints, turning
2366        // `const foo: boolean` into `const foo : boolean` which looks odd.
2367        // `rust-analyzer` does not have the padding for this case, and we have to accommodate both.
2368        //
2369        // We could trim the whole string, but being pessimistic on par with the situation above,
2370        // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2371        // Hence let's use a heuristic first to handle the most awkward case and look for more.
2372        let force_no_type_left_padding =
2373            lsp_adapter.name.0.as_ref() == "typescript-language-server";
2374
2375        let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2376            let resolve_state = if InlayHints::can_resolve_inlays(lsp_server.capabilities()) {
2377                ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2378            } else {
2379                ResolveState::Resolved
2380            };
2381
2382            let buffer = buffer.clone();
2383            cx.spawn(move |mut cx| async move {
2384                InlayHints::lsp_to_project_hint(
2385                    lsp_hint,
2386                    &buffer,
2387                    server_id,
2388                    resolve_state,
2389                    force_no_type_left_padding,
2390                    &mut cx,
2391                )
2392                .await
2393            })
2394        });
2395        future::join_all(hints)
2396            .await
2397            .into_iter()
2398            .collect::<anyhow::Result<_>>()
2399            .context("lsp to project inlay hints conversion")
2400    }
2401
2402    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2403        proto::InlayHints {
2404            project_id,
2405            buffer_id: buffer.remote_id().into(),
2406            start: Some(language::proto::serialize_anchor(&self.range.start)),
2407            end: Some(language::proto::serialize_anchor(&self.range.end)),
2408            version: serialize_version(&buffer.version()),
2409        }
2410    }
2411
2412    async fn from_proto(
2413        message: proto::InlayHints,
2414        _: Model<Project>,
2415        buffer: Model<Buffer>,
2416        mut cx: AsyncAppContext,
2417    ) -> Result<Self> {
2418        let start = message
2419            .start
2420            .and_then(language::proto::deserialize_anchor)
2421            .context("invalid start")?;
2422        let end = message
2423            .end
2424            .and_then(language::proto::deserialize_anchor)
2425            .context("invalid end")?;
2426        buffer
2427            .update(&mut cx, |buffer, _| {
2428                buffer.wait_for_version(deserialize_version(&message.version))
2429            })?
2430            .await?;
2431
2432        Ok(Self { range: start..end })
2433    }
2434
2435    fn response_to_proto(
2436        response: Vec<InlayHint>,
2437        _: &mut Project,
2438        _: PeerId,
2439        buffer_version: &clock::Global,
2440        _: &mut AppContext,
2441    ) -> proto::InlayHintsResponse {
2442        proto::InlayHintsResponse {
2443            hints: response
2444                .into_iter()
2445                .map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
2446                .collect(),
2447            version: serialize_version(buffer_version),
2448        }
2449    }
2450
2451    async fn response_from_proto(
2452        self,
2453        message: proto::InlayHintsResponse,
2454        _: Model<Project>,
2455        buffer: Model<Buffer>,
2456        mut cx: AsyncAppContext,
2457    ) -> anyhow::Result<Vec<InlayHint>> {
2458        buffer
2459            .update(&mut cx, |buffer, _| {
2460                buffer.wait_for_version(deserialize_version(&message.version))
2461            })?
2462            .await?;
2463
2464        let mut hints = Vec::new();
2465        for message_hint in message.hints {
2466            hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2467        }
2468
2469        Ok(hints)
2470    }
2471
2472    fn buffer_id_from_proto(message: &proto::InlayHints) -> Result<BufferId> {
2473        BufferId::new(message.buffer_id)
2474    }
2475}