lsp_command.rs

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