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:
commit
0f44648b38
3 changed files with 86 additions and 127 deletions
|
@ -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)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue