first pass at reloading on keymap change

Ben Kunkle created

Change summary

crates/settings_ui/src/keybindings.rs | 105 ++++++++++++++++++++++------
crates/settings_ui/src/settings_ui.rs |   2 
crates/zed/src/zed.rs                 |   1 
3 files changed, 84 insertions(+), 24 deletions(-)

Detailed changes

crates/settings_ui/src/keybindings.rs 🔗

@@ -1,9 +1,13 @@
-use std::fmt::Write as _;
+use std::{cell::RefCell, fmt::Write as _, rc::Rc};
 
 use db::anyhow::anyhow;
-use gpui::{AppContext as _, EventEmitter, FocusHandle, Focusable, actions};
+use gpui::{
+    AppContext as _, Context, Entity, EventEmitter, FocusHandle, Focusable, Global, Keymap,
+    Subscription, Task, actions,
+};
 use ui::{
-    ActiveTheme as _, App, ParentElement as _, Render, Styled as _, Table, Window, div, string_cell,
+    ActiveTheme as _, App, BorrowAppContext, ParentElement as _, Render, SharedString, Styled as _,
+    Table, Window, div, string_cell,
 };
 use workspace::{Item, SerializableItem, Workspace, register_serializable_item};
 
@@ -12,6 +16,9 @@ use crate::keybindings::persistence::KEYBINDING_EDITORS;
 actions!(zed, [OpenKeymapEditor]);
 
 pub fn init(cx: &mut App) {
+    let keymap_event_channel = KeymapEventChannel::new();
+    cx.set_global(keymap_event_channel);
+
     cx.observe_new(|workspace: &mut Workspace, _window, _cx| {
         workspace.register_action(|workspace, _: &OpenKeymapEditor, window, cx| {
             let open_keymap_editor = cx.new(|cx| KeymapEditor::new(cx));
@@ -23,8 +30,32 @@ pub fn init(cx: &mut App) {
     register_serializable_item::<KeymapEditor>(cx);
 }
 
+enum KeymapEvent {
+    KeymapChanged,
+}
+
+pub struct KeymapEventChannel {}
+
+impl EventEmitter<KeymapEvent> for KeymapEventChannel {}
+impl Global for KeymapEventChannel {}
+
+impl KeymapEventChannel {
+    fn new() -> Self {
+        Self {}
+    }
+
+    pub fn trigger_keymap_changed(cx: &mut App) {
+        cx.update_global(|_event_channel: &mut Self, _| {
+            dbg!("updating global");
+            *_event_channel = Self::new();
+        });
+    }
+}
+
 struct KeymapEditor {
     focus_handle: FocusHandle,
+    _keymap_subscription: Subscription,
+    processed_bindings: Vec<ProcessedKeybinding>,
 }
 
 impl EventEmitter<()> for KeymapEditor {}
@@ -37,12 +68,52 @@ impl Focusable for KeymapEditor {
 
 impl KeymapEditor {
     fn new(cx: &mut gpui::Context<Self>) -> Self {
+        let _keymap_subscription = cx.observe_global::<KeymapEventChannel>(|this, cx| {
+            let key_bindings = Self::process_bindings(cx);
+            this.processed_bindings = key_bindings;
+        });
         Self {
             focus_handle: cx.focus_handle(),
+            _keymap_subscription,
+            processed_bindings: vec![],
+        }
+    }
+
+    fn process_bindings(cx: &mut Context<Self>) -> Vec<ProcessedKeybinding> {
+        let key_bindings_ptr = cx.key_bindings();
+        let lock = key_bindings_ptr.borrow();
+        let key_bindings = lock.bindings();
+
+        let mut processed_bindings = Vec::new();
+
+        for key_binding in key_bindings {
+            let mut keystroke_text = String::new();
+            for keystroke in key_binding.keystrokes() {
+                write!(&mut keystroke_text, "{} ", keystroke.unparse()).ok();
+            }
+            let keystroke_text = keystroke_text.trim().to_string();
+
+            let context = key_binding
+                .predicate()
+                .map(|predicate| predicate.to_string())
+                .unwrap_or_else(|| "<global>".to_string());
+
+            processed_bindings.push(ProcessedKeybinding {
+                keystroke_text: keystroke_text.into(),
+                action: key_binding.action().name().into(),
+                context: context.into(),
+            })
         }
+        processed_bindings
     }
 }
 
+struct ProcessedKeybinding {
+    keystroke_text: SharedString,
+    action: SharedString,
+    context: SharedString,
+}
+
 impl SerializableItem for KeymapEditor {
     fn serialized_item_kind() -> &'static str {
         "KeymapEditor"
@@ -116,29 +187,17 @@ impl Item for KeymapEditor {
 
 impl Render for KeymapEditor {
     fn render(&mut self, _window: &mut Window, cx: &mut ui::Context<Self>) -> impl ui::IntoElement {
-        let key_bindings_ptr = cx.key_bindings();
-        let lock = key_bindings_ptr.borrow();
-        let key_bindings = lock.bindings();
+        dbg!("rendering");
+        if self.processed_bindings.is_empty() {
+            self.processed_bindings = Self::process_bindings(cx);
+        }
 
         let mut table = Table::new(vec!["Command", "Keystrokes", "Context"]);
-        for key_binding in key_bindings {
-            let mut keystroke_text = String::new();
-            for keystroke in key_binding.keystrokes() {
-                write!(&mut keystroke_text, "{} ", keystroke.unparse()).ok();
-            }
-            let keystroke_text = keystroke_text.trim().to_string();
-
-            let context = key_binding
-                .predicate()
-                .map(|predicate| predicate.to_string())
-                .unwrap_or_else(|| "<global>".to_string());
-
-            // dbg!(key_binding.action().name(), &keystroke_text, &context);
-
+        for key_binding in &self.processed_bindings {
             table = table.row(vec![
-                string_cell(key_binding.action().name()),
-                string_cell(keystroke_text),
-                string_cell(context),
+                string_cell(key_binding.action.clone()),
+                string_cell(key_binding.keystroke_text.clone()),
+                string_cell(key_binding.context.clone()),
                 // TODO: Add a source field
                 // string_cell(keybinding.source().to_string()),
             ]);

crates/settings_ui/src/settings_ui.rs 🔗

@@ -20,7 +20,7 @@ use workspace::{Workspace, with_active_or_new_workspace};
 
 use crate::appearance_settings_controls::AppearanceSettingsControls;
 
-mod keybindings;
+pub mod keybindings;
 
 pub struct SettingsUiFeatureFlag;
 

crates/zed/src/zed.rs 🔗

@@ -1419,6 +1419,7 @@ fn reload_keymaps(cx: &mut App, mut user_key_bindings: Vec<KeyBinding>) {
         "New Window",
         workspace::NewWindow,
     )]);
+    settings_ui::keybindings::KeymapEventChannel::trigger_keymap_changed(cx);
 }
 
 pub fn load_default_keymap(cx: &mut App) {