Merge pull request #1420 from zed-industries/goto-type-definition

Draft: Add "go to type definition" action
This commit is contained in:
Julia 2022-07-29 18:31:50 -04:00 committed by GitHub
commit 8cf56f8c6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 791 additions and 363 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

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

View file

@ -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<DisplayPoint>,
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<Anchor>,
pub symbol_range: Option<Range<Anchor>>,
pub kind: Option<LinkDefinitionKind>,
pub definitions: Vec<LocationLink>,
pub task: Option<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<Editor>,
) {
// 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<Editor>,
) {
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<Editor>,
) {
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::<LinkGoToDefinitionState>(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<Editor>) {
pub fn go_to_fetched_definition(
workspace: &mut Workspace,
GoToFetchedDefinition { point }: &GoToFetchedDefinition,
&GoToFetchedDefinition { point }: &GoToFetchedDefinition,
cx: &mut ViewContext<Workspace>,
) {
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<Workspace>,
) {
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<Workspace>,
) {
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::<GotoTypeDefinition, _, _>(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::<LinkGoToDefinitionState>(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::<LinkGoToDefinitionState>(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::<GotoTypeDefinition, _, _>(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::<GotoDefinition, _, _>(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::<lsp::request::GotoDefinition, _, _>(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::<LinkGoToDefinitionState>(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::<lsp::request::GotoDefinition, _, _>(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::<GotoDefinition, _, _>(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::<lsp::request::GotoDefinition, _, _>(move |_, _| async move {
// No definitions returned
Ok(Some(lsp::GotoDefinitionResponse::Link(vec![])))
});
test();
"});
let mut requests = cx
.lsp
.handle_request::<GotoDefinition, _, _>(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::<lsp::request::GotoDefinition, _, _>(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::<GotoDefinition, _, _>(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::<lsp::request::GotoDefinition, _, _>(move |_, _| async move {
.handle_request::<GotoDefinition, _, _>(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::<LinkGoToDefinitionState>(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::<lsp::request::GotoDefinition, _, _>(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::<GotoDefinition, _, _>(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();
"});
}
}

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

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

View file

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

View file

@ -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<ProjectTransaction> {
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<lsp::GotoDefinitionResponse>,
project: ModelHandle<Project>,
buffer: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
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::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<Project>,
_: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
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)
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<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>,
cx: AsyncAppContext,
) -> Result<Vec<LocationLink>> {
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<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 = location_links_to_proto(response, project, peer_id, cx);
proto::GetTypeDefinitionResponse { links }
}
async fn response_from_proto(
self,
message: proto::GetTypeDefinitionResponse,
project: ModelHandle<Project>,
_: ModelHandle<Buffer>,
cx: AsyncAppContext,
) -> Result<Vec<LocationLink>> {
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<Project>,
buffer: &ModelHandle<Buffer>,
cx: &mut AsyncAppContext,
) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
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<proto::LocationLink>,
project: ModelHandle<Project>,
mut cx: AsyncAppContext,
) -> Result<Vec<LocationLink>> {
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<lsp::GotoDefinitionResponse>,
project: ModelHandle<Project>,
buffer: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
) -> Result<Vec<LocationLink>> {
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<LocationLink>,
project: &mut Project,
peer_id: PeerId,
cx: &AppContext,
) -> Vec<proto::LocationLink> {
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<Location>;
@ -595,13 +717,8 @@ impl LspCommand for GetReferences {
mut cx: AsyncAppContext,
) -> Result<Vec<Location>> {
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 {

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