@@ -7663,12 +7663,64 @@ impl Editor {
let workspace = self.workspace()?;
let project = workspace.read(cx).project().clone();
let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
- Some(cx.spawn(|_, mut cx| async move {
- let locations = references.await?;
+ Some(cx.spawn(|editor, mut cx| async move {
+ let mut locations = references.await?;
+ let snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot())?;
+ let head_offset = text::ToOffset::to_offset(&head, &snapshot);
+
+ // LSP may return references that contain the item itself we requested `find_all_references` for (eg. rust-analyzer)
+ // So we will remove it from locations
+ // If there is only one reference, we will not do this filter cause it may make locations empty
+ if locations.len() > 1 {
+ cx.update(|cx| {
+ locations.retain(|location| {
+ // fn foo(x : i64) {
+ // ^
+ // println!(x);
+ // }
+ // It is ok to find reference when caret being at ^ (the end of the word)
+ // So we turn offset into inclusive to include the end of the word
+ !location
+ .range
+ .to_offset(location.buffer.read(cx))
+ .to_inclusive()
+ .contains(&head_offset)
+ });
+ })?;
+ }
+
if locations.is_empty() {
return Ok(());
}
+ // If there is one reference, just open it directly
+ if locations.len() == 1 {
+ let target = locations.pop().unwrap();
+
+ return editor.update(&mut cx, |editor, cx| {
+ let range = target.range.to_offset(target.buffer.read(cx));
+ let range = editor.range_for_match(&range);
+
+ if Some(&target.buffer) == editor.buffer().read(cx).as_singleton().as_ref() {
+ editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+ s.select_ranges([range]);
+ });
+ } else {
+ cx.window_context().defer(move |cx| {
+ let target_editor: View<Self> =
+ workspace.update(cx, |workspace, cx| {
+ workspace.open_project_item(target.buffer.clone(), cx)
+ });
+ target_editor.update(cx, |target_editor, cx| {
+ target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+ s.select_ranges([range]);
+ })
+ })
+ })
+ }
+ });
+ }
+
workspace.update(&mut cx, |workspace, cx| {
let title = locations
.first()
@@ -8430,6 +8430,105 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
);
}
+#[gpui::test]
+async fn test_find_all_references(cx: &mut gpui::TestAppContext) {
+ init_test(cx, |_| {});
+
+ let mut cx = EditorLspTestContext::new_rust(
+ lsp::ServerCapabilities {
+ document_formatting_provider: Some(lsp::OneOf::Left(true)),
+ ..Default::default()
+ },
+ cx,
+ )
+ .await;
+
+ cx.set_state(indoc! {"
+ fn foo(«paramˇ»: i64) {
+ println!(param);
+ }
+ "});
+
+ cx.lsp
+ .handle_request::<lsp::request::References, _, _>(move |_, _| async move {
+ Ok(Some(vec![
+ lsp::Location {
+ uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
+ range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 12)),
+ },
+ lsp::Location {
+ uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
+ range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 18)),
+ },
+ ]))
+ });
+
+ let references = cx
+ .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
+ .unwrap();
+
+ cx.executor().run_until_parked();
+
+ cx.executor().start_waiting();
+ references.await.unwrap();
+
+ cx.assert_editor_state(indoc! {"
+ fn foo(param: i64) {
+ println!(«paramˇ»);
+ }
+ "});
+
+ let references = cx
+ .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
+ .unwrap();
+
+ cx.executor().run_until_parked();
+
+ cx.executor().start_waiting();
+ references.await.unwrap();
+
+ cx.assert_editor_state(indoc! {"
+ fn foo(«paramˇ»: i64) {
+ println!(param);
+ }
+ "});
+
+ cx.set_state(indoc! {"
+ fn foo(param: i64) {
+ let a = param;
+ let aˇ = param;
+ let a = param;
+ println!(param);
+ }
+ "});
+
+ cx.lsp
+ .handle_request::<lsp::request::References, _, _>(move |_, _| async move {
+ Ok(Some(vec![lsp::Location {
+ uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
+ range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 9)),
+ }]))
+ });
+
+ let references = cx
+ .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
+ .unwrap();
+
+ cx.executor().run_until_parked();
+
+ cx.executor().start_waiting();
+ references.await.unwrap();
+
+ cx.assert_editor_state(indoc! {"
+ fn foo(param: i64) {
+ let a = param;
+ let «aˇ» = param;
+ let a = param;
+ println!(param);
+ }
+ "});
+}
+
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
let point = DisplayPoint::new(row as u32, column as u32);
point..point