1use crate::SharedString;
2use anyhow::{anyhow, Context, Result};
3use collections::{HashMap, HashSet};
4use lazy_static::lazy_static;
5use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
6use serde::Deserialize;
7use std::any::{type_name, Any};
8
9/// Actions are used to implement keyboard-driven UI.
10/// When you declare an action, you can bind keys to the action in the keymap and
11/// listeners for that action in the element tree.
12///
13/// To declare a list of simple actions, you can use the actions! macro, which defines a simple unit struct
14/// action for each listed action name.
15/// ```rust
16/// actions!(MoveUp, MoveDown, MoveLeft, MoveRight, Newline);
17/// ```
18/// More complex data types can also be actions. If you annotate your type with the `#[action]` proc macro,
19/// it will automatically
20/// ```
21/// #[action]
22/// pub struct SelectNext {
23/// pub replace_newest: bool,
24/// }
25///
26/// Any type A that satisfies the following bounds is automatically an action:
27///
28/// ```
29/// A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + std::fmt::Debug + 'static,
30/// ```
31///
32/// The `#[action]` annotation will derive these implementations for your struct automatically. If you
33/// want to control them manually, you can use the lower-level `#[register_action]` macro, which only
34/// generates the code needed to register your action before `main`. Then you'll need to implement all
35/// the traits manually.
36///
37/// ```
38/// #[gpui::register_action]
39/// #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::fmt::Debug)]
40/// pub struct Paste {
41/// pub content: SharedString,
42/// }
43///
44/// impl std::default::Default for Paste {
45/// fn default() -> Self {
46/// Self {
47/// content: SharedString::from("🍝"),
48/// }
49/// }
50/// }
51/// ```
52pub trait Action: std::fmt::Debug + 'static {
53 fn qualified_name() -> SharedString
54 where
55 Self: Sized;
56 fn build(value: Option<serde_json::Value>) -> Result<Box<dyn Action>>
57 where
58 Self: Sized;
59
60 fn partial_eq(&self, action: &dyn Action) -> bool;
61 fn boxed_clone(&self) -> Box<dyn Action>;
62 fn as_any(&self) -> &dyn Any;
63}
64
65// Types become actions by satisfying a list of trait bounds.
66impl<A> Action for A
67where
68 A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + std::fmt::Debug + 'static,
69{
70 fn qualified_name() -> SharedString {
71 // todo!() remove the 2 replacement when migration is done
72 type_name::<A>().replace("2::", "::").into()
73 }
74
75 fn build(params: Option<serde_json::Value>) -> Result<Box<dyn Action>>
76 where
77 Self: Sized,
78 {
79 let action = if let Some(params) = params {
80 serde_json::from_value(params).context("failed to deserialize action")?
81 } else {
82 Self::default()
83 };
84 Ok(Box::new(action))
85 }
86
87 fn partial_eq(&self, action: &dyn Action) -> bool {
88 action
89 .as_any()
90 .downcast_ref::<Self>()
91 .map_or(false, |a| self == a)
92 }
93
94 fn boxed_clone(&self) -> Box<dyn Action> {
95 Box::new(self.clone())
96 }
97
98 fn as_any(&self) -> &dyn Any {
99 self
100 }
101}
102
103type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
104
105lazy_static! {
106 static ref ACTION_REGISTRY: RwLock<ActionRegistry> = RwLock::default();
107}
108
109#[derive(Default)]
110struct ActionRegistry {
111 builders_by_name: HashMap<SharedString, ActionBuilder>,
112 all_names: Vec<SharedString>, // So we can return a static slice.
113}
114
115/// Register an action type to allow it to be referenced in keymaps.
116pub fn register_action<A: Action>() {
117 let name = A::qualified_name();
118 let mut lock = ACTION_REGISTRY.write();
119 lock.builders_by_name.insert(name.clone(), A::build);
120 lock.all_names.push(name);
121}
122
123/// Construct an action based on its name and optional JSON parameters sourced from the keymap.
124pub fn build_action(name: &str, params: Option<serde_json::Value>) -> Result<Box<dyn Action>> {
125 let lock = ACTION_REGISTRY.read();
126
127 let build_action = lock
128 .builders_by_name
129 .get(name)
130 .ok_or_else(|| anyhow!("no action type registered for {}", name))?;
131 (build_action)(params)
132}
133
134pub fn all_action_names() -> MappedRwLockReadGuard<'static, [SharedString]> {
135 let lock = ACTION_REGISTRY.read();
136 RwLockReadGuard::map(lock, |registry: &ActionRegistry| {
137 registry.all_names.as_slice()
138 })
139}
140
141/// Defines unit structs that can be used as actions.
142/// To use more complex data types as actions, annotate your type with the #[action] macro.
143#[macro_export]
144macro_rules! actions {
145 () => {};
146
147 ( $name:ident ) => {
148 #[gpui::register_action]
149 #[derive(::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug, ::std::cmp::PartialEq, $crate::serde::Deserialize)]
150 pub struct $name;
151 };
152
153 ( $name:ident, $($rest:tt)* ) => {
154 actions!($name);
155 actions!($($rest)*);
156 };
157}
158
159#[derive(Clone, Debug, Default, Eq, PartialEq)]
160pub struct DispatchContext {
161 set: HashSet<SharedString>,
162 map: HashMap<SharedString, SharedString>,
163}
164
165impl<'a> TryFrom<&'a str> for DispatchContext {
166 type Error = anyhow::Error;
167
168 fn try_from(value: &'a str) -> Result<Self> {
169 Self::parse(value)
170 }
171}
172
173impl DispatchContext {
174 pub fn parse(source: &str) -> Result<Self> {
175 let mut context = Self::default();
176 let source = skip_whitespace(source);
177 Self::parse_expr(&source, &mut context)?;
178 Ok(context)
179 }
180
181 fn parse_expr(mut source: &str, context: &mut Self) -> Result<()> {
182 if source.is_empty() {
183 return Ok(());
184 }
185
186 let key = source
187 .chars()
188 .take_while(|c| is_identifier_char(*c))
189 .collect::<String>();
190 source = skip_whitespace(&source[key.len()..]);
191 if let Some(suffix) = source.strip_prefix('=') {
192 source = skip_whitespace(suffix);
193 let value = source
194 .chars()
195 .take_while(|c| is_identifier_char(*c))
196 .collect::<String>();
197 source = skip_whitespace(&source[value.len()..]);
198 context.set(key, value);
199 } else {
200 context.insert(key);
201 }
202
203 Self::parse_expr(source, context)
204 }
205
206 pub fn is_empty(&self) -> bool {
207 self.set.is_empty() && self.map.is_empty()
208 }
209
210 pub fn clear(&mut self) {
211 self.set.clear();
212 self.map.clear();
213 }
214
215 pub fn extend(&mut self, other: &Self) {
216 for v in &other.set {
217 self.set.insert(v.clone());
218 }
219 for (k, v) in &other.map {
220 self.map.insert(k.clone(), v.clone());
221 }
222 }
223
224 pub fn insert<I: Into<SharedString>>(&mut self, identifier: I) {
225 self.set.insert(identifier.into());
226 }
227
228 pub fn set<S1: Into<SharedString>, S2: Into<SharedString>>(&mut self, key: S1, value: S2) {
229 self.map.insert(key.into(), value.into());
230 }
231}
232
233#[derive(Clone, Debug, Eq, PartialEq, Hash)]
234pub enum DispatchContextPredicate {
235 Identifier(SharedString),
236 Equal(SharedString, SharedString),
237 NotEqual(SharedString, SharedString),
238 Child(Box<DispatchContextPredicate>, Box<DispatchContextPredicate>),
239 Not(Box<DispatchContextPredicate>),
240 And(Box<DispatchContextPredicate>, Box<DispatchContextPredicate>),
241 Or(Box<DispatchContextPredicate>, Box<DispatchContextPredicate>),
242}
243
244impl DispatchContextPredicate {
245 pub fn parse(source: &str) -> Result<Self> {
246 let source = skip_whitespace(source);
247 let (predicate, rest) = Self::parse_expr(source, 0)?;
248 if let Some(next) = rest.chars().next() {
249 Err(anyhow!("unexpected character {next:?}"))
250 } else {
251 Ok(predicate)
252 }
253 }
254
255 pub fn eval(&self, contexts: &[&DispatchContext]) -> bool {
256 let Some(context) = contexts.last() else {
257 return false;
258 };
259 match self {
260 Self::Identifier(name) => context.set.contains(name),
261 Self::Equal(left, right) => context
262 .map
263 .get(left)
264 .map(|value| value == right)
265 .unwrap_or(false),
266 Self::NotEqual(left, right) => context
267 .map
268 .get(left)
269 .map(|value| value != right)
270 .unwrap_or(true),
271 Self::Not(pred) => !pred.eval(contexts),
272 Self::Child(parent, child) => {
273 parent.eval(&contexts[..contexts.len() - 1]) && child.eval(contexts)
274 }
275 Self::And(left, right) => left.eval(contexts) && right.eval(contexts),
276 Self::Or(left, right) => left.eval(contexts) || right.eval(contexts),
277 }
278 }
279
280 fn parse_expr(mut source: &str, min_precedence: u32) -> anyhow::Result<(Self, &str)> {
281 type Op = fn(
282 DispatchContextPredicate,
283 DispatchContextPredicate,
284 ) -> Result<DispatchContextPredicate>;
285
286 let (mut predicate, rest) = Self::parse_primary(source)?;
287 source = rest;
288
289 'parse: loop {
290 for (operator, precedence, constructor) in [
291 (">", PRECEDENCE_CHILD, Self::new_child as Op),
292 ("&&", PRECEDENCE_AND, Self::new_and as Op),
293 ("||", PRECEDENCE_OR, Self::new_or as Op),
294 ("==", PRECEDENCE_EQ, Self::new_eq as Op),
295 ("!=", PRECEDENCE_EQ, Self::new_neq as Op),
296 ] {
297 if source.starts_with(operator) && precedence >= min_precedence {
298 source = skip_whitespace(&source[operator.len()..]);
299 let (right, rest) = Self::parse_expr(source, precedence + 1)?;
300 predicate = constructor(predicate, right)?;
301 source = rest;
302 continue 'parse;
303 }
304 }
305 break;
306 }
307
308 Ok((predicate, source))
309 }
310
311 fn parse_primary(mut source: &str) -> anyhow::Result<(Self, &str)> {
312 let next = source
313 .chars()
314 .next()
315 .ok_or_else(|| anyhow!("unexpected eof"))?;
316 match next {
317 '(' => {
318 source = skip_whitespace(&source[1..]);
319 let (predicate, rest) = Self::parse_expr(source, 0)?;
320 if rest.starts_with(')') {
321 source = skip_whitespace(&rest[1..]);
322 Ok((predicate, source))
323 } else {
324 Err(anyhow!("expected a ')'"))
325 }
326 }
327 '!' => {
328 let source = skip_whitespace(&source[1..]);
329 let (predicate, source) = Self::parse_expr(&source, PRECEDENCE_NOT)?;
330 Ok((DispatchContextPredicate::Not(Box::new(predicate)), source))
331 }
332 _ if is_identifier_char(next) => {
333 let len = source
334 .find(|c: char| !is_identifier_char(c))
335 .unwrap_or(source.len());
336 let (identifier, rest) = source.split_at(len);
337 source = skip_whitespace(rest);
338 Ok((
339 DispatchContextPredicate::Identifier(identifier.to_string().into()),
340 source,
341 ))
342 }
343 _ => Err(anyhow!("unexpected character {next:?}")),
344 }
345 }
346
347 fn new_or(self, other: Self) -> Result<Self> {
348 Ok(Self::Or(Box::new(self), Box::new(other)))
349 }
350
351 fn new_and(self, other: Self) -> Result<Self> {
352 Ok(Self::And(Box::new(self), Box::new(other)))
353 }
354
355 fn new_child(self, other: Self) -> Result<Self> {
356 Ok(Self::Child(Box::new(self), Box::new(other)))
357 }
358
359 fn new_eq(self, other: Self) -> Result<Self> {
360 if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
361 Ok(Self::Equal(left, right))
362 } else {
363 Err(anyhow!("operands must be identifiers"))
364 }
365 }
366
367 fn new_neq(self, other: Self) -> Result<Self> {
368 if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
369 Ok(Self::NotEqual(left, right))
370 } else {
371 Err(anyhow!("operands must be identifiers"))
372 }
373 }
374}
375
376const PRECEDENCE_CHILD: u32 = 1;
377const PRECEDENCE_OR: u32 = 2;
378const PRECEDENCE_AND: u32 = 3;
379const PRECEDENCE_EQ: u32 = 4;
380const PRECEDENCE_NOT: u32 = 5;
381
382fn is_identifier_char(c: char) -> bool {
383 c.is_alphanumeric() || c == '_' || c == '-'
384}
385
386fn skip_whitespace(source: &str) -> &str {
387 let len = source
388 .find(|c: char| !c.is_whitespace())
389 .unwrap_or(source.len());
390 &source[len..]
391}
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396 use crate as gpui;
397 use DispatchContextPredicate::*;
398
399 #[test]
400 fn test_actions_definition() {
401 {
402 actions!(A, B, C, D, E, F, G);
403 }
404
405 {
406 actions!(
407 A,
408 B,
409 C,
410 D,
411 E,
412 F,
413 G, // Don't wrap, test the trailing comma
414 );
415 }
416 }
417
418 #[test]
419 fn test_parse_context() {
420 let mut expected = DispatchContext::default();
421 expected.set("foo", "bar");
422 expected.insert("baz");
423 assert_eq!(DispatchContext::parse("baz foo=bar").unwrap(), expected);
424 assert_eq!(DispatchContext::parse("foo = bar baz").unwrap(), expected);
425 assert_eq!(
426 DispatchContext::parse(" baz foo = bar baz").unwrap(),
427 expected
428 );
429 assert_eq!(DispatchContext::parse(" foo = bar baz").unwrap(), expected);
430 }
431
432 #[test]
433 fn test_parse_identifiers() {
434 // Identifiers
435 assert_eq!(
436 DispatchContextPredicate::parse("abc12").unwrap(),
437 Identifier("abc12".into())
438 );
439 assert_eq!(
440 DispatchContextPredicate::parse("_1a").unwrap(),
441 Identifier("_1a".into())
442 );
443 }
444
445 #[test]
446 fn test_parse_negations() {
447 assert_eq!(
448 DispatchContextPredicate::parse("!abc").unwrap(),
449 Not(Box::new(Identifier("abc".into())))
450 );
451 assert_eq!(
452 DispatchContextPredicate::parse(" ! ! abc").unwrap(),
453 Not(Box::new(Not(Box::new(Identifier("abc".into())))))
454 );
455 }
456
457 #[test]
458 fn test_parse_equality_operators() {
459 assert_eq!(
460 DispatchContextPredicate::parse("a == b").unwrap(),
461 Equal("a".into(), "b".into())
462 );
463 assert_eq!(
464 DispatchContextPredicate::parse("c!=d").unwrap(),
465 NotEqual("c".into(), "d".into())
466 );
467 assert_eq!(
468 DispatchContextPredicate::parse("c == !d")
469 .unwrap_err()
470 .to_string(),
471 "operands must be identifiers"
472 );
473 }
474
475 #[test]
476 fn test_parse_boolean_operators() {
477 assert_eq!(
478 DispatchContextPredicate::parse("a || b").unwrap(),
479 Or(
480 Box::new(Identifier("a".into())),
481 Box::new(Identifier("b".into()))
482 )
483 );
484 assert_eq!(
485 DispatchContextPredicate::parse("a || !b && c").unwrap(),
486 Or(
487 Box::new(Identifier("a".into())),
488 Box::new(And(
489 Box::new(Not(Box::new(Identifier("b".into())))),
490 Box::new(Identifier("c".into()))
491 ))
492 )
493 );
494 assert_eq!(
495 DispatchContextPredicate::parse("a && b || c&&d").unwrap(),
496 Or(
497 Box::new(And(
498 Box::new(Identifier("a".into())),
499 Box::new(Identifier("b".into()))
500 )),
501 Box::new(And(
502 Box::new(Identifier("c".into())),
503 Box::new(Identifier("d".into()))
504 ))
505 )
506 );
507 assert_eq!(
508 DispatchContextPredicate::parse("a == b && c || d == e && f").unwrap(),
509 Or(
510 Box::new(And(
511 Box::new(Equal("a".into(), "b".into())),
512 Box::new(Identifier("c".into()))
513 )),
514 Box::new(And(
515 Box::new(Equal("d".into(), "e".into())),
516 Box::new(Identifier("f".into()))
517 ))
518 )
519 );
520 assert_eq!(
521 DispatchContextPredicate::parse("a && b && c && d").unwrap(),
522 And(
523 Box::new(And(
524 Box::new(And(
525 Box::new(Identifier("a".into())),
526 Box::new(Identifier("b".into()))
527 )),
528 Box::new(Identifier("c".into())),
529 )),
530 Box::new(Identifier("d".into()))
531 ),
532 );
533 }
534
535 #[test]
536 fn test_parse_parenthesized_expressions() {
537 assert_eq!(
538 DispatchContextPredicate::parse("a && (b == c || d != e)").unwrap(),
539 And(
540 Box::new(Identifier("a".into())),
541 Box::new(Or(
542 Box::new(Equal("b".into(), "c".into())),
543 Box::new(NotEqual("d".into(), "e".into())),
544 )),
545 ),
546 );
547 assert_eq!(
548 DispatchContextPredicate::parse(" ( a || b ) ").unwrap(),
549 Or(
550 Box::new(Identifier("a".into())),
551 Box::new(Identifier("b".into())),
552 )
553 );
554 }
555}