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
1860#[async_trait(?Send)]
1861impl LspCommand for OnTypeFormatting {
1862    type Response = Option<Transaction>;
1863    type LspRequest = lsp::request::OnTypeFormatting;
1864    type ProtoRequest = proto::OnTypeFormatting;
1865
1866    fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
1867        let Some(on_type_formatting_options) =
1868            &server_capabilities.document_on_type_formatting_provider
1869        else {
1870            return false;
1871        };
1872        on_type_formatting_options
1873            .first_trigger_character
1874            .contains(&self.trigger)
1875            || on_type_formatting_options
1876                .more_trigger_character
1877                .iter()
1878                .flatten()
1879                .any(|chars| chars.contains(&self.trigger))
1880    }
1881
1882    fn to_lsp(
1883        &self,
1884        path: &Path,
1885        _: &Buffer,
1886        _: &Arc<LanguageServer>,
1887        _: &AppContext,
1888    ) -> lsp::DocumentOnTypeFormattingParams {
1889        lsp::DocumentOnTypeFormattingParams {
1890            text_document_position: lsp::TextDocumentPositionParams::new(
1891                lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1892                point_to_lsp(self.position),
1893            ),
1894            ch: self.trigger.clone(),
1895            options: lsp_formatting_options(self.options.tab_size),
1896        }
1897    }
1898
1899    async fn response_from_lsp(
1900        self,
1901        message: Option<Vec<lsp::TextEdit>>,
1902        project: Model<Project>,
1903        buffer: Model<Buffer>,
1904        server_id: LanguageServerId,
1905        mut cx: AsyncAppContext,
1906    ) -> Result<Option<Transaction>> {
1907        if let Some(edits) = message {
1908            let (lsp_adapter, lsp_server) =
1909                language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1910            Project::deserialize_edits(
1911                project,
1912                buffer,
1913                edits,
1914                self.push_to_history,
1915                lsp_adapter,
1916                lsp_server,
1917                &mut cx,
1918            )
1919            .await
1920        } else {
1921            Ok(None)
1922        }
1923    }
1924
1925    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1926        proto::OnTypeFormatting {
1927            project_id,
1928            buffer_id: buffer.remote_id().into(),
1929            position: Some(language::proto::serialize_anchor(
1930                &buffer.anchor_before(self.position),
1931            )),
1932            trigger: self.trigger.clone(),
1933            version: serialize_version(&buffer.version()),
1934        }
1935    }
1936
1937    async fn from_proto(
1938        message: proto::OnTypeFormatting,
1939        _: Model<Project>,
1940        buffer: Model<Buffer>,
1941        mut cx: AsyncAppContext,
1942    ) -> Result<Self> {
1943        let position = message
1944            .position
1945            .and_then(deserialize_anchor)
1946            .ok_or_else(|| anyhow!("invalid position"))?;
1947        buffer
1948            .update(&mut cx, |buffer, _| {
1949                buffer.wait_for_version(deserialize_version(&message.version))
1950            })?
1951            .await?;
1952
1953        let tab_size = buffer.update(&mut cx, |buffer, cx| {
1954            language_settings(buffer.language(), buffer.file(), cx).tab_size
1955        })?;
1956
1957        Ok(Self {
1958            position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1959            trigger: message.trigger.clone(),
1960            options: lsp_formatting_options(tab_size.get()).into(),
1961            push_to_history: false,
1962        })
1963    }
1964
1965    fn response_to_proto(
1966        response: Option<Transaction>,
1967        _: &mut Project,
1968        _: PeerId,
1969        _: &clock::Global,
1970        _: &mut AppContext,
1971    ) -> proto::OnTypeFormattingResponse {
1972        proto::OnTypeFormattingResponse {
1973            transaction: response
1974                .map(|transaction| language::proto::serialize_transaction(&transaction)),
1975        }
1976    }
1977
1978    async fn response_from_proto(
1979        self,
1980        message: proto::OnTypeFormattingResponse,
1981        _: Model<Project>,
1982        _: Model<Buffer>,
1983        _: AsyncAppContext,
1984    ) -> Result<Option<Transaction>> {
1985        let Some(transaction) = message.transaction else {
1986            return Ok(None);
1987        };
1988        Ok(Some(language::proto::deserialize_transaction(transaction)?))
1989    }
1990
1991    fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> Result<BufferId> {
1992        BufferId::new(message.buffer_id)
1993    }
1994}
1995
1996impl InlayHints {
1997    pub async fn lsp_to_project_hint(
1998        lsp_hint: lsp::InlayHint,
1999        buffer_handle: &Model<Buffer>,
2000        server_id: LanguageServerId,
2001        resolve_state: ResolveState,
2002        force_no_type_left_padding: bool,
2003        cx: &mut AsyncAppContext,
2004    ) -> anyhow::Result<InlayHint> {
2005        let kind = lsp_hint.kind.and_then(|kind| match kind {
2006            lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
2007            lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
2008            _ => None,
2009        });
2010
2011        let position = buffer_handle.update(cx, |buffer, _| {
2012            let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
2013            if kind == Some(InlayHintKind::Parameter) {
2014                buffer.anchor_before(position)
2015            } else {
2016                buffer.anchor_after(position)
2017            }
2018        })?;
2019        let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
2020            .await
2021            .context("lsp to project inlay hint conversion")?;
2022        let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
2023            false
2024        } else {
2025            lsp_hint.padding_left.unwrap_or(false)
2026        };
2027
2028        Ok(InlayHint {
2029            position,
2030            padding_left,
2031            padding_right: lsp_hint.padding_right.unwrap_or(false),
2032            label,
2033            kind,
2034            tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
2035                lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
2036                lsp::InlayHintTooltip::MarkupContent(markup_content) => {
2037                    InlayHintTooltip::MarkupContent(MarkupContent {
2038                        kind: match markup_content.kind {
2039                            lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2040                            lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2041                        },
2042                        value: markup_content.value,
2043                    })
2044                }
2045            }),
2046            resolve_state,
2047        })
2048    }
2049
2050    async fn lsp_inlay_label_to_project(
2051        lsp_label: lsp::InlayHintLabel,
2052        server_id: LanguageServerId,
2053    ) -> anyhow::Result<InlayHintLabel> {
2054        let label = match lsp_label {
2055            lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
2056            lsp::InlayHintLabel::LabelParts(lsp_parts) => {
2057                let mut parts = Vec::with_capacity(lsp_parts.len());
2058                for lsp_part in lsp_parts {
2059                    parts.push(InlayHintLabelPart {
2060                        value: lsp_part.value,
2061                        tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
2062                            lsp::InlayHintLabelPartTooltip::String(s) => {
2063                                InlayHintLabelPartTooltip::String(s)
2064                            }
2065                            lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2066                                InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2067                                    kind: match markup_content.kind {
2068                                        lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2069                                        lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2070                                    },
2071                                    value: markup_content.value,
2072                                })
2073                            }
2074                        }),
2075                        location: Some(server_id).zip(lsp_part.location),
2076                    });
2077                }
2078                InlayHintLabel::LabelParts(parts)
2079            }
2080        };
2081
2082        Ok(label)
2083    }
2084
2085    pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
2086        let (state, lsp_resolve_state) = match response_hint.resolve_state {
2087            ResolveState::Resolved => (0, None),
2088            ResolveState::CanResolve(server_id, resolve_data) => (
2089                1,
2090                resolve_data
2091                    .map(|json_data| {
2092                        serde_json::to_string(&json_data)
2093                            .expect("failed to serialize resolve json data")
2094                    })
2095                    .map(|value| proto::resolve_state::LspResolveState {
2096                        server_id: server_id.0 as u64,
2097                        value,
2098                    }),
2099            ),
2100            ResolveState::Resolving => (2, None),
2101        };
2102        let resolve_state = Some(proto::ResolveState {
2103            state,
2104            lsp_resolve_state,
2105        });
2106        proto::InlayHint {
2107            position: Some(language::proto::serialize_anchor(&response_hint.position)),
2108            padding_left: response_hint.padding_left,
2109            padding_right: response_hint.padding_right,
2110            label: Some(proto::InlayHintLabel {
2111                label: Some(match response_hint.label {
2112                    InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
2113                    InlayHintLabel::LabelParts(label_parts) => {
2114                        proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
2115                            parts: label_parts.into_iter().map(|label_part| {
2116                                let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
2117                                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 });
2118                                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 });
2119                                proto::InlayHintLabelPart {
2120                                value: label_part.value,
2121                                tooltip: label_part.tooltip.map(|tooltip| {
2122                                    let proto_tooltip = match tooltip {
2123                                        InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
2124                                        InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
2125                                            is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2126                                            value: markup_content.value,
2127                                        }),
2128                                    };
2129                                    proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
2130                                }),
2131                                location_url,
2132                                location_range_start,
2133                                location_range_end,
2134                                language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
2135                            }}).collect()
2136                        })
2137                    }
2138                }),
2139            }),
2140            kind: response_hint.kind.map(|kind| kind.name().to_string()),
2141            tooltip: response_hint.tooltip.map(|response_tooltip| {
2142                let proto_tooltip = match response_tooltip {
2143                    InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
2144                    InlayHintTooltip::MarkupContent(markup_content) => {
2145                        proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
2146                            is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2147                            value: markup_content.value,
2148                        })
2149                    }
2150                };
2151                proto::InlayHintTooltip {
2152                    content: Some(proto_tooltip),
2153                }
2154            }),
2155            resolve_state,
2156        }
2157    }
2158
2159    pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
2160        let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
2161            panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
2162        });
2163        let resolve_state_data = resolve_state
2164            .lsp_resolve_state.as_ref()
2165            .map(|lsp_resolve_state| {
2166                serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
2167                    .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
2168                    .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
2169            })
2170            .transpose()?;
2171        let resolve_state = match resolve_state.state {
2172            0 => ResolveState::Resolved,
2173            1 => {
2174                let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2175                    format!(
2176                        "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2177                    )
2178                })?;
2179                ResolveState::CanResolve(server_id, lsp_resolve_state)
2180            }
2181            2 => ResolveState::Resolving,
2182            invalid => {
2183                anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2184            }
2185        };
2186        Ok(InlayHint {
2187            position: message_hint
2188                .position
2189                .and_then(language::proto::deserialize_anchor)
2190                .context("invalid position")?,
2191            label: match message_hint
2192                .label
2193                .and_then(|label| label.label)
2194                .context("missing label")?
2195            {
2196                proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2197                proto::inlay_hint_label::Label::LabelParts(parts) => {
2198                    let mut label_parts = Vec::new();
2199                    for part in parts.parts {
2200                        label_parts.push(InlayHintLabelPart {
2201                            value: part.value,
2202                            tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2203                                Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2204                                    InlayHintLabelPartTooltip::String(s)
2205                                }
2206                                Some(
2207                                    proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2208                                        markup_content,
2209                                    ),
2210                                ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2211                                    kind: if markup_content.is_markdown {
2212                                        HoverBlockKind::Markdown
2213                                    } else {
2214                                        HoverBlockKind::PlainText
2215                                    },
2216                                    value: markup_content.value,
2217                                }),
2218                                None => InlayHintLabelPartTooltip::String(String::new()),
2219                            }),
2220                            location: {
2221                                match part
2222                                    .location_url
2223                                    .zip(
2224                                        part.location_range_start.and_then(|start| {
2225                                            Some(start..part.location_range_end?)
2226                                        }),
2227                                    )
2228                                    .zip(part.language_server_id)
2229                                {
2230                                    Some(((uri, range), server_id)) => Some((
2231                                        LanguageServerId(server_id as usize),
2232                                        lsp::Location {
2233                                            uri: lsp::Url::parse(&uri)
2234                                                .context("invalid uri in hint part {part:?}")?,
2235                                            range: lsp::Range::new(
2236                                                point_to_lsp(PointUtf16::new(
2237                                                    range.start.row,
2238                                                    range.start.column,
2239                                                )),
2240                                                point_to_lsp(PointUtf16::new(
2241                                                    range.end.row,
2242                                                    range.end.column,
2243                                                )),
2244                                            ),
2245                                        },
2246                                    )),
2247                                    None => None,
2248                                }
2249                            },
2250                        });
2251                    }
2252
2253                    InlayHintLabel::LabelParts(label_parts)
2254                }
2255            },
2256            padding_left: message_hint.padding_left,
2257            padding_right: message_hint.padding_right,
2258            kind: message_hint
2259                .kind
2260                .as_deref()
2261                .and_then(InlayHintKind::from_name),
2262            tooltip: message_hint.tooltip.and_then(|tooltip| {
2263                Some(match tooltip.content? {
2264                    proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2265                    proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2266                        InlayHintTooltip::MarkupContent(MarkupContent {
2267                            kind: if markup_content.is_markdown {
2268                                HoverBlockKind::Markdown
2269                            } else {
2270                                HoverBlockKind::PlainText
2271                            },
2272                            value: markup_content.value,
2273                        })
2274                    }
2275                })
2276            }),
2277            resolve_state,
2278        })
2279    }
2280
2281    pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
2282        lsp::InlayHint {
2283            position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2284            kind: hint.kind.map(|kind| match kind {
2285                InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2286                InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2287            }),
2288            text_edits: None,
2289            tooltip: hint.tooltip.and_then(|tooltip| {
2290                Some(match tooltip {
2291                    InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2292                    InlayHintTooltip::MarkupContent(markup_content) => {
2293                        lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2294                            kind: match markup_content.kind {
2295                                HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2296                                HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2297                                HoverBlockKind::Code { .. } => return None,
2298                            },
2299                            value: markup_content.value,
2300                        })
2301                    }
2302                })
2303            }),
2304            label: match hint.label {
2305                InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2306                InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2307                    label_parts
2308                        .into_iter()
2309                        .map(|part| lsp::InlayHintLabelPart {
2310                            value: part.value,
2311                            tooltip: part.tooltip.and_then(|tooltip| {
2312                                Some(match tooltip {
2313                                    InlayHintLabelPartTooltip::String(s) => {
2314                                        lsp::InlayHintLabelPartTooltip::String(s)
2315                                    }
2316                                    InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2317                                        lsp::InlayHintLabelPartTooltip::MarkupContent(
2318                                            lsp::MarkupContent {
2319                                                kind: match markup_content.kind {
2320                                                    HoverBlockKind::PlainText => {
2321                                                        lsp::MarkupKind::PlainText
2322                                                    }
2323                                                    HoverBlockKind::Markdown => {
2324                                                        lsp::MarkupKind::Markdown
2325                                                    }
2326                                                    HoverBlockKind::Code { .. } => return None,
2327                                                },
2328                                                value: markup_content.value,
2329                                            },
2330                                        )
2331                                    }
2332                                })
2333                            }),
2334                            location: part.location.map(|(_, location)| location),
2335                            command: None,
2336                        })
2337                        .collect(),
2338                ),
2339            },
2340            padding_left: Some(hint.padding_left),
2341            padding_right: Some(hint.padding_right),
2342            data: match hint.resolve_state {
2343                ResolveState::CanResolve(_, data) => data,
2344                ResolveState::Resolving | ResolveState::Resolved => None,
2345            },
2346        }
2347    }
2348
2349    pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2350        capabilities
2351            .inlay_hint_provider
2352            .as_ref()
2353            .and_then(|options| match options {
2354                OneOf::Left(_is_supported) => None,
2355                OneOf::Right(capabilities) => match capabilities {
2356                    lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2357                    lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
2358                        o.inlay_hint_options.resolve_provider
2359                    }
2360                },
2361            })
2362            .unwrap_or(false)
2363    }
2364}
2365
2366#[async_trait(?Send)]
2367impl LspCommand for InlayHints {
2368    type Response = Vec<InlayHint>;
2369    type LspRequest = lsp::InlayHintRequest;
2370    type ProtoRequest = proto::InlayHints;
2371
2372    fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
2373        let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else {
2374            return false;
2375        };
2376        match inlay_hint_provider {
2377            lsp::OneOf::Left(enabled) => *enabled,
2378            lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2379                lsp::InlayHintServerCapabilities::Options(_) => true,
2380                lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2381            },
2382        }
2383    }
2384
2385    fn to_lsp(
2386        &self,
2387        path: &Path,
2388        buffer: &Buffer,
2389        _: &Arc<LanguageServer>,
2390        _: &AppContext,
2391    ) -> lsp::InlayHintParams {
2392        lsp::InlayHintParams {
2393            text_document: lsp::TextDocumentIdentifier {
2394                uri: lsp::Url::from_file_path(path).unwrap(),
2395            },
2396            range: range_to_lsp(self.range.to_point_utf16(buffer)),
2397            work_done_progress_params: Default::default(),
2398        }
2399    }
2400
2401    async fn response_from_lsp(
2402        self,
2403        message: Option<Vec<lsp::InlayHint>>,
2404        project: Model<Project>,
2405        buffer: Model<Buffer>,
2406        server_id: LanguageServerId,
2407        mut cx: AsyncAppContext,
2408    ) -> anyhow::Result<Vec<InlayHint>> {
2409        let (lsp_adapter, lsp_server) =
2410            language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2411        // `typescript-language-server` adds padding to the left for type hints, turning
2412        // `const foo: boolean` into `const foo : boolean` which looks odd.
2413        // `rust-analyzer` does not have the padding for this case, and we have to accommodate both.
2414        //
2415        // We could trim the whole string, but being pessimistic on par with the situation above,
2416        // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2417        // Hence let's use a heuristic first to handle the most awkward case and look for more.
2418        let force_no_type_left_padding =
2419            lsp_adapter.name.0.as_ref() == "typescript-language-server";
2420
2421        let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2422            let resolve_state = if InlayHints::can_resolve_inlays(lsp_server.capabilities()) {
2423                ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2424            } else {
2425                ResolveState::Resolved
2426            };
2427
2428            let buffer = buffer.clone();
2429            cx.spawn(move |mut cx| async move {
2430                InlayHints::lsp_to_project_hint(
2431                    lsp_hint,
2432                    &buffer,
2433                    server_id,
2434                    resolve_state,
2435                    force_no_type_left_padding,
2436                    &mut cx,
2437                )
2438                .await
2439            })
2440        });
2441        future::join_all(hints)
2442            .await
2443            .into_iter()
2444            .collect::<anyhow::Result<_>>()
2445            .context("lsp to project inlay hints conversion")
2446    }
2447
2448    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2449        proto::InlayHints {
2450            project_id,
2451            buffer_id: buffer.remote_id().into(),
2452            start: Some(language::proto::serialize_anchor(&self.range.start)),
2453            end: Some(language::proto::serialize_anchor(&self.range.end)),
2454            version: serialize_version(&buffer.version()),
2455        }
2456    }
2457
2458    async fn from_proto(
2459        message: proto::InlayHints,
2460        _: Model<Project>,
2461        buffer: Model<Buffer>,
2462        mut cx: AsyncAppContext,
2463    ) -> Result<Self> {
2464        let start = message
2465            .start
2466            .and_then(language::proto::deserialize_anchor)
2467            .context("invalid start")?;
2468        let end = message
2469            .end
2470            .and_then(language::proto::deserialize_anchor)
2471            .context("invalid end")?;
2472        buffer
2473            .update(&mut cx, |buffer, _| {
2474                buffer.wait_for_version(deserialize_version(&message.version))
2475            })?
2476            .await?;
2477
2478        Ok(Self { range: start..end })
2479    }
2480
2481    fn response_to_proto(
2482        response: Vec<InlayHint>,
2483        _: &mut Project,
2484        _: PeerId,
2485        buffer_version: &clock::Global,
2486        _: &mut AppContext,
2487    ) -> proto::InlayHintsResponse {
2488        proto::InlayHintsResponse {
2489            hints: response
2490                .into_iter()
2491                .map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
2492                .collect(),
2493            version: serialize_version(buffer_version),
2494        }
2495    }
2496
2497    async fn response_from_proto(
2498        self,
2499        message: proto::InlayHintsResponse,
2500        _: Model<Project>,
2501        buffer: Model<Buffer>,
2502        mut cx: AsyncAppContext,
2503    ) -> anyhow::Result<Vec<InlayHint>> {
2504        buffer
2505            .update(&mut cx, |buffer, _| {
2506                buffer.wait_for_version(deserialize_version(&message.version))
2507            })?
2508            .await?;
2509
2510        let mut hints = Vec::new();
2511        for message_hint in message.hints {
2512            hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2513        }
2514
2515        Ok(hints)
2516    }
2517
2518    fn buffer_id_from_proto(message: &proto::InlayHints) -> Result<BufferId> {
2519        BufferId::new(message.buffer_id)
2520    }
2521}