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