axis config: bring failure under test

This commit is contained in:
i do not exist 2026-10-04 23:10:23 +03:00
parent 5aaa0f3e4e
commit a355e482c7
16 changed files with 1328 additions and 1149 deletions

View file

@ -1,155 +0,0 @@
use crate::*;
/// Collection of UI modes.
///
/// ```
/// // Empty mode registry:
/// let mut modes = tek::Modes::default();
/// assert_eq!(modes.len(), 0);
/// assert!(modes.get("Foo").is_none());
///
/// // Add a mode:
/// let _ = modes.add(&"Foo (name Foo) (info Bar)").unwrap();
/// assert_eq!(modes.len(), 1);
/// assert!(modes.get("Foo").is_some());
/// ```
#[derive(Default, Debug, Clone)]
pub struct Modes(pub Arc<RwLock<BTreeMap<Arc<str>, Arc<Mode>>>>);
/// Group of view and keys definitions.
///
/// ```
/// let mode = tek::Mode::default();
/// ```
#[derive(Default, Debug)]
pub struct Mode {
pub path: PathBuf,
pub name: Vec<Arc<str>>,
pub info: Vec<Arc<str>>,
pub view: Vec<Arc<View<Tui, App>>>,
pub keys: Vec<Arc<str>>,
pub axis: BTreeMap<Arc<str>, (Arc<str>, Arc<str>)>,
pub modes: Modes,
}
impl Modes {
/// Register a mode.
pub fn add <'a> (&self, expr: impl Language) -> UsuallyRef<'a, ()> {
let name = expr.head()?.ok_or("mode: missing name")?;
let body = expr.tail()?.ok_or("mode: missing body")?;
let mut mode = Mode::default();
body.each(&mut mode, |c, s|c.add(s))?;
self.0.write().insert(name.into(), Arc::new(mode));
Ok(())
}
/// Get a mode by name.
pub fn get (&self, name: impl AsRef<str>) -> Option<Arc<Mode>> {
self.0.read().get(name.as_ref()).cloned()
}
/// Run something for each mode.
pub fn for_each <T> (&self, mut ator: impl FnMut(&str, &Mode)->T) {
for (k, v) in self.0.read().iter() {
let _ = ator(k.as_ref(), v.as_ref());
}
}
/// Count modes.
pub fn len (&self) -> usize {
self.0.read().len()
}
}
impl Mode {
/// Add a definition to the mode.
///
/// Supported definitions:
///
/// - (name ...) -> name
/// - (info ...) -> description
/// - (keys ...) -> key bindings
/// - (mode ...) -> submode
/// - ... -> view
///
/// ```
/// let mut mode = tek::Mode::default().add("(name hello)").unwrap();
/// ```
pub fn add (&mut self, dsl: impl Language) -> Usually<&mut Self> {
Ok(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() {
let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
match head {
"self" => self.add_mode(tail)?,
"bind" => self.add_bind(tail)?,
"axis" => self.add_axis(tail)?,
"name" => { self.name.push(tail.into()); self },
"info" => { self.info.push(tail.into()); self },
"view" => { self.view.push(View::new(tail)?.into()); self },
_ => { self.view.push(View::new(tail)?.into()); self },
}
} else if let Ok(Some(word)) = dsl.word() {
self.view.push(View::new(word)?.into());
self
} else {
return Err(format!("Mode::add: unexpected: {dsl:?}").into());
})
}
fn add_mode (&mut self, tail: &str) -> Usually<&mut Self> {
let name = tail.head()?.ok_or("submode: missing name")?;
let body = tail.tail()?.ok_or("submode: missing body")?;
let mut submode = Mode::default();
body.each(&mut submode, |c, s|c.add(s))?;
let modes = self.modes.clone();
modes.0.write().insert(name.into(), Arc::new(submode));
Ok(self)
}
fn add_bind (&mut self, tail: &str) -> Usually<&mut Self> {
tail.each(self, |mode: &mut Self, expr: &str|{
mode.keys.push(expr.trim().into());
Ok(mode)
})
}
fn add_axis (&mut self, tail: &str) -> Usually<&mut Self> {
let mut axis: Option<Arc<str>> = None;
let mut dec: Option<Arc<str>> = None;
let mut inc: Option<Arc<str>> = None;
tail.each(0, |index: usize, src: &str|if index == 0 {
axis = Some(src.into());
Ok(1)
} else if index == 1 {
dec = Some(src.into());
Ok(2)
} else if index == 2 {
inc = Some(src.into());
Ok(3)
} else {
err!("expected (mode (axis [Name] [command] [command])), got: {tail}")
})?;
if let Some(axis) = axis && let Some(dec) = dec && let Some(inc) = inc {
self.keys.push(format!("((dec {axis}) {dec})").into());
self.keys.push(format!("((inc {axis}) {inc})").into());
Ok(self)
} else {
err!("expected (mode (axis X Decrement Increment)), got: {tail}")
}
}
}
impl AsMut<Mode> for Mode {
fn as_mut (&mut self) -> &mut Self {
self
}
}
impl ControlAxis {
pub fn get_axis <'a> (_: &App, src: impl Language) -> Perhaps<ControlAxis> {
eval_word(src, |word|Ok(ControlAxis(word.into())))
}
}