Register actions globally before main

This commit is contained in:
Nathan Sobo 2023-11-07 20:58:37 -07:00
parent 80630cd4d9
commit 814e62050c
8 changed files with 33 additions and 169 deletions

View file

@ -80,7 +80,6 @@ pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
init_settings(cx); init_settings(cx);
let client = Arc::downgrade(client); let client = Arc::downgrade(client);
cx.register_action_type::<SignIn>();
cx.on_action({ cx.on_action({
let client = client.clone(); let client = client.clone();
move |_: &SignIn, cx| { move |_: &SignIn, cx| {
@ -93,7 +92,6 @@ pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
} }
}); });
cx.register_action_type::<SignOut>();
cx.on_action({ cx.on_action({
let client = client.clone(); let client = client.clone();
move |_: &SignOut, cx| { move |_: &SignOut, cx| {
@ -106,7 +104,6 @@ pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
} }
}); });
cx.register_action_type::<Reconnect>();
cx.on_action({ cx.on_action({
let client = client.clone(); let client = client.clone();
move |_: &Reconnect, cx| { move |_: &Reconnect, cx| {

View file

@ -406,130 +406,6 @@ pub fn init_settings(cx: &mut AppContext) {
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
init_settings(cx); init_settings(cx);
// cx.register_action_type(Editor::new_file);
// cx.register_action_type(Editor::new_file_in_direction);
// cx.register_action_type(Editor::cancel);
// cx.register_action_type(Editor::newline);
// cx.register_action_type(Editor::newline_above);
// cx.register_action_type(Editor::newline_below);
// cx.register_action_type(Editor::backspace);
// cx.register_action_type(Editor::delete);
// cx.register_action_type(Editor::tab);
// cx.register_action_type(Editor::tab_prev);
// cx.register_action_type(Editor::indent);
// cx.register_action_type(Editor::outdent);
// cx.register_action_type(Editor::delete_line);
// cx.register_action_type(Editor::join_lines);
// cx.register_action_type(Editor::sort_lines_case_sensitive);
// cx.register_action_type(Editor::sort_lines_case_insensitive);
// cx.register_action_type(Editor::reverse_lines);
// cx.register_action_type(Editor::shuffle_lines);
// cx.register_action_type(Editor::convert_to_upper_case);
// cx.register_action_type(Editor::convert_to_lower_case);
// cx.register_action_type(Editor::convert_to_title_case);
// cx.register_action_type(Editor::convert_to_snake_case);
// cx.register_action_type(Editor::convert_to_kebab_case);
// cx.register_action_type(Editor::convert_to_upper_camel_case);
// cx.register_action_type(Editor::convert_to_lower_camel_case);
// cx.register_action_type(Editor::delete_to_previous_word_start);
// cx.register_action_type(Editor::delete_to_previous_subword_start);
// cx.register_action_type(Editor::delete_to_next_word_end);
// cx.register_action_type(Editor::delete_to_next_subword_end);
// cx.register_action_type(Editor::delete_to_beginning_of_line);
// cx.register_action_type(Editor::delete_to_end_of_line);
// cx.register_action_type(Editor::cut_to_end_of_line);
// cx.register_action_type(Editor::duplicate_line);
// cx.register_action_type(Editor::move_line_up);
// cx.register_action_type(Editor::move_line_down);
// cx.register_action_type(Editor::transpose);
// cx.register_action_type(Editor::cut);
// cx.register_action_type(Editor::copy);
// cx.register_action_type(Editor::paste);
// cx.register_action_type(Editor::undo);
// cx.register_action_type(Editor::redo);
cx.register_action_type::<MoveUp>();
// cx.register_action_type(Editor::move_page_up);
cx.register_action_type::<MoveDown>();
// cx.register_action_type(Editor::move_page_down);
// cx.register_action_type(Editor::next_screen);
cx.register_action_type::<MoveLeft>();
cx.register_action_type::<MoveRight>();
// cx.register_action_type(Editor::move_to_previous_word_start);
// cx.register_action_type(Editor::move_to_previous_subword_start);
// cx.register_action_type(Editor::move_to_next_word_end);
// cx.register_action_type(Editor::move_to_next_subword_end);
// cx.register_action_type(Editor::move_to_beginning_of_line);
// cx.register_action_type(Editor::move_to_end_of_line);
// cx.register_action_type(Editor::move_to_start_of_paragraph);
// cx.register_action_type(Editor::move_to_end_of_paragraph);
// cx.register_action_type(Editor::move_to_beginning);
// cx.register_action_type(Editor::move_to_end);
// cx.register_action_type(Editor::select_up);
// cx.register_action_type(Editor::select_down);
// cx.register_action_type(Editor::select_left);
// cx.register_action_type(Editor::select_right);
// cx.register_action_type(Editor::select_to_previous_word_start);
// cx.register_action_type(Editor::select_to_previous_subword_start);
// cx.register_action_type(Editor::select_to_next_word_end);
// cx.register_action_type(Editor::select_to_next_subword_end);
// cx.register_action_type(Editor::select_to_beginning_of_line);
// cx.register_action_type(Editor::select_to_end_of_line);
// cx.register_action_type(Editor::select_to_start_of_paragraph);
// cx.register_action_type(Editor::select_to_end_of_paragraph);
// cx.register_action_type(Editor::select_to_beginning);
// cx.register_action_type(Editor::select_to_end);
// cx.register_action_type(Editor::select_all);
// cx.register_action_type(Editor::select_all_matches);
// cx.register_action_type(Editor::select_line);
// cx.register_action_type(Editor::split_selection_into_lines);
// cx.register_action_type(Editor::add_selection_above);
// cx.register_action_type(Editor::add_selection_below);
// cx.register_action_type(Editor::select_next);
// cx.register_action_type(Editor::select_previous);
// cx.register_action_type(Editor::toggle_comments);
// cx.register_action_type(Editor::select_larger_syntax_node);
// cx.register_action_type(Editor::select_smaller_syntax_node);
// cx.register_action_type(Editor::move_to_enclosing_bracket);
// cx.register_action_type(Editor::undo_selection);
// cx.register_action_type(Editor::redo_selection);
// cx.register_action_type(Editor::go_to_diagnostic);
// cx.register_action_type(Editor::go_to_prev_diagnostic);
// cx.register_action_type(Editor::go_to_hunk);
// cx.register_action_type(Editor::go_to_prev_hunk);
// cx.register_action_type(Editor::go_to_definition);
// cx.register_action_type(Editor::go_to_definition_split);
// cx.register_action_type(Editor::go_to_type_definition);
// cx.register_action_type(Editor::go_to_type_definition_split);
// cx.register_action_type(Editor::fold);
// cx.register_action_type(Editor::fold_at);
// cx.register_action_type(Editor::unfold_lines);
// cx.register_action_type(Editor::unfold_at);
// cx.register_action_type(Editor::gutter_hover);
// cx.register_action_type(Editor::fold_selected_ranges);
// cx.register_action_type(Editor::show_completions);
// cx.register_action_type(Editor::toggle_code_actions);
// cx.register_action_type(Editor::open_excerpts);
// cx.register_action_type(Editor::toggle_soft_wrap);
// cx.register_action_type(Editor::toggle_inlay_hints);
// cx.register_action_type(Editor::reveal_in_finder);
// cx.register_action_type(Editor::copy_path);
// cx.register_action_type(Editor::copy_relative_path);
// cx.register_action_type(Editor::copy_highlight_json);
// cx.add_async_action(Editor::format);
// cx.register_action_type(Editor::restart_language_server);
// cx.register_action_type(Editor::show_character_palette);
// cx.add_async_action(Editor::confirm_completion);
// cx.add_async_action(Editor::confirm_code_action);
// cx.add_async_action(Editor::rename);
// cx.add_async_action(Editor::confirm_rename);
// cx.add_async_action(Editor::find_all_references);
// cx.register_action_type(Editor::next_copilot_suggestion);
// cx.register_action_type(Editor::previous_copilot_suggestion);
// cx.register_action_type(Editor::copilot_suggest);
// cx.register_action_type(Editor::context_menu_first);
// cx.register_action_type(Editor::context_menu_prev);
// cx.register_action_type(Editor::context_menu_next);
// cx.register_action_type(Editor::context_menu_last);
hover_popover::init(cx); hover_popover::init(cx);
scroll::actions::init(cx); scroll::actions::init(cx);

View file

@ -1,8 +1,8 @@
use crate::SharedString; use crate::SharedString;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
use ctor::ctor; use lazy_static::lazy_static;
use parking_lot::Mutex; use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
use serde::Deserialize; use serde::Deserialize;
use std::any::{type_name, Any}; use std::any::{type_name, Any};
@ -21,23 +21,41 @@ pub trait Action: std::fmt::Debug + 'static {
type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>; type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
#[ctor] lazy_static! {
static ACTION_BUILDERS: Mutex<HashMap<SharedString, ActionBuilder>> = Mutex::default(); static ref ACTION_REGISTRY: RwLock<ActionRegistry> = RwLock::default();
}
#[derive(Default)]
struct ActionRegistry {
builders_by_name: HashMap<SharedString, ActionBuilder>,
all_names: Vec<SharedString>, // So we can return a static slice.
}
/// Register an action type to allow it to be referenced in keymaps. /// Register an action type to allow it to be referenced in keymaps.
pub fn register_action<A: Action>() { pub fn register_action<A: Action>() {
ACTION_BUILDERS.lock().insert(A::qualified_name(), A::build); let name = A::qualified_name();
let mut lock = ACTION_REGISTRY.write();
lock.builders_by_name.insert(name.clone(), A::build);
lock.all_names.push(name);
} }
/// Construct an action based on its name and optional JSON parameters sourced from the keymap. /// Construct an action based on its name and optional JSON parameters sourced from the keymap.
pub fn build_action(name: &str, params: Option<serde_json::Value>) -> Result<Box<dyn Action>> { pub fn build_action(name: &str, params: Option<serde_json::Value>) -> Result<Box<dyn Action>> {
let lock = &ACTION_BUILDERS.lock(); let lock = ACTION_REGISTRY.read();
let build_action = lock let build_action = lock
.builders_by_name
.get(name) .get(name)
.ok_or_else(|| anyhow!("no action type registered for {}", name))?; .ok_or_else(|| anyhow!("no action type registered for {}", name))?;
(build_action)(params) (build_action)(params)
} }
pub fn all_action_names() -> MappedRwLockReadGuard<'static, [SharedString]> {
let lock = ACTION_REGISTRY.read();
RwLockReadGuard::map(lock, |registry: &ActionRegistry| {
registry.all_names.as_slice()
})
}
// actions defines structs that can be used as actions. // actions defines structs that can be used as actions.
#[macro_export] #[macro_export]
macro_rules! actions { macro_rules! actions {

View file

@ -17,9 +17,9 @@ use crate::{
current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle, current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle,
AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
Entity, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId, Entity, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId,
PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SharedString, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SubscriberSet,
SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window,
View, Window, WindowContext, WindowHandle, WindowId, WindowContext, WindowHandle, WindowId,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque}; use collections::{HashMap, HashSet, VecDeque};
@ -140,7 +140,6 @@ impl App {
} }
} }
type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
pub(crate) type FrameCallback = Box<dyn FnOnce(&mut AppContext)>; pub(crate) type FrameCallback = Box<dyn FnOnce(&mut AppContext)>;
type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>; type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>; type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
@ -176,7 +175,6 @@ pub struct AppContext {
pub(crate) keymap: Arc<Mutex<Keymap>>, pub(crate) keymap: Arc<Mutex<Keymap>>,
pub(crate) global_action_listeners: pub(crate) global_action_listeners:
HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self)>>>, HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self)>>>,
action_builders: HashMap<SharedString, ActionBuilder>,
pending_effects: VecDeque<Effect>, pending_effects: VecDeque<Effect>,
pub(crate) pending_notifications: HashSet<EntityId>, pub(crate) pending_notifications: HashSet<EntityId>,
pub(crate) pending_global_notifications: HashSet<TypeId>, pub(crate) pending_global_notifications: HashSet<TypeId>,
@ -234,7 +232,6 @@ impl AppContext {
windows: SlotMap::with_key(), windows: SlotMap::with_key(),
keymap: Arc::new(Mutex::new(Keymap::default())), keymap: Arc::new(Mutex::new(Keymap::default())),
global_action_listeners: HashMap::default(), global_action_listeners: HashMap::default(),
action_builders: HashMap::default(),
pending_effects: VecDeque::new(), pending_effects: VecDeque::new(),
pending_notifications: HashSet::default(), pending_notifications: HashSet::default(),
pending_global_notifications: HashSet::default(), pending_global_notifications: HashSet::default(),
@ -695,10 +692,6 @@ impl AppContext {
) )
} }
pub fn all_action_names<'a>(&'a self) -> impl Iterator<Item = SharedString> + 'a {
self.action_builders.keys().cloned()
}
/// Move the global of the given type to the stack. /// Move the global of the given type to the stack.
pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> { pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> {
GlobalLease::new( GlobalLease::new(
@ -761,24 +754,6 @@ impl AppContext {
})); }));
} }
/// Register an action type to allow it to be referenced in keymaps.
pub fn register_action_type<A: Action>(&mut self) {
self.action_builders.insert(A::qualified_name(), A::build);
}
/// Construct an action based on its name and parameters.
pub fn build_action(
&mut self,
name: &str,
params: Option<serde_json::Value>,
) -> Result<Box<dyn Action>> {
let build = self
.action_builders
.get(name)
.ok_or_else(|| anyhow!("no action type registered for {}", name))?;
(build)(params)
}
/// Event handlers propagate events by default. Call this method to stop dispatching to /// Event handlers propagate events by default. Call this method to stop dispatching to
/// event handlers with a lower z-index (mouse) or higher in the tree (keyboard). This is /// event handlers with a lower z-index (mouse) or higher in the tree (keyboard). This is
/// the opposite of [propagate]. It's also possible to cancel a call to [propagate] by /// the opposite of [propagate]. It's also possible to cancel a call to [propagate] by

View file

@ -1,14 +1,14 @@
// Input: // Input:
// //
// struct Foo {} // struct FooBar {}
// Output: // Output:
// //
// struct Foo {} // struct FooBar {}
// //
// #[allow(non_snake_case)] // #[allow(non_snake_case)]
// #[gpui2::ctor] // #[gpui2::ctor]
// fn register_Foo_builder() { // fn register_foobar_builder() {
// gpui2::register_action_builder::<Foo>() // gpui2::register_action_builder::<Foo>()
// } // }
use proc_macro::TokenStream; use proc_macro::TokenStream;

View file

@ -73,9 +73,9 @@ impl KeymapFile {
"Expected first item in array to be a string." "Expected first item in array to be a string."
))); )));
}; };
cx.build_action(&name, Some(data)) gpui::build_action(&name, Some(data))
} }
Value::String(name) => cx.build_action(&name, None), Value::String(name) => gpui::build_action(&name, None),
Value::Null => Ok(no_action()), Value::Null => Ok(no_action()),
_ => { _ => {
return Some(Err(anyhow!("Expected two-element array, got {action:?}"))) return Some(Err(anyhow!("Expected two-element array, got {action:?}")))

View file

@ -15,8 +15,6 @@ impl FocusStory {
KeyBinding::new("cmd-a", ActionB, Some("child-1")), KeyBinding::new("cmd-a", ActionB, Some("child-1")),
KeyBinding::new("cmd-c", ActionC, None), KeyBinding::new("cmd-c", ActionC, None),
]); ]);
cx.register_action_type::<ActionA>();
cx.register_action_type::<ActionB>();
cx.build_view(move |cx| Self {}) cx.build_view(move |cx| Self {})
} }

View file

@ -107,7 +107,7 @@ impl LspAdapter for JsonLspAdapter {
&self, &self,
cx: &mut AppContext, cx: &mut AppContext,
) -> BoxFuture<'static, serde_json::Value> { ) -> BoxFuture<'static, serde_json::Value> {
let action_names = cx.all_action_names().collect::<Vec<_>>(); let action_names = gpui::all_action_names();
let staff_mode = cx.is_staff(); let staff_mode = cx.is_staff();
let language_names = &self.languages.language_names(); let language_names = &self.languages.language_names();
let settings_schema = cx.global::<SettingsStore>().json_schema( let settings_schema = cx.global::<SettingsStore>().json_schema(