diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index e6c4d36984..e7fe9fe28d 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -165,6 +165,8 @@ gpui::actions!( GoToPrevHunk, GoToTypeDefinition, GoToTypeDefinitionSplit, + GoToImplementation, + GoToImplementationSplit, OpenUrl, HalfPageDown, HalfPageUp, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 3bf439f5d4..568b94ce9d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1346,6 +1346,7 @@ pub(crate) struct NavigationData { enum GotoDefinitionKind { Symbol, Type, + Implementation, } #[derive(Debug, Clone)] @@ -7352,6 +7353,18 @@ impl Editor { self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx); } + pub fn go_to_implementation(&mut self, _: &GoToImplementation, cx: &mut ViewContext) { + self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx); + } + + pub fn go_to_implementation_split( + &mut self, + _: &GoToImplementationSplit, + cx: &mut ViewContext, + ) { + self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx); + } + pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext) { self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx); } @@ -7389,6 +7402,7 @@ impl Editor { let definitions = project.update(cx, |project, cx| match kind { GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx), GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx), + GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx), }); cx.spawn(|editor, mut cx| async move { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 4431d2315e..6c5635400b 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -260,6 +260,8 @@ impl EditorElement { register_action(view, cx, Editor::go_to_prev_hunk); register_action(view, cx, Editor::go_to_definition); register_action(view, cx, Editor::go_to_definition_split); + register_action(view, cx, Editor::go_to_implementation); + register_action(view, cx, Editor::go_to_implementation_split); register_action(view, cx, Editor::go_to_type_definition); register_action(view, cx, Editor::go_to_type_definition_split); register_action(view, cx, Editor::open_url); diff --git a/crates/editor/src/mouse_context_menu.rs b/crates/editor/src/mouse_context_menu.rs index bda55e01ed..e7f0397485 100644 --- a/crates/editor/src/mouse_context_menu.rs +++ b/crates/editor/src/mouse_context_menu.rs @@ -1,6 +1,6 @@ use crate::{ - DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToTypeDefinition, - Rename, RevealInFinder, SelectMode, ToggleCodeActions, + DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToImplementation, + GoToTypeDefinition, Rename, RevealInFinder, SelectMode, ToggleCodeActions, }; use gpui::{DismissEvent, Pixels, Point, Subscription, View, ViewContext}; @@ -48,6 +48,7 @@ pub fn deploy_context_menu( menu.action("Rename Symbol", Box::new(Rename)) .action("Go to Definition", Box::new(GoToDefinition)) .action("Go to Type Definition", Box::new(GoToTypeDefinition)) + .action("Go to Implementation", Box::new(GoToImplementation)) .action("Find All References", Box::new(FindAllReferences)) .action( "Code Actions", diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index ad3a211e4c..b0379049a3 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -1136,6 +1136,7 @@ impl LanguageServer { document_formatting_provider: Some(OneOf::Left(true)), document_range_formatting_provider: Some(OneOf::Left(true)), definition_provider: Some(OneOf::Left(true)), + implementation_provider: Some(ImplementationProviderCapability::Simple(true)), type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)), ..Default::default() } diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 202da1e973..9e432c1eac 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -105,6 +105,10 @@ pub(crate) struct GetTypeDefinition { pub position: PointUtf16, } +pub(crate) struct GetImplementation { + pub position: PointUtf16, +} + pub(crate) struct GetReferences { pub position: PointUtf16, } @@ -492,6 +496,99 @@ impl LspCommand for GetDefinition { } } +#[async_trait(?Send)] +impl LspCommand for GetImplementation { + type Response = Vec; + type LspRequest = lsp::request::GotoImplementation; + type ProtoRequest = proto::GetImplementation; + + fn to_lsp( + &self, + path: &Path, + _: &Buffer, + _: &Arc, + _: &AppContext, + ) -> lsp::GotoImplementationParams { + lsp::GotoImplementationParams { + 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, + project: Model, + buffer: Model, + server_id: LanguageServerId, + cx: AsyncAppContext, + ) -> Result> { + location_links_from_lsp(message, project, buffer, server_id, cx).await + } + + fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetImplementation { + proto::GetImplementation { + 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::GetImplementation, + _: Model, + buffer: Model, + mut cx: AsyncAppContext, + ) -> Result { + 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, + project: &mut Project, + peer_id: PeerId, + _: &clock::Global, + cx: &mut AppContext, + ) -> proto::GetImplementationResponse { + let links = location_links_to_proto(response, project, peer_id, cx); + proto::GetImplementationResponse { links } + } + + async fn response_from_proto( + self, + message: proto::GetImplementationResponse, + project: Model, + _: Model, + cx: AsyncAppContext, + ) -> Result> { + location_links_from_proto(message.links, project, cx).await + } + + fn buffer_id_from_proto(message: &proto::GetImplementation) -> Result { + BufferId::new(message.buffer_id) + } +} + #[async_trait(?Send)] impl LspCommand for GetTypeDefinition { type Response = Vec; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 4924cc0c3d..952a1bf378 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4646,6 +4646,7 @@ impl Project { cx, ) } + pub fn type_definition( &self, buffer: &Model, @@ -4653,10 +4654,33 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - self.type_definition_impl(buffer, position, cx) } + fn implementation_impl( + &self, + buffer: &Model, + position: PointUtf16, + cx: &mut ModelContext, + ) -> Task>> { + self.request_lsp( + buffer.clone(), + LanguageServerToQuery::Primary, + GetImplementation { position }, + cx, + ) + } + + pub fn implementation( + &self, + buffer: &Model, + position: T, + cx: &mut ModelContext, + ) -> Task>> { + let position = position.to_point_utf16(buffer.read(cx)); + self.implementation_impl(buffer, position, cx) + } + fn references_impl( &self, buffer: &Model, diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 52117d3abf..699c7e9a1e 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -12,6 +12,14 @@ message Envelope { uint32 id = 1; optional uint32 responding_to = 2; optional PeerId original_sender_id = 3; + + /* + When you are adding a new message type, instead of adding it in semantic order + and bumping the message ID's of everything that follows, add it at the end of the + file and bump the max number. See this + https://github.com/zed-industries/zed/pull/7890#discussion_r1496621823 + + */ oneof payload { Hello hello = 4; Ack ack = 5; @@ -48,6 +56,7 @@ message Envelope { GetDefinitionResponse get_definition_response = 33; GetTypeDefinition get_type_definition = 34; GetTypeDefinitionResponse get_type_definition_response = 35; + GetReferences get_references = 36; GetReferencesResponse get_references_response = 37; GetDocumentHighlights get_document_highlights = 38; @@ -183,7 +192,10 @@ message Envelope { LspExtExpandMacroResponse lsp_ext_expand_macro_response = 155; SetRoomParticipantRole set_room_participant_role = 156; - UpdateUserChannels update_user_channels = 157; + UpdateUserChannels update_user_channels = 157; + + GetImplementation get_implementation = 162; + GetImplementationResponse get_implementation_response = 163; } reserved 158 to 161; @@ -503,6 +515,16 @@ message GetTypeDefinition { message GetTypeDefinitionResponse { repeated LocationLink links = 1; } +message GetImplementation { + uint64 project_id = 1; + uint64 buffer_id = 2; + Anchor position = 3; + repeated VectorClockEntry version = 4; + } + +message GetImplementationResponse { + repeated LocationLink links = 1; +} message GetReferences { uint64 project_id = 1; diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 9b885d1840..8e112ab56a 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -192,6 +192,8 @@ messages!( (GetReferencesResponse, Background), (GetTypeDefinition, Background), (GetTypeDefinitionResponse, Background), + (GetImplementation, Background), + (GetImplementationResponse, Background), (GetUsers, Foreground), (Hello, Foreground), (IncomingCall, Foreground), @@ -312,6 +314,7 @@ request_messages!( (GetCodeActions, GetCodeActionsResponse), (GetCompletions, GetCompletionsResponse), (GetDefinition, GetDefinitionResponse), + (GetImplementation, GetImplementationResponse), (GetDocumentHighlights, GetDocumentHighlightsResponse), (GetHover, GetHoverResponse), (GetNotifications, GetNotificationsResponse), @@ -388,6 +391,7 @@ entity_messages!( GetCodeActions, GetCompletions, GetDefinition, + GetImplementation, GetDocumentHighlights, GetHover, GetProjectSymbols,