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,
GoToTypeDefinition,
GoToTypeDefinitionSplit,
GoToImplementation,
GoToImplementationSplit,
OpenUrl,
HalfPageDown,
HalfPageUp,

View File

@ -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>) {
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>) {
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 {

View File

@ -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);

View File

@ -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",

View File

@ -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()
}

View File

@ -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<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)]
impl LspCommand for GetTypeDefinition {
type Response = Vec<LocationLink>;

View File

@ -4646,6 +4646,7 @@ impl Project {
cx,
)
}
pub fn type_definition<T: ToPointUtf16>(
&self,
buffer: &Model<Buffer>,
@ -4653,10 +4654,33 @@ impl Project {
cx: &mut ModelContext<Self>,
) -> Task<Result<Vec<LocationLink>>> {
let position = position.to_point_utf16(buffer.read(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(
&self,
buffer: &Model<Buffer>,

View File

@ -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;
@ -184,6 +193,9 @@ message Envelope {
SetRoomParticipantRole set_room_participant_role = 156;
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;

View File

@ -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,