Editor: support go to implementation (#7890)

Release Notes:

- Added "Go to implementation" support in editor.
This commit is contained in:
Leon Huston 2024-02-23 02:22:04 +13:00 committed by GitHub
parent 94bc216bbd
commit b716035d02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 171 additions and 4 deletions

View File

@ -165,6 +165,8 @@ gpui::actions!(
GoToPrevHunk, GoToPrevHunk,
GoToTypeDefinition, GoToTypeDefinition,
GoToTypeDefinitionSplit, GoToTypeDefinitionSplit,
GoToImplementation,
GoToImplementationSplit,
OpenUrl, OpenUrl,
HalfPageDown, HalfPageDown,
HalfPageUp, HalfPageUp,

View File

@ -1346,6 +1346,7 @@ pub(crate) struct NavigationData {
enum GotoDefinitionKind { enum GotoDefinitionKind {
Symbol, Symbol,
Type, Type,
Implementation,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -7352,6 +7353,18 @@ impl Editor {
self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx); self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
} }
pub fn go_to_implementation(&mut self, _: &GoToImplementation, cx: &mut ViewContext<Self>) {
self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx);
}
pub fn go_to_implementation_split(
&mut self,
_: &GoToImplementationSplit,
cx: &mut ViewContext<Self>,
) {
self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx);
}
pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) { pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx); 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 { let definitions = project.update(cx, |project, cx| match kind {
GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx), GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
GotoDefinitionKind::Type => project.type_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 { cx.spawn(|editor, mut cx| async move {

View File

@ -260,6 +260,8 @@ impl EditorElement {
register_action(view, cx, Editor::go_to_prev_hunk); 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);
register_action(view, cx, Editor::go_to_definition_split); 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);
register_action(view, cx, Editor::go_to_type_definition_split); register_action(view, cx, Editor::go_to_type_definition_split);
register_action(view, cx, Editor::open_url); register_action(view, cx, Editor::open_url);

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToTypeDefinition, DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToImplementation,
Rename, RevealInFinder, SelectMode, ToggleCodeActions, GoToTypeDefinition, Rename, RevealInFinder, SelectMode, ToggleCodeActions,
}; };
use gpui::{DismissEvent, Pixels, Point, Subscription, View, ViewContext}; use gpui::{DismissEvent, Pixels, Point, Subscription, View, ViewContext};
@ -48,6 +48,7 @@ pub fn deploy_context_menu(
menu.action("Rename Symbol", Box::new(Rename)) menu.action("Rename Symbol", Box::new(Rename))
.action("Go to Definition", Box::new(GoToDefinition)) .action("Go to Definition", Box::new(GoToDefinition))
.action("Go to Type Definition", Box::new(GoToTypeDefinition)) .action("Go to Type Definition", Box::new(GoToTypeDefinition))
.action("Go to Implementation", Box::new(GoToImplementation))
.action("Find All References", Box::new(FindAllReferences)) .action("Find All References", Box::new(FindAllReferences))
.action( .action(
"Code Actions", "Code Actions",

View File

@ -1136,6 +1136,7 @@ impl LanguageServer {
document_formatting_provider: Some(OneOf::Left(true)), document_formatting_provider: Some(OneOf::Left(true)),
document_range_formatting_provider: Some(OneOf::Left(true)), document_range_formatting_provider: Some(OneOf::Left(true)),
definition_provider: Some(OneOf::Left(true)), definition_provider: Some(OneOf::Left(true)),
implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)), type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
..Default::default() ..Default::default()
} }

View File

@ -105,6 +105,10 @@ pub(crate) struct GetTypeDefinition {
pub position: PointUtf16, pub position: PointUtf16,
} }
pub(crate) struct GetImplementation {
pub position: PointUtf16,
}
pub(crate) struct GetReferences { pub(crate) struct GetReferences {
pub position: PointUtf16, pub position: PointUtf16,
} }
@ -492,6 +496,99 @@ impl LspCommand for GetDefinition {
} }
} }
#[async_trait(?Send)]
impl LspCommand for GetImplementation {
type Response = Vec<LocationLink>;
type LspRequest = lsp::request::GotoImplementation;
type ProtoRequest = proto::GetImplementation;
fn to_lsp(
&self,
path: &Path,
_: &Buffer,
_: &Arc<LanguageServer>,
_: &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<lsp::GotoImplementationResponse>,
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::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<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::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<Project>,
_: Model<Buffer>,
cx: AsyncAppContext,
) -> Result<Vec<LocationLink>> {
location_links_from_proto(message.links, project, cx).await
}
fn buffer_id_from_proto(message: &proto::GetImplementation) -> Result<BufferId> {
BufferId::new(message.buffer_id)
}
}
#[async_trait(?Send)] #[async_trait(?Send)]
impl LspCommand for GetTypeDefinition { impl LspCommand for GetTypeDefinition {
type Response = Vec<LocationLink>; type Response = Vec<LocationLink>;

View File

@ -4646,6 +4646,7 @@ impl Project {
cx, cx,
) )
} }
pub fn type_definition<T: ToPointUtf16>( pub fn type_definition<T: ToPointUtf16>(
&self, &self,
buffer: &Model<Buffer>, buffer: &Model<Buffer>,
@ -4653,10 +4654,33 @@ impl Project {
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Task<Result<Vec<LocationLink>>> { ) -> Task<Result<Vec<LocationLink>>> {
let position = position.to_point_utf16(buffer.read(cx)); let position = position.to_point_utf16(buffer.read(cx));
self.type_definition_impl(buffer, position, cx) self.type_definition_impl(buffer, position, cx)
} }
fn implementation_impl(
&self,
buffer: &Model<Buffer>,
position: PointUtf16,
cx: &mut ModelContext<Self>,
) -> Task<Result<Vec<LocationLink>>> {
self.request_lsp(
buffer.clone(),
LanguageServerToQuery::Primary,
GetImplementation { position },
cx,
)
}
pub fn implementation<T: ToPointUtf16>(
&self,
buffer: &Model<Buffer>,
position: T,
cx: &mut ModelContext<Self>,
) -> Task<Result<Vec<LocationLink>>> {
let position = position.to_point_utf16(buffer.read(cx));
self.implementation_impl(buffer, position, cx)
}
fn references_impl( fn references_impl(
&self, &self,
buffer: &Model<Buffer>, buffer: &Model<Buffer>,

View File

@ -12,6 +12,14 @@ message Envelope {
uint32 id = 1; uint32 id = 1;
optional uint32 responding_to = 2; optional uint32 responding_to = 2;
optional PeerId original_sender_id = 3; 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 { oneof payload {
Hello hello = 4; Hello hello = 4;
Ack ack = 5; Ack ack = 5;
@ -48,6 +56,7 @@ message Envelope {
GetDefinitionResponse get_definition_response = 33; GetDefinitionResponse get_definition_response = 33;
GetTypeDefinition get_type_definition = 34; GetTypeDefinition get_type_definition = 34;
GetTypeDefinitionResponse get_type_definition_response = 35; GetTypeDefinitionResponse get_type_definition_response = 35;
GetReferences get_references = 36; GetReferences get_references = 36;
GetReferencesResponse get_references_response = 37; GetReferencesResponse get_references_response = 37;
GetDocumentHighlights get_document_highlights = 38; GetDocumentHighlights get_document_highlights = 38;
@ -184,6 +193,9 @@ message Envelope {
SetRoomParticipantRole set_room_participant_role = 156; 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; reserved 158 to 161;
@ -503,6 +515,16 @@ message GetTypeDefinition {
message GetTypeDefinitionResponse { message GetTypeDefinitionResponse {
repeated LocationLink links = 1; 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 { message GetReferences {
uint64 project_id = 1; uint64 project_id = 1;

View File

@ -192,6 +192,8 @@ messages!(
(GetReferencesResponse, Background), (GetReferencesResponse, Background),
(GetTypeDefinition, Background), (GetTypeDefinition, Background),
(GetTypeDefinitionResponse, Background), (GetTypeDefinitionResponse, Background),
(GetImplementation, Background),
(GetImplementationResponse, Background),
(GetUsers, Foreground), (GetUsers, Foreground),
(Hello, Foreground), (Hello, Foreground),
(IncomingCall, Foreground), (IncomingCall, Foreground),
@ -312,6 +314,7 @@ request_messages!(
(GetCodeActions, GetCodeActionsResponse), (GetCodeActions, GetCodeActionsResponse),
(GetCompletions, GetCompletionsResponse), (GetCompletions, GetCompletionsResponse),
(GetDefinition, GetDefinitionResponse), (GetDefinition, GetDefinitionResponse),
(GetImplementation, GetImplementationResponse),
(GetDocumentHighlights, GetDocumentHighlightsResponse), (GetDocumentHighlights, GetDocumentHighlightsResponse),
(GetHover, GetHoverResponse), (GetHover, GetHoverResponse),
(GetNotifications, GetNotificationsResponse), (GetNotifications, GetNotificationsResponse),
@ -388,6 +391,7 @@ entity_messages!(
GetCodeActions, GetCodeActions,
GetCompletions, GetCompletions,
GetDefinition, GetDefinition,
GetImplementation,
GetDocumentHighlights, GetDocumentHighlights,
GetHover, GetHover,
GetProjectSymbols, GetProjectSymbols,