3555 lines · 123.3 KB · Rust
RawDownload
mod signature_help; use crate::{    lsp_store::{LocalLspStore, LspStore},    CodeAction, CompletionSource, CoreCompletion, DocumentHighlight, DocumentSymbol, Hover,    HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel, InlayHintLabelPart,    InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink, LspAction, MarkupContent,    PrepareRenameResponse, ProjectTransaction, ResolveState,};use anyhow::{anyhow, Context as _, Result};use async_trait::async_trait;use client::proto::{self, PeerId};use clock::Global;use collections::HashSet;use futures::future;use gpui::{App, AsyncApp, Entity};use language::{    language_settings::{language_settings, InlayHintKind, LanguageSettings},    point_from_lsp, point_to_lsp,    proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},    range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind,    OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped,};use lsp::{    AdapterServerCapabilities, CodeActionKind, CodeActionOptions, CompletionContext,    CompletionListItemDefaultsEditRange, CompletionTriggerKind, DocumentHighlightKind,    LanguageServer, LanguageServerId, LinkedEditingRangeServerCapabilities, OneOf, RenameOptions,    ServerCapabilities,};use signature_help::{lsp_to_proto_signature, proto_to_lsp_signature};use std::{cmp::Reverse, mem, ops::Range, path::Path, sync::Arc};use text::{BufferId, LineEnding}; pub use signature_help::SignatureHelp; 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(crate) fn file_path_to_lsp_url(path: &Path) -> Result<lsp::Url> {    match lsp::Url::from_file_path(path) {        Ok(url) => Ok(url),        Err(()) => Err(anyhow!(            "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 {        true    }     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)]pub struct GetDefinition {    pub position: PointUtf16,} #[derive(Debug)]pub(crate) struct GetDeclaration {    pub position: PointUtf16,} #[derive(Debug)]pub(crate) struct GetTypeDefinition {    pub position: PointUtf16,} #[derive(Debug)]pub(crate) struct GetImplementation {    pub position: PointUtf16,} #[derive(Debug)]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,} #[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(Debug)]pub(crate) struct InlayHints {    pub range: Range<Anchor>,} #[derive(Debug, Copy, Clone)]pub(crate) struct GetCodeLens; 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,} #[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 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,            )),            _ => Err(anyhow!("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,        mut cx: AsyncApp,    ) -> Result<PrepareRenameResponse> {        buffer.update(&mut 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);                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)            .ok_or_else(|| anyhow!("invalid position"))?;        buffer            .update(&mut cx, |buffer, _| {                buffer.wait_for_version(deserialize_version(&message.version))            })?            .await?;         Ok(Self {            position: buffer.update(&mut 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),            ) {                Ok(PrepareRenameResponse::Success(start..end))            } else {                Err(anyhow!(                    "Missing start or end position in remote project PrepareRenameResponse"                ))            }        } else if message.only_unprepared_rename_supported {            Ok(PrepareRenameResponse::OnlyUnpreparedRenameSupported)        } else {            Ok(PrepareRenameResponse::InvalidPosition)        }    }     fn buffer_id_from_proto(message: &proto::PrepareRename) -> Result<BufferId> {        BufferId::new(message.buffer_id)    }} #[async_trait(?Send)]impl LspCommand for PerformRename {    type Response = ProjectTransaction;    type LspRequest = lsp::request::Rename;    type ProtoRequest = proto::PerformRename;     fn display_name(&self) -> &str {        "Rename"    }     fn to_lsp(        &self,        path: &Path,        _: &Buffer,        _: &Arc<LanguageServer>,        _: &App,    ) -> Result<lsp::RenameParams> {        Ok(lsp::RenameParams {            text_document_position: make_lsp_text_document_position(path, self.position)?,            new_name: self.new_name.clone(),            work_done_progress_params: Default::default(),        })    }     async fn response_from_lsp(        self,        message: Option<lsp::WorkspaceEdit>,        lsp_store: Entity<LspStore>,        buffer: Entity<Buffer>,        server_id: LanguageServerId,        mut cx: AsyncApp,    ) -> Result<ProjectTransaction> {        if let Some(edit) = message {            let (lsp_adapter, lsp_server) =                language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;            LocalLspStore::deserialize_workspace_edit(                lsp_store,                edit,                self.push_to_history,                lsp_adapter,                lsp_server,                &mut cx,            )            .await        } else {            Ok(ProjectTransaction::default())        }    }     fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {        proto::PerformRename {            project_id,            buffer_id: buffer.remote_id().into(),            position: Some(language::proto::serialize_anchor(                &buffer.anchor_before(self.position),            )),            new_name: self.new_name.clone(),            version: serialize_version(&buffer.version()),        }    } 

Lines 1–500 of 3555.