use crate::*; use std::path::PathBuf; use notify_debouncer_full::notify::{RecursiveMode, PollWatcher, Config as NotifyConfig, Watcher}; impl AsRef for Config { fn as_ref (&self) -> &Config { self } } /// Write initial contents of configuration. pub fn config_init > (config: C) -> Usually { if config.as_ref().find_file().is_none() { std::fs::write(config.as_ref().place_file()?, Config::DEFAULTS)?; } Ok(if let Some(path) = config.as_ref().find_file() { config_load(config, std::fs::read_to_string(&path)?)? } else { return Err(format!("config_init: not found").into()) }) } pub fn config_load , L: Language> (config: C, src: L) -> Usually { config.as_ref().clear(); config.as_ref().stamp.store(quanta::Clock::new().raw(), Relaxed); src.each(config, |c, s|config_load_item(c, s)) } pub fn config_load_item , L: Language> (config: C, src: L) -> Usually { if let Some(expr) = src.expr()? { config_load_kind(config, expr) } else { Err(format!("Config::add_one: tried to add empty item").into()) } } pub fn config_load_kind , L: Language> (config: C, src: L) -> Usually { //if let Some(tail) = src.tail()? { match src.head()? { Some("mode") => modes_add(&config.as_ref().modes, src.tail()), Some("keys") => load_bind(&config.as_ref().binds, src.tail()), Some("view") => load_view(&config.as_ref().views, src.tail()), _ => return Err(format!("Config::load: expected view/keys/mode expr, got: {src:?}").into()) }?; //} Ok(config) } /// Watch a config's file for changes. pub fn config_watch ( config: Arc, poll: Option ) -> Usually<()> { let poll = poll.unwrap_or(Duration::from_millis(250)); let opts = NotifyConfig::default().with_poll_interval(poll); let mut watcher = ::notify_debouncer_full::notify::poll::PollWatcher::new({ let config = config.clone(); move|result|{ match result { Ok(_events) => if let Err(e) = config_init(config.as_ref()) { *config.as_ref().error.write().unwrap() = Some(format!("{e:?}").into()); panic!("{e:?}"); } else { //println!("config updated"); }, Err(errors) => { panic!("{errors:?}"); } } } }, opts)?; if let Some(path) = config.as_ref().get_file() { //println!("watching: {path:?}"); watcher.watch(&path, RecursiveMode::NonRecursive)?; *config.as_ref().watch.write().unwrap() = Some(watcher); Ok(()) } else { Err(format!("no config path").into()) } } /// Register a mode. pub fn modes_add <'a> (modes: &Modes, expr: impl Language) -> UsuallyRef<'a, ()> { let name = expr.head()?.ok_or("mode: missing name")?; let body = expr.tail()?.ok_or("mode: missing body")?; let mode = Mode::default(); let mode = body.each(mode, |c,s|mode_add(c,s))?; modes.0.write().unwrap().insert(name.into(), Arc::new(mode)); Ok(()) } /// Add a definition to the mode. /// /// Supported definitions: /// /// - (name ...) -> name /// - (info ...) -> description /// - (keys ...) -> key bindings /// - (mode ...) -> submode /// - ... -> view /// /// ``` /// let mut mode: tek::Mode> = Default::default(); /// mode.add("(name hello)").unwrap(); /// ``` pub fn mode_add (mut mode: Mode, dsl: impl Language) -> Usually { Ok(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() { //println!("Mode::add: {head} {:?}", expr.tail()); let tail = expr.tail()?.map(|x|x.trim()).unwrap_or(""); match head { "mode" => { let name = tail.head()?.ok_or("submode: missing name")?; let body = tail.tail()?.ok_or("submode: missing body")?; let submode = Mode::default(); let submode = body.each(submode, |c,s|mode_add(c,s))?; let modes = mode.modes.clone(); modes.0.write().unwrap().insert(name.into(), Arc::new(submode)); mode }, "keys" => { dsl.each(mode, |mut mode: Mode, expr: &str|{ mode.keys.push(expr.trim().into()); Ok(mode) })? }, "name" => { mode.name.push(tail.into()); mode }, "info" => { mode.info.push(tail.into()); mode }, "view" => { mode.view.push(tail.into()); mode }, _ => { mode.view.push(expr.into()); mode }, } } else if let Ok(Some(word)) = dsl.word() { mode.view.push(word.into()); mode } else { return Err(format!("Mode::add: unexpected: {dsl:?}").into()); }) } /// Load custom view definition. pub fn load_view <'a> (views: &Views, expr: impl Language) -> UsuallyRef<'a, ()> { let name = expr.head()?.ok_or("view: missing name")?; let body = expr.tail()?.ok_or("view: missing body")?; views.write().unwrap().insert( name.into(), body.src()?.unwrap_or_default().into() ); Ok(()) } pub fn load_bind <'a> (binds: &Binds, expr: impl Language) -> UsuallyRef<'a, ()> { let name = expr.head()?.ok_or("bind: missing name")?; let body = expr.tail()?.ok_or("bind: missing body")?; binds.write().unwrap().insert(name.into(), { let mut map = Bind::new(); body.each((), |_, item: &str|if item.expr().head() == Ok(Some("see")) { // TODO Ok(()) } else if let Ok(Some(_word)) = item.expr().head().word() { let expr = item.expr()?; let head = expr.head()?; if let Some(event) = TuiKey::from_dsl(&head)?.to_crossterm() { map.add(TuiEvent(event), Binding { commands: [item.expr()?.tail()?.unwrap_or_default().into()].into(), condition: None, description: None, source: None }); Ok(()) } else if Some(":char") == item.expr()?.head()? { // TODO return Ok(()) } else { return Err(format!("Config::load_bind: invalid key: {:?}", item.expr()?.head()?).into()) } } else { return Err(format!("Config::load_bind: unexpected: {item:?}").into()) })?; map }); Ok(()) } /// Configuration: mode, view, and bind definitions. /// /// ``` /// let config = tek::Config::default(); /// ``` /// /// ``` /// // Some dizzle. /// // What indentation to use here lol? /// let source = stringify!((mode :menu (name Menu) /// (info Mode selector.) (keys :axis/y :confirm) /// (view (bg (g 0) (bsp/s :ports/out /// (bsp/n :ports/in /// (bg (g 30) (bsp/s (fixed/y 7 :logo) /// (fill :dialog/menu))))))))); /// // Add this definition to the config and try to load it. /// // A "mode" is basically a state machine /// // with associated input and output definitions. /// tek::Config::default().add(&source).unwrap().get_mode(":menu").unwrap(); /// ``` #[derive(Default, Debug)] pub struct Config { /// XDG base directories of running user. pub dirs: BaseDirectories, /// Active collection of interaction modes. pub modes: Modes, /// Active collection of event bindings. pub binds: Binds, /// Active collection of view definitions. pub views: Views, /// Error caught during reloading pub error: RwLock>>, /// Timestamp pub stamp: AtomicU64, /// Watcher pub watch: RwLock> } /// Collection of UI modes. /// /// ``` /// let mut modes = tek::Modes::default(); /// assert_eq!(modes.len(), 0); /// let _ = modes.add(&":foo", &"(mode (name Foo) (info Bar))"); /// assert_eq!(modes.len(), 1); /// ``` #[derive(Default, Debug, Clone)] pub struct Modes(Arc, Arc>>>); /// Group of view and keys definitions. /// /// ``` /// let mode = tek::Mode::>::default(); /// ``` #[derive(Default, Debug)] pub struct Mode { pub path: PathBuf, pub name: Vec>, pub info: Vec>, pub view: Vec>, pub keys: Vec>, pub modes: Modes, } impl Config { /// Default configuration directory. const CONFIG_DIR: &'static str = "tek"; /// Default configuration subdirectory. const CONFIG_SUB: &'static str = "v0"; /// Default configuration file name. const CONFIG: &'static str = "tek.edn"; /// Default configuration contents. const DEFAULTS: &'static str = include_str!("tek.edn"); /// Create a new app configuration from a set of XDG base directories, pub fn new (dirs: Option) -> Self { Self { dirs: dirs.unwrap_or_else(||{ BaseDirectories::with_profile(Self::CONFIG_DIR, Self::CONFIG_SUB) }), stamp: quanta::Clock::new().raw().into(), ..Default::default() } } /// Create, initialize, and watch a new configuration. pub fn watched (callback: impl FnOnce(Arc)->T) -> Usually { let config = Self::init_new(None)?; config_watch(config.clone(), None)?; let result = callback(config); Ok(result) } /// Create and initialize a new configuration. pub fn init_new (_dirs: Option) -> Usually> { config_init(Arc::new(Self::new(None))) } /// Find the config file. fn find_file (&self) -> Option { self.dirs.find_config_file(Self::CONFIG) } /// Place config file in default location. fn place_file (&self) -> Result { self.dirs.place_config_file(Self::CONFIG) } /// Get path to config file. fn get_file (&self) -> Option { self.dirs.get_config_file(Self::CONFIG) } /// Get a mode by name. pub fn get_mode (&self, mode: impl AsRef) -> Option> { self.modes.get(mode) } /// Print the configuration. pub fn print (&self) { print_config(self) } /// Make this configuration empty. fn clear (&self) { *self.modes.0.write().unwrap() = Default::default(); *self.views.write().unwrap() = Default::default(); *self.binds.write().unwrap() = Default::default(); } pub fn get_view (&self, name: impl AsRef) -> Option> { self.views.read().unwrap().get(name.as_ref()).cloned() } } pub use self::view::*; mod view { use crate::*; /// Collection of custom view definitions. pub type Views = Arc, Arc>>>; } pub use self::mode::*; mod mode { use crate::*; impl Modes { /// Get a mode by name. pub fn get (&self, name: impl AsRef) -> Option> { self.0.read().unwrap().get(name.as_ref()).cloned() } /// Run something for each mode. pub fn for_each (&self, mut ator: impl FnMut(&str, &Mode)->T) { for (k, v) in self.0.read().unwrap().iter() { let _ = ator(k.as_ref(), v.as_ref()); } } /// Count modes. pub fn len (&self) -> usize { self.0.read().unwrap().len() } } } pub use self::bind::*; mod bind { use crate::*; /// Collection of input bindings. pub type Binds = Arc, Bind>>>>; /// An map of input events (e.g. [TuiEvent]) to [Binding]s. /// /// ``` /// let lang = "(@x (nop)) (@y (nop) (nop))"; /// let bind = tek::Bind::>::load(&lang).unwrap(); /// assert_eq!(bind.query(&'x'.into()).map(|x|x.len()), Some(1)); /// //assert_eq!(bind.query(&'y'.into()).map(|x|x.len()), Some(2)); /// ``` #[derive(Debug)] pub struct Bind( /// Map of each event (e.g. key combination) to /// all command expressions bound to it by /// all loaded input layers. pub BTreeMap>> ); /// A sequence of zero or more commands (e.g. [AppCommand]), /// optionally filtered by [Condition] to form layers. /// /// ``` /// //FIXME: Why does it overflow? /// //let binding: Binding<()> = tek::Binding { ..Default::default() }; /// ``` #[derive(Debug, Clone)] pub struct Binding { pub commands: Arc<[C]>, pub condition: Option, pub description: Option>, pub source: Option>, } /// Condition that must evaluate to true in order to enable an input layer. /// /// ``` /// let condition = tek::Condition(std::sync::Arc::new(Box::new(||{true}))); /// ``` #[derive(Clone)] pub struct Condition(pub Arcbool + Send + Sync>>); impl Bind> { } /// Default is always empty map regardless if `E` and `C` implement [Default]. impl Default for Bind { fn default () -> Self { Self(Default::default()) } } impl Default for Binding { fn default () -> Self { Self { commands: Default::default(), condition: Default::default(), description: Default::default(), source: Default::default(), } } } impl Bind { /// Create a new event map pub fn new () -> Self { Default::default() } /// Add a binding to an owned event map. pub fn def (mut self, event: E, binding: Binding) -> Self { self.add(event, binding); self } /// Add a binding to an event map. pub fn add (&mut self, event: E, binding: Binding) -> &mut Self { if !self.0.contains_key(&event) { self.0.insert(event.clone(), Default::default()); } self.0.get_mut(&event).unwrap().push(binding); self } /// Return the binding(s) that correspond to an event. pub fn query (&self, event: &E) -> Option<&[Binding]> { self.0.get(event).map(|x|x.as_slice()) } /// Return the first binding that corresponds to an event, considering conditions. pub fn dispatch (&self, event: &E) -> Option<&Binding> { self.query(event) .map(|bb|bb.iter().filter(|b|b.condition.as_ref().map(|c|(c.0)()).unwrap_or(true)).next()) .flatten() } } impl_debug!(Condition |self, w| { write!(w, "*") }); } pub fn print_config (config: &Config) { use ::ansi_term::Color::*; println!("{:?}", config.dirs); for (k, v) in config.views.read().unwrap().iter() { println!("{} {} {v}", Green.paint("VIEW"), Green.bold().paint(format!("{k:<16}"))); } for (k, v) in config.binds.read().unwrap().iter() { println!("{} {}", Green.paint("BIND"), Green.bold().paint(format!("{k:<16}"))); for (k, v) in v.0.iter() { print!("{} ", &Yellow.paint(match &k.0 { Event::Key(KeyEvent { modifiers, .. }) => format!("{:>16}", format!("{modifiers}")), _ => unimplemented!() })); print!("{}", &Yellow.bold().paint(match &k.0 { Event::Key(KeyEvent { code, .. }) => format!("{:<10}", format!("{code}")), _ => unimplemented!() })); for v in v.iter() { print!(" => {:?}", v.commands); print!(" {}", v.condition.as_ref().map(|x|format!("{x:?}")).unwrap_or_default()); println!(" {}", v.description.as_ref().map(|x|x.as_ref()).unwrap_or_default()); //println!(" {:?}", v.source); } } } config.modes.for_each(|k, v|{ println!(); for v in v.name.iter() { print!("{}", Green.bold().paint(format!("{v} "))); } for v in v.info.iter() { print!("\n{}", Green.paint(format!("{v}"))); } print!("\n{} {}", Blue.paint("TOOL"), Green.bold().paint(format!("{k:<16}"))); print!("\n{}", Blue.paint("KEYS")); for v in v.keys.iter() { print!("{}", Green.paint(format!(" {v}"))); } println!(); v.modes.for_each(|k, v|{ print!("{} {} {:?}", Blue.paint("MODE"), Green.bold().paint(format!("{k:<16}")), v.name); print!( " INFO={:?}", v.info); print!( " VIEW={:?}", v.view); println!(" KEYS={:?}", v.keys); }); print!("{}", Blue.paint("VIEW")); for v in v.view.iter() { print!("{}", Green.paint(format!(" {v}"))); } println!(); }); }