mod signature_help; use crate::{ lsp_store::{LocalLspStore, LspStore}, CodeAction, CoreCompletion, DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink, MarkupContent, PrepareRenameResponse, 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, Entity, 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, RenameOptions, 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() }} 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: &AppContext, ) -> 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: &AppContext, ) -> Result<<Self::LspRequest as lsp::request::Request>::Params>; async fn response_from_lsp( self, message: <Self::LspRequest as lsp::request::Request>::Result, lsp_store: Model<LspStore>, 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, lsp_store: Model<LspStore>, buffer: Model<Buffer>, cx: AsyncAppContext, ) -> Result<Self>; fn response_to_proto( response: Self::Response, lsp_store: &mut LspStore, 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, lsp_store: Model<LspStore>, buffer: Model<Buffer>, cx: AsyncAppContext, ) -> 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(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)]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: &AppContext, ) -> 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>, _: &AppContext, ) -> Result<lsp::TextDocumentPositionParams> { make_lsp_text_document_position(path, self.position) } async fn response_from_lsp( self, message: Option<lsp::PrepareRenameResponse>, _: Model<LspStore>, buffer: Model<Buffer>, _: LanguageServerId, mut cx: AsyncAppContext, ) -> 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 { .. }) => { Err(anyhow!("Invalid for language server to send a `defaultBehavior` response to `prepareRename`")) } 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, _: Model<LspStore>, 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: PrepareRenameResponse, _: &mut LspStore, _: PeerId, buffer_version: &clock::Global, _: &mut AppContext, ) -> 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, _: Model<LspStore>, buffer: Model<Buffer>, mut cx: AsyncAppContext, ) -> 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>, _: &AppContext, ) -> 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: Model<LspStore>, 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(&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()), } } async fn from_proto( message: proto::PerformRename, _: Model<LspStore>, 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)) })?Lines 1–500 of 3180. Load more
mod signature_help; use crate::{ lsp_store::{LocalLspStore, LspStore}, CodeAction, CoreCompletion, DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink, MarkupContent, PrepareRenameResponse, 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, Entity, 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, RenameOptions, 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() } } 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: &AppContext, ) -> 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: &AppContext, ) -> Result<<Self::LspRequest as lsp::request::Request>::Params>; async fn response_from_lsp( self, message: <Self::LspRequest as lsp::request::Request>::Result, lsp_store: Model<LspStore>, 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, lsp_store: Model<LspStore>, buffer: Model<Buffer>, cx: AsyncAppContext, ) -> Result<Self>; fn response_to_proto( response: Self::Response, lsp_store: &mut LspStore, 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, lsp_store: Model<LspStore>, buffer: Model<Buffer>, cx: AsyncAppContext, ) -> 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(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)] 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: &AppContext, ) -> 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>, _: &AppContext, ) -> Result<lsp::TextDocumentPositionParams> { make_lsp_text_document_position(path, self.position) } async fn response_from_lsp( self, message: Option<lsp::PrepareRenameResponse>, _: Model<LspStore>, buffer: Model<Buffer>, _: LanguageServerId, mut cx: AsyncAppContext, ) -> 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 { .. }) => { Err(anyhow!("Invalid for language server to send a `defaultBehavior` response to `prepareRename`")) } 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, _: Model<LspStore>, 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: PrepareRenameResponse, _: &mut LspStore, _: PeerId, buffer_version: &clock::Global, _: &mut AppContext, ) -> 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, _: Model<LspStore>, buffer: Model<Buffer>, mut cx: AsyncAppContext, ) -> 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>, _: &AppContext, ) -> 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: Model<LspStore>, 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(&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()), } } async fn from_proto( message: proto::PerformRename, _: Model<LspStore>, 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)) })?