diff --git a/.old/tek.rs.old b/.old/tek.rs.old
index 8c0fd9d1..e64fd51b 100644
--- a/.old/tek.rs.old
+++ b/.old/tek.rs.old
@@ -1907,3 +1907,207 @@ from_edn!("mixer/track" => |jack: &Arc<RwLock<JackConnection>>, args| -> MixerTr
         //})
     //}
 //}
+use std::sync::{Arc, RwLock};
+use std::collections::BTreeMap;
+pub use clojure_reader::edn::Edn;
+
+//#[derive(Debug, Copy, Clone, Default, PartialEq)]
+//pub struct Items<'a>(&'a [Item<'a>]);
+//impl<'a> Items<'a> {
+    //fn iter (&'a self) -> ItemsIterator<'a> {
+        //ItemsIterator(0, self.0)
+    //}
+//}
+
+//pub struct ItemsIterator<'a>(usize, &'a [Item<'a>]);
+//impl<'a> Iterator for ItemsIterator<'a> {
+    //type Item = &'a Item<'a>;
+    //fn next (&mut self) -> Option<Self::Item> {
+        //let item = self.1.get(self.0);
+        //self.0 += 1;
+        //item
+    //}
+//}
+
+/*
+
+nice but doesn't work without compile time slice concat
+(which i guess could be implemeted using an unsafe linked list?)
+never done that one before im ny life, might try
+
+use konst::slice_concat;
+
+const fn read <'a> (
+    chars: impl Iterator<Item = char>
+) -> Result<Range<'a>, ParseError> {
+    use Range::*;
+    let mut state = Range::Nil;
+    let mut tokens: &[Range<'a>] = &[];
+    while let Some(c) = chars.next() {
+        state = match state {
+            // must begin expression
+            Nil => match c {
+                ' ' => Nil,
+                '(' => Exp(&[]),
+                ':' => Sym(&[]),
+                '1'..'9' => Num(digit(c)),
+                'a'..'z' => Key(&[&[c]]),
+                _ => return Err(ParseError::Unexpected(c))
+            },
+            Num(b) => match c {
+                ' ' => return Ok(Num(digit(c))),
+                '1'..'9' => Num(b*10+digit(c)),
+                _ => return Err(ParseError::Unexpected(c))
+            }
+            Sym([]) => match c {
+                'a'..'z' => Sym(&[c]),
+                _ => return Err(ParseError::Unexpected(c))
+            },
+            Sym([b @ ..]) => match c {
+                ' ' => return Ok(Sym(&b)),
+                'a'..'z' | '0'..'9' | '-' => Sym(&[..b, c]),
+                _ => return Err(ParseError::Unexpected(c))
+            }
+            Key([[b @ ..]]) => match c {
+                ' ' => return Ok(Key(&[&b])),
+                '/' => Key(&[&b, &[]]),
+                'a'..'z' | '0'..'9' | '-' => Key(&[&[..b, c], &[]]),
+                _ => return Err(ParseError::Unexpected(c))
+            }
+            Key([s @ .., []]) => match c {
+                'a'..'z' => Key(&[..s, &[c]]),
+                _ => return Err(ParseError::Unexpected(c))
+            }
+            Key([s @ .., [b @ ..]]) => match c {
+                '/' => Key([..s, &b, &[]]),
+                'a'..'z' | '0'..'9' | '-' => Key(&[..s, &[..b, c]]),
+                _ => return Err(ParseError::Unexpected(c))
+            }
+            // expression must begin with key or symbol
+            Exp([]) => match c {
+                ' ' => Exp(&[]),
+                ')' => return Err(ParseError::Empty),
+                ':' => Exp(&[Sym(&[':'])]),
+                 c  => Exp(&[Key(&[&[c]])]),
+            },
+
+            // expression can't begin with number
+            Exp([Num(num)]) => return Err(ParseError::Unexpected(c)),
+
+            // symbol begins with : and lowercase a-z
+            Exp([Sym([':'])]) => match c {
+                'a'..'z' => Exp(&[Sym(&[':', c])]),
+                       _ => return Err(ParseError::Unexpected(c)),
+            },
+
+            // any other char is part of symbol until space or )
+            Exp([Sym([':', b @ ..])]) => match c {
+                ')' => { tokens = &[..tokens, Exp(&[Sym(&[":", ..b])])]; Nil },
+                ' ' => Exp(&[Sym(&[':', ..b]), Nil]),
+                 c  => Exp(&[Sym(&[':', ..b, c])]),
+            },
+
+            // key begins with lowercase a-z
+            Exp([Key([])]) => match c {
+                'a'..'z' => Exp([Key([[c]])]),
+                       _ => return Err(ParseError::Unexpected(c)),
+            },
+
+            // any other char is part of key until slash space or )
+            Exp([Key([[b @ ..]])]) => match c {
+                '/' => Exp(&[Key(&[[..b], []])]),
+                ' ' => Exp(&[Key(&[[..b]]), Nil]),
+                ')' => { tokens = &[..tokens, Exp(&[Sym(&[":", ..b])])]; Nil },
+                 c  => Exp(&[Key(&[[..b, c]])])
+            }
+
+            // slash adds new section to key
+            Exp([Key([b @ .., []])]) => match c {
+                '/' => Exp(&[Key(&[[..b], []])]),
+                ' ' => Exp(&[Key(&[[..b]]), Nil]),
+                ')' => { tokens = &[..tokens, Exp(&[Sym(&[":", ..b])])]; Nil },
+                 c  => Exp(&[Key(&[[..b, c]])])
+            }
+
+        }
+    }
+    Ok(state)
+}
+*/
+
+
+/// EDN parsing helper.
+#[macro_export] macro_rules! edn {
+    ($edn:ident { $($pat:pat => $expr:expr),* $(,)? }) => {
+        match $edn { $($pat => $expr),* }
+    };
+    ($edn:ident in $args:ident { $($pat:pat => $expr:expr),* $(,)? }) => {
+        for $edn in $args {
+            edn!($edn { $($pat => $expr),* })
+        }
+    };
+}
+
+pub trait FromEdn<C>: Sized {
+    const ID: &'static str;
+    fn from_edn (context: C, expr: &[Edn<'_>]) ->
+        std::result::Result<Self, Box<dyn std::error::Error>>;
+}
+
+/// Implements the [FromEdn] trait.
+#[macro_export] macro_rules! from_edn {
+    ($id:expr => |$context:tt:$Context:ty, $args:ident| -> $T:ty $body:block) => {
+        impl FromEdn<$Context> for $T {
+            const ID: &'static str = $id;
+            fn from_edn <'e> ($context: $Context, $args: &[Edn<'e>]) -> Usually<Self> {
+                $body
+            }
+        }
+    }
+}
+use crate::*;
+use std::sync::Arc;
+pub enum EdnIterator {
+    Nil,
+    Sym(Arc<str>),
+    Exp(Vec<EdnIterator>)
+}
+impl EdnIterator {
+    pub fn new (item: &EdnItem) -> Self {
+        use EdnItem::*;
+        match item {
+            Sym(t) => Self::Sym(t.clone()),
+            Exp(i) => Self::Exp(i.iter().map(EdnIterator::new).collect()),
+            _ => Self::Nil,
+        }
+    }
+}
+impl Iterator for EdnIterator {
+    type Item = EdnItem;
+    fn next (&mut self) -> Option<Self::Item> {
+        use EdnIterator::*;
+        match self {
+            Sym(t) => {
+                let t = *t;
+                *self = Nil;
+                Some(Sym(t))
+            },
+            Exp(v) => match v.as_mut_slice() {
+                [a] => if let Some(next) = a.next() {
+                    Some(next)
+                } else {
+                    *self = Exp(v.split_off(1));
+                    self.next()
+                },
+                _ => {
+                    *self = Nil;
+                    None
+                }
+            },
+            _ => {
+                *self = Nil;
+                None
+            }
+        }
+    }
+}
diff --git a/edn/README.md b/edn/README.md
index 360886b6..fda6c354 100644
--- a/edn/README.md
+++ b/edn/README.md
@@ -69,7 +69,7 @@ keys are implemented by the underlying subsystem:
 ### symbols
 
 symbols that start with `:` represent the names of variables
-provided by the `EdnProvide` trait - such as command parameters,
+provided by the `Context` trait - such as command parameters,
 or entire layout components:
 
 ```edn
diff --git a/edn/src/_edn_scratch.rs b/edn/src/_edn_scratch.rs
deleted file mode 100644
index c6e9d9f5..00000000
--- a/edn/src/_edn_scratch.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-use std::sync::{Arc, RwLock};
-use std::collections::BTreeMap;
-pub use clojure_reader::edn::Edn;
-
-//#[derive(Debug, Copy, Clone, Default, PartialEq)]
-//pub struct Items<'a>(&'a [Item<'a>]);
-//impl<'a> Items<'a> {
-    //fn iter (&'a self) -> ItemsIterator<'a> {
-        //ItemsIterator(0, self.0)
-    //}
-//}
-
-//pub struct ItemsIterator<'a>(usize, &'a [Item<'a>]);
-//impl<'a> Iterator for ItemsIterator<'a> {
-    //type Item = &'a Item<'a>;
-    //fn next (&mut self) -> Option<Self::Item> {
-        //let item = self.1.get(self.0);
-        //self.0 += 1;
-        //item
-    //}
-//}
-
-/*
-
-nice but doesn't work without compile time slice concat
-(which i guess could be implemeted using an unsafe linked list?)
-never done that one before im ny life, might try
-
-use konst::slice_concat;
-
-const fn read <'a> (
-    chars: impl Iterator<Item = char>
-) -> Result<Range<'a>, ParseError> {
-    use Range::*;
-    let mut state = Range::Nil;
-    let mut tokens: &[Range<'a>] = &[];
-    while let Some(c) = chars.next() {
-        state = match state {
-            // must begin expression
-            Nil => match c {
-                ' ' => Nil,
-                '(' => Exp(&[]),
-                ':' => Sym(&[]),
-                '1'..'9' => Num(digit(c)),
-                'a'..'z' => Key(&[&[c]]),
-                _ => return Err(ParseError::Unexpected(c))
-            },
-            Num(b) => match c {
-                ' ' => return Ok(Num(digit(c))),
-                '1'..'9' => Num(b*10+digit(c)),
-                _ => return Err(ParseError::Unexpected(c))
-            }
-            Sym([]) => match c {
-                'a'..'z' => Sym(&[c]),
-                _ => return Err(ParseError::Unexpected(c))
-            },
-            Sym([b @ ..]) => match c {
-                ' ' => return Ok(Sym(&b)),
-                'a'..'z' | '0'..'9' | '-' => Sym(&[..b, c]),
-                _ => return Err(ParseError::Unexpected(c))
-            }
-            Key([[b @ ..]]) => match c {
-                ' ' => return Ok(Key(&[&b])),
-                '/' => Key(&[&b, &[]]),
-                'a'..'z' | '0'..'9' | '-' => Key(&[&[..b, c], &[]]),
-                _ => return Err(ParseError::Unexpected(c))
-            }
-            Key([s @ .., []]) => match c {
-                'a'..'z' => Key(&[..s, &[c]]),
-                _ => return Err(ParseError::Unexpected(c))
-            }
-            Key([s @ .., [b @ ..]]) => match c {
-                '/' => Key([..s, &b, &[]]),
-                'a'..'z' | '0'..'9' | '-' => Key(&[..s, &[..b, c]]),
-                _ => return Err(ParseError::Unexpected(c))
-            }
-            // expression must begin with key or symbol
-            Exp([]) => match c {
-                ' ' => Exp(&[]),
-                ')' => return Err(ParseError::Empty),
-                ':' => Exp(&[Sym(&[':'])]),
-                 c  => Exp(&[Key(&[&[c]])]),
-            },
-
-            // expression can't begin with number
-            Exp([Num(num)]) => return Err(ParseError::Unexpected(c)),
-
-            // symbol begins with : and lowercase a-z
-            Exp([Sym([':'])]) => match c {
-                'a'..'z' => Exp(&[Sym(&[':', c])]),
-                       _ => return Err(ParseError::Unexpected(c)),
-            },
-
-            // any other char is part of symbol until space or )
-            Exp([Sym([':', b @ ..])]) => match c {
-                ')' => { tokens = &[..tokens, Exp(&[Sym(&[":", ..b])])]; Nil },
-                ' ' => Exp(&[Sym(&[':', ..b]), Nil]),
-                 c  => Exp(&[Sym(&[':', ..b, c])]),
-            },
-
-            // key begins with lowercase a-z
-            Exp([Key([])]) => match c {
-                'a'..'z' => Exp([Key([[c]])]),
-                       _ => return Err(ParseError::Unexpected(c)),
-            },
-
-            // any other char is part of key until slash space or )
-            Exp([Key([[b @ ..]])]) => match c {
-                '/' => Exp(&[Key(&[[..b], []])]),
-                ' ' => Exp(&[Key(&[[..b]]), Nil]),
-                ')' => { tokens = &[..tokens, Exp(&[Sym(&[":", ..b])])]; Nil },
-                 c  => Exp(&[Key(&[[..b, c]])])
-            }
-
-            // slash adds new section to key
-            Exp([Key([b @ .., []])]) => match c {
-                '/' => Exp(&[Key(&[[..b], []])]),
-                ' ' => Exp(&[Key(&[[..b]]), Nil]),
-                ')' => { tokens = &[..tokens, Exp(&[Sym(&[":", ..b])])]; Nil },
-                 c  => Exp(&[Key(&[[..b, c]])])
-            }
-
-        }
-    }
-    Ok(state)
-}
-*/
-
-
-/// EDN parsing helper.
-#[macro_export] macro_rules! edn {
-    ($edn:ident { $($pat:pat => $expr:expr),* $(,)? }) => {
-        match $edn { $($pat => $expr),* }
-    };
-    ($edn:ident in $args:ident { $($pat:pat => $expr:expr),* $(,)? }) => {
-        for $edn in $args {
-            edn!($edn { $($pat => $expr),* })
-        }
-    };
-}
-
-pub trait FromEdn<C>: Sized {
-    const ID: &'static str;
-    fn from_edn (context: C, expr: &[Edn<'_>]) ->
-        std::result::Result<Self, Box<dyn std::error::Error>>;
-}
-
-/// Implements the [FromEdn] trait.
-#[macro_export] macro_rules! from_edn {
-    ($id:expr => |$context:tt:$Context:ty, $args:ident| -> $T:ty $body:block) => {
-        impl FromEdn<$Context> for $T {
-            const ID: &'static str = $id;
-            fn from_edn <'e> ($context: $Context, $args: &[Edn<'e>]) -> Usually<Self> {
-                $body
-            }
-        }
-    }
-}
diff --git a/edn/src/describe.rs b/edn/src/describe.rs
deleted file mode 100644
index 100382fd..00000000
--- a/edn/src/describe.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use crate::*;
-
-pub trait TryFromEdn<'a, T>: Sized {
-    fn try_from_edn (state: &'a T, head: &Atom<impl AsRef<str>>, tail: &'a [Atom<impl AsRef<str>>]) ->
-        Option<Self>;
-}
-
-pub trait TryIntoEdn<'a, T>: Sized {
-    fn try_from_edn (state: &'a T, head: &Atom<impl AsRef<str>>, tail: &'a [Atom<impl AsRef<str>>]) ->
-        Option<Self>;
-}
diff --git a/edn/src/edn_iter.rs b/edn/src/edn_iter.rs
deleted file mode 100644
index 6ef3c025..00000000
--- a/edn/src/edn_iter.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-use crate::*;
-use std::sync::Arc;
-pub enum EdnIterator {
-    Nil,
-    Sym(Arc<str>),
-    Exp(Vec<EdnIterator>)
-}
-impl EdnIterator {
-    pub fn new (item: &EdnItem) -> Self {
-        use EdnItem::*;
-        match item {
-            Sym(t) => Self::Sym(t.clone()),
-            Exp(i) => Self::Exp(i.iter().map(EdnIterator::new).collect()),
-            _ => Self::Nil,
-        }
-    }
-}
-impl Iterator for EdnIterator {
-    type Item = EdnItem;
-    fn next (&mut self) -> Option<Self::Item> {
-        use EdnIterator::*;
-        match self {
-            Sym(t) => {
-                let t = *t;
-                *self = Nil;
-                Some(Sym(t))
-            },
-            Exp(v) => match v.as_mut_slice() {
-                [a] => if let Some(next) = a.next() {
-                    Some(next)
-                } else {
-                    *self = Exp(v.split_off(1));
-                    self.next()
-                },
-                _ => {
-                    *self = Nil;
-                    None
-                }
-            },
-            _ => {
-                *self = Nil;
-                None
-            }
-        }
-    }
-}
diff --git a/edn/src/lib.rs b/edn/src/lib.rs
index 38b92b92..2087dc1e 100644
--- a/edn/src/lib.rs
+++ b/edn/src/lib.rs
@@ -1,8 +1,6 @@
 #![feature(type_alias_impl_trait)]
 #![feature(impl_trait_in_fn_trait_return)]
-mod token;    pub use self::token::*;
-mod provide;  pub use self::provide::*;
-mod describe; pub use self::describe::*;
+mod token; pub use self::token::*;
 pub(crate) use std::fmt::{Debug, Display, Formatter, Error as FormatError};
 pub(crate) use std::sync::Arc;
 #[cfg(test)] #[test] fn test_lang () -> Result<(), ParseError> {
@@ -32,10 +30,18 @@ pub(crate) use std::sync::Arc;
     ] {
         let items = Atom::read_all(example)?;
         //panic!("{layout:?}");
-        //let content = <dyn EdnViewData<::tek_engine::tui::Tui>>::from(&layout);
+        //let content = <dyn ViewContext<::tek_engine::tui::Tui>>::from(&layout);
     }
     Ok(())
 }
-#[macro_export] macro_rules! from_edn {
-    ($($x:tt)*) => {}
+#[cfg(test)] #[test] fn test_token () -> Result<(), Box<dyn std::error::Error>> {
+    use Token::*;
+    assert_eq!(Nil, Token::chomp_one("")?);
+    assert_eq!(Nil, Token::chomp_one("  \n    \r   \t  ")?);
+    assert_eq!(Num("8", 0, 1), Token::chomp_one("8")?);
+    assert_eq!(Num("   8     ", 3, 1), Token::chomp_one("   8     ")?);
+    assert_eq!(Sym(":foo", 0, 4), Token::chomp_one(":foo")?);
+    assert_eq!(Sym("@bar", 0, 4), Token::chomp_one("@bar")?);
+    assert_eq!(Key("foo/bar", 0, 7), Token::chomp_one("foo/bar")?);
+    Ok(())
 }
diff --git a/edn/src/provide.rs b/edn/src/provide.rs
deleted file mode 100644
index 34914b9b..00000000
--- a/edn/src/provide.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-use crate::*;
-/// Map EDN tokens to parameters of a given type for a given context
-pub trait EdnProvide<'a, U>: Sized {
-    fn get (&'a self, _edn: &'a Atom<impl AsRef<str>>) -> Option<U> {
-        None
-    }
-    fn get_or_fail (&'a self, edn: &'a Atom<impl AsRef<str>>) -> U {
-        self.get(edn).expect("no value")
-    }
-}
-impl<'a, T: EdnProvide<'a, U>, U> EdnProvide<'a, U> for &T {
-    fn get (&'a self, edn: &'a Atom<impl AsRef<str>>) -> Option<U> {
-        (*self).get(edn)
-    }
-    fn get_or_fail (&'a self, edn: &'a Atom<impl AsRef<str>>) -> U {
-        (*self).get_or_fail(edn)
-    }
-}
-impl<'a, T: EdnProvide<'a, U>, U> EdnProvide<'a, U> for Option<T> {
-    fn get (&'a self, edn: &'a Atom<impl AsRef<str>>) -> Option<U> {
-        self.as_ref().map(|s|s.get(edn)).flatten()
-    }
-    fn get_or_fail (&'a self, edn: &'a Atom<impl AsRef<str>>) -> U {
-        self.as_ref().map(|s|s.get_or_fail(edn)).expect("no provider")
-    }
-}
-/// Implement `EdnProvide` for a context and type.
-#[macro_export] macro_rules! edn_provide {
-    // Provide a value to the EDN template
-    ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
-        impl<'a> EdnProvide<'a, $type> for $State {
-            fn get (&'a $self, edn: &'a Atom<impl AsRef<str>>) -> Option<$type> {
-                use Atom::*;
-                Some(match edn.to_ref() { $(Sym($pat) => $expr,)* _ => return None })
-            }
-        }
-    };
-    // Provide a value more generically
-    ($lt:lifetime: $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
-        impl<$lt> EdnProvide<$lt, $type> for $State {
-            fn get (&$lt $self, edn: &$lt Atom<impl AsRef<str>>) -> Option<$type> {
-                use Atom::*;
-                Some(match edn.to_ref() { $(Sym($pat) => $expr,)* _ => return None })
-            }
-        }
-    };
-}
-/// Implement `EdnProvide` for a context and numeric type.
-///
-/// This enables support for numeric literals.
-#[macro_export] macro_rules! edn_provide_num {
-    // Provide a value that may also be a numeric literal in the EDN, to a generic implementation.
-    ($type:ty:|$self:ident:<$T:ident:$Trait:path>|{ $($pat:pat => $expr:expr),* $(,)? }) => {
-        impl<'a, $T: $Trait> EdnProvide<'a, $type> for $T {
-            fn get (&'a $self, edn: &'a Atom<impl AsRef<str>>) -> Option<$type> {
-                use Atom::*;
-                Some(match edn.to_ref() { $(Sym($pat) => $expr,)* Num(n) => n as $type, _ => return None })
-            }
-        }
-    };
-    // Provide a value that may also be a numeric literal in the EDN, to a concrete implementation.
-    ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
-        impl<'a> EdnProvide<'a, $type> for $State {
-            fn get (&'a $self, edn: &'a Atom<impl AsRef<str>>) -> Option<$type> {
-                use Atom::*;
-                Some(match edn.to_ref() { $(Sym($pat) => $expr,)* Num(n) => n as $type, _ => return None })
-            }
-        }
-    };
-}
-/// Implement `EdnProvide` for a context and content type.
-///
-/// This enables support for layout expressions.
-#[macro_export] macro_rules! edn_provide_content {
-    (|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
-        impl<'a, E: Output> EdnProvide<'a, Box<dyn Render<E> + 'a>> for $State {
-            fn get (&'a $self, edn: &'a Atom<impl AsRef<str>>) -> Option<Box<dyn Render<E> + 'a>> {
-                Some(match edn.to_ref() {
-                    $(Atom::Sym($pat) => $expr),*,
-                    _ => return None
-                })
-            }
-        }
-    };
-    ($Output:ty: |$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
-        impl<'a> EdnProvide<'a, Box<dyn Render<$Output> + 'a>> for $State {
-            fn get (&'a $self, edn: &'a Atom<impl AsRef<str>>) -> Option<Box<dyn Render<$Output> + 'a>> {
-                Some(match edn.to_ref() {
-                    $(Atom::Sym($pat) => $expr),*,
-                    _ => return None
-                })
-            }
-        }
-    }
-}
diff --git a/edn/src/token.rs b/edn/src/token.rs
index 975b234d..e860813b 100644
--- a/edn/src/token.rs
+++ b/edn/src/token.rs
@@ -1,9 +1,14 @@
 use crate::*;
+//use std::iter::IntoIterator;
 use konst::iter::{ConstIntoIter, IsIteratorKind};
 use konst::string::{split_at, str_range, char_indices};
+use itertools::join;
 use self::ParseError::*;
 use self::TokenKind::*;
-#[derive(Debug)] pub enum ParseError { Unimplemented, Empty, Incomplete, Unexpected(char), Code(u8), }
+type TokenResult<'a> = Result<Token<'a>, ParseError>;
+#[derive(Debug)] pub enum ParseError {
+    Unimplemented, Empty, Incomplete, Unexpected(char), Code(u8),
+}
 impl std::fmt::Display for ParseError {
     fn fmt (&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
         match self {
@@ -16,7 +21,6 @@ impl std::fmt::Display for ParseError {
     }
 }
 impl std::error::Error for ParseError {}
-/// Iterator helper macro because I can't find the canonical one in [konst] docs.
 macro_rules! iterate {
     ($expr:expr => $arg: pat => $body:expr) => {
         let mut iter = $expr;
@@ -26,7 +30,11 @@ macro_rules! iterate {
         }
     }
 }
-pub struct TokenIterator<'a>(&'a str);
+#[derive(Clone, PartialEq)] pub struct TokenIterator<'a>(&'a str);
+impl<'a> Iterator for TokenIterator<'a> {
+    type Item = TokenResult<'a>;
+    fn next (&mut self) -> Option<TokenResult<'a>> { self.next_mut().map(|(result, _)|result) }
+}
 impl<'a> ConstIntoIter for TokenIterator<'a> {
     type Kind = IsIteratorKind;
     type Item = Token<'a>;
@@ -34,16 +42,18 @@ impl<'a> ConstIntoIter for TokenIterator<'a> {
 }
 impl<'a> TokenIterator<'a> {
     pub const fn new (source: &'a str) -> Self { Self(source) }
-    pub const fn split (self, index: usize) -> Self { Self(split_at(self.0, index).1) }
-    pub const fn next (self) -> Option<(Result<Token<'a>, ParseError>, Self)> {
-        let src = self.0;
-        let mut token: Token<'a> = Token::new(src, Nil, 0, 0, 0);
-        iterate!(char_indices(src) => (index, c) => token = match token.kind() {
+    pub const fn split (&self, index: usize) -> Self { Self(split_at(self.0, index).1) }
+    pub const fn next (mut self) -> Option<(TokenResult<'a>, Self)> {
+        Self::next_mut(&mut self)
+    }
+    pub const fn next_mut (&mut self) -> Option<(TokenResult<'a>, Self)> {
+        let mut token: Token<'a> = Token::new(self.0, Nil, 0, 0, 0);
+        iterate!(char_indices(self.0) => (index, c) => token = match token.kind() {
             Nil => match c {
-                '('                => Token::new(src, Exp, index, 1, 1),
-                ':'|'@'            => Token::new(src, Sym, index, 1, 0),
-                '0'..='9'          => Token::new(src, Num, index, 1, 0),
-                '/'|'a'..='z'      => Token::new(src, Key, index, 1, 0),
+                '('                => Token::new(self.0, Exp, index, 1, 1),
+                ':'|'@'            => Token::new(self.0, Sym, index, 1, 0),
+                '0'..='9'          => Token::new(self.0, Num, index, 1, 0),
+                '/'|'a'..='z'      => Token::new(self.0, Key, index, 1, 0),
                 ' '|'\n'|'\r'|'\t' => token.grow(),
                 _ => return Some((Err(Unexpected(c)), self.split(token.end())))
             },
@@ -63,7 +73,7 @@ impl<'a> TokenIterator<'a> {
                 _ => return Some((Err(Unexpected(c)), self.split(token.end())))
             },
             Exp => match token.depth {
-                0 => return Some((Ok(token), Self(split_at(src, token.end()).1))),
+                0 => return Some((Ok(token), Self(split_at(self.0, token.end()).1))),
                 _ => match c {
                     ')' => match token.grow_out() {
                         Ok(token) => token,
@@ -77,31 +87,10 @@ impl<'a> TokenIterator<'a> {
         match token.kind() { Nil => None, _ => Some((Err(ParseError::Incomplete), self.split(token.end()))) }
     }
 }
-pub struct AtomIterator<'a>(TokenIterator<'a>);
-impl<'a> ConstIntoIter for AtomIterator<'a> {
-    type Kind = IsIteratorKind;
-    type Item = Atom<&'a str>;
-    type IntoIter = Self;
+#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum TokenKind {
+    #[default] Nil, Num, Sym, Key, Exp
 }
-impl<'a> AtomIterator<'a> {
-    pub const fn new (tokens: TokenIterator<'a>) -> Self { Self(tokens) }
-    pub const fn next (mut self) -> Option<(Result<Atom<&'a str>, ParseError>, Self)> {
-        match self.0.next() {
-            None => None,
-            Some((result, next)) => match result {
-                Err(e) => Some((Err(e), Self(next))),
-                Ok(token) => match token.to_ref_atom() {
-                    Err(e) => Some((Err(e), Self(next))),
-                    Ok(atom) => Some((Ok(atom), Self(next))),
-                }
-            }
-        }
-    }
-}
-#[derive(Debug, Copy, Clone, Default, PartialEq)]
-pub enum TokenKind { #[default] Nil, Num, Sym, Key, Exp }
-#[derive(Debug, Copy, Clone, Default, PartialEq)]
-pub struct Token<'a> {
+#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'a> {
     source: &'a str,
     kind:   TokenKind,
     start:  usize,
@@ -115,7 +104,13 @@ impl<'a> Token<'a> {
         Self { source, kind, start, length, depth }
     }
     pub const fn end (&self) -> usize { self.start + self.length }
-    pub const fn slice (&self) -> &str { str_range(self.source, self.start, self.end()) }
+    pub const fn slice (&'a self) -> &'a str {
+        self.slice_source(self.source)
+        //str_range(self.source, self.start, self.end())
+    }
+    pub const fn slice_source <'b> (&'a self, source: &'b str) -> &'b str {
+        str_range(source, self.start, self.end())
+    }
     pub const fn kind (&self) -> TokenKind { Nil }
     pub const fn grow (self) -> Self {
         Self { length: self.length + 1, ..self }
@@ -129,27 +124,15 @@ impl<'a> Token<'a> {
             d => Ok(Self { length: self.length + 1, depth: d - 1, ..self })
         }
     }
-    pub const fn to_ref_atom (&'a self) -> Result<Atom<&'a str>, ParseError> {
+    pub const fn to_ref_atom (&'a self) -> Result<RefAtom<'a>, ParseError> {
         Ok(match self.kind {
             Nil => return Err(ParseError::Empty),
             Num => match to_number(self.slice()) {
-                Ok(n) => Atom::Num(n),
+                Ok(n) => RefAtom::Num(n),
                 Err(e) => return Err(e)
             },
-            Sym => Atom::Sym(self.slice()),
-            Key => Atom::Key(self.slice()),
-            Exp => todo!()
-        })
-    }
-    pub fn to_arc_atom (&self) -> Result<Atom<Arc<str>>, ParseError> {
-        Ok(match self.kind {
-            Nil => return Err(ParseError::Empty),
-            Num => match to_number(self.slice()) {
-                Ok(n) => Atom::Num(n),
-                Err(e) => return Err(e)
-            },
-            Sym => Atom::Sym(self.slice().into()),
-            Key => Atom::Key(self.slice().into()),
+            Sym => RefAtom::Sym(self.slice()),
+            Key => RefAtom::Key(self.slice()),
             Exp => todo!()
         })
     }
@@ -169,156 +152,225 @@ const fn to_digit (c: char) -> Result<usize, ParseError> {
         _ => return Err(Unexpected(c))
     })
 }
-#[derive(Clone, PartialEq)] pub enum Atom<T> { Num(usize), Sym(T), Key(T), Exp(Vec<Atom<T>>) }
-impl<'a, T: 'a> Atom<T> {
-    pub fn transform <U: 'a, F: Fn(&'a T)->U + Clone> (&'a self, f: F) -> Atom<U> {
-        use Atom::*;
+pub trait Atom<'a>: Sized {
+    fn kind (&self) -> TokenKind;
+    fn text (&self) -> &str;
+    fn read_all (source: &'a str) -> impl Iterator<Item = Result<Self, ParseError>>;
+}
+#[derive(Clone, PartialEq)] pub enum RefAtom<'a> {
+    Num(usize),
+    Sym(&'a str),
+    Key(&'a str),
+    Exp(TokenIterator<'a>),
+}
+impl<'a> RefAtom<'a> {
+    pub fn to_arc_atom (&self) -> ArcAtom {
+        todo!()
+    }
+}
+impl<'a> Atom<'a> for RefAtom<'a> {
+    fn kind (&self) -> TokenKind {
         match self {
-            Num(n) => Num(*n),
-            Sym(t) => Sym(f(t)),
-            Key(t) => Key(f(t)),
-            Exp(e) => Exp(e.iter().map(|i|i.transform(f.clone())).collect())
+            Self::Num(_) => TokenKind::Num,
+            Self::Sym(_) => TokenKind::Sym,
+            Self::Key(_) => TokenKind::Key,
+            Self::Exp(_) => TokenKind::Exp,
         }
     }
-}
-impl<'a, T: AsRef<str>> Atom<T> {
-    pub fn to_ref (&'a self) -> Atom<&'a str> {
-        self.transform(|t|t.as_ref())
+    fn text (&self) -> &str {
+        match self {
+            Self::Num(_)|Self::Exp(_) => "",
+            Self::Sym(s) => s,
+            Self::Key(k) => k,
+        }
     }
-    pub fn to_arc (&'a self) -> Atom<Arc<str>> {
-        self.transform(|t|t.as_ref().into())
+    fn read_all (source: &'a str) -> impl Iterator<Item = Result<Self, ParseError>> {
+        TokenIterator::new(source).map(move |result: TokenResult<'a>|match result{
+            Err(e) => Err(e),
+            Ok(token) => match token.kind {
+                Nil => Err(ParseError::Empty),
+                Num => match to_number(token.slice_source(source)) {
+                    Ok(n) => Ok(RefAtom::Num(n)),
+                    Err(e) => return Err(e)
+                },
+                Sym => Ok(RefAtom::Sym(token.slice_source(source))),
+                Key => Ok(RefAtom::Key(token.slice_source(source))),
+                Exp => Ok(RefAtom::Exp(TokenIterator::new(token.slice_source(source))))
+            },
+        })
     }
 }
-impl<'a> Atom<&'a str> {
-    pub const fn read_all_ref (_: &'a str) -> Result<Vec<Self>, ParseError> {
-        Err(Unimplemented)
-    }
-}
-impl<T: Debug> Debug for Atom<T> {
+impl<'a> Debug for RefAtom<'a> {
     fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> {
-        use Atom::*;
         match self {
-            Num(u) => write!(f, "(num {u})"),
-            Sym(u) => write!(f, "(sym {u:?})"),
-            Key(u) => write!(f, "(key {u:?})"),
-            Exp(e) => write!(f, "(exp {})",
-                itertools::join(e.iter().map(|i|format!("{:?}", i)), ","))
+            RefAtom::Num(n) => write!(f, "(num {n})"),
+            RefAtom::Sym(t) => write!(f, "(sym {t:?})"),
+            RefAtom::Key(t) => write!(f, "(key {t:?})"),
+            RefAtom::Exp(u) => write!(f, "(exp {})", join(u.clone().map(|t|format!("{t:?}")), ","))
         }
     }
 }
-impl<T: Display> Display for Atom<T> {
+#[derive(Clone, PartialEq)] pub enum ArcAtom {
+    Num(usize),
+    Sym(Arc<str>),
+    Key(Arc<str>),
+    Exp(Vec<ArcAtom>),
+}
+impl<'a> Atom<'a> for ArcAtom {
+    fn kind (&self) -> TokenKind {
+        match self {
+            Self::Num(_) => TokenKind::Num,
+            Self::Sym(_) => TokenKind::Sym,
+            Self::Key(_) => TokenKind::Key,
+            Self::Exp(_) => TokenKind::Exp,
+        }
+    }
+    fn text (&self) -> &str {
+        match self {
+            Self::Num(_)|Self::Exp(_) => "",
+            Self::Sym(s) => s.as_ref(),
+            Self::Key(k) => k.as_ref(),
+        }
+    }
+    fn read_all (source: &'a str) -> impl Iterator<Item = Result<Self, ParseError>> {
+        TokenIterator::new(source).map(move |result: TokenResult<'a>|match result{
+            Err(e) => Err(e),
+            Ok(token) => match token.kind {
+                Nil => Err(ParseError::Empty),
+                Num => match to_number(token.slice_source(source).into()) {
+                    Ok(n) => Ok(ArcAtom::Num(n)),
+                    Err(e) => return Err(e)
+                },
+                Sym => Ok(ArcAtom::Sym(token.slice_source(source).into())),
+                Key => Ok(ArcAtom::Key(token.slice_source(source).into())),
+                Exp => Ok(ArcAtom::Exp({
+                    let mut iter = Self::read_all(token.slice_source(source));
+                    let mut atoms = vec![];
+                    while let Some(atom) = iter.next() {
+                        atoms.push(atom?);
+                    }
+                    atoms
+                }))
+            },
+        })
+    }
+}
+impl<'a> Debug for ArcAtom {
     fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> {
-        use Atom::*;
-        use itertools::join;
         match self {
-            Num(u) => write!(f, "{u}"),
-            Sym(u) => write!(f, "{u}"),
-            Key(u) => write!(f, "{u}"),
-            Exp(e) => write!(f, "({})", join(e.iter().map(|i|format!("{}", i)), " "))
+            Self::Num(n) => write!(f, "(num {n})"),
+            Self::Sym(t) => write!(f, "(sym {t:?})"),
+            Self::Key(t) => write!(f, "(key {t:?})"),
+            Self::Exp(u) => write!(f, "(exp {u:?})"),
         }
     }
 }
-//impl<'a> Token<'a> {
-    //pub const fn chomp_one (source: &'a str) -> Result<Token<'a>, ParseError> {
-        //match Self::chomp(source) {
-            //Ok((_, token)) => Ok(token),
-            //Err(e) => Err(e)
-        //}
-    //}
-    //pub const fn from_nil (c: char) -> Result<(&'a str, Token<'a>), ParseError> {
-        //match c {
-            //' '|'\n'|'\r'|'\t' => Nil,
-            //'('       => Exp(source, index, 1, 1),
-            //':'|'@'   => Sym(source, index, 1),
-            //'0'..='9' => Num(source, index, 1),
-            //'a'..='z' => Key(source, index, 1),
-            //_ => return Err(Unexpected(c))
-        //}
-    //}
-    //pub const fn chomp (source: &'a str) -> Result<(&'a str, Token<'a>), ParseError> {
-        //let mut state = Self::Nil;
-        //let mut chars = char_indices(source);
-        //while let Some(((index, c), next)) = chars.next() {
-            //state = match state {
-                //// must begin expression
-                //Nil => Self::from_nil(c),
-                //Num(_, _, 0) => unreachable!(),
-                //Sym(_, _, 0) => unreachable!(),
-                //Key(_, _, 0) => unreachable!(),
-                //Num(src, idx, len) => match c {
-                    //'0'..='9' => Num(src, idx, len + 1),
-                    //' '|'\n'|'\r'|'\t' => return Ok((split(src, idx+len).1, Num(src, idx, len))),
-                    //_ => return Err(Unexpected(c))
-                //},
-                //Sym(src, idx, len) => match c {
-                    //'a'..='z'|'0'..='9'|'-' => Sym(src, idx, len + 1),
-                    //' '|'\n'|'\r'|'\t' => return Ok((split(src, idx+len).1, Sym(src, idx, len))),
-                    //_ => return Err(Unexpected(c))
-                //},
-                //Key(src, idx, len) => match c {
-                    //'a'..='z'|'0'..='9'|'-'|'/' => Key(src, idx, len + 1),
-                    //' '|'\n'|'\r'|'\t' => return Ok((split(src, idx+len).1, Key(src, idx, len))),
-                    //_ => return Err(Unexpected(c))
-                //},
-                //Exp(src, idx, len, 0) => match c {
-                    //' '|'\n'|'\r'|'\t' => return Ok((split(src, idx+len).1, Exp(src, idx, len, 0))),
-                    //_ => return Err(Unexpected(c))
-                //},
-                //Exp(src, idx, len, balance) => match c {
-                    //')' => Exp(src, idx, len + 1, balance - 1),
-                    //'(' => Exp(src, idx, len + 1, balance + 1),
-                    //_ => Exp(src, idx, len + 1, balance)
-                //},
-            //};
-            //chars = next
-        //}
-        //Ok(("", state))
-    //}
-    //pub fn src (&self) -> &str {
-        //match self {
-            //Self::Nil => "",
-            //Self::Num(src, _, _) => src,
-            //Self::Sym(src, _, _) => src,
-            //Self::Key(src, _, _) => src,
-            //Self::Exp(src, _, _, _) => src,
-        //}
-    //}
-    //pub fn str (&self) -> &str {
-        //match self {
-            //Self::Nil => "",
-            //Self::Num(src, start, len) => &src[*start..start+len],
-            //Self::Sym(src, start, len) => &src[*start..start+len],
-            //Self::Key(src, start, len) => &src[*start..start+len],
-            //Self::Exp(src, start, len, 0) => &src[*start..(start+len)],
-            //Self::Exp(src, start, len, d) => panic!(
-                //"unclosed delimiter with depth {d} in:\n{}",
-                //&src[*start..(start+len)]
-            //)
-        //}
-    //}
-    //pub fn to_atom_ref (&'a self) -> Result<Atom<&'a str>, ParseError> {
-        //use Atom::*;
-        //Ok(match self {
-            //Token::Nil => Nil,
-            //Token::Num(_, _, _) => Num(Token::number(self.str())),
-            //Token::Sym(_, _, _) => Sym(self.str().into()),
-            //Token::Key(_, _, _) => Key(self.str().into()),
-            //Token::Exp(_, _, _, _) => Exp(match Atom::read_all_ref(self.str()) {
-                //Ok(exp) => exp,
-                //Err(e) => return Err(e)
-            //}),
-        //})
-    //}
-//}
+impl<'a> Display for ArcAtom {
+    fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> {
+        match self {
+            Self::Num(n) => write!(f, "{n}"),
+            Self::Sym(t) => write!(f, "{t}"),
+            Self::Key(t) => write!(f, "{t}"),
+            Self::Exp(u) => write!(f, "({})", join(u.iter().map(|i|format!("{}", &i)), " "))
+        }
+    }
+}
+/// Map EDN tokens to parameters of a given type for a given context
+pub trait Context<'a, U>: Sized {
+    fn get (&'a self, _edn: &'a impl Atom<'a>) -> Option<U> {
+        None
+    }
+    fn get_or_fail (&'a self, edn: &'a impl Atom<'a>) -> U {
+        self.get(edn).expect("no value")
+    }
+}
+impl<'a, T: Context<'a, U>, U> Context<'a, U> for &T {
+    fn get (&'a self, edn: &'a impl Atom<'a>) -> Option<U> {
+        (*self).get(edn)
+    }
+    fn get_or_fail (&'a self, edn: &'a impl Atom<'a>) -> U {
+        (*self).get_or_fail(edn)
+    }
+}
+impl<'a, T: Context<'a, U>, U> Context<'a, U> for Option<T> {
+    fn get (&'a self, edn: &'a impl Atom<'a>) -> Option<U> {
+        self.as_ref().map(|s|s.get(edn)).flatten()
+    }
+    fn get_or_fail (&'a self, edn: &'a impl Atom<'a>) -> U {
+        self.as_ref().map(|s|s.get_or_fail(edn)).expect("no provider")
+    }
+}
+/// Implement `Context` for a context and type.
+#[macro_export] macro_rules! edn_provide {
+    // Provide a value to the EDN template
+    ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
+        impl<'a> Context<'a, $type> for $State {
+            fn get (&'a $self, edn: &'a impl Atom<'a>) -> Option<$type> {
+                use RefAtom::*;
+                Some(match edn.to_ref() { $(Sym($pat) => $expr,)* _ => return None })
+            }
+        }
+    };
+    // Provide a value more generically
+    ($lt:lifetime: $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
+        impl<$lt> Context<$lt, $type> for $State {
+            fn get (&$lt $self, edn: &$lt impl Atom<'a>) -> Option<$type> {
+                use RefAtom::*;
+                Some(match edn.to_ref() { $(Sym($pat) => $expr,)* _ => return None })
+            }
+        }
+    };
+}
+/// Implement `Context` for a context and numeric type.
+///
+/// This enables support for numeric literals.
+#[macro_export] macro_rules! edn_provide_num {
+    // Provide a value that may also be a numeric literal in the EDN, to a generic implementation.
+    ($type:ty:|$self:ident:<$T:ident:$Trait:path>|{ $($pat:pat => $expr:expr),* $(,)? }) => {
+        impl<'a, $T: $Trait> Context<'a, $type> for $T {
+            fn get (&'a $self, edn: &'a impl Atom<'a>) -> Option<$type> {
+                use RefAtom::*;
+                Some(match edn.to_ref() { $(Sym($pat) => $expr,)* Num(n) => n as $type, _ => return None })
+            }
+        }
+    };
+    // Provide a value that may also be a numeric literal in the EDN, to a concrete implementation.
+    ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
+        impl<'a> Context<'a, $type> for $State {
+            fn get (&'a $self, edn: &'a impl Atom<'a>) -> Option<$type> {
+                use RefAtom::*;
+                Some(match edn.to_ref() { $(Sym($pat) => $expr,)* Num(n) => n as $type, _ => return None })
+            }
+        }
+    };
+}
+/// Implement `Context` for a context and content type.
+///
+/// This enables support for layout expressions.
+#[macro_export] macro_rules! edn_provide_content {
+    (|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
+        impl<'a, E: Output> Context<'a, Box<dyn Render<E> + 'a>> for $State {
+            fn get (&'a $self, edn: &'a impl Atom<'a>) -> Option<Box<dyn Render<E> + 'a>> {
+                use RefAtom::*;
+                Some(match edn.to_ref() { $(RefAtom::Sym($pat) => $expr),*, _ => return None })
+            }
+        }
+    };
+    ($Output:ty: |$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
+        impl<'a> Context<'a, Box<dyn Render<$Output> + 'a>> for $State {
+            fn get (&'a $self, edn: &'a impl Atom<'a>) -> Option<Box<dyn Render<$Output> + 'a>> {
+                use RefAtom::*;
+                Some(match edn.to_ref() { $(Sym($pat) => $expr),*, _ => return None })
+            }
+        }
+    }
+}
+pub trait TryFromEdn<'a, T>: Sized {
+    fn try_from_edn (state: &'a T, head: &impl Atom<'a>, tail: &'a [impl Atom<'a>]) ->
+        Option<Self>;
+}
 
-#[cfg(test)] #[test] fn test_edn_token () -> Result<(), Box<dyn std::error::Error>> {
-    use Token::*;
-    assert_eq!(Nil, Token::chomp_one("")?);
-    assert_eq!(Nil, Token::chomp_one("  \n    \r   \t  ")?);
-    assert_eq!(Num("8", 0, 1), Token::chomp_one("8")?);
-    assert_eq!(Num("   8     ", 3, 1), Token::chomp_one("   8     ")?);
-    assert_eq!(Sym(":foo", 0, 4), Token::chomp_one(":foo")?);
-    assert_eq!(Sym("@bar", 0, 4), Token::chomp_one("@bar")?);
-    assert_eq!(Key("foo/bar", 0, 7), Token::chomp_one("foo/bar")?);
-    Ok(())
+pub trait TryIntoEdn<'a, T>: Sized {
+    fn try_from_edn (state: &'a T, head: &impl Atom<'a>, tail: &'a [impl Atom<'a>]) ->
+        Option<Self>;
 }
diff --git a/input/src/keymap.rs b/input/src/keymap.rs
index 41e19be8..260dfded 100644
--- a/input/src/keymap.rs
+++ b/input/src/keymap.rs
@@ -1,46 +1,77 @@
 use crate::*;
-pub struct KeyMap<'a, T>(AtomIterator<'a>);
-impl<'a, T> KeyMap<'a, T> {
-    pub const fn new (source: &'a str) -> Self {
-        Self(AtomIterator::new(TokenIterator::new(source)))
+pub trait KeyMap {
+    /// Try to find a command that matches the currently pressed key
+    fn command <S, C: EdnCommand<S>> (&self, state: &S, input: &impl EdnInput) -> Option<C>;
+}
+pub struct SourceKeyMap<'a>(&'a str);
+impl<'a> KeyMap for SourceKeyMap<'a> {
+    fn command <S, C: EdnCommand<S>> (&self, state: &S, input: &impl EdnInput) -> Option<C> {
+        todo!();
+        None
     }
-    /// Try to find a binding matching the currently pressed key
-    pub fn command <S, C> (&self, state: &S, input: &impl EdnInput) -> Option<C>
-    where
-        C: Command<S> + EdnCommand<S>
-    {
-        use Atom::*;
-        let mut command: Option<C> = None;
-        match self {
-            Self::TokenIterator(t) => todo!(),
-            Self::TokenSlice(t) => todo!(),
-            Self::TokenVec(t) => todo!(),
-            //Self::AtomIterator(t) => todo!(),
-            Self::AtomSlice(t) => todo!(),
-            Self::Atom(t) => todo!(),
-        }
-        for item in self.0.iter() {
-            if let Exp(e) = item {
-                match e.as_slice() {
-                    [Sym(key), c, args @ ..] if input.matches_edn(key) => {
-                        command = C::from_edn(state, c, args);
-                        if command.is_some() {
-                            break
-                        }
+}
+pub struct ParsedKeyMap<'a>(TokenIterator<'a>);
+impl<'a> KeyMap for ParsedKeyMap<'a> {
+    fn command <S, C: EdnCommand<S>> (&self, state: &S, input: &impl EdnInput) -> Option<C> {
+        todo!();
+        None
+    }
+}
+pub struct RefKeyMap<'a>(TokenIterator<'a>);
+impl<'a> KeyMap for RefKeyMap<'a> {
+    fn command <S, C: EdnCommand<S>> (&self, state: &S, input: &impl EdnInput) -> Option<C> {
+        todo!();
+        //for token in self.0 {
+            //match token?.kind() {
+                //TokenKind::Exp => match atoms.as_slice() {
+                    //[key, command, args @ ..] => match (key.kind(), key.text()) {
+                        //(TokenKind::Sym, key) => {
+                            //if input.matches_edn(key) {
+                                //let command = C::from_edn(state, command, args);
+                                //if command.is_some() {
+                                    //return command
+                                //}
+                            //}
+                        //},
+                        //_ => panic!("invalid config: {item}")
+                    //},
+                    //_ => panic!("invalid config: {item}")
+                //}
+                //_ => panic!("invalid config: {item}")
+            //}
+        //}
+        None
+    }
+}
+pub struct ArcKeyMap(Vec<ArcAtom>);
+impl<'a> KeyMap for ArcKeyMap {
+    fn command <S, C: EdnCommand<S>> (&self, state: &S, input: &impl EdnInput) -> Option<C> {
+        for atom in self.0.iter() {
+            match atom {
+                ArcAtom::Exp(atoms) => match atoms.as_slice() {
+                    [key, command, args @ ..] => match (key.kind(), key.text()) {
+                        (TokenKind::Sym, key) => {
+                            if input.matches_edn(key) {
+                                let command = C::from_edn(state, command, args);
+                                if command.is_some() {
+                                    return command
+                                }
+                            }
+                        },
+                        _ => panic!("invalid config: {atom}")
                     },
-                    _ => {}
+                    _ => panic!("invalid config: {atom}")
                 }
-            } else {
-                panic!("invalid config")
+                _ => panic!("invalid config: {atom}")
             }
         }
-        command
+        None
     }
 }
 /// [Input] state that can be matched against an [Atom].
 pub trait EdnInput: Input {
     fn matches_edn (&self, token: &str) -> bool;
-    fn get_event (_: &Atom<impl AsRef<str>>) -> Option<Self::Event> {
+    fn get_event <'a> (_: &impl Atom<'a>) -> Option<Self::Event> {
         None
     }
 }
@@ -48,8 +79,8 @@ pub trait EdnInput: Input {
 pub trait EdnCommand<C>: Command<C> {
     fn from_edn <'a> (
         state: &C,
-        head: &Atom<impl AsRef<str>>,
-        tail: &'a [Atom<impl AsRef<str>>]
+        head: &impl Atom<'a>,
+        tail: &'a [impl Atom<'a>]
     ) -> Option<Self>;
 }
 /** Implement `EdnCommand` for given `State` and `Command` */
@@ -78,8 +109,8 @@ pub trait EdnCommand<C>: Command<C> {
         impl<$T: $Trait> EdnCommand<$T> for $Command {
             fn from_edn <'a> (
                 $state: &$T,
-                head: &Atom<impl AsRef<str>>,
-                tail: &'a [Atom<impl AsRef<str>>]
+                head: &impl Atom,
+                tail: &'a [impl Atom]
             ) -> Option<Self> {
                 $(if let (Atom::Key($key), [ // if the identifier matches
                     // bind argument ids
@@ -88,7 +119,7 @@ pub trait EdnCommand<C>: Command<C> {
                     $(, $rest @ ..)?
                 ]) = (head.to_ref(), tail) {
                     $(
-                        $(let $arg: Option<$type> = EdnProvide::<$type>::get($state, $arg);)?
+                        $(let $arg: Option<$type> = Context::<$type>::get($state, $arg);)?
                     )*
                     //$(edn_command!(@bind $state => $arg $(?)? : $type);)*
                     return Some($command)
@@ -121,8 +152,8 @@ pub trait EdnCommand<C>: Command<C> {
         impl EdnCommand<$State> for $Command {
             fn from_edn <'a> (
                 $state: &$State,
-                head: &Atom<impl AsRef<str>>,
-                tail: &'a [Atom<impl AsRef<str>>],
+                head: &impl Atom,
+                tail: &'a [impl Atom],
             ) -> Option<Self> {
                 $(if let (Atom::Key($key), [ // if the identifier matches
                     // bind argument ids
@@ -131,7 +162,7 @@ pub trait EdnCommand<C>: Command<C> {
                     $(, $rest @ ..)?
                 ]) = (head.to_ref(), tail) {
                     $(
-                        $(let $arg: Option<$type> = EdnProvide::<$type>::get($state, $arg);)?
+                        $(let $arg: Option<$type> = Context::<$type>::get($state, $arg);)?
                     )*
                     //$(edn_command!(@bind $state => $arg $(?)? : $type);)*
                     return Some($command)
@@ -141,10 +172,10 @@ pub trait EdnCommand<C>: Command<C> {
         }
     };
     (@bind $state:ident =>$arg:ident ? : $type:ty) => {
-        let $arg: Option<$type> = EdnProvide::<$type>::get($state, $arg);
+        let $arg: Option<$type> = Context::<$type>::get($state, $arg);
     };
     (@bind $state:ident => $arg:ident : $type:ty) => {
-        let $arg: $type = EdnProvide::<$type>::get_or_fail($state, $arg);
+        let $arg: $type = Context::<$type>::get_or_fail($state, $arg);
     };
 }
 #[cfg(test)] #[test] fn test_edn_keymap () -> Usually<()> {
diff --git a/output/src/op_align.rs b/output/src/op_align.rs
index d38153b7..a4942fb2 100644
--- a/output/src/op_align.rs
+++ b/output/src/op_align.rs
@@ -1,4 +1,5 @@
 use crate::*;
+use RefAtom::*;
 #[derive(Debug, Copy, Clone, Default)] pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W }
 pub struct Align<E, A>(PhantomData<E>, Alignment, A);
 impl<E, A> Align<E, A> {
@@ -44,13 +45,12 @@ impl<E: Output, A: Content<E>> Content<E> for Align<E, A> {
         to.place(Content::layout(self, to.area()), &self.content())
     }
 }
-impl<'a, E: Output + 'a, T: EdnViewData<'a, E>> TryFromEdn<'a, T> for Align<E, RenderBox<'a, E>> {
+impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromEdn<'a, T> for Align<E, RenderBox<'a, E>> {
     fn try_from_edn (
         state: &'a T,
-        head: &Atom<impl AsRef<str>>,
-        tail: &'a [Atom<impl AsRef<str>>]
+        head: &impl Atom,
+        tail: &'a [impl Atom]
     ) -> Option<Self> {
-        use Atom::*;
         Some(match (head.to_ref(), tail) {
             (Key("align/c"),  [a]) => Self::c(state.get_content(a).expect("no content")),
             (Key("align/x"),  [a]) => Self::x(state.get_content(a).expect("no content")),
diff --git a/output/src/op_bsp.rs b/output/src/op_bsp.rs
index 22868d49..51ca5c77 100644
--- a/output/src/op_bsp.rs
+++ b/output/src/op_bsp.rs
@@ -1,4 +1,5 @@
 use crate::*;
+use RefAtom::*;
 pub use self::Direction::*;
 /// A cardinal direction.
 #[derive(Copy, Clone, PartialEq)]
@@ -92,9 +93,8 @@ impl<E: Output, A: Content<E>, B: Content<E>> BspAreas<E, A, B> for Bsp<E, A, B>
     fn direction (&self) -> Direction { self.1 }
     fn contents (&self) -> (&A, &B) { (&self.2, &self.3) }
 }
-impl<'a, E: Output + 'a, T: EdnViewData<'a, E>> TryFromEdn<'a, T> for Bsp<E, RenderBox<'a, E>, RenderBox<'a, E>> {
-    fn try_from_edn (s: &'a T, head: &Atom<impl AsRef<str>>, tail: &'a [Atom<impl AsRef<str>>]) -> Option<Self> {
-        use Atom::*;
+impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromEdn<'a, T> for Bsp<E, RenderBox<'a, E>, RenderBox<'a, E>> {
+    fn try_from_edn (s: &'a T, head: &impl Atom, tail: &'a [impl Atom]) -> Option<Self> {
         Some(match (head.to_ref(), tail) {
             (Key("bsp/n"), [a, b]) => Self::n(
                 s.get_content(a).expect("no south"),
diff --git a/output/src/op_cond.rs b/output/src/op_cond.rs
index 1f6dd186..f91d48fd 100644
--- a/output/src/op_cond.rs
+++ b/output/src/op_cond.rs
@@ -1,4 +1,5 @@
 use crate::*;
+use RefAtom::*;
 /// Show an item only when a condition is true.
 pub struct When<E, A>(pub PhantomData<E>, pub bool, pub A);
 impl<E, A> When<E, A> {
@@ -6,11 +7,10 @@ impl<E, A> When<E, A> {
         Self(Default::default(), c, a)
     }
 }
-impl<'a, E: Output + 'a, T: EdnViewData<'a, E>> TryFromEdn<'a, T> for When<E, RenderBox<'a, E>> {
+impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromEdn<'a, T> for When<E, RenderBox<'a, E>> {
     fn try_from_edn (
-        state: &'a T, head: &Atom<impl AsRef<str>>, tail: &'a [Atom<impl AsRef<str>>]
+        state: &'a T, head: &impl Atom, tail: &'a [impl Atom]
     ) -> Option<Self> {
-        use Atom::*;
         if let (Key("when"), [condition, content]) = (head.to_ref(), tail) {
             Some(Self(
                 Default::default(),
@@ -47,9 +47,8 @@ impl<E, A, B> Either<E, A, B> {
         Self(Default::default(), c, a, b)
     }
 }
-impl<'a, E: Output + 'a, T: EdnViewData<'a, E>> TryFromEdn<'a, T> for Either<E, RenderBox<'a, E>, RenderBox<'a, E>> {
-    fn try_from_edn (state: &'a T, head: &Atom<impl AsRef<str>>, tail: &'a [Atom<impl AsRef<str>>]) -> Option<Self> {
-        use Atom::*;
+impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromEdn<'a, T> for Either<E, RenderBox<'a, E>, RenderBox<'a, E>> {
+    fn try_from_edn (state: &'a T, head: &impl Atom, tail: &'a [impl Atom]) -> Option<Self> {
         if let (Key("either"), [condition, content, alternative]) = (head.to_ref(), tail) {
             Some(Self::new(
                 state.get_bool(condition).expect("either: no condition"),
diff --git a/output/src/op_iter.rs b/output/src/op_iter.rs
index 18428485..8096592c 100644
--- a/output/src/op_iter.rs
+++ b/output/src/op_iter.rs
@@ -1,5 +1,5 @@
 use crate::*;
-
+use RefAtom::*;
 pub fn map_south<O: Output>(
     item_offset: O::Unit,
     item_height: O::Unit,
diff --git a/output/src/op_transform.rs b/output/src/op_transform.rs
index 97d9dea9..afc088c5 100644
--- a/output/src/op_transform.rs
+++ b/output/src/op_transform.rs
@@ -1,4 +1,5 @@
 use crate::*;
+use RefAtom::*;
 /// Defines an enum that transforms its content
 /// along either the X axis, the Y axis, or both.
 ///
@@ -26,11 +27,10 @@ macro_rules! transform_xy {
                 $area
             }
         }
-        impl<'a, E: Output + 'a, T: EdnViewData<'a, E>> TryFromEdn<'a, T> for $Enum<E, RenderBox<'a, E>> {
+        impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromEdn<'a, T> for $Enum<E, RenderBox<'a, E>> {
             fn try_from_edn (
-                state: &'a T, head: &Atom<impl AsRef<str>>, tail: &'a [Atom<impl AsRef<str>>]
+                state: &'a T, head: &impl Atom, tail: &'a [impl Atom]
             ) -> Option<Self> {
-                use Atom::*;
                 Some(match (head.to_ref(), tail) {
                     (Key($x),  [a]) => Self::x(state.get_content(a).expect("no content")),
                     (Key($y),  [a]) => Self::y(state.get_content(a).expect("no content")),
@@ -82,11 +82,10 @@ macro_rules! transform_xy_unit {
                 $layout.into()
             }
         }
-        impl<'a, E: Output + 'a, T: EdnViewData<'a, E>> TryFromEdn<'a, T> for $Enum<E, E::Unit, RenderBox<'a, E>> {
+        impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromEdn<'a, T> for $Enum<E, E::Unit, RenderBox<'a, E>> {
             fn try_from_edn (
-                state: &'a T, head: &Atom<impl AsRef<str>>, tail: &'a [Atom<impl AsRef<str>>]
+                state: &'a T, head: &impl Atom, tail: &'a [impl Atom]
             ) -> Option<Self> {
-                use Atom::*;
                 Some(match (head.to_ref(), tail) {
                     (Key($x),  [x, a]) => Self::x(
                         state.get_unit(x).expect("no x"),
diff --git a/output/src/view.rs b/output/src/view.rs
index d31fb466..5c255223 100644
--- a/output/src/view.rs
+++ b/output/src/view.rs
@@ -1,11 +1,10 @@
 use crate::*;
 use std::{sync::Arc, marker::PhantomData};
-use Atom::*;
 /// Define an EDN-backed view.
 ///
 /// This consists of:
 /// * render callback (implementation of [Content])
-/// * value providers (implementations of [EdnProvide])
+/// * value providers (implementations of [Context])
 #[macro_export] macro_rules! edn_view {
     ($Output:ty: |$self:ident: $App:ty| $content:expr; {
         $( $type:ty { $($sym:literal => $value:expr),* } );*
@@ -14,19 +13,19 @@ use Atom::*;
             fn content(&$self) -> impl Render<$Output> { $content }
         }
         $(
-            impl<'a> EdnProvide<'a, $type> for $App {
-                fn get (&'a $self, edn: &'a Atom<impl AsRef<str>>) -> Option<$type> {
+            impl<'a> Context<'a, $type> for $App {
+                fn get (&'a $self, edn: &'a impl Atom<'a>) -> Option<$type> {
                     Some(match edn.to_ref() { $(Atom::Sym($sym) => $value,)* _ => return None })
                 }
             }
         )*
     }
 }
-/// Implements `EdnProvide` for content components and expressions
+/// Implements `Context` for content components and expressions
 #[macro_export] macro_rules! edn_provide_content {
     (|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
-        impl<'a, E: Output> EdnProvide<'a, Box<dyn Render<E> + 'a>> for $State {
-            fn get (&'a $self, edn: &'a Atom<impl AsRef<str>>) -> Option<Box<dyn Render<E> + 'a>> {
+        impl<'a, E: Output> Context<'a, Box<dyn Render<E> + 'a>> for $State {
+            fn get (&'a $self, edn: &'a impl Atom<'a>) -> Option<Box<dyn Render<E> + 'a>> {
                 Some(match edn.to_ref() {
                     $(Atom::Sym($pat) => $expr),*,
                     _ => return None
@@ -35,8 +34,8 @@ use Atom::*;
         }
     };
     ($Output:ty: |$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
-        impl<'a> EdnProvide<'a, Box<dyn Render<$Output> + 'a>> for $State {
-            fn get (&'a $self, edn: &'a Atom<impl AsRef<str>>) -> Option<Box<dyn Render<$Output> + 'a>> {
+        impl<'a> Context<'a, Box<dyn Render<$Output> + 'a>> for $State {
+            fn get (&'a $self, edn: &'a impl Atom<'a>) -> Option<Box<dyn Render<$Output> + 'a>> {
                 Some(match edn.to_ref() {
                     $(Atom::Sym($pat) => $expr),*,
                     _ => return None
@@ -46,14 +45,14 @@ use Atom::*;
     }
 }
 /// Renders from EDN source and context.
-#[derive(Default)] pub enum EdnView<'a, E: Output, T: EdnViewData<'a, E> + std::fmt::Debug> {
+#[derive(Default)] pub enum EdnView<'a, E: Output, T: ViewContext<'a, E> + std::fmt::Debug> {
     #[default] Inert,
     Ok(T, Atom<Arc<str>>),
     //render: Box<dyn Fn(&'a T)->Box<dyn Render<E> + Send + Sync + 'a> + Send + Sync + 'a>
     Err(String),
     _Unused(PhantomData<&'a E>),
 }
-impl<'a, E: Output, T: EdnViewData<'a, E> + std::fmt::Debug> std::fmt::Debug for EdnView<'a, E, T> {
+impl<'a, E: Output, T: ViewContext<'a, E> + std::fmt::Debug> std::fmt::Debug for EdnView<'a, E, T> {
     fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
         match self {
             Self::Inert | Self::_Unused(_) =>
@@ -66,24 +65,24 @@ impl<'a, E: Output, T: EdnViewData<'a, E> + std::fmt::Debug> std::fmt::Debug for
         }
     }
 }
-impl<'a, E: Output, T> EdnViewData<'a, E> for T where T:
-    EdnProvide<'a, bool> +
-    EdnProvide<'a, usize> +
-    EdnProvide<'a, E::Unit> +
-    EdnProvide<'a, Box<dyn Render<E> + 'a>>
+impl<'a, E: Output, T> ViewContext<'a, E> for T where T:
+    Context<'a, bool> +
+    Context<'a, usize> +
+    Context<'a, E::Unit> +
+    Context<'a, Box<dyn Render<E> + 'a>>
 {}
-impl<'a, E: Output, T: EdnViewData<'a, E> + std::fmt::Debug> EdnView<'a, E, T> {
+impl<'a, E: Output, T: ViewContext<'a, E> + std::fmt::Debug> EdnView<'a, E, T> {
     pub fn from_source (state: T, source: &'a str) -> Self {
         match Atom::read_one(&source) {
             Ok((layout, _)) => Self::Ok(state, layout),
             Err(error) => Self::Err(format!("{error} in {source}"))
         }
     }
-    pub fn from_items (state: T, items: Vec<Atom<impl AsRef<str>>>) -> Self {
+    pub fn from_items (state: T, items: Vec<impl Atom<'a>>) -> Self {
         Self::Ok(state, Atom::Exp(items).to_arc())
     }
 }
-impl<E: Output, T: for<'a>EdnViewData<'a, E> + Send + Sync + std::fmt::Debug> Content<E> for EdnView<'_, E, T> {
+impl<E: Output, T: for<'a>ViewContext<'a, E> + Send + Sync + std::fmt::Debug> Content<E> for EdnView<'_, E, T> {
     fn content (&self) -> impl Render<E> {
         match self {
             Self::Ok(state, layout) => state.get_content(layout),
@@ -107,31 +106,31 @@ macro_rules! edn_try_delegate {
 }
 
 /// Provides values to the template
-pub trait EdnViewData<'a, E: Output>:
-    EdnProvide<'a, bool> +
-    EdnProvide<'a, usize> +
-    EdnProvide<'a, E::Unit> +
-    EdnProvide<'a, Box<dyn Render<E> + 'a>>
+pub trait ViewContext<'a, E: Output>:
+    Context<'a, bool> +
+    Context<'a, usize> +
+    Context<'a, E::Unit> +
+    Context<'a, Box<dyn Render<E> + 'a>>
 {
-    fn get_bool (&'a self, item: &'a Atom<impl AsRef<str>>) -> Option<bool> {
+    fn get_bool (&'a self, item: &'a impl Atom<'a>) -> Option<bool> {
         Some(match &item {
             Sym(s) => match s.as_ref() {
                 ":false" | ":f" => false,
                 ":true"  | ":t" => true,
-                _ => return EdnProvide::get(self, item)
+                _ => return Context::get(self, item)
             },
             Num(0) => false,
             Num(_) => true,
-            _ => return EdnProvide::get(self, item)
+            _ => return Context::get(self, item)
         })
     }
-    fn get_usize (&'a self, item: &'a Atom<impl AsRef<str>>) -> Option<usize> {
-        Some(match &item { Num(n) => *n, _ => return EdnProvide::get(self, item) })
+    fn get_usize (&'a self, item: &'a impl Atom<'a>) -> Option<usize> {
+        Some(match &item { Num(n) => *n, _ => return Context::get(self, item) })
     }
-    fn get_unit (&'a self, item: &'a Atom<impl AsRef<str>>) -> Option<E::Unit> {
-        Some(match &item { Num(n) => (*n as u16).into(), _ => return EdnProvide::get(self, item) })
+    fn get_unit (&'a self, item: &'a impl Atom<'a>) -> Option<E::Unit> {
+        Some(match &item { Num(n) => (*n as u16).into(), _ => return Context::get(self, item) })
     }
-    fn get_content (&'a self, item: &'a Atom<impl AsRef<str>>) -> Option<Box<dyn Render<E> + 'a>> where E: 'a {
+    fn get_content (&'a self, item: &'a impl Atom<'a>) -> Option<Box<dyn Render<E> + 'a>> where E: 'a {
         Some(match item {
             Nil => Box::new(()),
             Exp(ref e) => {
@@ -149,9 +148,9 @@ pub trait EdnViewData<'a, E: Output>:
                 edn_try_delegate!(self, e, Pull::<_, _, RenderBox<'a, E>>);
                 edn_try_delegate!(self, e, Margin::<_, _, RenderBox<'a, E>>);
                 edn_try_delegate!(self, e, Padding::<_, _, RenderBox<'a, E>>);
-                EdnProvide::get_or_fail(self, &item)
+                Context::get_or_fail(self, &item)
             },
-            _ => EdnProvide::get_or_fail(self, &item)
+            _ => Context::get_or_fail(self, &item)
         })
         //panic!("no content")
     }