From 9e25564abb6642566f27e2b33a8f50545ba96fcc Mon Sep 17 00:00:00 2001 From: i do not exist Date: Sun, 23 Aug 2026 06:12:01 +0300 Subject: [PATCH] fix config loading --- src/config.rs | 167 ++++++++++++++++++------------------ src/device/arrange/track.rs | 45 +++++----- src/tek.rs | 25 ++---- tengri | 2 +- 4 files changed, 116 insertions(+), 123 deletions(-) diff --git a/src/config.rs b/src/config.rs index 5a13144c..208a123b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,55 +2,88 @@ use crate::*; use std::path::PathBuf; use notify_debouncer_full::notify::{RecursiveMode, PollWatcher, Config as NotifyConfig, Watcher}; -/// Write initial contents of configuration. -pub fn config_init (config: &Config) -> UsuallyRef<'static, ()> { - //println!("\r\ninit {}", quanta::Clock::new().raw()); - config.clear(); - let path = Config::CONFIG; - let defaults = Config::DEFAULTS; - config.stamp.store(quanta::Clock::new().raw(), Relaxed); - if config.find_file().is_none() { - //println!("Creating {path:?}"); - std::fs::write(config.place_file()?, defaults)?; +impl AsRef for Config { + fn as_ref (&self) -> &Config { + self } - Ok(if let Some(path) = config.find_file() { - //println!("Loading {path:?}"); - let src = std::fs::read_to_string(&path)?; - src.as_str().each((), &mut move|ctx, item: &str|{ - config_add(config, item); - Ok(ctx) - }); +} + +/// 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!("{path}: not found").into()) + return Err(format!("config_init: not found").into()) }) } -/// Add statements to configuration from [Dsl] source. -pub fn config_add <'a> (config: &'a Config, dsl: &'a str) -> UsuallyRef<'a, &'a Config> { - dsl.each(config, &mut move|ctx: &'a Config, item: &'a str|if let Some(expr) = item.expr()? { - //if let (Some(name), Some(body)) = (expr.tail()?.head()?, expr.tail()?.tail()?,) { - match expr.head()? { - Some("mode") => expr.tail()?.map(|tail|modes_add(&config.modes, tail)), - Some("keys") => expr.tail()?.map(|tail|load_bind(&config.binds, tail)), - Some("view") => expr.tail()?.map(|tail|load_view(&config.views, tail)), - _ => return Err(format!("Config::load: expected view/keys/mode expr, got: {item:?}").into()) - }.transpose()?; - //} - Ok(config) +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: &'a str) -> UsuallyRef<'a, ()> { +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, move|mut submode: Mode, item: &str|{ - mode_add(&mut submode, &item); - Ok(submode) - })?; + let mode = body.each(mode, |c,s|mode_add(c,s))?; modes.0.write().unwrap().insert(name.into(), Arc::new(mode)); Ok(()) } @@ -69,8 +102,8 @@ pub fn modes_add <'a> (modes: &Modes, expr: &'a str) -> UsuallyRef<'a, ()> { /// let mut mode: tek::Mode> = Default::default(); /// mode.add("(name hello)").unwrap(); /// ``` -pub fn mode_add <'a> (mode: &'a mut Mode, dsl: &'a str) -> PerhapsRef<'a, &'a mut Mode> { - Ok(Some(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() { +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 { @@ -78,16 +111,13 @@ pub fn mode_add <'a> (mode: &'a mut Mode, dsl: &'a str) -> PerhapsRef<'a, &'a mu 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, move|mut submode: Mode, item: &str|{ - mode_add(&mut submode, &item); - Ok(submode) - })?; + 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: &'a mut Mode, expr: &'a str|{ + dsl.each(mode, |mut mode: Mode, expr: &str|{ mode.keys.push(expr.trim().into()); Ok(mode) })? @@ -102,11 +132,11 @@ pub fn mode_add <'a> (mode: &'a mut Mode, dsl: &'a str) -> PerhapsRef<'a, &'a mu mode } else { return Err(format!("Mode::add: unexpected: {dsl:?}").into()); - })) + }) } /// Load custom view definition. -pub fn load_view <'a> (views: &Views, expr: &'a str,) -> UsuallyRef<'a, ()> { +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( @@ -116,12 +146,12 @@ pub fn load_view <'a> (views: &Views, expr: &'a str,) -> UsuallyRef<'a, ()> { Ok(()) } -pub fn load_bind <'a> (binds: &Binds, expr: &'a str) -> UsuallyRef<'a, ()> { +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((), &mut |_, item: &str|if item.expr().head() == Ok(Some("see")) { + body.each((), |_, item: &str|if item.expr().head() == Ok(Some("see")) { // TODO Ok(()) } else if let Ok(Some(_word)) = item.expr().head().word() { @@ -241,47 +271,14 @@ impl Config { /// Create, initialize, and watch a new configuration. pub fn watched (callback: impl FnOnce(Arc)->T) -> Usually { let config = Self::init_new(None)?; - Self::watch(config.clone(), None)?; + config_watch(config.clone(), None)?; let result = callback(config); Ok(result) } /// Create and initialize a new configuration. - pub fn init_new (_dirs: Option) -> UsuallyRef<'static, Arc> { - let config = Arc::new(Self::new(None)); - config_init(&config)?; - Ok(config) - } - - /// Watch a config's file for changes. - pub fn watch (config: Arc, poll: Option) -> UsuallyRef<'static, ()> { - let handler = { - let config = config.clone(); - move |result|match result { - Ok(_events) => if let Err(e) = config_init(&config) { - *config.error.write().unwrap() = Some(format!("{e:?}").into()); - panic!("{e:?}"); - } else { - //println!("config updated"); - }, - Err(errors) => { - panic!("{errors:?}"); - } - } - }; - let mut watcher = ::notify_debouncer_full::notify::poll::PollWatcher::new( - handler, - NotifyConfig::default() - .with_poll_interval(poll.unwrap_or(Duration::from_millis(250))) - )?; - if let Some(path) = config.get_file() { - //println!("watching: {path:?}"); - watcher.watch(&path, RecursiveMode::NonRecursive)?; - *config.watch.write().unwrap() = Some(watcher); - Ok(()) - } else { - Err(format!("no config path").into()) - } + pub fn init_new (_dirs: Option) -> Usually> { + config_init(Arc::new(Self::new(None))) } /// Find the config file. @@ -316,6 +313,10 @@ impl Config { *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::*; diff --git a/src/device/arrange/track.rs b/src/device/arrange/track.rs index 895a9689..c6d9712d 100644 --- a/src/device/arrange/track.rs +++ b/src/device/arrange/track.rs @@ -371,32 +371,33 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra button_2("T", "+", false), button_2("S", "+", false), ), - bg(theme.darker.term, iter_east(||self.tracks_with_sizes().map(|(index, track, x1, _x2)|{ - let b = if selected.track() == Some(index) { - track.color.light.term - } else { - track.color.base.term - }; - bg(b, south( - east!( - "·t", - index, - " ", - fg(Rgb(255, 255, 255), bold(true, &track.name)) - ) - .align_nw() - .full_w(), - "" - )) - .exact_w(track_width(index, track)) - .exact_h(2) - }))) + bg(theme.darker.term, iter_east(||self.tracks_with_sizes() + .map(|(index, track, _x1, _x2)|{ + let b = if selected.track() == Some(index) { + track.color.light.term + } else { + track.color.base.term + }; + bg(b, south( + east!( + "·t", + index, + " ", + fg(Rgb(255, 255, 255), bold(true, &track.name)) + ) + .align_nw() + .full_w(), + "" + )) + .exact_w(track_width(index, track)) + .exact_h(2) + }))) ) ) } /// Draw outputs per track - fn view_track_outputs <'a> (&'a self, theme: ItemTheme, _h: u16) -> impl Draw<'_, Tui> { + fn view_track_outputs <'a> (&'a self, theme: ItemTheme, _h: u16) -> impl Draw<'a, Tui> { view_track_row_section(theme, south(button_2("o", "utput", false).align_w().full_w(), draw(|to: &mut Tui|{ @@ -431,7 +432,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra } /// Draw inputs per track - fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Draw<'_, Tui> { + fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Draw<'a, Tui> { let mut height = 0u16; for track in self.tracks().iter() { height = height.max(track.sequencer.midi_ins.len() as u16); diff --git a/src/tek.rs b/src/tek.rs index e401a3c3..38f1cc18 100644 --- a/src/tek.rs +++ b/src/tek.rs @@ -856,7 +856,7 @@ mod device { } } - pub fn view_device (state: &App) -> impl Draw<'_, Tui> { + pub fn view_device <'a> (state: &'a App) -> impl Draw<'a, Tui> { let selected = state.dialog.device_kind().unwrap(); south( bold(true, "Add device"), @@ -917,7 +917,7 @@ mod draw { if let Some(mode) = self.mode.as_ref().and_then(|m|self.config.get_mode(m)) { let mut error = false; for (index, dsl) in mode.view.iter().enumerate() { - match self.draw_mode(to, dsl) { + match self.interpret(to, dsl) { Ok(None) => {}, Ok(Some(XYWH(.., w, h))) => { self.size.0.store(w as usize, Relaxed); @@ -942,11 +942,7 @@ mod draw { Ok(()) } - fn draw_mode <'a, L: Language + 'a> (&'a self, to: &mut Tui, dsl: L) -> Drawn<'a, u16> { - self.interpret(to, dsl) - } - - fn draw_debug (&self, to: &mut Tui) -> Drawn { + #[allow(unused)] fn draw_debug (&self, to: &mut Tui) -> Drawn<'_, u16> { east( format!("{}x{} ", self.size.0.load(Relaxed), self.size.1.load(Relaxed)), format!("{}% {} ", self.perf.percentage().unwrap_or_default(), self.perf.clock.raw() / 1000000000), @@ -957,7 +953,7 @@ mod draw { impl<'a> Interpret<'a, Tui, Option>> for App { fn interpret (&'a self, to: &mut Tui, dsl: L) -> Drawn<'a, u16> { if let Some(expr) = dsl.expr()? { - interpret_keyword!(self, to, expr, [ + interpret_keyword!(self, to, &expr, [ kw_when, kw_either, kw_split, kw_align, kw_exact, kw_min, kw_max, kw_push, kw_full, kw_tui_text, kw_tui_fg, kw_tui_bg ]); @@ -993,15 +989,10 @@ mod draw { Some(":editor") => "TODO Editor".draw(to), Some(":transport") => view_transport(true, "", "", "").draw(to), Some(":debug") => format!("[{:?}]", to.area()).exact_h(1).draw(to), - Some(_) => { - let views = self.config.views.read().unwrap(); - if let Some(lang) = views.get(word.src()?.unwrap()) { - let lang = lang.clone(); - std::mem::drop(views); - self.draw_mode(to, lang) - } else { - fg(Color::Rgb(128, 32, 32), format!("undefined: {word:?}")).draw(to) - } + Some(_) => if let Some(lang) = self.config.get_view(word) { + self.interpret(to, lang) + } else { + fg(Color::Rgb(128, 32, 32), format!("undefined: {word:?}")).draw(to) }, _ => unreachable!() } diff --git a/tengri b/tengri index e69d4287..41ef6214 160000 --- a/tengri +++ b/tengri @@ -1 +1 @@ -Subproject commit e69d4287e0ee1f751d2f096f7f85b43628cef259 +Subproject commit 41ef62146bd74e3e69fa48eae5b8f1581acee149