Checkpoint
This commit is contained in:
parent
b0acaed02f
commit
8a11053f1f
4 changed files with 104 additions and 27 deletions
|
@ -15,14 +15,47 @@ 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 {
|
||||
self.set.is_empty() && self.map.is_empty()
|
||||
}
|
||||
|
@ -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() {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
})
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue