Restructure action macro to assign a namespace to every action
Also, allow arbitrary types to be used as Actions via the impl_actions macro Co-authored-by: Nathan Sobo <nathan@zed.dev> Co-authored-by: Keith Simmons <keith@zed.dev>
This commit is contained in:
co-authored by
Nathan Sobo
Keith Simmons
parent
206b0f0f8c
commit
5242a3a6dc
+19
-93
@@ -1,3 +1,5 @@
|
||||
pub mod action;
|
||||
|
||||
use crate::{
|
||||
elements::ElementBox,
|
||||
executor::{self, Task},
|
||||
@@ -7,6 +9,7 @@ use crate::{
|
||||
util::post_inc,
|
||||
AssetCache, AssetSource, ClipboardItem, FontCache, PathPromptOptions, TextLayoutCache,
|
||||
};
|
||||
pub use action::*;
|
||||
use anyhow::{anyhow, Result};
|
||||
use collections::btree_map;
|
||||
use keymap::MatchResult;
|
||||
@@ -142,89 +145,6 @@ pub trait ElementStateContext: DerefMut<Target = MutableAppContext> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Action: 'static + AnyAction {
|
||||
type Argument: 'static + Clone;
|
||||
}
|
||||
|
||||
pub trait AnyAction {
|
||||
fn id(&self) -> TypeId;
|
||||
fn name(&self) -> &'static str;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn boxed_clone(&self) -> Box<dyn AnyAction>;
|
||||
fn boxed_clone_as_any(&self) -> Box<dyn Any>;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! action {
|
||||
($name:ident, $arg:ty) => {
|
||||
#[derive(Clone)]
|
||||
pub struct $name(pub $arg);
|
||||
|
||||
impl $crate::Action for $name {
|
||||
type Argument = $arg;
|
||||
}
|
||||
|
||||
impl $crate::AnyAction for $name {
|
||||
fn id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<$name>()
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
stringify!($name)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn $crate::AnyAction> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn boxed_clone_as_any(&self) -> Box<dyn std::any::Any> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$arg> for $name {
|
||||
fn from(arg: $arg) -> Self {
|
||||
Self(arg)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident) => {
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct $name;
|
||||
|
||||
impl $crate::Action for $name {
|
||||
type Argument = ();
|
||||
}
|
||||
|
||||
impl $crate::AnyAction for $name {
|
||||
fn id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<$name>()
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
stringify!($name)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn $crate::AnyAction> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn boxed_clone_as_any(&self) -> Box<dyn std::any::Any> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Menu<'a> {
|
||||
pub name: &'a str,
|
||||
pub items: Vec<MenuItem<'a>>,
|
||||
@@ -234,7 +154,7 @@ pub enum MenuItem<'a> {
|
||||
Action {
|
||||
name: &'a str,
|
||||
keystroke: Option<&'a str>,
|
||||
action: Box<dyn AnyAction>,
|
||||
action: Box<dyn Action>,
|
||||
},
|
||||
Separator,
|
||||
}
|
||||
@@ -787,8 +707,8 @@ impl ReadViewWith for TestAppContext {
|
||||
}
|
||||
|
||||
type ActionCallback =
|
||||
dyn FnMut(&mut dyn AnyView, &dyn AnyAction, &mut MutableAppContext, usize, usize);
|
||||
type GlobalActionCallback = dyn FnMut(&dyn AnyAction, &mut MutableAppContext);
|
||||
dyn FnMut(&mut dyn AnyView, &dyn Action, &mut MutableAppContext, usize, usize);
|
||||
type GlobalActionCallback = dyn FnMut(&dyn Action, &mut MutableAppContext);
|
||||
|
||||
type SubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext) -> bool>;
|
||||
type GlobalSubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>;
|
||||
@@ -963,7 +883,7 @@ impl MutableAppContext {
|
||||
{
|
||||
let handler = Box::new(
|
||||
move |view: &mut dyn AnyView,
|
||||
action: &dyn AnyAction,
|
||||
action: &dyn Action,
|
||||
cx: &mut MutableAppContext,
|
||||
window_id: usize,
|
||||
view_id: usize| {
|
||||
@@ -1009,7 +929,7 @@ impl MutableAppContext {
|
||||
A: Action,
|
||||
F: 'static + FnMut(&A, &mut MutableAppContext),
|
||||
{
|
||||
let handler = Box::new(move |action: &dyn AnyAction, cx: &mut MutableAppContext| {
|
||||
let handler = Box::new(move |action: &dyn Action, cx: &mut MutableAppContext| {
|
||||
let action = action.as_any().downcast_ref().unwrap();
|
||||
handler(action, cx);
|
||||
});
|
||||
@@ -1338,7 +1258,7 @@ impl MutableAppContext {
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
path: &[usize],
|
||||
action: &dyn AnyAction,
|
||||
action: &dyn Action,
|
||||
) -> bool {
|
||||
self.update(|this| {
|
||||
this.halt_action_dispatch = false;
|
||||
@@ -1398,7 +1318,7 @@ impl MutableAppContext {
|
||||
self.dispatch_global_action_any(&action);
|
||||
}
|
||||
|
||||
fn dispatch_global_action_any(&mut self, action: &dyn AnyAction) -> bool {
|
||||
fn dispatch_global_action_any(&mut self, action: &dyn Action) -> bool {
|
||||
self.update(|this| {
|
||||
if let Some((name, mut handler)) = this.global_actions.remove_entry(&action.id()) {
|
||||
handler(action, this);
|
||||
@@ -4655,7 +4575,7 @@ impl RefCounts {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::elements::*;
|
||||
use crate::{elements::*, impl_actions};
|
||||
use smol::future::poll_once;
|
||||
use std::{
|
||||
cell::Cell,
|
||||
@@ -5801,7 +5721,10 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
action!(Action, &'static str);
|
||||
#[derive(Clone)]
|
||||
pub struct Action(pub &'static str);
|
||||
|
||||
impl_actions!(test, [Action]);
|
||||
|
||||
let actions = Rc::new(RefCell::new(Vec::new()));
|
||||
|
||||
@@ -5909,7 +5832,10 @@ mod tests {
|
||||
|
||||
#[crate::test(self)]
|
||||
fn test_dispatch_keystroke(cx: &mut MutableAppContext) {
|
||||
action!(Action, &'static str);
|
||||
#[derive(Clone)]
|
||||
pub struct Action(pub &'static str);
|
||||
|
||||
impl_actions!(test, [Action]);
|
||||
|
||||
struct View {
|
||||
id: usize,
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
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>;
|
||||
fn boxed_clone_as_any(&self) -> Box<dyn Any>;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_actions {
|
||||
($namespace:path, [ $($name:ident),* $(,)? ]) => {
|
||||
$(
|
||||
impl $crate::action::Action for $name {
|
||||
fn id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<$name>()
|
||||
}
|
||||
|
||||
fn namespace(&self) -> &'static str {
|
||||
stringify!($namespace)
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
stringify!($name)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn $crate::action::Action> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn boxed_clone_as_any(&self) -> Box<dyn std::any::Any> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! actions {
|
||||
($namespace:path, [ $($name:ident),* $(,)? ]) => {
|
||||
|
||||
$(
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct $name;
|
||||
)*
|
||||
|
||||
$crate::impl_actions!($namespace, [ $($name),* ]);
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::Action;
|
||||
use anyhow::anyhow;
|
||||
use std::{
|
||||
any::Any,
|
||||
@@ -6,8 +7,6 @@ use std::{
|
||||
};
|
||||
use tree_sitter::{Language, Node, Parser};
|
||||
|
||||
use crate::{Action, AnyAction};
|
||||
|
||||
extern "C" {
|
||||
fn tree_sitter_context_predicate() -> Language;
|
||||
}
|
||||
@@ -28,7 +27,7 @@ pub struct Keymap(Vec<Binding>);
|
||||
|
||||
pub struct Binding {
|
||||
keystrokes: Vec<Keystroke>,
|
||||
action: Box<dyn AnyAction>,
|
||||
action: Box<dyn Action>,
|
||||
context: Option<ContextPredicate>,
|
||||
}
|
||||
|
||||
@@ -73,7 +72,7 @@ where
|
||||
pub enum MatchResult {
|
||||
None,
|
||||
Pending,
|
||||
Action(Box<dyn AnyAction>),
|
||||
Action(Box<dyn Action>),
|
||||
}
|
||||
|
||||
impl Debug for MatchResult {
|
||||
@@ -329,7 +328,7 @@ impl ContextPredicate {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::action;
|
||||
use crate::{actions, impl_actions};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -420,9 +419,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_matcher() -> anyhow::Result<()> {
|
||||
action!(A, &'static str);
|
||||
action!(B);
|
||||
action!(Ab);
|
||||
#[derive(Clone)]
|
||||
pub struct A(pub &'static str);
|
||||
impl_actions!(test, [A]);
|
||||
actions!(test, [B, Ab]);
|
||||
|
||||
impl PartialEq for A {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::{
|
||||
vector::Vector2F,
|
||||
},
|
||||
text_layout::{LineLayout, RunStyle},
|
||||
AnyAction, ClipboardItem, Menu, Scene,
|
||||
Action, ClipboardItem, Menu, Scene,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use async_task::Runnable;
|
||||
@@ -66,7 +66,7 @@ pub(crate) trait ForegroundPlatform {
|
||||
fn on_open_files(&self, callback: Box<dyn FnMut(Vec<PathBuf>)>);
|
||||
fn run(&self, on_finish_launching: Box<dyn FnOnce() -> ()>);
|
||||
|
||||
fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn AnyAction)>);
|
||||
fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn Action)>);
|
||||
fn set_menus(&self, menus: Vec<Menu>);
|
||||
fn prompt_for_paths(
|
||||
&self,
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{
|
||||
executor,
|
||||
keymap::Keystroke,
|
||||
platform::{self, CursorStyle},
|
||||
AnyAction, ClipboardItem, Event, Menu, MenuItem,
|
||||
Action, ClipboardItem, Event, Menu, MenuItem,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use block::ConcreteBlock;
|
||||
@@ -107,10 +107,10 @@ pub struct MacForegroundPlatformState {
|
||||
resign_active: Option<Box<dyn FnMut()>>,
|
||||
quit: Option<Box<dyn FnMut()>>,
|
||||
event: Option<Box<dyn FnMut(crate::Event) -> bool>>,
|
||||
menu_command: Option<Box<dyn FnMut(&dyn AnyAction)>>,
|
||||
menu_command: Option<Box<dyn FnMut(&dyn Action)>>,
|
||||
open_files: Option<Box<dyn FnMut(Vec<PathBuf>)>>,
|
||||
finish_launching: Option<Box<dyn FnOnce() -> ()>>,
|
||||
menu_actions: Vec<Box<dyn AnyAction>>,
|
||||
menu_actions: Vec<Box<dyn Action>>,
|
||||
}
|
||||
|
||||
impl MacForegroundPlatform {
|
||||
@@ -235,7 +235,7 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn AnyAction)>) {
|
||||
fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn Action)>) {
|
||||
self.0.borrow_mut().menu_command = Some(callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::{CursorStyle, WindowBounds};
|
||||
use crate::{
|
||||
geometry::vector::{vec2f, Vector2F},
|
||||
AnyAction, ClipboardItem,
|
||||
Action, ClipboardItem,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use parking_lot::Mutex;
|
||||
@@ -72,7 +72,7 @@ impl super::ForegroundPlatform for ForegroundPlatform {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_menu_command(&self, _: Box<dyn FnMut(&dyn AnyAction)>) {}
|
||||
fn on_menu_command(&self, _: Box<dyn FnMut(&dyn Action)>) {}
|
||||
|
||||
fn set_menus(&self, _: Vec<crate::Menu>) {}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
json::{self, ToJson},
|
||||
platform::Event,
|
||||
text_layout::TextLayoutCache,
|
||||
Action, AnyAction, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox,
|
||||
Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox,
|
||||
ElementStateContext, Entity, FontSystem, ModelHandle, ReadModel, ReadView, Scene,
|
||||
UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle,
|
||||
};
|
||||
@@ -223,7 +223,7 @@ impl Presenter {
|
||||
|
||||
pub struct DispatchDirective {
|
||||
pub path: Vec<usize>,
|
||||
pub action: Box<dyn AnyAction>,
|
||||
pub action: Box<dyn Action>,
|
||||
}
|
||||
|
||||
pub struct LayoutContext<'a> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
action, elements::*, AppContext, Entity, MutableAppContext, RenderContext, View, ViewContext,
|
||||
WeakViewHandle,
|
||||
actions, elements::*, impl_actions, AppContext, Entity, MutableAppContext, RenderContext, View,
|
||||
ViewContext, WeakViewHandle,
|
||||
};
|
||||
|
||||
pub struct Select {
|
||||
@@ -25,8 +25,11 @@ pub enum ItemType {
|
||||
Unselected,
|
||||
}
|
||||
|
||||
action!(ToggleSelect);
|
||||
action!(SelectItem, usize);
|
||||
#[derive(Clone)]
|
||||
pub struct SelectItem(pub usize);
|
||||
|
||||
actions!(select, [ToggleSelect]);
|
||||
impl_actions!(select, [SelectItem]);
|
||||
|
||||
pub enum Event {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user