Detailed changes
@@ -184,6 +184,11 @@
// Whether to show the signature help after completion or a bracket pair inserted.
// If `auto_signature_help` is enabled, this setting will be treated as enabled also.
"show_signature_help_after_edits": false,
+ // What to do when go to definition yields no results.
+ //
+ // 1. Do nothing: `none`
+ // 2. Find references for the same symbol: `find_all_references` (default)
+ "go_to_definition_fallback": "find_all_references",
// Whether to show wrap guides (vertical rulers) in the editor.
// Setting this to true will show a guide at the 'preferred_line_length' value
// if 'soft_wrap' is set to 'preferred_line_length', and will show any
@@ -60,6 +60,7 @@ use collections::{BTreeMap, HashMap, HashSet, VecDeque};
use convert_case::{Case, Casing};
use display_map::*;
pub use display_map::{DisplayPoint, FoldPlaceholder};
+use editor_settings::GoToDefinitionFallback;
pub use editor_settings::{
CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
};
@@ -12662,15 +12663,21 @@ impl Editor {
) -> Task<Result<Navigated>> {
let definition =
self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
+ let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
cx.spawn_in(window, async move |editor, cx| {
if definition.await? == Navigated::Yes {
return Ok(Navigated::Yes);
}
- match editor.update_in(cx, |editor, window, cx| {
- editor.find_all_references(&FindAllReferences, window, cx)
- })? {
- Some(references) => references.await,
- None => Ok(Navigated::No),
+ match fallback_strategy {
+ GoToDefinitionFallback::None => Ok(Navigated::No),
+ GoToDefinitionFallback::FindAllReferences => {
+ match editor.update_in(cx, |editor, window, cx| {
+ editor.find_all_references(&FindAllReferences, window, cx)
+ })? {
+ Some(references) => references.await,
+ None => Ok(Navigated::No),
+ }
+ }
}
})
}
@@ -36,6 +36,8 @@ pub struct EditorSettings {
pub search: SearchSettings,
pub auto_signature_help: bool,
pub show_signature_help_after_edits: bool,
+ #[serde(default)]
+ pub go_to_definition_fallback: GoToDefinitionFallback,
pub jupyter: Jupyter,
}
@@ -211,6 +213,17 @@ pub struct SearchSettings {
pub regex: bool,
}
+/// What to do when go to definition yields no results.
+#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum GoToDefinitionFallback {
+ /// Disables the fallback.
+ None,
+ /// Looks up references of the same symbol instead.
+ #[default]
+ FindAllReferences,
+}
+
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct EditorSettingsContent {
/// Whether the cursor blinks in the editor.
@@ -330,6 +343,13 @@ pub struct EditorSettingsContent {
/// Default: false
pub show_signature_help_after_edits: Option<bool>,
+ /// Whether to follow-up empty go to definition responses from the language server or not.
+ /// `FindAllReferences` allows to look up references of the same symbol instead.
+ /// `None` disables the fallback.
+ ///
+ /// Default: FindAllReferences
+ pub go_to_definition_fallback: Option<GoToDefinitionFallback>,
+
/// Jupyter REPL settings.
pub jupyter: Option<JupyterContent>,
}
@@ -16434,6 +16434,69 @@ async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestApp
});
}
+#[gpui::test]
+async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
+ init_test(cx, |_| {});
+ cx.update(|cx| {
+ let mut editor_settings = EditorSettings::get_global(cx).clone();
+ editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
+ EditorSettings::override_global(editor_settings, cx);
+ });
+ let mut cx = EditorLspTestContext::new_rust(
+ lsp::ServerCapabilities {
+ definition_provider: Some(lsp::OneOf::Left(true)),
+ references_provider: Some(lsp::OneOf::Left(true)),
+ ..lsp::ServerCapabilities::default()
+ },
+ cx,
+ )
+ .await;
+ let original_state = r#"fn one() {
+ let mut a = ˇtwo();
+ }
+
+ fn two() {}"#
+ .unindent();
+ cx.set_state(&original_state);
+
+ let mut go_to_definition = cx
+ .lsp
+ .set_request_handler::<lsp::request::GotoDefinition, _, _>(
+ move |_, _| async move { Ok(None) },
+ );
+ let _references = cx
+ .lsp
+ .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
+ panic!("Should not call for references with no go to definition fallback")
+ });
+
+ let navigated = cx
+ .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
+ .await
+ .expect("Failed to navigate to lookup references");
+ go_to_definition
+ .next()
+ .await
+ .expect("Should have called the go_to_definition handler");
+
+ assert_eq!(
+ navigated,
+ Navigated::No,
+ "Should have navigated to references as a fallback after empty GoToDefinition response"
+ );
+ cx.assert_editor_state(&original_state);
+ let editors = cx.update_workspace(|workspace, _, cx| {
+ workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
+ });
+ cx.update_editor(|_, _, _| {
+ assert_eq!(
+ editors.len(),
+ 1,
+ "After unsuccessful fallback, no other editor should have been opened"
+ );
+ });
+}
+
#[gpui::test]
async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
init_test(cx, |_| {});
@@ -1,5 +1,5 @@
use crate::{
- editor_settings::MultiCursorModifier,
+ editor_settings::{GoToDefinitionFallback, MultiCursorModifier},
hover_popover::{self, InlayHover},
scroll::ScrollAmount,
Anchor, Editor, EditorSettings, EditorSnapshot, FindAllReferences, GoToDefinition,
@@ -174,7 +174,12 @@ impl Editor {
if definition_revealed == Navigated::Yes {
return None;
}
- editor.find_all_references(&FindAllReferences, window, cx)
+ match EditorSettings::get_global(cx).go_to_definition_fallback {
+ GoToDefinitionFallback::None => None,
+ GoToDefinitionFallback::FindAllReferences => {
+ editor.find_all_references(&FindAllReferences, window, cx)
+ }
+ }
})
.ok()
.flatten();