Merge pull request #1254 from zed-industries/command-palette-filter

Keith Simmons created

Command palette filter

Change summary

Cargo.lock                                    |  2 
crates/command_palette/Cargo.toml             |  1 
crates/command_palette/src/command_palette.rs | 57 ++++++++++++++++++--
crates/gpui/src/app/action.rs                 |  5 +
crates/vim/Cargo.toml                         |  1 
crates/vim/src/vim.rs                         |  8 ++
6 files changed, 68 insertions(+), 6 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -917,6 +917,7 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
 name = "command_palette"
 version = "0.1.0"
 dependencies = [
+ "collections",
  "ctor",
  "editor",
  "env_logger",
@@ -5601,6 +5602,7 @@ version = "0.1.0"
 dependencies = [
  "assets",
  "collections",
+ "command_palette",
  "editor",
  "gpui",
  "indoc",

crates/command_palette/Cargo.toml 🔗

@@ -8,6 +8,7 @@ path = "src/command_palette.rs"
 doctest = false
 
 [dependencies]
+collections = { path = "../collections" }
 editor = { path = "../editor" }
 fuzzy = { path = "../fuzzy" }
 gpui = { path = "../gpui" }

crates/command_palette/src/command_palette.rs 🔗

@@ -1,3 +1,4 @@
+use collections::HashSet;
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
     actions,
@@ -10,6 +11,11 @@ use settings::Settings;
 use std::cmp;
 use workspace::Workspace;
 
+#[derive(Default)]
+pub struct CommandPaletteFilter {
+    pub filtered_namespaces: HashSet<&'static str>,
+}
+
 pub fn init(cx: &mut MutableAppContext) {
     cx.add_action(CommandPalette::toggle);
     Picker::<CommandPalette>::init(cx);
@@ -45,14 +51,24 @@ impl CommandPalette {
         let this = cx.weak_handle();
         let actions = cx
             .available_actions(cx.window_id(), focused_view_id)
-            .map(|(name, action, bindings)| Command {
-                name: humanize_action_name(name),
-                action,
-                keystrokes: bindings
-                    .last()
-                    .map_or(Vec::new(), |binding| binding.keystrokes().to_vec()),
+            .filter_map(|(name, action, bindings)| {
+                if cx.has_global::<CommandPaletteFilter>() {
+                    let filter = cx.global::<CommandPaletteFilter>();
+                    if filter.filtered_namespaces.contains(action.namespace()) {
+                        return None;
+                    }
+                }
+
+                Some(Command {
+                    name: humanize_action_name(name),
+                    action,
+                    keystrokes: bindings
+                        .last()
+                        .map_or(Vec::new(), |binding| binding.keystrokes().to_vec()),
+                })
             })
             .collect();
+
         let picker = cx.add_view(|cx| Picker::new(this, cx));
         Self {
             picker,
@@ -368,5 +384,34 @@ mod tests {
         editor.read_with(cx, |editor, cx| {
             assert_eq!(editor.text(cx), "ab");
         });
+
+        // Add namespace filter, and redeploy the palette
+        cx.update(|cx| {
+            cx.update_default_global::<CommandPaletteFilter, _, _>(|filter, _| {
+                filter.filtered_namespaces.insert("editor");
+            })
+        });
+
+        workspace.update(cx, |workspace, cx| {
+            CommandPalette::toggle(workspace, &Toggle, cx);
+        });
+
+        // Assert editor command not present
+        let palette = workspace.read_with(cx, |workspace, _| {
+            workspace
+                .modal()
+                .unwrap()
+                .clone()
+                .downcast::<CommandPalette>()
+                .unwrap()
+        });
+
+        palette
+            .update(cx, |palette, cx| {
+                palette.update_matches("bcksp".to_string(), cx)
+            })
+            .await;
+
+        palette.update(cx, |palette, _| assert!(palette.matches.is_empty()));
     }
 }

crates/gpui/src/app/action.rs 🔗

@@ -2,6 +2,7 @@ use std::any::{Any, TypeId};
 
 pub trait Action: 'static {
     fn id(&self) -> TypeId;
+    fn namespace(&self) -> &'static str;
     fn name(&self) -> &'static str;
     fn as_any(&self) -> &dyn Any;
     fn boxed_clone(&self) -> Box<dyn Action>;
@@ -80,6 +81,10 @@ macro_rules! impl_internal_actions {
 macro_rules! __impl_action {
     ($namespace:path, $name:ident, $from_json_fn:item) => {
         impl $crate::action::Action for $name {
+            fn namespace(&self) -> &'static str {
+                stringify!($namespace)
+            }
+
             fn name(&self) -> &'static str {
                 stringify!($name)
             }

crates/vim/Cargo.toml 🔗

@@ -10,6 +10,7 @@ doctest = false
 [dependencies]
 assets = { path = "../assets" }
 collections = { path = "../collections" }
+command_palette = { path = "../command_palette" }
 editor = { path = "../editor" }
 gpui = { path = "../gpui" }
 language = { path = "../language" }

crates/vim/src/vim.rs 🔗

@@ -10,6 +10,7 @@ mod utils;
 mod visual;
 
 use collections::HashMap;
+use command_palette::CommandPaletteFilter;
 use editor::{Bias, CursorShape, Editor, Input};
 use gpui::{impl_actions, MutableAppContext, Subscription, ViewContext, WeakViewHandle};
 use serde::Deserialize;
@@ -124,6 +125,13 @@ impl Vim {
             if enabled {
                 self.state.mode = Mode::Normal;
             }
+            cx.update_default_global::<CommandPaletteFilter, _, _>(|filter, _| {
+                if enabled {
+                    filter.filtered_namespaces.remove("vim");
+                } else {
+                    filter.filtered_namespaces.insert("vim");
+                }
+            });
             self.sync_editor_options(cx);
         }
     }