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>(cx: &mut AppContext, update: F)
 43    where
 44        F: FnOnce(&mut Self, &mut AppContext),
 45    {
 46        if cx.has_global::<GlobalCommandPaletteFilter>() {
 47            cx.update_global(|this: &mut GlobalCommandPaletteFilter, cx| update(&mut this.0, cx))
 48        }
 49    }
 50
 51    /// Returns whether the given [`Action`] is hidden by the filter.
 52    pub fn is_hidden(&self, action: &dyn Action) -> bool {
 53        let name = action.name();
 54        let namespace = name.split("::").next().unwrap_or("malformed action name");
 55
 56        self.hidden_namespaces.contains(namespace)
 57            || self.hidden_action_types.contains(&action.type_id())
 58    }
 59
 60    /// Hides all actions in the given namespace.
 61    pub fn hide_namespace(&mut self, namespace: &'static str) {
 62        self.hidden_namespaces.insert(namespace);
 63    }
 64
 65    /// Shows all actions in the given namespace.
 66    pub fn show_namespace(&mut self, namespace: &'static str) {
 67        self.hidden_namespaces.remove(namespace);
 68    }
 69
 70    /// Hides all actions with the given types.
 71    pub fn hide_action_types(&mut self, action_types: &[TypeId]) {
 72        self.hidden_action_types.extend(action_types);
 73    }
 74
 75    /// Shows all actions with the given types.
 76    pub fn show_action_types<'a>(&mut self, action_types: impl Iterator<Item = &'a TypeId>) {
 77        for action_type in action_types {
 78            self.hidden_action_types.remove(action_type);
 79        }
 80    }
 81}
 82
 83/// The result of intercepting a command palette command.
 84pub struct CommandInterceptResult {
 85    /// The action produced as a result of the interception.
 86    pub action: Box<dyn Action>,
 87    // TODO: Document this field.
 88    #[allow(missing_docs)]
 89    pub string: String,
 90    // TODO: Document this field.
 91    #[allow(missing_docs)]
 92    pub positions: Vec<usize>,
 93}
 94
 95/// An interceptor for the command palette.
 96#[derive(Default)]
 97pub struct CommandPaletteInterceptor(
 98    Option<Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>>,
 99);
100
101#[derive(Default)]
102struct GlobalCommandPaletteInterceptor(CommandPaletteInterceptor);
103
104impl Global for GlobalCommandPaletteInterceptor {}
105
106impl CommandPaletteInterceptor {
107    /// Returns the global [`CommandPaletteInterceptor`], if one is set.
108    pub fn try_global(cx: &AppContext) -> Option<&CommandPaletteInterceptor> {
109        cx.try_global::<GlobalCommandPaletteInterceptor>()
110            .map(|interceptor| &interceptor.0)
111    }
112
113    /// Updates the global [`CommandPaletteInterceptor`] using the given closure.
114    pub fn update_global<F, R>(cx: &mut AppContext, update: F) -> R
115    where
116        F: FnOnce(&mut Self, &mut AppContext) -> R,
117    {
118        cx.update_global(|this: &mut GlobalCommandPaletteInterceptor, cx| update(&mut this.0, cx))
119    }
120
121    /// Intercepts the given query from the command palette.
122    pub fn intercept(&self, query: &str, cx: &AppContext) -> Option<CommandInterceptResult> {
123        let handler = self.0.as_ref()?;
124
125        (handler)(query, cx)
126    }
127
128    /// Clears the global interceptor.
129    pub fn clear(&mut self) {
130        self.0 = None;
131    }
132
133    /// Sets the global interceptor.
134    ///
135    /// This will override the previous interceptor, if it exists.
136    pub fn set(
137        &mut self,
138        handler: Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>,
139    ) {
140        self.0 = Some(handler);
141    }
142}