Checkpoint

This commit is contained in:
Antonio Scandurra 2023-10-20 11:44:19 +02:00
parent b0acaed02f
commit 8a11053f1f
4 changed files with 104 additions and 27 deletions

View file

@ -15,12 +15,45 @@ pub struct DispatchContext {
map: HashMap<SharedString, SharedString>,
}
impl DispatchContext {
pub fn new() -> Self {
DispatchContext {
set: HashSet::default(),
map: HashMap::default(),
impl<'a> TryFrom<&'a str> for DispatchContext {
type Error = anyhow::Error;
fn try_from(value: &'a str) -> Result<Self> {
Self::parse(value)
}
}
impl DispatchContext {
pub fn parse(source: &str) -> Result<Self> {
let mut context = Self::default();
let source = skip_whitespace(source);
Self::parse_expr(&source, &mut context)?;
Ok(context)
}
fn parse_expr(mut source: &str, context: &mut Self) -> Result<()> {
if source.is_empty() {
return Ok(());
}
let key = source
.chars()
.take_while(|ch| ch.is_alphanumeric())
.collect::<String>();
source = skip_whitespace(&source[key.len()..]);
if let Some(suffix) = source.strip_prefix('=') {
source = skip_whitespace(suffix);
let value = source
.chars()
.take_while(|ch| ch.is_alphanumeric())
.collect::<String>();
source = skip_whitespace(&source[value.len()..]);
context.set(key, value);
} else {
context.insert(key);
}
Self::parse_expr(source, context)
}
pub fn is_empty(&self) -> bool {
@ -41,11 +74,11 @@ impl DispatchContext {
}
}
pub fn add_identifier<I: Into<SharedString>>(&mut self, identifier: I) {
pub fn insert<I: Into<SharedString>>(&mut self, identifier: I) {
self.set.insert(identifier.into());
}
pub fn add_key<S1: Into<SharedString>, S2: Into<SharedString>>(&mut self, key: S1, value: S2) {
pub fn set<S1: Into<SharedString>, S2: Into<SharedString>>(&mut self, key: S1, value: S2) {
self.map.insert(key.into(), value.into());
}
}
@ -63,7 +96,7 @@ pub enum DispatchContextPredicate {
impl DispatchContextPredicate {
pub fn parse(source: &str) -> Result<Self> {
let source = Self::skip_whitespace(source);
let source = skip_whitespace(source);
let (predicate, rest) = Self::parse_expr(source, 0)?;
if let Some(next) = rest.chars().next() {
Err(anyhow!("unexpected character {next:?}"))
@ -113,7 +146,7 @@ impl DispatchContextPredicate {
("!=", PRECEDENCE_EQ, Self::new_neq as Op),
] {
if source.starts_with(operator) && precedence >= min_precedence {
source = Self::skip_whitespace(&source[operator.len()..]);
source = skip_whitespace(&source[operator.len()..]);
let (right, rest) = Self::parse_expr(source, precedence + 1)?;
predicate = constructor(predicate, right)?;
source = rest;
@ -133,17 +166,17 @@ impl DispatchContextPredicate {
.ok_or_else(|| anyhow!("unexpected eof"))?;
match next {
'(' => {
source = Self::skip_whitespace(&source[1..]);
source = skip_whitespace(&source[1..]);
let (predicate, rest) = Self::parse_expr(source, 0)?;
if rest.starts_with(')') {
source = Self::skip_whitespace(&rest[1..]);
source = skip_whitespace(&rest[1..]);
Ok((predicate, source))
} else {
Err(anyhow!("expected a ')'"))
}
}
'!' => {
let source = Self::skip_whitespace(&source[1..]);
let source = skip_whitespace(&source[1..]);
let (predicate, source) = Self::parse_expr(&source, PRECEDENCE_NOT)?;
Ok((DispatchContextPredicate::Not(Box::new(predicate)), source))
}
@ -152,7 +185,7 @@ impl DispatchContextPredicate {
.find(|c: char| !(c.is_alphanumeric() || c == '_'))
.unwrap_or(source.len());
let (identifier, rest) = source.split_at(len);
source = Self::skip_whitespace(rest);
source = skip_whitespace(rest);
Ok((
DispatchContextPredicate::Identifier(identifier.to_string().into()),
source,
@ -162,13 +195,6 @@ impl DispatchContextPredicate {
}
}
fn skip_whitespace(source: &str) -> &str {
let len = source
.find(|c: char| !c.is_whitespace())
.unwrap_or(source.len());
&source[len..]
}
fn new_or(self, other: Self) -> Result<Self> {
Ok(Self::Or(Box::new(self), Box::new(other)))
}
@ -204,9 +230,31 @@ const PRECEDENCE_AND: u32 = 3;
const PRECEDENCE_EQ: u32 = 4;
const PRECEDENCE_NOT: u32 = 5;
fn skip_whitespace(source: &str) -> &str {
let len = source
.find(|c: char| !c.is_whitespace())
.unwrap_or(source.len());
&source[len..]
}
#[cfg(test)]
mod tests {
use super::DispatchContextPredicate::{self, *};
use super::*;
use DispatchContextPredicate::*;
#[test]
fn test_parse_context() {
let mut expected = DispatchContext::default();
expected.set("foo", "bar");
expected.insert("baz");
assert_eq!(DispatchContext::parse("baz foo=bar").unwrap(), expected);
assert_eq!(DispatchContext::parse("foo = bar baz").unwrap(), expected);
assert_eq!(
DispatchContext::parse(" baz foo = bar baz").unwrap(),
expected
);
assert_eq!(DispatchContext::parse(" foo = bar baz").unwrap(), expected);
}
#[test]
fn test_parse_identifiers() {

View file

@ -595,7 +595,7 @@ pub struct InteractiveElementState {
impl<V> Default for StatelessInteraction<V> {
fn default() -> Self {
Self {
dispatch_context: DispatchContext::new(),
dispatch_context: DispatchContext::default(),
mouse_down_listeners: SmallVec::new(),
mouse_up_listeners: SmallVec::new(),
mouse_move_listeners: SmallVec::new(),

View file

@ -39,6 +39,23 @@ impl Action for ActionB {
}
}
#[derive(Clone)]
struct ActionC;
impl Action for ActionC {
fn eq(&self, action: &dyn Action) -> bool {
action.as_any().downcast_ref::<Self>().is_some()
}
fn boxed_clone(&self) -> Box<dyn Action> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct FocusStory {
text: View<()>,
}
@ -46,8 +63,9 @@ pub struct FocusStory {
impl FocusStory {
pub fn view(cx: &mut WindowContext) -> View<()> {
cx.bind_keys([
KeyBinding::new("cmd-a", ActionA, None),
KeyBinding::new("cmd-b", ActionB, None),
KeyBinding::new("cmd-a", ActionA, Some("parent")),
KeyBinding::new("cmd-a", ActionB, Some("child-1")),
KeyBinding::new("cmd-c", ActionC, None),
]);
let theme = rose_pine();
@ -63,6 +81,7 @@ impl FocusStory {
let child_2 = cx.focus_handle();
view(cx.entity(|cx| ()), move |_, cx| {
div()
.context("parent")
.on_action(|_, action: &ActionA, phase, cx| {
println!("Action A dispatched on parent during {:?}", phase);
})
@ -86,7 +105,8 @@ impl FocusStory {
.focus_in(|style| style.bg(color_3))
.child(
div()
.id("child 1")
.id("child-1")
.context("child-1")
.on_action(|_, action: &ActionA, phase, cx| {
println!("Action A dispatched on child 1 during {:?}", phase);
})
@ -110,7 +130,8 @@ impl FocusStory {
)
.child(
div()
.id("child 2")
.id("child-2")
.context("child-2")
.on_action(|_, action: &ActionB, phase, cx| {
println!("Action B dispatched on child 2 during {:?}", phase);
})

View file

@ -1,16 +1,24 @@
use std::{
borrow::Cow,
fmt::{self, Debug},
hash::{Hash, Hasher},
sync::Arc,
};
#[derive(PartialEq, Eq)]
pub enum ArcCow<'a, T: ?Sized> {
Borrowed(&'a T),
Owned(Arc<T>),
}
use std::hash::{Hash, Hasher};
impl<'a, T: ?Sized + PartialEq> PartialEq for ArcCow<'a, T> {
fn eq(&self, other: &Self) -> bool {
let a = self.as_ref();
let b = other.as_ref();
a == b
}
}
impl<'a, T: ?Sized + Eq> Eq for ArcCow<'a, T> {}
impl<'a, T: ?Sized + Hash> Hash for ArcCow<'a, T> {
fn hash<H: Hasher>(&self, state: &mut H) {