editor: Goto references skip multibuffer if single match (#43026)
Co-authored-by: Agus <agus@zed.dev>
This commit is contained in:
co-authored by
Agus
parent
62c312b35f
commit
66789607c9
@@ -453,8 +453,6 @@ actions!(
|
||||
CollapseAllDiffHunks,
|
||||
/// Expands macros recursively at cursor position.
|
||||
ExpandMacroRecursively,
|
||||
/// Finds all references to the symbol at cursor.
|
||||
FindAllReferences,
|
||||
/// Finds the next match in the search.
|
||||
FindNextMatch,
|
||||
/// Finds the previous match in the search.
|
||||
@@ -827,3 +825,20 @@ actions!(
|
||||
WrapSelectionsInTag
|
||||
]
|
||||
);
|
||||
|
||||
/// Finds all references to the symbol at cursor.
|
||||
#[derive(PartialEq, Clone, Deserialize, JsonSchema, Action)]
|
||||
#[action(namespace = editor)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct FindAllReferences {
|
||||
#[serde(default = "default_true")]
|
||||
pub always_open_multibuffer: bool,
|
||||
}
|
||||
|
||||
impl Default for FindAllReferences {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
always_open_multibuffer: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16815,7 +16815,7 @@ impl Editor {
|
||||
GoToDefinitionFallback::None => Ok(Navigated::No),
|
||||
GoToDefinitionFallback::FindAllReferences => {
|
||||
match editor.update_in(cx, |editor, window, cx| {
|
||||
editor.find_all_references(&FindAllReferences, window, cx)
|
||||
editor.find_all_references(&FindAllReferences::default(), window, cx)
|
||||
})? {
|
||||
Some(references) => references.await,
|
||||
None => Ok(Navigated::No),
|
||||
@@ -17371,20 +17371,21 @@ impl Editor {
|
||||
|
||||
pub fn find_all_references(
|
||||
&mut self,
|
||||
_: &FindAllReferences,
|
||||
action: &FindAllReferences,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<Task<Result<Navigated>>> {
|
||||
let selection = self
|
||||
.selections
|
||||
.newest::<MultiBufferOffset>(&self.display_snapshot(cx));
|
||||
let always_open_multibuffer = action.always_open_multibuffer;
|
||||
let selection = self.selections.newest_anchor();
|
||||
let multi_buffer = self.buffer.read(cx);
|
||||
let head = selection.head();
|
||||
|
||||
let multi_buffer_snapshot = multi_buffer.snapshot(cx);
|
||||
let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
|
||||
let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
|
||||
let head = selection_offset.head();
|
||||
|
||||
let head_anchor = multi_buffer_snapshot.anchor_at(
|
||||
head,
|
||||
if head < selection.tail() {
|
||||
if head < selection_offset.tail() {
|
||||
Bias::Right
|
||||
} else {
|
||||
Bias::Left
|
||||
@@ -17430,6 +17431,15 @@ impl Editor {
|
||||
let buffer = location.buffer.read(cx);
|
||||
(location.buffer, location.range.to_point(buffer))
|
||||
})
|
||||
// if special-casing the single-match case, remove ranges
|
||||
// that intersect current selection
|
||||
.filter(|(location_buffer, location)| {
|
||||
if always_open_multibuffer || &buffer != location_buffer {
|
||||
return true;
|
||||
}
|
||||
|
||||
!location.contains_inclusive(&selection_point.range())
|
||||
})
|
||||
.into_group_map()
|
||||
})?;
|
||||
if locations.is_empty() {
|
||||
@@ -17439,6 +17449,60 @@ impl Editor {
|
||||
ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
|
||||
ranges.dedup();
|
||||
}
|
||||
let mut num_locations = 0;
|
||||
for ranges in locations.values_mut() {
|
||||
ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
|
||||
ranges.dedup();
|
||||
num_locations += ranges.len();
|
||||
}
|
||||
|
||||
if num_locations == 1 && !always_open_multibuffer {
|
||||
let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
|
||||
let target_range = target_ranges.first().unwrap().clone();
|
||||
|
||||
return editor.update_in(cx, |editor, window, cx| {
|
||||
let range = target_range.to_point(target_buffer.read(cx));
|
||||
let range = editor.range_for_match(&range);
|
||||
let range = range.start..range.start;
|
||||
|
||||
if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
|
||||
editor.go_to_singleton_buffer_range(range, window, cx);
|
||||
} else {
|
||||
let pane = workspace.read(cx).active_pane().clone();
|
||||
window.defer(cx, move |window, cx| {
|
||||
let target_editor: Entity<Self> =
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
let pane = workspace.active_pane().clone();
|
||||
|
||||
let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
|
||||
let keep_old_preview = preview_tabs_settings
|
||||
.enable_keep_preview_on_code_navigation;
|
||||
let allow_new_preview = preview_tabs_settings
|
||||
.enable_preview_file_from_code_navigation;
|
||||
|
||||
workspace.open_project_item(
|
||||
pane,
|
||||
target_buffer.clone(),
|
||||
true,
|
||||
true,
|
||||
keep_old_preview,
|
||||
allow_new_preview,
|
||||
window,
|
||||
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.go_to_singleton_buffer_range(range, window, cx);
|
||||
pane.update(cx, |pane, _| pane.enable_history());
|
||||
});
|
||||
});
|
||||
}
|
||||
Navigated::No
|
||||
});
|
||||
}
|
||||
|
||||
workspace.update_in(cx, |workspace, window, cx| {
|
||||
let target = locations
|
||||
@@ -17476,7 +17540,7 @@ impl Editor {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Opens a multibuffer with the given project locations in it
|
||||
/// Opens a multibuffer with the given project locations in it.
|
||||
pub fn open_locations_in_multibuffer(
|
||||
workspace: &mut Workspace,
|
||||
locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
|
||||
|
||||
@@ -22579,7 +22579,7 @@ async fn test_find_all_references_editor_reuse(cx: &mut TestAppContext) {
|
||||
});
|
||||
let navigated = cx
|
||||
.update_editor(|editor, window, cx| {
|
||||
editor.find_all_references(&FindAllReferences, window, cx)
|
||||
editor.find_all_references(&FindAllReferences::default(), window, cx)
|
||||
})
|
||||
.unwrap()
|
||||
.await
|
||||
@@ -22615,7 +22615,7 @@ async fn test_find_all_references_editor_reuse(cx: &mut TestAppContext) {
|
||||
);
|
||||
let navigated = cx
|
||||
.update_editor(|editor, window, cx| {
|
||||
editor.find_all_references(&FindAllReferences, window, cx)
|
||||
editor.find_all_references(&FindAllReferences::default(), window, cx)
|
||||
})
|
||||
.unwrap()
|
||||
.await
|
||||
@@ -22667,7 +22667,7 @@ async fn test_find_all_references_editor_reuse(cx: &mut TestAppContext) {
|
||||
});
|
||||
let navigated = cx
|
||||
.update_editor(|editor, window, cx| {
|
||||
editor.find_all_references(&FindAllReferences, window, cx)
|
||||
editor.find_all_references(&FindAllReferences::default(), window, cx)
|
||||
})
|
||||
.unwrap()
|
||||
.await
|
||||
@@ -28916,3 +28916,65 @@ async fn test_multibuffer_scroll_cursor_top_margin(cx: &mut TestAppContext) {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_find_references_single_case(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
references_provider: Some(lsp::OneOf::Left(true)),
|
||||
..lsp::ServerCapabilities::default()
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
|
||||
let before = indoc!(
|
||||
r#"
|
||||
fn main() {
|
||||
let aˇbc = 123;
|
||||
let xyz = abc;
|
||||
}
|
||||
"#
|
||||
);
|
||||
let after = indoc!(
|
||||
r#"
|
||||
fn main() {
|
||||
let abc = 123;
|
||||
let xyz = ˇabc;
|
||||
}
|
||||
"#
|
||||
);
|
||||
|
||||
cx.lsp
|
||||
.set_request_handler::<lsp::request::References, _, _>(async move |params, _| {
|
||||
Ok(Some(vec![
|
||||
lsp::Location {
|
||||
uri: params.text_document_position.text_document.uri.clone(),
|
||||
range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 11)),
|
||||
},
|
||||
lsp::Location {
|
||||
uri: params.text_document_position.text_document.uri,
|
||||
range: lsp::Range::new(lsp::Position::new(2, 14), lsp::Position::new(2, 17)),
|
||||
},
|
||||
]))
|
||||
});
|
||||
|
||||
cx.set_state(before);
|
||||
|
||||
let action = FindAllReferences {
|
||||
always_open_multibuffer: false,
|
||||
};
|
||||
|
||||
let navigated = cx
|
||||
.update_editor(|editor, window, cx| editor.find_all_references(&action, window, cx))
|
||||
.expect("should have spawned a task")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(navigated, Navigated::No);
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
cx.assert_editor_state(after);
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ impl Editor {
|
||||
match EditorSettings::get_global(cx).go_to_definition_fallback {
|
||||
GoToDefinitionFallback::None => None,
|
||||
GoToDefinitionFallback::FindAllReferences => {
|
||||
editor.find_all_references(&FindAllReferences, window, cx)
|
||||
editor.find_all_references(&FindAllReferences::default(), window, cx)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -235,7 +235,10 @@ pub fn deploy_context_menu(
|
||||
.action("Go to Declaration", Box::new(GoToDeclaration))
|
||||
.action("Go to Type Definition", Box::new(GoToTypeDefinition))
|
||||
.action("Go to Implementation", Box::new(GoToImplementation))
|
||||
.action("Find All References", Box::new(FindAllReferences))
|
||||
.action(
|
||||
"Find All References",
|
||||
Box::new(FindAllReferences::default()),
|
||||
)
|
||||
.separator()
|
||||
.action("Rename Symbol", Box::new(Rename))
|
||||
.action("Format Buffer", Box::new(Format))
|
||||
|
||||
@@ -247,7 +247,10 @@ pub fn app_menus(cx: &mut App) -> Vec<Menu> {
|
||||
MenuItem::action("Go to Definition", editor::actions::GoToDefinition),
|
||||
MenuItem::action("Go to Declaration", editor::actions::GoToDeclaration),
|
||||
MenuItem::action("Go to Type Definition", editor::actions::GoToTypeDefinition),
|
||||
MenuItem::action("Find All References", editor::actions::FindAllReferences),
|
||||
MenuItem::action(
|
||||
"Find All References",
|
||||
editor::actions::FindAllReferences::default(),
|
||||
),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Next Problem", editor::actions::GoToDiagnostic::default()),
|
||||
MenuItem::action(
|
||||
|
||||
Reference in New Issue
Block a user