2887 lines · 101.5 KB · Rust
RawDownload
mod signature_help; use crate::{    CodeAction, CoreCompletion, DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint,    InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location,    LocationLink, MarkupContent, Project, ProjectTransaction, ResolveState,};use anyhow::{anyhow, Context, Result};use async_trait::async_trait;use client::proto::{self, PeerId};use clock::Global;use collections::HashSet;use futures::future;use gpui::{AppContext, AsyncAppContext, Model};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,    ServerCapabilities,};use signature_help::{lsp_to_proto_signature, proto_to_lsp_signature};use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};use text::{BufferId, LineEnding}; pub use signature_help::{    SignatureHelp, SIGNATURE_HELP_HIGHLIGHT_CURRENT, SIGNATURE_HELP_HIGHLIGHT_OVERLOAD,}; 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()    }} #[async_trait(?Send)]pub trait LspCommand: 'static + Sized + Send {    type Response: 'static + Default + Send;    type LspRequest: 'static + Send + lsp::request::Request;    type ProtoRequest: 'static + Send + proto::RequestMessage;     fn check_capabilities(&self, _: AdapterServerCapabilities) -> bool {        true    }     fn status(&self) -> Option<String> {        None    }     fn to_lsp(        &self,        path: &Path,        buffer: &Buffer,        language_server: &Arc<LanguageServer>,        cx: &AppContext,    ) -> <Self::LspRequest as lsp::request::Request>::Params;     async fn response_from_lsp(        self,        message: <Self::LspRequest as lsp::request::Request>::Result,        project: Model<Project>,        buffer: Model<Buffer>,        server_id: LanguageServerId,        cx: AsyncAppContext,    ) -> Result<Self::Response>;     fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;     async fn from_proto(        message: Self::ProtoRequest,        project: Model<Project>,        buffer: Model<Buffer>,        cx: AsyncAppContext,    ) -> Result<Self>;     fn response_to_proto(        response: Self::Response,        project: &mut Project,        peer_id: PeerId,        buffer_version: &clock::Global,        cx: &mut AppContext,    ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;     async fn response_from_proto(        self,        message: <Self::ProtoRequest as proto::RequestMessage>::Response,        project: Model<Project>,        buffer: Model<Buffer>,        cx: AsyncAppContext,    ) -> Result<Self::Response>;     fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId>;} pub(crate) struct PrepareRename {    pub position: PointUtf16,} pub(crate) struct PerformRename {    pub position: PointUtf16,    pub new_name: String,    pub push_to_history: bool,} pub struct GetDefinition {    pub position: PointUtf16,} pub(crate) struct GetTypeDefinition {    pub position: PointUtf16,} pub(crate) struct GetImplementation {    pub position: PointUtf16,} pub(crate) struct GetReferences {    pub position: PointUtf16,} pub(crate) struct GetDocumentHighlights {    pub position: PointUtf16,} #[derive(Clone)]pub(crate) struct GetSignatureHelp {    pub position: PointUtf16,} #[derive(Clone)]pub(crate) struct GetHover {    pub position: PointUtf16,} pub(crate) struct GetCompletions {    pub position: PointUtf16,    pub context: CompletionContext,} #[derive(Clone)]pub(crate) struct GetCodeActions {    pub range: Range<Anchor>,    pub kinds: Option<Vec<lsp::CodeActionKind>>,} pub(crate) struct OnTypeFormatting {    pub position: PointUtf16,    pub trigger: String,    pub options: lsp::FormattingOptions,    pub push_to_history: bool,} pub(crate) struct InlayHints {    pub range: Range<Anchor>,} pub(crate) struct LinkedEditingRange {    pub position: Anchor,} #[async_trait(?Send)]impl LspCommand for PrepareRename {    type Response = Option<Range<Anchor>>;    type LspRequest = lsp::request::PrepareRenameRequest;    type ProtoRequest = proto::PrepareRename;     fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {        if let Some(lsp::OneOf::Right(rename)) = &capabilities.server_capabilities.rename_provider {            rename.prepare_provider == Some(true)        } else {            false        }    }     fn to_lsp(        &self,        path: &Path,        _: &Buffer,        _: &Arc<LanguageServer>,        _: &AppContext,    ) -> lsp::TextDocumentPositionParams {        lsp::TextDocumentPositionParams {            text_document: lsp::TextDocumentIdentifier {                uri: lsp::Url::from_file_path(path).unwrap(),            },            position: point_to_lsp(self.position),        }    }     async fn response_from_lsp(        self,        message: Option<lsp::PrepareRenameResponse>,        _: Model<Project>,        buffer: Model<Buffer>,        _: LanguageServerId,        mut cx: AsyncAppContext,    ) -> Result<Option<Range<Anchor>>> {        buffer.update(&mut cx, |buffer, _| {            if let Some(                lsp::PrepareRenameResponse::Range(range)                | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },            ) = message            {                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                {                    return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));                }            }            Ok(None)        })?    }     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,        _: Model<Project>,        buffer: Model<Buffer>,        mut cx: AsyncAppContext,    ) -> 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(        range: Option<Range<Anchor>>,        _: &mut Project,        _: PeerId,        buffer_version: &clock::Global,        _: &mut AppContext,    ) -> proto::PrepareRenameResponse {        proto::PrepareRenameResponse {            can_rename: range.is_some(),            start: range                .as_ref()                .map(|range| language::proto::serialize_anchor(&range.start)),            end: range                .as_ref()                .map(|range| language::proto::serialize_anchor(&range.end)),            version: serialize_version(buffer_version),        }    }     async fn response_from_proto(        self,        message: proto::PrepareRenameResponse,        _: Model<Project>,        buffer: Model<Buffer>,        mut cx: AsyncAppContext,    ) -> Result<Option<Range<Anchor>>> {        if message.can_rename {            buffer                .update(&mut cx, |buffer, _| {                    buffer.wait_for_version(deserialize_version(&message.version))                })?                .await?;            let start = message.start.and_then(deserialize_anchor);            let end = message.end.and_then(deserialize_anchor);            Ok(start.zip(end).map(|(start, end)| start..end))        } else {            Ok(None)        }    }     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 to_lsp(        &self,        path: &Path,        _: &Buffer,        _: &Arc<LanguageServer>,        _: &AppContext,    ) -> lsp::RenameParams {        lsp::RenameParams {            text_document_position: lsp::TextDocumentPositionParams {                text_document: lsp::TextDocumentIdentifier {                    uri: lsp::Url::from_file_path(path).unwrap(),                },                position: point_to_lsp(self.position),            },            new_name: self.new_name.clone(),            work_done_progress_params: Default::default(),        }    }     async fn response_from_lsp(        self,        message: Option<lsp::WorkspaceEdit>,        project: Model<Project>,        buffer: Model<Buffer>,        server_id: LanguageServerId,        mut cx: AsyncAppContext,    ) -> Result<ProjectTransaction> {        if let Some(edit) = message {            let (lsp_adapter, lsp_server) =                language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;            Project::deserialize_workspace_edit(                project,                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()),        }    }     async fn from_proto(        message: proto::PerformRename,        _: Model<Project>,        buffer: Model<Buffer>,        mut cx: AsyncAppContext,    ) -> 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))?,            new_name: message.new_name,            push_to_history: false,        })    }     fn response_to_proto(        response: ProjectTransaction,        project: &mut Project,        peer_id: PeerId,        _: &clock::Global,        cx: &mut AppContext,    ) -> proto::PerformRenameResponse {        let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);        proto::PerformRenameResponse {            transaction: Some(transaction),        }    }     async fn response_from_proto(        self,        message: proto::PerformRenameResponse,        project: Model<Project>,        _: Model<Buffer>,        cx: AsyncAppContext,    ) -> Result<ProjectTransaction> {        let message = message            .transaction            .ok_or_else(|| anyhow!("missing transaction"))?;        Project::deserialize_project_transaction(            project.downgrade(),            message,            self.push_to_history,            cx,        )        .await    }     fn buffer_id_from_proto(message: &proto::PerformRename) -> Result<BufferId> {        BufferId::new(message.buffer_id)    }} #[async_trait(?Send)]impl LspCommand for GetDefinition {    type Response = Vec<LocationLink>;    type LspRequest = lsp::request::GotoDefinition;    type ProtoRequest = proto::GetDefinition;     fn to_lsp(        &self,        path: &Path,        _: &Buffer,        _: &Arc<LanguageServer>,        _: &AppContext,    ) -> lsp::GotoDefinitionParams {        lsp::GotoDefinitionParams {            text_document_position_params: lsp::TextDocumentPositionParams {                text_document: lsp::TextDocumentIdentifier {                    uri: lsp::Url::from_file_path(path).unwrap(),                },                position: point_to_lsp(self.position),            },            work_done_progress_params: Default::default(),            partial_result_params: Default::default(),        }    }     async fn response_from_lsp(        self,        message: Option<lsp::GotoDefinitionResponse>,        project: Model<Project>,        buffer: Model<Buffer>,        server_id: LanguageServerId,        cx: AsyncAppContext,    ) -> Result<Vec<LocationLink>> {        location_links_from_lsp(message, project, buffer, server_id, cx).await    }     fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {        proto::GetDefinition {            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::GetDefinition,        _: Model<Project>,        buffer: Model<Buffer>,        mut cx: AsyncAppContext,    ) -> 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: Vec<LocationLink>,        project: &mut Project,        peer_id: PeerId,        _: &clock::Global,        cx: &mut AppContext,    ) -> proto::GetDefinitionResponse {        let links = location_links_to_proto(response, project, peer_id, cx);        proto::GetDefinitionResponse { links }    }

Lines 1–500 of 2887.