diff --git a/assets/settings/default.json b/assets/settings/default.json index 18c66c912f..252b52c674 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -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 diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 6da720ee6b..ac7110c66c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -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> { 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), + } + } } }) } diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 90a4e8f1f0..a3c033ce72 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -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, + /// 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, + /// Jupyter REPL settings. pub jupyter: Option, } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index ca7921b87a..5a81ecca0c 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -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::( + move |_, _| async move { Ok(None) }, + ); + let _references = cx + .lsp + .set_request_handler::(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::(cx).collect::>() + }); + 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, |_| {}); diff --git a/crates/editor/src/hover_links.rs b/crates/editor/src/hover_links.rs index 38bac73ee0..004c7863b8 100644 --- a/crates/editor/src/hover_links.rs +++ b/crates/editor/src/hover_links.rs @@ -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();