diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 72252edd71..28eec35def 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -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", diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 819aa5ccd5..558a6bfd98 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -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); +enum GotoDefinitionKind { + Symbol, + Type, +} + impl Editor { pub fn single_line( field_editor_style: Option, @@ -4693,6 +4700,22 @@ impl Editor { workspace: &mut Workspace, _: &GoToDefinition, cx: &mut ViewContext, + ) { + Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx); + } + + pub fn go_to_type_definition( + workspace: &mut Workspace, + _: &GoToTypeDefinition, + cx: &mut ViewContext, + ) { + Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx); + } + + fn go_to_definition_of_kind( + kind: GotoDefinitionKind, + workspace: &mut Workspace, + cx: &mut ViewContext, ) { 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| { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index b8bee49d8d..fa4c126671 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -6,7 +6,9 @@ use super::{ use crate::{ display_map::{BlockStyle, DisplaySnapshot, TransformBlock}, hover_popover::HoverAt, - link_go_to_definition::{CmdChanged, GoToFetchedDefinition, UpdateGoToDefinitionLink}, + link_go_to_definition::{ + CmdShiftChanged, GoToFetchedDefinition, GoToFetchedTypeDefinition, UpdateGoToDefinitionLink, + }, mouse_context_menu::DeployMouseContextMenu, EditorStyle, }; @@ -122,7 +124,12 @@ impl EditorElement { if cmd && paint.text_bounds.contains_point(position) { let (point, overshoot) = paint.point_for_position(&self.snapshot(cx), layout, position); if overshoot.is_zero() { - cx.dispatch_action(GoToFetchedDefinition { point }); + if shift { + cx.dispatch_action(GoToFetchedTypeDefinition { point }); + } else { + cx.dispatch_action(GoToFetchedDefinition { point }); + } + return true; } } @@ -238,8 +245,12 @@ impl EditorElement { fn mouse_moved( &self, - position: Vector2F, - cmd: bool, + MouseMovedEvent { + cmd, + shift, + position, + .. + }: MouseMovedEvent, layout: &LayoutState, paint: &PaintState, cx: &mut EventContext, @@ -260,6 +271,7 @@ impl EditorElement { cx.dispatch_action(UpdateGoToDefinitionLink { point, cmd_held: cmd, + shift_held: shift, }); if paint @@ -283,8 +295,11 @@ impl EditorElement { true } - fn modifiers_changed(&self, cmd: bool, cx: &mut EventContext) -> bool { - cx.dispatch_action(CmdChanged { cmd_down: cmd }); + fn modifiers_changed(&self, event: ModifiersChangedEvent, cx: &mut EventContext) -> bool { + cx.dispatch_action(CmdShiftChanged { + cmd_down: event.cmd, + shift_down: event.shift, + }); false } @@ -1534,32 +1549,34 @@ impl Element for EditorElement { paint, cx, ), + Event::MouseDown(MouseButtonEvent { button: MouseButton::Right, position, .. }) => self.mouse_right_down(*position, layout, paint, cx), + Event::MouseUp(MouseButtonEvent { button: MouseButton::Left, position, .. }) => self.mouse_up(*position, cx), + Event::MouseMoved(MouseMovedEvent { pressed_button: Some(MouseButton::Left), position, .. }) => self.mouse_dragged(*position, layout, paint, cx), + Event::ScrollWheel(ScrollWheelEvent { position, delta, precise, }) => self.scroll(*position, *delta, *precise, layout, paint, cx), - Event::ModifiersChanged(ModifiersChangedEvent { cmd, .. }) => { - self.modifiers_changed(*cmd, cx) - } - Event::MouseMoved(MouseMovedEvent { position, cmd, .. }) => { - self.mouse_moved(*position, *cmd, layout, paint, cx) - } + + &Event::ModifiersChanged(event) => self.modifiers_changed(event, cx), + + &Event::MouseMoved(event) => self.mouse_moved(event, layout, paint, cx), _ => false, } diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index f0dc359b4b..b57179c07d 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -8,18 +8,21 @@ use util::TryFutureExt; use workspace::Workspace; use crate::{ - Anchor, DisplayPoint, Editor, EditorSnapshot, Event, GoToDefinition, Select, SelectPhase, + Anchor, DisplayPoint, Editor, EditorSnapshot, Event, GoToDefinition, GoToTypeDefinition, + Select, SelectPhase, }; #[derive(Clone, PartialEq)] pub struct UpdateGoToDefinitionLink { pub point: Option, pub cmd_held: bool, + pub shift_held: bool, } #[derive(Clone, PartialEq)] -pub struct CmdChanged { +pub struct CmdShiftChanged { pub cmd_down: bool, + pub shift_down: bool, } #[derive(Clone, PartialEq)] @@ -27,28 +30,44 @@ pub struct GoToFetchedDefinition { pub point: DisplayPoint, } +#[derive(Clone, PartialEq)] +pub struct GoToFetchedTypeDefinition { + pub point: DisplayPoint, +} + impl_internal_actions!( editor, - [UpdateGoToDefinitionLink, CmdChanged, GoToFetchedDefinition] + [ + UpdateGoToDefinitionLink, + CmdShiftChanged, + GoToFetchedDefinition, + GoToFetchedTypeDefinition + ] ); pub fn init(cx: &mut MutableAppContext) { cx.add_action(update_go_to_definition_link); - cx.add_action(cmd_changed); + cx.add_action(cmd_shift_changed); cx.add_action(go_to_fetched_definition); + cx.add_action(go_to_fetched_type_definition); } #[derive(Default)] pub struct LinkGoToDefinitionState { pub last_mouse_location: Option, pub symbol_range: Option>, + pub kind: Option, pub definitions: Vec, pub task: Option>>, } pub fn update_go_to_definition_link( editor: &mut Editor, - &UpdateGoToDefinitionLink { point, cmd_held }: &UpdateGoToDefinitionLink, + &UpdateGoToDefinitionLink { + point, + cmd_held, + shift_held, + }: &UpdateGoToDefinitionLink, cx: &mut ViewContext, ) { // Store new mouse point as an anchor @@ -72,7 +91,13 @@ pub fn update_go_to_definition_link( editor.link_go_to_definition_state.last_mouse_location = point.clone(); if cmd_held { if let Some(point) = point { - show_link_definition(editor, point, snapshot, cx); + let kind = if shift_held { + LinkDefinitionKind::Type + } else { + LinkDefinitionKind::Symbol + }; + + show_link_definition(kind, editor, point, snapshot, cx); return; } } @@ -80,9 +105,12 @@ pub fn update_go_to_definition_link( hide_link_definition(editor, cx); } -pub fn cmd_changed( +pub fn cmd_shift_changed( editor: &mut Editor, - &CmdChanged { cmd_down }: &CmdChanged, + &CmdShiftChanged { + cmd_down, + shift_down, + }: &CmdShiftChanged, cx: &mut ViewContext, ) { if let Some(point) = editor @@ -92,19 +120,37 @@ pub fn cmd_changed( { if cmd_down { let snapshot = editor.snapshot(cx); - show_link_definition(editor, point.clone(), snapshot, cx); + let kind = if shift_down { + LinkDefinitionKind::Type + } else { + LinkDefinitionKind::Symbol + }; + + show_link_definition(kind, editor, point.clone(), snapshot, cx); } else { hide_link_definition(editor, cx) } } } +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum LinkDefinitionKind { + Symbol, + Type, +} + pub fn show_link_definition( + definition_kind: LinkDefinitionKind, editor: &mut Editor, trigger_point: Anchor, snapshot: EditorSnapshot, cx: &mut ViewContext, ) { + let same_kind = editor.link_go_to_definition_state.kind == Some(definition_kind); + if !same_kind { + hide_link_definition(editor, cx); + } + if editor.pending_rename.is_some() { return; } @@ -135,17 +181,20 @@ pub fn show_link_definition( return; }; - // Don't request again if the location is within the symbol region of a previous request + // Don't request again if the location is within the symbol region of a previous request with the same kind if let Some(symbol_range) = &editor.link_go_to_definition_state.symbol_range { - if symbol_range + let point_after_start = symbol_range .start .cmp(&trigger_point, &snapshot.buffer_snapshot) - .is_le() - && symbol_range - .end - .cmp(&trigger_point, &snapshot.buffer_snapshot) - .is_ge() - { + .is_le(); + + let point_before_end = symbol_range + .end + .cmp(&trigger_point, &snapshot.buffer_snapshot) + .is_ge(); + + let point_within_range = point_after_start && point_before_end; + if point_within_range && same_kind { return; } } @@ -154,8 +203,14 @@ pub fn show_link_definition( async move { // query the LSP for definition info let definition_request = cx.update(|cx| { - project.update(cx, |project, cx| { - project.definition(&buffer, buffer_position.clone(), cx) + project.update(cx, |project, cx| match definition_kind { + LinkDefinitionKind::Symbol => { + project.definition(&buffer, buffer_position.clone(), cx) + } + + LinkDefinitionKind::Type => { + project.type_definition(&buffer, buffer_position.clone(), cx) + } }) }); @@ -181,6 +236,7 @@ pub fn show_link_definition( this.update(&mut cx, |this, cx| { // Clear any existing highlights this.clear_text_highlights::(cx); + this.link_go_to_definition_state.kind = Some(definition_kind); this.link_go_to_definition_state.symbol_range = result .as_ref() .and_then(|(symbol_range, _)| symbol_range.clone()); @@ -258,7 +314,24 @@ pub fn hide_link_definition(editor: &mut Editor, cx: &mut ViewContext) { pub fn go_to_fetched_definition( workspace: &mut Workspace, - GoToFetchedDefinition { point }: &GoToFetchedDefinition, + &GoToFetchedDefinition { point }: &GoToFetchedDefinition, + cx: &mut ViewContext, +) { + go_to_fetched_definition_of_kind(LinkDefinitionKind::Symbol, workspace, point, cx); +} + +pub fn go_to_fetched_type_definition( + workspace: &mut Workspace, + &GoToFetchedTypeDefinition { point }: &GoToFetchedTypeDefinition, + cx: &mut ViewContext, +) { + go_to_fetched_definition_of_kind(LinkDefinitionKind::Type, workspace, point, cx); +} + +fn go_to_fetched_definition_of_kind( + kind: LinkDefinitionKind, + workspace: &mut Workspace, + point: DisplayPoint, cx: &mut ViewContext, ) { let active_item = workspace.active_item(cx); @@ -271,13 +344,14 @@ pub fn go_to_fetched_definition( return; }; - let definitions = editor_handle.update(cx, |editor, cx| { + let (cached_definitions, cached_definitions_kind) = editor_handle.update(cx, |editor, cx| { let definitions = editor.link_go_to_definition_state.definitions.clone(); hide_link_definition(editor, cx); - definitions + (definitions, editor.link_go_to_definition_state.kind) }); - if !definitions.is_empty() { + let is_correct_kind = cached_definitions_kind == Some(kind); + if !cached_definitions.is_empty() && is_correct_kind { editor_handle.update(cx, |editor, cx| { if !editor.focused { cx.focus_self(); @@ -285,7 +359,7 @@ pub fn go_to_fetched_definition( } }); - Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx); + Editor::navigate_to_definitions(workspace, editor_handle, cached_definitions, cx); } else { editor_handle.update(cx, |editor, cx| { editor.select( @@ -298,7 +372,13 @@ pub fn go_to_fetched_definition( ); }); - Editor::go_to_definition(workspace, &GoToDefinition, cx); + match kind { + LinkDefinitionKind::Symbol => Editor::go_to_definition(workspace, &GoToDefinition, cx), + + LinkDefinitionKind::Type => { + Editor::go_to_type_definition(workspace, &GoToTypeDefinition, cx) + } + } } } @@ -306,11 +386,128 @@ pub fn go_to_fetched_definition( mod tests { use futures::StreamExt; use indoc::indoc; + use lsp::request::{GotoDefinition, GotoTypeDefinition}; use crate::test::EditorLspTestContext; use super::*; + #[gpui::test] + async fn test_link_go_to_type_definition(cx: &mut gpui::TestAppContext) { + let mut cx = EditorLspTestContext::new_rust( + lsp::ServerCapabilities { + hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), + ..Default::default() + }, + cx, + ) + .await; + + cx.set_state(indoc! {" + struct A; + let v|ariable = A; + "}); + + // Basic hold cmd+shift, expect highlight in region if response contains type definition + let hover_point = cx.display_point(indoc! {" + struct A; + let v|ariable = A; + "}); + let symbol_range = cx.lsp_range(indoc! {" + struct A; + let [variable] = A; + "}); + let target_range = cx.lsp_range(indoc! {" + struct [A]; + let variable = A; + "}); + + let mut requests = + cx.handle_request::(move |url, _, _| async move { + Ok(Some(lsp::GotoTypeDefinitionResponse::Link(vec![ + lsp::LocationLink { + origin_selection_range: Some(symbol_range), + target_uri: url.clone(), + target_range, + target_selection_range: target_range, + }, + ]))) + }); + + // Press cmd+shift to trigger highlight + cx.update_editor(|editor, cx| { + update_go_to_definition_link( + editor, + &UpdateGoToDefinitionLink { + point: Some(hover_point), + cmd_held: true, + shift_held: true, + }, + cx, + ); + }); + requests.next().await; + cx.foreground().run_until_parked(); + cx.assert_editor_text_highlights::(indoc! {" + struct A; + let [variable] = A; + "}); + + // Unpress shift causes highlight to go away (normal goto-definition is not valid here) + cx.update_editor(|editor, cx| { + cmd_shift_changed( + editor, + &CmdShiftChanged { + cmd_down: true, + shift_down: false, + }, + cx, + ); + }); + // Assert no link highlights + cx.assert_editor_text_highlights::(indoc! {" + struct A; + let variable = A; + "}); + + // Cmd+shift click without existing definition requests and jumps + let hover_point = cx.display_point(indoc! {" + struct A; + let v|ariable = A; + "}); + let target_range = cx.lsp_range(indoc! {" + struct [A]; + let variable = A; + "}); + + let mut requests = + cx.handle_request::(move |url, _, _| async move { + Ok(Some(lsp::GotoTypeDefinitionResponse::Link(vec![ + lsp::LocationLink { + origin_selection_range: None, + target_uri: url, + target_range, + target_selection_range: target_range, + }, + ]))) + }); + + cx.update_workspace(|workspace, cx| { + go_to_fetched_type_definition( + workspace, + &GoToFetchedTypeDefinition { point: hover_point }, + cx, + ); + }); + requests.next().await; + cx.foreground().run_until_parked(); + + cx.assert_editor_state(indoc! {" + struct [A}; + let variable = A; + "}); + } + #[gpui::test] async fn test_link_go_to_definition(cx: &mut gpui::TestAppContext) { let mut cx = EditorLspTestContext::new_rust( @@ -327,7 +524,8 @@ mod tests { do_work(); fn do_work() - test();"}); + test(); + "}); // Basic hold cmd, expect highlight in region if response contains definition let hover_point = cx.display_point(indoc! {" @@ -335,38 +533,41 @@ mod tests { do_w|ork(); fn do_work() - test();"}); - + test(); + "}); let symbol_range = cx.lsp_range(indoc! {" fn test() [do_work](); fn do_work() - test();"}); + test(); + "}); let target_range = cx.lsp_range(indoc! {" fn test() do_work(); fn [do_work]() - test();"}); + test(); + "}); + + let mut requests = cx.handle_request::(move |url, _, _| async move { + Ok(Some(lsp::GotoDefinitionResponse::Link(vec![ + lsp::LocationLink { + origin_selection_range: Some(symbol_range), + target_uri: url.clone(), + target_range, + target_selection_range: target_range, + }, + ]))) + }); - let mut requests = - cx.handle_request::(move |url, _, _| async move { - Ok(Some(lsp::GotoDefinitionResponse::Link(vec![ - lsp::LocationLink { - origin_selection_range: Some(symbol_range), - target_uri: url.clone(), - target_range, - target_selection_range: target_range, - }, - ]))) - }); cx.update_editor(|editor, cx| { update_go_to_definition_link( editor, &UpdateGoToDefinitionLink { point: Some(hover_point), cmd_held: true, + shift_held: false, }, cx, ); @@ -378,11 +579,19 @@ mod tests { [do_work](); fn do_work() - test();"}); + test(); + "}); // Unpress cmd causes highlight to go away cx.update_editor(|editor, cx| { - cmd_changed(editor, &CmdChanged { cmd_down: false }, cx); + cmd_shift_changed( + editor, + &CmdShiftChanged { + cmd_down: false, + shift_down: false, + }, + cx, + ); }); // Assert no link highlights cx.assert_editor_text_highlights::(indoc! {" @@ -390,28 +599,29 @@ mod tests { do_work(); fn do_work() - test();"}); + test(); + "}); // Response without source range still highlights word cx.update_editor(|editor, _| editor.link_go_to_definition_state.last_mouse_location = None); - let mut requests = - cx.handle_request::(move |url, _, _| async move { - Ok(Some(lsp::GotoDefinitionResponse::Link(vec![ - lsp::LocationLink { - // No origin range - origin_selection_range: None, - target_uri: url.clone(), - target_range, - target_selection_range: target_range, - }, - ]))) - }); + let mut requests = cx.handle_request::(move |url, _, _| async move { + Ok(Some(lsp::GotoDefinitionResponse::Link(vec![ + lsp::LocationLink { + // No origin range + origin_selection_range: None, + target_uri: url.clone(), + target_range, + target_selection_range: target_range, + }, + ]))) + }); cx.update_editor(|editor, cx| { update_go_to_definition_link( editor, &UpdateGoToDefinitionLink { point: Some(hover_point), cmd_held: true, + shift_held: false, }, cx, ); @@ -424,7 +634,8 @@ mod tests { [do_work](); fn do_work() - test();"}); + test(); + "}); // Moving mouse to location with no response dismisses highlight let hover_point = cx.display_point(indoc! {" @@ -432,19 +643,21 @@ mod tests { do_work(); fn do_work() - test();"}); - let mut requests = - cx.lsp - .handle_request::(move |_, _| async move { - // No definitions returned - Ok(Some(lsp::GotoDefinitionResponse::Link(vec![]))) - }); + test(); + "}); + let mut requests = cx + .lsp + .handle_request::(move |_, _| async move { + // No definitions returned + Ok(Some(lsp::GotoDefinitionResponse::Link(vec![]))) + }); cx.update_editor(|editor, cx| { update_go_to_definition_link( editor, &UpdateGoToDefinitionLink { point: Some(hover_point), cmd_held: true, + shift_held: false, }, cx, ); @@ -458,7 +671,8 @@ mod tests { do_work(); fn do_work() - test();"}); + test(); + "}); // Move mouse without cmd and then pressing cmd triggers highlight let hover_point = cx.display_point(indoc! {" @@ -466,13 +680,15 @@ mod tests { do_work(); fn do_work() - te|st();"}); + te|st(); + "}); cx.update_editor(|editor, cx| { update_go_to_definition_link( editor, &UpdateGoToDefinitionLink { point: Some(hover_point), cmd_held: false, + shift_held: false, }, cx, ); @@ -485,34 +701,43 @@ mod tests { do_work(); fn do_work() - test();"}); + test(); + "}); let symbol_range = cx.lsp_range(indoc! {" fn test() do_work(); fn do_work() - [test]();"}); + [test](); + "}); let target_range = cx.lsp_range(indoc! {" fn [test]() do_work(); fn do_work() - test();"}); + test(); + "}); - let mut requests = - cx.handle_request::(move |url, _, _| async move { - Ok(Some(lsp::GotoDefinitionResponse::Link(vec![ - lsp::LocationLink { - origin_selection_range: Some(symbol_range), - target_uri: url, - target_range, - target_selection_range: target_range, - }, - ]))) - }); + let mut requests = cx.handle_request::(move |url, _, _| async move { + Ok(Some(lsp::GotoDefinitionResponse::Link(vec![ + lsp::LocationLink { + origin_selection_range: Some(symbol_range), + target_uri: url, + target_range, + target_selection_range: target_range, + }, + ]))) + }); cx.update_editor(|editor, cx| { - cmd_changed(editor, &CmdChanged { cmd_down: true }, cx); + cmd_shift_changed( + editor, + &CmdShiftChanged { + cmd_down: true, + shift_down: false, + }, + cx, + ); }); requests.next().await; cx.foreground().run_until_parked(); @@ -522,7 +747,8 @@ mod tests { do_work(); fn do_work() - [test]();"}); + [test](); + "}); // Moving within symbol range doesn't re-request let hover_point = cx.display_point(indoc! {" @@ -530,13 +756,15 @@ mod tests { do_work(); fn do_work() - tes|t();"}); + tes|t(); + "}); cx.update_editor(|editor, cx| { update_go_to_definition_link( editor, &UpdateGoToDefinitionLink { point: Some(hover_point), cmd_held: true, + shift_held: false, }, cx, ); @@ -547,7 +775,8 @@ mod tests { do_work(); fn do_work() - [test]();"}); + [test](); + "}); // Cmd click with existing definition doesn't re-request and dismisses highlight cx.update_workspace(|workspace, cx| { @@ -555,7 +784,7 @@ mod tests { }); // Assert selection moved to to definition cx.lsp - .handle_request::(move |_, _| async move { + .handle_request::(move |_, _| async move { // Empty definition response to make sure we aren't hitting the lsp and using // the cached location instead Ok(Some(lsp::GotoDefinitionResponse::Link(vec![]))) @@ -565,14 +794,16 @@ mod tests { do_work(); fn do_work() - test();"}); + test(); + "}); // Assert no link highlights after jump cx.assert_editor_text_highlights::(indoc! {" fn test() do_work(); fn do_work() - test();"}); + test(); + "}); // Cmd click without existing definition requests and jumps let hover_point = cx.display_point(indoc! {" @@ -580,25 +811,26 @@ mod tests { do_w|ork(); fn do_work() - test();"}); + test(); + "}); let target_range = cx.lsp_range(indoc! {" fn test() do_work(); fn [do_work]() - test();"}); + test(); + "}); - let mut requests = - cx.handle_request::(move |url, _, _| async move { - Ok(Some(lsp::GotoDefinitionResponse::Link(vec![ - lsp::LocationLink { - origin_selection_range: None, - target_uri: url, - target_range, - target_selection_range: target_range, - }, - ]))) - }); + let mut requests = cx.handle_request::(move |url, _, _| async move { + Ok(Some(lsp::GotoDefinitionResponse::Link(vec![ + lsp::LocationLink { + origin_selection_range: None, + target_uri: url, + target_range, + target_selection_range: target_range, + }, + ]))) + }); cx.update_workspace(|workspace, cx| { go_to_fetched_definition(workspace, &GoToFetchedDefinition { point: hover_point }, cx); }); @@ -610,6 +842,7 @@ mod tests { do_work(); fn [do_work}() - test();"}); + test(); + "}); } } diff --git a/crates/editor/src/mouse_context_menu.rs b/crates/editor/src/mouse_context_menu.rs index ce2faf8fa6..513a9ed99c 100644 --- a/crates/editor/src/mouse_context_menu.rs +++ b/crates/editor/src/mouse_context_menu.rs @@ -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", diff --git a/crates/gpui/src/platform/event.rs b/crates/gpui/src/platform/event.rs index e833bd8eb5..6ac75926be 100644 --- a/crates/gpui/src/platform/event.rs +++ b/crates/gpui/src/platform/event.rs @@ -11,7 +11,7 @@ pub struct KeyUpEvent { pub keystroke: Keystroke, } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub struct ModifiersChangedEvent { pub ctrl: bool, pub alt: bool, @@ -19,7 +19,7 @@ pub struct ModifiersChangedEvent { pub cmd: bool, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Copy, Debug, Default)] pub struct ScrollWheelEvent { pub position: Vector2F, pub delta: Vector2F, diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 8c689808ae..5c8916e34e 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -1,3 +1,4 @@ +pub use lsp_types::request::*; pub use lsp_types::*; use anyhow::{anyhow, Context, Result}; diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 0c30ee2924..4d679ec9cb 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -8,11 +8,11 @@ use gpui::{AppContext, AsyncAppContext, ModelHandle}; use language::{ point_from_lsp, point_to_lsp, proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version}, - range_from_lsp, Anchor, Bias, Buffer, PointUtf16, ToPointUtf16, + range_from_lsp, Anchor, Bias, Buffer, CachedLspAdapter, PointUtf16, ToPointUtf16, }; -use lsp::{DocumentHighlightKind, ServerCapabilities}; +use lsp::{DocumentHighlightKind, LanguageServer, ServerCapabilities}; use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag}; -use std::{cmp::Reverse, ops::Range, path::Path}; +use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc}; #[async_trait(?Send)] pub(crate) trait LspCommand: 'static + Sized { @@ -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, } @@ -238,13 +242,7 @@ impl LspCommand for PerformRename { mut cx: AsyncAppContext, ) -> Result { if let Some(edit) = message { - let (lsp_adapter, lsp_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"))?; + let (lsp_adapter, lsp_server) = language_server_for_buffer(&project, &buffer, &mut cx)?; Project::deserialize_workspace_edit( project, edit, @@ -352,83 +350,9 @@ impl LspCommand for GetDefinition { message: Option, project: ModelHandle, buffer: ModelHandle, - mut cx: AsyncAppContext, + cx: AsyncAppContext, ) -> Result> { - 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::GotoDefinitionResponse::Scalar(loc) => { - unresolved_links.push((None, loc.uri, loc.range)); - } - lsp::GotoDefinitionResponse::Array(locs) => { - unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range))); - } - lsp::GotoDefinitionResponse::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) + location_links_from_lsp(message, project, buffer, cx).await } fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition { @@ -469,32 +393,7 @@ impl LspCommand for GetDefinition { _: &clock::Global, cx: &AppContext, ) -> proto::GetDefinitionResponse { - 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(); + let links = location_links_to_proto(response, project, peer_id, cx); proto::GetDefinitionResponse { links } } @@ -503,61 +402,9 @@ impl LspCommand for GetDefinition { message: proto::GetDefinitionResponse, project: ModelHandle, _: ModelHandle, - mut cx: AsyncAppContext, + cx: AsyncAppContext, ) -> Result> { - 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) + location_links_from_proto(message.links, project, cx).await } fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 { @@ -565,6 +412,281 @@ impl LspCommand for GetDefinition { } } +#[async_trait(?Send)] +impl LspCommand for GetTypeDefinition { + type Response = Vec; + 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, + project: ModelHandle, + buffer: ModelHandle, + cx: AsyncAppContext, + ) -> Result> { + location_links_from_lsp(message, project, buffer, cx).await + } + + 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, + buffer: ModelHandle, + 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.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)), + }) + } + + fn response_to_proto( + response: Vec, + project: &mut Project, + peer_id: PeerId, + _: &clock::Global, + cx: &AppContext, + ) -> proto::GetTypeDefinitionResponse { + let links = location_links_to_proto(response, project, peer_id, cx); + proto::GetTypeDefinitionResponse { links } + } + + async fn response_from_proto( + self, + message: proto::GetTypeDefinitionResponse, + project: ModelHandle, + _: ModelHandle, + cx: AsyncAppContext, + ) -> Result> { + location_links_from_proto(message.links, project, cx).await + } + + fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> u64 { + message.buffer_id + } +} + +fn language_server_for_buffer( + project: &ModelHandle, + buffer: &ModelHandle, + cx: &mut AsyncAppContext, +) -> Result<(Arc, Arc)> { + 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")) +} + +async fn location_links_from_proto( + proto_links: Vec, + project: ModelHandle, + mut cx: AsyncAppContext, +) -> Result> { + let mut links = Vec::new(); + + for link in proto_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) +} + +async fn location_links_from_lsp( + message: Option, + project: ModelHandle, + buffer: ModelHandle, + mut cx: AsyncAppContext, +) -> Result> { + let message = match message { + Some(message) => message, + None => return Ok(Vec::new()), + }; + + let mut unresolved_links = Vec::new(); + match message { + lsp::GotoDefinitionResponse::Scalar(loc) => { + unresolved_links.push((None, loc.uri, loc.range)); + } + + lsp::GotoDefinitionResponse::Array(locs) => { + unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range))); + } + + lsp::GotoDefinitionResponse::Link(links) => { + unresolved_links.extend(links.into_iter().map(|l| { + ( + l.origin_selection_range, + l.target_uri, + l.target_selection_range, + ) + })); + } + } + + let (lsp_adapter, language_server) = language_server_for_buffer(&project, &buffer, &mut cx)?; + let mut definitions = Vec::new(); + 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 location_links_to_proto( + links: Vec, + project: &mut Project, + peer_id: PeerId, + cx: &AppContext, +) -> Vec { + links + .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() +} + #[async_trait(?Send)] impl LspCommand for GetReferences { type Response = Vec; @@ -595,13 +717,8 @@ impl LspCommand for GetReferences { mut cx: AsyncAppContext, ) -> Result> { let mut references = 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"))?; + let (lsp_adapter, language_server) = + language_server_for_buffer(&project, &buffer, &mut cx)?; if let Some(locations) = locations { for lsp_location in locations { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 898dbb5a2f..8adc10ba55 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -3250,6 +3250,16 @@ impl Project { self.request_lsp(buffer.clone(), GetDefinition { position }, cx) } + pub fn type_definition( + &self, + buffer: &ModelHandle, + position: T, + cx: &mut ModelContext, + ) -> Task>> { + let position = position.to_point_utf16(buffer.read(cx)); + self.request_lsp(buffer.clone(), GetTypeDefinition { position }, cx) + } + pub fn references( &self, buffer: &ModelHandle, diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 35f3049edb..f52815a8be 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -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; diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index e3844a8692..8cd5ca36fb 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -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, diff --git a/crates/rpc/src/rpc.rs b/crates/rpc/src/rpc.rs index 015ac10707..c4017015f9 100644 --- a/crates/rpc/src/rpc.rs +++ b/crates/rpc/src/rpc.rs @@ -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; diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index e8297a1727..73817ca2e3 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -274,6 +274,10 @@ pub fn menus() -> Vec> { 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),