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, ProjectPath, ProjectTransaction, PulledDiagnostics, ResolveState, lsp_store::{LocalLspStore, LspDocumentLink, 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, TaskExt, prelude::FluentBuilder};use language::{ Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind, CharScopeContext, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped, language_settings::{InlayHintKind, LanguageSettings}, lsp_to_symbol_kind, 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, ops::Range, path::Path, str::FromStr, sync::Arc};use text::{BufferId, LineEnding};use util::rel_path::RelPath;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, PartialEq, Eq, Hash)]pub struct EditPredictionDefinition { pub path: ProjectPath, pub range: Range<Unclipped<PointUtf16>>,} #[derive(Debug, Clone, Copy)]pub(crate) struct GetEditPredictionDefinitions { 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 GetEditPredictionTypeDefinitions { 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; #[derive(Debug, Copy, Clone)]pub(crate) struct GetDocumentLinks; 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![], },Lines 1–500 of 5475. Load more
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, ProjectPath, ProjectTransaction, PulledDiagnostics, ResolveState, lsp_store::{LocalLspStore, LspDocumentLink, 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, TaskExt, prelude::FluentBuilder}; use language::{ Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind, CharScopeContext, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped, language_settings::{InlayHintKind, LanguageSettings}, lsp_to_symbol_kind, 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, ops::Range, path::Path, str::FromStr, sync::Arc}; use text::{BufferId, LineEnding}; use util::rel_path::RelPath; 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, PartialEq, Eq, Hash)] pub struct EditPredictionDefinition { pub path: ProjectPath, pub range: Range<Unclipped<PointUtf16>>, } #[derive(Debug, Clone, Copy)] pub(crate) struct GetEditPredictionDefinitions { 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 GetEditPredictionTypeDefinitions { 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; #[derive(Debug, Copy, Clone)] pub(crate) struct GetDocumentLinks; 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![], },