Add "go to type definition" action

This commit is contained in:
ForLoveOfCats 2022-07-29 11:41:08 -04:00
parent a842016380
commit 5149c15329
10 changed files with 377 additions and 75 deletions

View file

@ -192,6 +192,7 @@
"shift-f8": "editor::GoToPrevDiagnostic",
"f2": "editor::Rename",
"f12": "editor::GoToDefinition",
"cmd-f12": "editor::GoToTypeDefinition",
"alt-shift-f12": "editor::FindAllReferences",
"ctrl-m": "editor::MoveToEnclosingBracket",
"alt-cmd-[": "editor::Fold",

View file

@ -187,6 +187,7 @@ actions!(
SelectLargerSyntaxNode,
SelectSmallerSyntaxNode,
GoToDefinition,
GoToTypeDefinition,
MoveToEnclosingBracket,
UndoSelection,
RedoSelection,
@ -297,6 +298,7 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(Editor::go_to_diagnostic);
cx.add_action(Editor::go_to_prev_diagnostic);
cx.add_action(Editor::go_to_definition);
cx.add_action(Editor::go_to_type_definition);
cx.add_action(Editor::page_up);
cx.add_action(Editor::page_down);
cx.add_action(Editor::fold);
@ -895,6 +897,11 @@ pub struct NavigationData {
pub struct EditorCreated(pub ViewHandle<Editor>);
enum GotoDefinitionKind {
Symbol,
Type,
}
impl Editor {
pub fn single_line(
field_editor_style: Option<GetFieldEditorTheme>,
@ -4693,6 +4700,22 @@ impl Editor {
workspace: &mut Workspace,
_: &GoToDefinition,
cx: &mut ViewContext<Workspace>,
) {
Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
}
pub fn go_to_type_definition(
workspace: &mut Workspace,
_: &GoToTypeDefinition,
cx: &mut ViewContext<Workspace>,
) {
Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
}
fn go_to_definition_of_kind(
kind: GotoDefinitionKind,
workspace: &mut Workspace,
cx: &mut ViewContext<Workspace>,
) {
let active_item = workspace.active_item(cx);
let editor_handle = if let Some(editor) = active_item
@ -4714,7 +4737,11 @@ impl Editor {
};
let project = workspace.project().clone();
let definitions = project.update(cx, |project, cx| project.definition(&buffer, head, cx));
let definitions = project.update(cx, |project, cx| match kind {
GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
});
cx.spawn(|workspace, mut cx| async move {
let definitions = definitions.await?;
workspace.update(&mut cx, |workspace, cx| {

View file

@ -2,8 +2,8 @@ use context_menu::ContextMenuItem;
use gpui::{geometry::vector::Vector2F, impl_internal_actions, MutableAppContext, ViewContext};
use crate::{
DisplayPoint, Editor, EditorMode, Event, FindAllReferences, GoToDefinition, Rename, SelectMode,
ToggleCodeActions,
DisplayPoint, Editor, EditorMode, Event, FindAllReferences, GoToDefinition, GoToTypeDefinition,
Rename, SelectMode, ToggleCodeActions,
};
#[derive(Clone, PartialEq)]
@ -50,6 +50,7 @@ pub fn deploy_context_menu(
vec![
ContextMenuItem::item("Rename Symbol", Rename),
ContextMenuItem::item("Go To Definition", GoToDefinition),
ContextMenuItem::item("Go To Type Definition", GoToTypeDefinition),
ContextMenuItem::item("Find All References", FindAllReferences),
ContextMenuItem::item(
"Code Actions",

View file

@ -1,3 +1,4 @@
pub use lsp_types::request::*;
pub use lsp_types::*;
use anyhow::{anyhow, Context, Result};

View file

@ -75,6 +75,10 @@ pub(crate) struct GetDefinition {
pub position: PointUtf16,
}
pub(crate) struct GetTypeDefinition {
pub position: PointUtf16,
}
pub(crate) struct GetReferences {
pub position: PointUtf16,
}
@ -565,6 +569,243 @@ impl LspCommand for GetDefinition {
}
}
#[async_trait(?Send)]
impl LspCommand for GetTypeDefinition {
type Response = Vec<LocationLink>;
type LspRequest = lsp::request::GotoTypeDefinition;
type ProtoRequest = proto::GetTypeDefinition;
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoTypeDefinitionParams {
lsp::GotoTypeDefinitionParams {
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::GotoTypeDefinitionResponse>,
project: ModelHandle<Project>,
buffer: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
) -> Result<Vec<LocationLink>> {
let mut definitions = Vec::new();
let (lsp_adapter, language_server) = project
.read_with(&cx, |project, cx| {
project
.language_server_for_buffer(buffer.read(cx), cx)
.map(|(adapter, server)| (adapter.clone(), server.clone()))
})
.ok_or_else(|| anyhow!("no language server found for buffer"))?;
if let Some(message) = message {
let mut unresolved_links = Vec::new();
match message {
lsp::GotoTypeDefinitionResponse::Scalar(loc) => {
unresolved_links.push((None, loc.uri, loc.range));
}
lsp::GotoTypeDefinitionResponse::Array(locs) => {
unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
}
lsp::GotoTypeDefinitionResponse::Link(links) => {
unresolved_links.extend(links.into_iter().map(|l| {
(
l.origin_selection_range,
l.target_uri,
l.target_selection_range,
)
}));
}
}
for (origin_range, target_uri, target_range) in unresolved_links {
let target_buffer_handle = project
.update(&mut cx, |this, cx| {
this.open_local_buffer_via_lsp(
target_uri,
language_server.server_id(),
lsp_adapter.name.clone(),
cx,
)
})
.await?;
cx.read(|cx| {
let origin_location = origin_range.map(|origin_range| {
let origin_buffer = buffer.read(cx);
let origin_start = origin_buffer
.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
let origin_end = origin_buffer
.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
Location {
buffer: buffer.clone(),
range: origin_buffer.anchor_after(origin_start)
..origin_buffer.anchor_before(origin_end),
}
});
let target_buffer = target_buffer_handle.read(cx);
let target_start = target_buffer
.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
let target_end = target_buffer
.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
let target_location = Location {
buffer: target_buffer_handle,
range: target_buffer.anchor_after(target_start)
..target_buffer.anchor_before(target_end),
};
definitions.push(LocationLink {
origin: origin_location,
target: target_location,
})
});
}
}
Ok(definitions)
}
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition {
proto::GetTypeDefinition {
project_id,
buffer_id: buffer.remote_id(),
position: Some(language::proto::serialize_anchor(
&buffer.anchor_before(self.position),
)),
version: serialize_version(&buffer.version()),
}
}
async fn from_proto(
message: proto::GetTypeDefinition,
_: ModelHandle<Project>,
buffer: ModelHandle<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.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
})
}
fn response_to_proto(
response: Vec<LocationLink>,
project: &mut Project,
peer_id: PeerId,
_: &clock::Global,
cx: &AppContext,
) -> proto::GetTypeDefinitionResponse {
let links = response
.into_iter()
.map(|definition| {
let origin = definition.origin.map(|origin| {
let buffer = project.serialize_buffer_for_peer(&origin.buffer, peer_id, cx);
proto::Location {
start: Some(serialize_anchor(&origin.range.start)),
end: Some(serialize_anchor(&origin.range.end)),
buffer: Some(buffer),
}
});
let buffer =
project.serialize_buffer_for_peer(&definition.target.buffer, peer_id, cx);
let target = proto::Location {
start: Some(serialize_anchor(&definition.target.range.start)),
end: Some(serialize_anchor(&definition.target.range.end)),
buffer: Some(buffer),
};
proto::LocationLink {
origin,
target: Some(target),
}
})
.collect();
proto::GetTypeDefinitionResponse { links }
}
async fn response_from_proto(
self,
message: proto::GetTypeDefinitionResponse,
project: ModelHandle<Project>,
_: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
) -> Result<Vec<LocationLink>> {
let mut links = Vec::new();
for link in message.links {
let origin = match link.origin {
Some(origin) => {
let buffer = origin
.buffer
.ok_or_else(|| anyhow!("missing origin buffer"))?;
let buffer = project
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.await?;
let start = origin
.start
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing origin start"))?;
let end = origin
.end
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing origin end"))?;
buffer
.update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
.await;
Some(Location {
buffer,
range: start..end,
})
}
None => None,
};
let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
let buffer = target.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
let buffer = project
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.await?;
let start = target
.start
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target start"))?;
let end = target
.end
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target end"))?;
buffer
.update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
.await;
let target = Location {
buffer,
range: start..end,
};
links.push(LocationLink { origin, target })
}
Ok(links)
}
fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> u64 {
message.buffer_id
}
}
#[async_trait(?Send)]
impl LspCommand for GetReferences {
type Response = Vec<Location>;

View file

@ -3250,6 +3250,16 @@ impl Project {
self.request_lsp(buffer.clone(), GetDefinition { position }, cx)
}
pub fn type_definition<T: ToPointUtf16>(
&self,
buffer: &ModelHandle<Buffer>,
position: T,
cx: &mut ModelContext<Self>,
) -> Task<Result<Vec<LocationLink>>> {
let position = position.to_point_utf16(buffer.read(cx));
self.request_lsp(buffer.clone(), GetTypeDefinition { position }, cx)
}
pub fn references<T: ToPointUtf16>(
&self,
buffer: &ModelHandle<Buffer>,

View file

@ -26,85 +26,87 @@ message Envelope {
GetDefinition get_definition = 20;
GetDefinitionResponse get_definition_response = 21;
GetReferences get_references = 22;
GetReferencesResponse get_references_response = 23;
GetDocumentHighlights get_document_highlights = 24;
GetDocumentHighlightsResponse get_document_highlights_response = 25;
GetProjectSymbols get_project_symbols = 26;
GetProjectSymbolsResponse get_project_symbols_response = 27;
OpenBufferForSymbol open_buffer_for_symbol = 28;
OpenBufferForSymbolResponse open_buffer_for_symbol_response = 29;
GetTypeDefinition get_type_definition = 22;
GetTypeDefinitionResponse get_type_definition_response = 23;
GetReferences get_references = 24;
GetReferencesResponse get_references_response = 25;
GetDocumentHighlights get_document_highlights = 26;
GetDocumentHighlightsResponse get_document_highlights_response = 27;
GetProjectSymbols get_project_symbols = 28;
GetProjectSymbolsResponse get_project_symbols_response = 29;
OpenBufferForSymbol open_buffer_for_symbol = 30;
OpenBufferForSymbolResponse open_buffer_for_symbol_response = 31;
UpdateProject update_project = 30;
RegisterProjectActivity register_project_activity = 31;
UpdateWorktree update_worktree = 32;
UpdateWorktreeExtensions update_worktree_extensions = 33;
UpdateProject update_project = 32;
RegisterProjectActivity register_project_activity = 33;
UpdateWorktree update_worktree = 34;
UpdateWorktreeExtensions update_worktree_extensions = 35;
CreateProjectEntry create_project_entry = 34;
RenameProjectEntry rename_project_entry = 35;
CopyProjectEntry copy_project_entry = 36;
DeleteProjectEntry delete_project_entry = 37;
ProjectEntryResponse project_entry_response = 38;
CreateProjectEntry create_project_entry = 36;
RenameProjectEntry rename_project_entry = 37;
CopyProjectEntry copy_project_entry = 38;
DeleteProjectEntry delete_project_entry = 39;
ProjectEntryResponse project_entry_response = 40;
UpdateDiagnosticSummary update_diagnostic_summary = 39;
StartLanguageServer start_language_server = 40;
UpdateLanguageServer update_language_server = 41;
UpdateDiagnosticSummary update_diagnostic_summary = 41;
StartLanguageServer start_language_server = 42;
UpdateLanguageServer update_language_server = 43;
OpenBufferById open_buffer_by_id = 42;
OpenBufferByPath open_buffer_by_path = 43;
OpenBufferResponse open_buffer_response = 44;
UpdateBuffer update_buffer = 45;
UpdateBufferFile update_buffer_file = 46;
SaveBuffer save_buffer = 47;
BufferSaved buffer_saved = 48;
BufferReloaded buffer_reloaded = 49;
ReloadBuffers reload_buffers = 50;
ReloadBuffersResponse reload_buffers_response = 51;
FormatBuffers format_buffers = 52;
FormatBuffersResponse format_buffers_response = 53;
GetCompletions get_completions = 54;
GetCompletionsResponse get_completions_response = 55;
ApplyCompletionAdditionalEdits apply_completion_additional_edits = 56;
ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 57;
GetCodeActions get_code_actions = 58;
GetCodeActionsResponse get_code_actions_response = 59;
GetHover get_hover = 60;
GetHoverResponse get_hover_response = 61;
ApplyCodeAction apply_code_action = 62;
ApplyCodeActionResponse apply_code_action_response = 63;
PrepareRename prepare_rename = 64;
PrepareRenameResponse prepare_rename_response = 65;
PerformRename perform_rename = 66;
PerformRenameResponse perform_rename_response = 67;
SearchProject search_project = 68;
SearchProjectResponse search_project_response = 69;
OpenBufferById open_buffer_by_id = 44;
OpenBufferByPath open_buffer_by_path = 45;
OpenBufferResponse open_buffer_response = 46;
UpdateBuffer update_buffer = 47;
UpdateBufferFile update_buffer_file = 48;
SaveBuffer save_buffer = 49;
BufferSaved buffer_saved = 50;
BufferReloaded buffer_reloaded = 51;
ReloadBuffers reload_buffers = 52;
ReloadBuffersResponse reload_buffers_response = 53;
FormatBuffers format_buffers = 54;
FormatBuffersResponse format_buffers_response = 55;
GetCompletions get_completions = 56;
GetCompletionsResponse get_completions_response = 57;
ApplyCompletionAdditionalEdits apply_completion_additional_edits = 58;
ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 59;
GetCodeActions get_code_actions = 60;
GetCodeActionsResponse get_code_actions_response = 61;
GetHover get_hover = 62;
GetHoverResponse get_hover_response = 63;
ApplyCodeAction apply_code_action = 64;
ApplyCodeActionResponse apply_code_action_response = 65;
PrepareRename prepare_rename = 66;
PrepareRenameResponse prepare_rename_response = 67;
PerformRename perform_rename = 68;
PerformRenameResponse perform_rename_response = 69;
SearchProject search_project = 70;
SearchProjectResponse search_project_response = 71;
GetChannels get_channels = 70;
GetChannelsResponse get_channels_response = 71;
JoinChannel join_channel = 72;
JoinChannelResponse join_channel_response = 73;
LeaveChannel leave_channel = 74;
SendChannelMessage send_channel_message = 75;
SendChannelMessageResponse send_channel_message_response = 76;
ChannelMessageSent channel_message_sent = 77;
GetChannelMessages get_channel_messages = 78;
GetChannelMessagesResponse get_channel_messages_response = 79;
GetChannels get_channels = 72;
GetChannelsResponse get_channels_response = 73;
JoinChannel join_channel = 74;
JoinChannelResponse join_channel_response = 75;
LeaveChannel leave_channel = 76;
SendChannelMessage send_channel_message = 77;
SendChannelMessageResponse send_channel_message_response = 78;
ChannelMessageSent channel_message_sent = 79;
GetChannelMessages get_channel_messages = 80;
GetChannelMessagesResponse get_channel_messages_response = 81;
UpdateContacts update_contacts = 80;
UpdateInviteInfo update_invite_info = 81;
ShowContacts show_contacts = 82;
UpdateContacts update_contacts = 82;
UpdateInviteInfo update_invite_info = 83;
ShowContacts show_contacts = 84;
GetUsers get_users = 83;
FuzzySearchUsers fuzzy_search_users = 84;
UsersResponse users_response = 85;
RequestContact request_contact = 86;
RespondToContactRequest respond_to_contact_request = 87;
RemoveContact remove_contact = 88;
GetUsers get_users = 85;
FuzzySearchUsers fuzzy_search_users = 86;
UsersResponse users_response = 87;
RequestContact request_contact = 88;
RespondToContactRequest respond_to_contact_request = 89;
RemoveContact remove_contact = 90;
Follow follow = 89;
FollowResponse follow_response = 90;
UpdateFollowers update_followers = 91;
Unfollow unfollow = 92;
Follow follow = 91;
FollowResponse follow_response = 92;
UpdateFollowers update_followers = 93;
Unfollow unfollow = 94;
}
}
@ -263,6 +265,17 @@ message GetDefinitionResponse {
repeated LocationLink links = 1;
}
message GetTypeDefinition {
uint64 project_id = 1;
uint64 buffer_id = 2;
Anchor position = 3;
repeated VectorClockEntry version = 4;
}
message GetTypeDefinitionResponse {
repeated LocationLink links = 1;
}
message GetReferences {
uint64 project_id = 1;
uint64 buffer_id = 2;

View file

@ -106,6 +106,8 @@ messages!(
(GetCompletionsResponse, Background),
(GetDefinition, Background),
(GetDefinitionResponse, Background),
(GetTypeDefinition, Background),
(GetTypeDefinitionResponse, Background),
(GetDocumentHighlights, Background),
(GetDocumentHighlightsResponse, Background),
(GetReferences, Background),
@ -183,6 +185,7 @@ request_messages!(
(GetHover, GetHoverResponse),
(GetCompletions, GetCompletionsResponse),
(GetDefinition, GetDefinitionResponse),
(GetTypeDefinition, GetTypeDefinitionResponse),
(GetDocumentHighlights, GetDocumentHighlightsResponse),
(GetReferences, GetReferencesResponse),
(GetProjectSymbols, GetProjectSymbolsResponse),
@ -226,6 +229,7 @@ entity_messages!(
GetCodeActions,
GetCompletions,
GetDefinition,
GetTypeDefinition,
GetDocumentHighlights,
GetHover,
GetReferences,

View file

@ -6,4 +6,4 @@ pub use conn::Connection;
pub use peer::*;
mod macros;
pub const PROTOCOL_VERSION: u32 = 28;
pub const PROTOCOL_VERSION: u32 = 29;

View file

@ -274,6 +274,10 @@ pub fn menus() -> Vec<Menu<'static>> {
name: "Go to Definition",
action: Box::new(editor::GoToDefinition),
},
MenuItem::Action {
name: "Go to Type Definition",
action: Box::new(editor::GoToTypeDefinition),
},
MenuItem::Action {
name: "Go to References",
action: Box::new(editor::FindAllReferences),