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