5035 lines · 175.0 KB · Rust
RawDownload
pub mod signature_help; use crate::{    CodeAction, CompletionSource, CoreCompletion, CoreCompletionResponse, DocumentColor,    DocumentHighlight, DocumentSymbol, Hover, HoverBlock, HoverBlockKind, InlayHint,    InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location,    LocationLink, LspAction, LspPullDiagnostics, MarkupContent, PrepareRenameResponse,    ProjectTransaction, PulledDiagnostics, ResolveState,    lsp_store::{LocalLspStore, LspFoldingRange, LspStore},};use anyhow::{Context as _, Result};use async_trait::async_trait;use client::proto::{self, PeerId};use clock::Global;use collections::HashMap;use futures::future;use gpui::{App, AsyncApp, Entity, SharedString, Task, prelude::FluentBuilder};use language::{    Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind, CharScopeContext,    OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped,    language_settings::{InlayHintKind, LanguageSettings, language_settings},    point_from_lsp, point_to_lsp,    proto::{        deserialize_anchor, deserialize_anchor_range, deserialize_version, serialize_anchor,        serialize_anchor_range, serialize_version,    },    range_from_lsp, range_to_lsp,};use lsp::{    AdapterServerCapabilities, CodeActionKind, CodeActionOptions, CodeDescription,    CompletionContext, CompletionListItemDefaultsEditRange, CompletionTriggerKind,    DocumentHighlightKind, LanguageServer, LanguageServerId, LinkedEditingRangeServerCapabilities,    OneOf, RenameOptions, ServerCapabilities,};use serde_json::Value;use signature_help::{lsp_to_proto_signature, proto_to_lsp_signature};use std::{    cmp::Reverse, collections::hash_map, mem, ops::Range, path::Path, str::FromStr, sync::Arc,};use text::{BufferId, LineEnding};use util::{ResultExt as _, debug_panic}; pub use signature_help::SignatureHelp; fn code_action_kind_matches(requested: &lsp::CodeActionKind, actual: &lsp::CodeActionKind) -> bool {    let requested_str = requested.as_str();    let actual_str = actual.as_str();     // Exact match or hierarchical match    actual_str == requested_str        || actual_str            .strip_prefix(requested_str)            .is_some_and(|suffix| suffix.starts_with('.'))} pub fn lsp_formatting_options(settings: &LanguageSettings) -> lsp::FormattingOptions {    lsp::FormattingOptions {        tab_size: settings.tab_size.into(),        insert_spaces: !settings.hard_tabs,        trim_trailing_whitespace: Some(settings.remove_trailing_whitespace_on_save),        trim_final_newlines: Some(settings.ensure_final_newline_on_save),        insert_final_newline: Some(settings.ensure_final_newline_on_save),        ..lsp::FormattingOptions::default()    }} pub fn file_path_to_lsp_url(path: &Path) -> Result<lsp::Uri> {    match lsp::Uri::from_file_path(path) {        Ok(url) => Ok(url),        Err(()) => anyhow::bail!("Invalid file path provided to LSP request: {path:?}"),    }} pub(crate) fn make_text_document_identifier(path: &Path) -> Result<lsp::TextDocumentIdentifier> {    Ok(lsp::TextDocumentIdentifier {        uri: file_path_to_lsp_url(path)?,    })} pub(crate) fn make_lsp_text_document_position(    path: &Path,    position: PointUtf16,) -> Result<lsp::TextDocumentPositionParams> {    Ok(lsp::TextDocumentPositionParams {        text_document: make_text_document_identifier(path)?,        position: point_to_lsp(position),    })} #[async_trait(?Send)]pub trait LspCommand: 'static + Sized + Send + std::fmt::Debug {    type Response: 'static + Default + Send + std::fmt::Debug;    type LspRequest: 'static + Send + lsp::request::Request;    type ProtoRequest: 'static + Send + proto::RequestMessage;     fn display_name(&self) -> &str;     fn status(&self) -> Option<String> {        None    }     fn to_lsp_params_or_response(        &self,        path: &Path,        buffer: &Buffer,        language_server: &Arc<LanguageServer>,        cx: &App,    ) -> Result<        LspParamsOrResponse<<Self::LspRequest as lsp::request::Request>::Params, Self::Response>,    > {        if self.check_capabilities(language_server.adapter_server_capabilities()) {            Ok(LspParamsOrResponse::Params(self.to_lsp(                path,                buffer,                language_server,                cx,            )?))        } else {            Ok(LspParamsOrResponse::Response(Default::default()))        }    }     /// When false, `to_lsp_params_or_response` default implementation will return the default response.    fn check_capabilities(&self, _: AdapterServerCapabilities) -> bool;     fn to_lsp(        &self,        path: &Path,        buffer: &Buffer,        language_server: &Arc<LanguageServer>,        cx: &App,    ) -> Result<<Self::LspRequest as lsp::request::Request>::Params>;     async fn response_from_lsp(        self,        message: <Self::LspRequest as lsp::request::Request>::Result,        lsp_store: Entity<LspStore>,        buffer: Entity<Buffer>,        server_id: LanguageServerId,        cx: AsyncApp,    ) -> Result<Self::Response>;     fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;     async fn from_proto(        message: Self::ProtoRequest,        lsp_store: Entity<LspStore>,        buffer: Entity<Buffer>,        cx: AsyncApp,    ) -> Result<Self>;     fn response_to_proto(        response: Self::Response,        lsp_store: &mut LspStore,        peer_id: PeerId,        buffer_version: &clock::Global,        cx: &mut App,    ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;     async fn response_from_proto(        self,        message: <Self::ProtoRequest as proto::RequestMessage>::Response,        lsp_store: Entity<LspStore>,        buffer: Entity<Buffer>,        cx: AsyncApp,    ) -> Result<Self::Response>;     fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId>;} pub enum LspParamsOrResponse<P, R> {    Params(P),    Response(R),} #[derive(Debug)]pub(crate) struct PrepareRename {    pub position: PointUtf16,} #[derive(Debug)]pub(crate) struct PerformRename {    pub position: PointUtf16,    pub new_name: String,    pub push_to_history: bool,} #[derive(Debug, Clone, Copy)]pub struct GetDefinitions {    pub position: PointUtf16,} #[derive(Debug, Clone, Copy)]pub(crate) struct GetDeclarations {    pub position: PointUtf16,} #[derive(Debug, Clone, Copy)]pub(crate) struct GetTypeDefinitions {    pub position: PointUtf16,} #[derive(Debug, Clone, Copy)]pub(crate) struct GetImplementations {    pub position: PointUtf16,} #[derive(Debug, Clone, Copy)]pub(crate) struct GetReferences {    pub position: PointUtf16,} #[derive(Debug)]pub(crate) struct GetDocumentHighlights {    pub position: PointUtf16,} #[derive(Debug, Copy, Clone)]pub(crate) struct GetDocumentSymbols; #[derive(Clone, Debug)]pub(crate) struct GetSignatureHelp {    pub position: PointUtf16,} #[derive(Clone, Debug)]pub(crate) struct GetHover {    pub position: PointUtf16,} #[derive(Debug)]pub(crate) struct GetCompletions {    pub position: PointUtf16,    pub context: CompletionContext,    pub server_id: Option<lsp::LanguageServerId>,} #[derive(Clone, Debug)]pub(crate) struct GetCodeActions {    pub range: Range<Anchor>,    pub kinds: Option<Vec<lsp::CodeActionKind>>,} #[derive(Debug)]pub(crate) struct OnTypeFormatting {    pub position: PointUtf16,    pub trigger: String,    pub options: lsp::FormattingOptions,    pub push_to_history: bool,} #[derive(Clone, Debug)]pub(crate) struct InlayHints {    pub range: Range<Anchor>,} #[derive(Debug, Clone, Copy)]pub(crate) struct SemanticTokensFull {    pub for_server: Option<LanguageServerId>,} #[derive(Debug, Clone)]pub(crate) struct SemanticTokensDelta {    pub previous_result_id: SharedString,} #[derive(Debug)]pub(crate) enum SemanticTokensResponse {    Full {        data: Vec<u32>,        result_id: Option<SharedString>,    },    Delta {        edits: Vec<SemanticTokensEdit>,        result_id: Option<SharedString>,    },} impl Default for SemanticTokensResponse {    fn default() -> Self {        Self::Delta {            edits: Vec::new(),            result_id: None,        }    }} #[derive(Debug)]pub(crate) struct SemanticTokensEdit {    pub start: u32,    pub delete_count: u32,    pub data: Vec<u32>,} #[derive(Debug, Copy, Clone)]pub(crate) struct GetCodeLens; #[derive(Debug, Copy, Clone)]pub(crate) struct GetDocumentColor; #[derive(Debug, Copy, Clone)]pub(crate) struct GetFoldingRanges; impl GetCodeLens {    pub(crate) fn can_resolve_lens(capabilities: &ServerCapabilities) -> bool {        capabilities            .code_lens_provider            .as_ref()            .and_then(|code_lens_options| code_lens_options.resolve_provider)            .unwrap_or(false)    }} #[derive(Debug)]pub(crate) struct LinkedEditingRange {    pub position: Anchor,} #[derive(Clone, Debug)]pub struct GetDocumentDiagnostics {    /// We cannot blindly rely on server's capabilities.diagnostic_provider, as they're a singular field, whereas    /// a server can register multiple diagnostic providers post-mortem.    pub registration_id: Option<SharedString>,    pub identifier: Option<SharedString>,    pub previous_result_id: Option<SharedString>,} #[async_trait(?Send)]impl LspCommand for PrepareRename {    type Response = PrepareRenameResponse;    type LspRequest = lsp::request::PrepareRenameRequest;    type ProtoRequest = proto::PrepareRename;     fn display_name(&self) -> &str {        "Prepare rename"    }     fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {        capabilities            .server_capabilities            .rename_provider            .is_some_and(|capability| match capability {                OneOf::Left(enabled) => enabled,                OneOf::Right(options) => options.prepare_provider.unwrap_or(false),            })    }     fn to_lsp_params_or_response(        &self,        path: &Path,        buffer: &Buffer,        language_server: &Arc<LanguageServer>,        cx: &App,    ) -> Result<LspParamsOrResponse<lsp::TextDocumentPositionParams, PrepareRenameResponse>> {        let rename_provider = language_server            .adapter_server_capabilities()            .server_capabilities            .rename_provider;        match rename_provider {            Some(lsp::OneOf::Right(RenameOptions {                prepare_provider: Some(true),                ..            })) => Ok(LspParamsOrResponse::Params(self.to_lsp(                path,                buffer,                language_server,                cx,            )?)),            Some(lsp::OneOf::Right(_)) => Ok(LspParamsOrResponse::Response(                PrepareRenameResponse::OnlyUnpreparedRenameSupported,            )),            Some(lsp::OneOf::Left(true)) => Ok(LspParamsOrResponse::Response(                PrepareRenameResponse::OnlyUnpreparedRenameSupported,            )),            _ => anyhow::bail!("Rename not supported"),        }    }     fn to_lsp(        &self,        path: &Path,        _: &Buffer,        _: &Arc<LanguageServer>,        _: &App,    ) -> Result<lsp::TextDocumentPositionParams> {        make_lsp_text_document_position(path, self.position)    }     async fn response_from_lsp(        self,        message: Option<lsp::PrepareRenameResponse>,        _: Entity<LspStore>,        buffer: Entity<Buffer>,        _: LanguageServerId,        cx: AsyncApp,    ) -> Result<PrepareRenameResponse> {        buffer.read_with(&cx, |buffer, _| match message {            Some(lsp::PrepareRenameResponse::Range(range))            | Some(lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. }) => {                let Range { start, end } = range_from_lsp(range);                if buffer.clip_point_utf16(start, Bias::Left) == start.0                    && buffer.clip_point_utf16(end, Bias::Left) == end.0                {                    Ok(PrepareRenameResponse::Success(                        buffer.anchor_after(start)..buffer.anchor_before(end),                    ))                } else {                    Ok(PrepareRenameResponse::InvalidPosition)                }            }            Some(lsp::PrepareRenameResponse::DefaultBehavior { .. }) => {                let snapshot = buffer.snapshot();                let (range, _) = snapshot.surrounding_word(self.position, None);                let range = snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end);                Ok(PrepareRenameResponse::Success(range))            }            None => Ok(PrepareRenameResponse::InvalidPosition),        })    }     fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {        proto::PrepareRename {            project_id,            buffer_id: buffer.remote_id().into(),            position: Some(language::proto::serialize_anchor(                &buffer.anchor_before(self.position),            )),            version: serialize_version(&buffer.version()),        }    }     async fn from_proto(        message: proto::PrepareRename,        _: Entity<LspStore>,        buffer: Entity<Buffer>,        mut cx: AsyncApp,    ) -> Result<Self> {        let position = message            .position            .and_then(deserialize_anchor)            .context("invalid position")?;        buffer            .update(&mut cx, |buffer, _| {                buffer.wait_for_version(deserialize_version(&message.version))            })            .await?;         Ok(Self {            position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),        })    }     fn response_to_proto(        response: PrepareRenameResponse,        _: &mut LspStore,        _: PeerId,        buffer_version: &clock::Global,        _: &mut App,    ) -> proto::PrepareRenameResponse {        match response {            PrepareRenameResponse::Success(range) => proto::PrepareRenameResponse {                can_rename: true,                only_unprepared_rename_supported: false,                start: Some(language::proto::serialize_anchor(&range.start)),                end: Some(language::proto::serialize_anchor(&range.end)),                version: serialize_version(buffer_version),            },            PrepareRenameResponse::OnlyUnpreparedRenameSupported => proto::PrepareRenameResponse {                can_rename: false,                only_unprepared_rename_supported: true,                start: None,                end: None,                version: vec![],            },            PrepareRenameResponse::InvalidPosition => proto::PrepareRenameResponse {                can_rename: false,                only_unprepared_rename_supported: false,                start: None,                end: None,                version: vec![],            },        }    }     async fn response_from_proto(        self,        message: proto::PrepareRenameResponse,        _: Entity<LspStore>,        buffer: Entity<Buffer>,        mut cx: AsyncApp,    ) -> Result<PrepareRenameResponse> {        if message.can_rename {            buffer                .update(&mut cx, |buffer, _| {                    buffer.wait_for_version(deserialize_version(&message.version))                })                .await?;            if let (Some(start), Some(end)) = (                message.start.and_then(deserialize_anchor),                message.end.and_then(deserialize_anchor),

Lines 1–500 of 5035.