Merge pull request #2423 from zed-industries/fix-panic-in-go-to-definition

Fix panic when clicking on a definition
This commit is contained in:
Antonio Scandurra 2023-04-29 15:02:18 +02:00 committed by GitHub
commit 0f44648b38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 127 deletions

View file

@ -1331,6 +1331,10 @@ impl Editor {
&self.buffer &self.buffer
} }
fn workspace(&self, cx: &AppContext) -> Option<ViewHandle<Workspace>> {
self.workspace.as_ref()?.0.upgrade(cx)
}
pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> { pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
self.buffer().read(cx).title(cx) self.buffer().read(cx).title(cx)
} }
@ -5558,93 +5562,77 @@ impl Editor {
} }
} }
pub fn go_to_definition( pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
workspace: &mut Workspace, self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, cx);
_: &GoToDefinition,
cx: &mut ViewContext<Workspace>,
) {
Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
} }
pub fn go_to_type_definition( pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
workspace: &mut Workspace, self.go_to_definition_of_kind(GotoDefinitionKind::Type, cx);
_: &GoToTypeDefinition,
cx: &mut ViewContext<Workspace>,
) {
Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
} }
fn go_to_definition_of_kind( fn go_to_definition_of_kind(&mut self, kind: GotoDefinitionKind, cx: &mut ViewContext<Self>) {
kind: GotoDefinitionKind, let Some(workspace) = self.workspace(cx) else { return };
workspace: &mut Workspace, let buffer = self.buffer.read(cx);
cx: &mut ViewContext<Workspace>, let head = self.selections.newest::<usize>(cx).head();
) {
let active_item = workspace.active_item(cx);
let editor_handle = if let Some(editor) = active_item
.as_ref()
.and_then(|item| item.act_as::<Self>(cx))
{
editor
} else {
return;
};
let editor = editor_handle.read(cx);
let buffer = editor.buffer.read(cx);
let head = editor.selections.newest::<usize>(cx).head();
let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) { let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
text_anchor text_anchor
} else { } else {
return; return;
}; };
let project = workspace.project().clone(); let project = workspace.read(cx).project().clone();
let definitions = project.update(cx, |project, cx| match kind { let definitions = project.update(cx, |project, cx| match kind {
GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx), GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx), GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
}); });
cx.spawn_labeled("Fetching Definition...", |workspace, mut cx| async move { cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
let definitions = definitions.await?; let definitions = definitions.await?;
workspace.update(&mut cx, |workspace, cx| { editor.update(&mut cx, |editor, cx| {
Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx); editor.navigate_to_definitions(definitions, cx);
})?; })?;
Ok::<(), anyhow::Error>(()) Ok::<(), anyhow::Error>(())
}) })
.detach_and_log_err(cx); .detach_and_log_err(cx);
} }
pub fn navigate_to_definitions( pub fn navigate_to_definitions(
workspace: &mut Workspace, &mut self,
editor_handle: ViewHandle<Editor>, mut definitions: Vec<LocationLink>,
definitions: Vec<LocationLink>, cx: &mut ViewContext<Editor>,
cx: &mut ViewContext<Workspace>,
) { ) {
let pane = workspace.active_pane().clone(); let Some(workspace) = self.workspace(cx) else { return };
let pane = workspace.read(cx).active_pane().clone();
// If there is one definition, just open it directly // If there is one definition, just open it directly
if let [definition] = definitions.as_slice() { if definitions.len() == 1 {
let definition = definitions.pop().unwrap();
let range = definition let range = definition
.target .target
.range .range
.to_offset(definition.target.buffer.read(cx)); .to_offset(definition.target.buffer.read(cx));
let target_editor_handle = if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
workspace.open_project_item(definition.target.buffer.clone(), cx); self.change_selections(Some(Autoscroll::fit()), cx, |s| {
target_editor_handle.update(cx, |target_editor, cx| {
// When selecting a definition in a different buffer, disable the nav history
// to avoid creating a history entry at the previous cursor location.
if editor_handle != target_editor_handle {
pane.update(cx, |pane, _| pane.disable_history());
}
target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges([range]); s.select_ranges([range]);
}); });
} else {
pane.update(cx, |pane, _| pane.enable_history()); cx.window_context().defer(move |cx| {
}); let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
workspace.open_project_item(definition.target.buffer.clone(), cx)
});
target_editor.update(cx, |target_editor, cx| {
// When selecting a definition in a different buffer, disable the nav history
// to avoid creating a history entry at the previous cursor location.
pane.update(cx, |pane, _| pane.disable_history());
target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges([range]);
});
pane.update(cx, |pane, _| pane.enable_history());
});
});
}
} else if !definitions.is_empty() { } else if !definitions.is_empty() {
let replica_id = editor_handle.read(cx).replica_id(cx); let replica_id = self.replica_id(cx);
let title = definitions let title = definitions
.iter() .iter()
.find(|definition| definition.origin.is_some()) .find(|definition| definition.origin.is_some())
@ -5664,7 +5652,9 @@ impl Editor {
.into_iter() .into_iter()
.map(|definition| definition.target) .map(|definition| definition.target)
.collect(); .collect();
Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx) workspace.update(cx, |workspace, cx| {
Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
})
} }
} }

View file

@ -309,25 +309,17 @@ impl EditorElement {
editor.select(SelectPhase::End, cx); editor.select(SelectPhase::End, cx);
} }
if let Some(workspace) = editor if !pending_nonempty_selections && cmd && text_bounds.contains_point(position) {
.workspace let (point, target_point) = position_map.point_for_position(text_bounds, position);
.as_ref()
.and_then(|(workspace, _)| workspace.upgrade(cx))
{
if !pending_nonempty_selections && cmd && text_bounds.contains_point(position) {
let (point, target_point) = position_map.point_for_position(text_bounds, position);
if point == target_point { if point == target_point {
workspace.update(cx, |workspace, cx| { if shift {
if shift { go_to_fetched_type_definition(editor, point, cx);
go_to_fetched_type_definition(workspace, point, cx); } else {
} else { go_to_fetched_definition(editor, point, cx);
go_to_fetched_definition(workspace, point, cx);
}
});
return true;
} }
return true;
} }
} }

View file

@ -1,15 +1,11 @@
use std::ops::Range; use std::ops::Range;
use crate::{Anchor, DisplayPoint, Editor, EditorSnapshot, SelectPhase};
use gpui::{Task, ViewContext}; use gpui::{Task, ViewContext};
use language::{Bias, ToOffset}; use language::{Bias, ToOffset};
use project::LocationLink; use project::LocationLink;
use settings::Settings; use settings::Settings;
use util::TryFutureExt; use util::TryFutureExt;
use workspace::Workspace;
use crate::{
Anchor, DisplayPoint, Editor, EditorSnapshot, GoToDefinition, GoToTypeDefinition, SelectPhase,
};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct LinkGoToDefinitionState { pub struct LinkGoToDefinitionState {
@ -250,70 +246,51 @@ pub fn hide_link_definition(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
} }
pub fn go_to_fetched_definition( pub fn go_to_fetched_definition(
workspace: &mut Workspace, editor: &mut Editor,
point: DisplayPoint, point: DisplayPoint,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Editor>,
) { ) {
go_to_fetched_definition_of_kind(LinkDefinitionKind::Symbol, workspace, point, cx); go_to_fetched_definition_of_kind(LinkDefinitionKind::Symbol, editor, point, cx);
} }
pub fn go_to_fetched_type_definition( pub fn go_to_fetched_type_definition(
workspace: &mut Workspace, editor: &mut Editor,
point: DisplayPoint, point: DisplayPoint,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Editor>,
) { ) {
go_to_fetched_definition_of_kind(LinkDefinitionKind::Type, workspace, point, cx); go_to_fetched_definition_of_kind(LinkDefinitionKind::Type, editor, point, cx);
} }
fn go_to_fetched_definition_of_kind( fn go_to_fetched_definition_of_kind(
kind: LinkDefinitionKind, kind: LinkDefinitionKind,
workspace: &mut Workspace, editor: &mut Editor,
point: DisplayPoint, point: DisplayPoint,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Editor>,
) { ) {
let active_item = workspace.active_item(cx); let cached_definitions = editor.link_go_to_definition_state.definitions.clone();
let editor_handle = if let Some(editor) = active_item hide_link_definition(editor, cx);
.as_ref() let cached_definitions_kind = editor.link_go_to_definition_state.kind;
.and_then(|item| item.act_as::<Editor>(cx))
{
editor
} else {
return;
};
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, editor.link_go_to_definition_state.kind)
});
let is_correct_kind = cached_definitions_kind == Some(kind); let is_correct_kind = cached_definitions_kind == Some(kind);
if !cached_definitions.is_empty() && is_correct_kind { if !cached_definitions.is_empty() && is_correct_kind {
editor_handle.update(cx, |editor, cx| { if !editor.focused {
if !editor.focused { cx.focus_self();
cx.focus_self(); }
}
});
Editor::navigate_to_definitions(workspace, editor_handle, cached_definitions, cx); editor.navigate_to_definitions(cached_definitions, cx);
} else { } else {
editor_handle.update(cx, |editor, cx| { editor.select(
editor.select( SelectPhase::Begin {
SelectPhase::Begin { position: point,
position: point, add: false,
add: false, click_count: 1,
click_count: 1, },
}, cx,
cx, );
);
});
match kind { match kind {
LinkDefinitionKind::Symbol => Editor::go_to_definition(workspace, &GoToDefinition, cx), LinkDefinitionKind::Symbol => editor.go_to_definition(&Default::default(), cx),
LinkDefinitionKind::Type => editor.go_to_type_definition(&Default::default(), cx),
LinkDefinitionKind::Type => {
Editor::go_to_type_definition(workspace, &GoToTypeDefinition, cx)
}
} }
} }
} }
@ -426,8 +403,8 @@ mod tests {
]))) ])))
}); });
cx.update_workspace(|workspace, cx| { cx.update_editor(|editor, cx| {
go_to_fetched_type_definition(workspace, hover_point, cx); go_to_fetched_type_definition(editor, hover_point, cx);
}); });
requests.next().await; requests.next().await;
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
@ -635,8 +612,8 @@ mod tests {
"}); "});
// Cmd click with existing definition doesn't re-request and dismisses highlight // Cmd click with existing definition doesn't re-request and dismisses highlight
cx.update_workspace(|workspace, cx| { cx.update_editor(|editor, cx| {
go_to_fetched_definition(workspace, hover_point, cx); go_to_fetched_definition(editor, hover_point, cx);
}); });
// Assert selection moved to to definition // Assert selection moved to to definition
cx.lsp cx.lsp
@ -676,8 +653,8 @@ mod tests {
}, },
]))) ])))
}); });
cx.update_workspace(|workspace, cx| { cx.update_editor(|editor, cx| {
go_to_fetched_definition(workspace, hover_point, cx); go_to_fetched_definition(editor, hover_point, cx);
}); });
requests.next().await; requests.next().await;
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();