fix config loading

This commit is contained in:
i do not exist 2026-08-23 06:12:01 +03:00
parent a126d98f3c
commit 9e25564abb
4 changed files with 116 additions and 123 deletions

View file

@ -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<Config> 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 <C: AsRef<Config>> (config: C) -> Usually<C> {
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 <C: AsRef<Config>, L: Language> (config: C, src: L) -> Usually<C> {
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 <C: AsRef<Config>, L: Language> (config: C, src: L) -> Usually<C> {
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 <C: AsRef<Config>, L: Language> (config: C, src: L) -> Usually<C> {
//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<Config>, poll: Option<Duration>
) -> 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<std::sync::Arc<str>> = 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<Mode> {
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 <T: 'static> (callback: impl FnOnce(Arc<Config>)->T) -> Usually<T> {
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<BaseDirectories>) -> UsuallyRef<'static, Arc<Self>> {
let config = Arc::new(Self::new(None));
config_init(&config)?;
Ok(config)
}
/// Watch a config's file for changes.
pub fn watch (config: Arc<Config>, poll: Option<Duration>) -> 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<BaseDirectories>) -> Usually<Arc<Self>> {
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<str>) -> Option<Arc<str>> {
self.views.read().unwrap().get(name.as_ref()).cloned()
}
}
pub use self::view::*;

View file

@ -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);

View file

@ -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<u16> {
#[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<XYWH<u16>>> for App {
fn interpret <L: Language + 'a> (&'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!()
}

2
tengri

@ -1 +1 @@
Subproject commit e69d4287e0ee1f751d2f096f7f85b43628cef259
Subproject commit 41ef62146bd74e3e69fa48eae5b8f1581acee149