command_palette_hooks.rs

  1//! Provides hooks for customizing the behavior of the command palette.
  2
  3#![deny(missing_docs)]
  4
  5use std::any::TypeId;
  6
  7use collections::HashSet;
  8use derive_more::{Deref, DerefMut};
  9use gpui::{Action, AppContext, BorrowAppContext, Global};
 10
 11/// Initializes the command palette hooks.
 12pub fn init(cx: &mut AppContext) {
 13    cx.set_global(GlobalCommandPaletteFilter::default());
 14    cx.set_global(GlobalCommandPaletteInterceptor::default());
 15}
 16
 17/// A filter for the command palette.
 18#[derive(Default)]
 19pub struct CommandPaletteFilter {
 20    hidden_namespaces: HashSet<&'static str>,
 21    hidden_action_types: HashSet<TypeId>,
 22}
 23
 24#[derive(Deref, DerefMut, Default)]
 25struct GlobalCommandPaletteFilter(CommandPaletteFilter);
 26
 27impl Global for GlobalCommandPaletteFilter {}
 28
 29impl CommandPaletteFilter {
 30    /// Returns the global [`CommandPaletteFilter`], if one is set.
 31    pub fn try_global(cx: &AppContext) -> Option<&CommandPaletteFilter> {
 32        cx.try_global::<GlobalCommandPaletteFilter>()
 33            .map(|filter| &filter.0)
 34    }
 35
 36    /// Returns a mutable reference to the global [`CommandPaletteFilter`].
 37    pub fn global_mut(cx: &mut AppContext) -> &mut Self {
 38        cx.global_mut::<GlobalCommandPaletteFilter>()
 39    }
 40
 41    /// Updates the global [`CommandPaletteFilter`] using the given closure.
 42    pub fn update_global<F, R>(cx: &mut AppContext, update: F) -> R
 43    where
 44        F: FnOnce(&mut Self, &mut AppContext) -> R,
 45    {
 46        cx.update_global(|this: &mut GlobalCommandPaletteFilter, cx| update(&mut this.0, cx))
 47    }
 48
 49    /// Returns whether the given [`Action`] is hidden by the filter.
 50    pub fn is_hidden(&self, action: &dyn Action) -> bool {
 51        let name = action.name();
 52        let namespace = name.split("::").next().unwrap_or("malformed action name");
 53
 54        self.hidden_namespaces.contains(namespace)
 55            || self.hidden_action_types.contains(&action.type_id())
 56    }
 57
 58    /// Hides all actions in the given namespace.
 59    pub fn hide_namespace(&mut self, namespace: &'static str) {
 60        self.hidden_namespaces.insert(namespace);
 61    }
 62
 63    /// Shows all actions in the given namespace.
 64    pub fn show_namespace(&mut self, namespace: &'static str) {
 65        self.hidden_namespaces.remove(namespace);
 66    }
 67
 68    /// Hides all actions with the given types.
 69    pub fn hide_action_types(&mut self, action_types: &[TypeId]) {
 70        self.hidden_action_types.extend(action_types);
 71    }
 72
 73    /// Shows all actions with the given types.
 74    pub fn show_action_types<'a>(&mut self, action_types: impl Iterator<Item = &'a TypeId>) {
 75        for action_type in action_types {
 76            self.hidden_action_types.remove(action_type);
 77        }
 78    }
 79}
 80
 81/// The result of intercepting a command palette command.
 82pub struct CommandInterceptResult {
 83    /// The action produced as a result of the interception.
 84    pub action: Box<dyn Action>,
 85    // TODO: Document this field.
 86    #[allow(missing_docs)]
 87    pub string: String,
 88    // TODO: Document this field.
 89    #[allow(missing_docs)]
 90    pub positions: Vec<usize>,
 91}
 92
 93/// An interceptor for the command palette.
 94#[derive(Default)]
 95pub struct CommandPaletteInterceptor(
 96    Option<Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>>,
 97);
 98
 99#[derive(Default)]
100struct GlobalCommandPaletteInterceptor(CommandPaletteInterceptor);
101
102impl Global for GlobalCommandPaletteInterceptor {}
103
104impl CommandPaletteInterceptor {
105    /// Returns the global [`CommandPaletteInterceptor`], if one is set.
106    pub fn try_global(cx: &AppContext) -> Option<&CommandPaletteInterceptor> {
107        cx.try_global::<GlobalCommandPaletteInterceptor>()
108            .map(|interceptor| &interceptor.0)
109    }
110
111    /// Updates the global [`CommandPaletteInterceptor`] using the given closure.
112    pub fn update_global<F, R>(cx: &mut AppContext, update: F) -> R
113    where
114        F: FnOnce(&mut Self, &mut AppContext) -> R,
115    {
116        cx.update_global(|this: &mut GlobalCommandPaletteInterceptor, cx| update(&mut this.0, cx))
117    }
118
119    /// Intercepts the given query from the command palette.
120    pub fn intercept(&self, query: &str, cx: &AppContext) -> Option<CommandInterceptResult> {
121        let handler = self.0.as_ref()?;
122
123        (handler)(query, cx)
124    }
125
126    /// Clears the global interceptor.
127    pub fn clear(&mut self) {
128        self.0 = None;
129    }
130
131    /// Sets the global interceptor.
132    ///
133    /// This will override the previous interceptor, if it exists.
134    pub fn set(
135        &mut self,
136        handler: Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>,
137    ) {
138        self.0 = Some(handler);
139    }
140}