diff --git a/crates/gpui/src/action.rs b/crates/gpui/src/action.rs index 9e979a31ff..e099bfec28 100644 --- a/crates/gpui/src/action.rs +++ b/crates/gpui/src/action.rs @@ -225,6 +225,7 @@ pub(crate) struct ActionRegistry { all_names: Vec<&'static str>, // So we can return a static slice. deprecated_aliases: HashMap<&'static str, &'static str>, // deprecated name -> preferred name deprecation_messages: HashMap<&'static str, &'static str>, // action name -> deprecation message + documentation: HashMap<&'static str, &'static str>, // action name -> documentation } impl Default for ActionRegistry { @@ -232,6 +233,7 @@ impl Default for ActionRegistry { let mut this = ActionRegistry { by_name: Default::default(), names_by_type_id: Default::default(), + documentation: Default::default(), all_names: Default::default(), deprecated_aliases: Default::default(), deprecation_messages: Default::default(), @@ -327,6 +329,9 @@ impl ActionRegistry { if let Some(deprecation_msg) = action.deprecation_message { self.deprecation_messages.insert(name, deprecation_msg); } + if let Some(documentation) = action.documentation { + self.documentation.insert(name, documentation); + } } /// Construct an action based on its name and optional JSON parameters sourced from the keymap. @@ -388,6 +393,10 @@ impl ActionRegistry { pub fn deprecation_messages(&self) -> &HashMap<&'static str, &'static str> { &self.deprecation_messages } + + pub fn documentation(&self) -> &HashMap<&'static str, &'static str> { + &self.documentation + } } /// Generate a list of all the registered actions. diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 2ecc3dadaa..ef462ae084 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1403,11 +1403,16 @@ impl App { self.actions.deprecated_aliases() } - /// Get a list of all action deprecation messages. + /// Get a map from an action name to the deprecation messages. pub fn action_deprecation_messages(&self) -> &HashMap<&'static str, &'static str> { self.actions.deprecation_messages() } + /// Get a map from an action name to the documentation. + pub fn action_documentation(&self) -> &HashMap<&'static str, &'static str> { + self.actions.documentation() + } + /// Register a callback to be invoked when the application is about to quit. /// It is not possible to cancel the quit event at this point. pub fn on_app_quit( diff --git a/crates/settings_ui/src/keybindings.rs b/crates/settings_ui/src/keybindings.rs index 2b814c43b8..184a2cac37 100644 --- a/crates/settings_ui/src/keybindings.rs +++ b/crates/settings_ui/src/keybindings.rs @@ -254,7 +254,9 @@ impl KeymapEditor { let key_bindings_ptr = cx.key_bindings(); let lock = key_bindings_ptr.borrow(); let key_bindings = lock.bindings(); - let mut unmapped_action_names = HashSet::from_iter(cx.all_action_names()); + let mut unmapped_action_names = + HashSet::from_iter(cx.all_action_names().into_iter().copied()); + let action_documentation = cx.action_documentation(); let mut processed_bindings = Vec::new(); let mut string_match_candidates = Vec::new(); @@ -280,6 +282,7 @@ impl KeymapEditor { let action_input = key_binding .action_input() .map(|input| SyntaxHighlightedText::new(input, json_language.clone())); + let action_docs = action_documentation.get(action_name).copied(); let index = processed_bindings.len(); let string_match_candidate = StringMatchCandidate::new(index, &action_name); @@ -288,6 +291,7 @@ impl KeymapEditor { ui_key_binding, action: action_name.into(), action_input, + action_docs, context: Some(context), source, }); @@ -301,8 +305,9 @@ impl KeymapEditor { processed_bindings.push(ProcessedKeybinding { keystroke_text: empty.clone(), ui_key_binding: None, - action: (*action_name).into(), + action: action_name.into(), action_input: None, + action_docs: action_documentation.get(action_name).copied(), context: None, source: None, }); @@ -537,6 +542,7 @@ struct ProcessedKeybinding { ui_key_binding: Option, action: SharedString, action_input: Option, + action_docs: Option<&'static str>, context: Option, source: Option<(KeybindSource, SharedString)>, } @@ -635,7 +641,26 @@ impl Render for KeymapEditor { let candidate_id = this.matches.get(index)?.candidate_id; let binding = &this.keybindings[candidate_id]; - let action = binding.action.clone().into_any_element(); + let action = div() + .child(binding.action.clone()) + .id(("keymap action", index)) + .tooltip({ + let action_name = binding.action.clone(); + let action_docs = binding.action_docs; + move |_, cx| { + let action_tooltip = Tooltip::new( + command_palette::humanize_action_name( + &action_name, + ), + ); + let action_tooltip = match action_docs { + Some(docs) => action_tooltip.meta(docs), + None => action_tooltip, + }; + cx.new(|_| action_tooltip).into() + } + }) + .into_any_element(); let keystrokes = binding.ui_key_binding.clone().map_or( binding.keystroke_text.clone().into_any_element(), IntoElement::into_any_element, diff --git a/crates/settings_ui/src/ui_components/table.rs b/crates/settings_ui/src/ui_components/table.rs index 0ab00fdcf7..c3b70d7d4f 100644 --- a/crates/settings_ui/src/ui_components/table.rs +++ b/crates/settings_ui/src/ui_components/table.rs @@ -12,8 +12,7 @@ use ui::{ ComponentScope, Div, ElementId, FixedWidth as _, FluentBuilder as _, Indicator, InteractiveElement as _, IntoElement, ParentElement, Pixels, RegisterComponent, RenderOnce, Scrollbar, ScrollbarState, StatefulInteractiveElement as _, Styled, StyledExt as _, - StyledTypography, Tooltip, Window, div, example_group_with_title, h_flex, px, single_example, - v_flex, + StyledTypography, Window, div, example_group_with_title, h_flex, px, single_example, v_flex, }; struct UniformListData { @@ -474,7 +473,6 @@ pub fn render_row( let row = div().w_full().child( h_flex() .id("table_row") - .tooltip(Tooltip::text("Hit enter to edit")) .w_full() .justify_between() .px_1p5()