From c0add81ff4043703fb63a8b6f6b79e5359a2ad33 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 16 Aug 2025 16:28:43 +0300 Subject: [PATCH] wip: new error --- Justfile | 1 + crates/app/app.rs | 300 +++++++++++++++++++++++----------------------- deps/tengri | 2 +- 3 files changed, 155 insertions(+), 148 deletions(-) diff --git a/Justfile b/Justfile index 475359ee..569c216f 100644 --- a/Justfile +++ b/Justfile @@ -1,4 +1,5 @@ export RUSTFLAGS := "--cfg procmacro2_semver_exempt -Zmacro-backtrace" +export RUST_BACKTRACE := "1" debug := "reset && cargo run --" release := "reset && cargo run --release --" diff --git a/crates/app/app.rs b/crates/app/app.rs index 5e2c5495..4476a88e 100644 --- a/crates/app/app.rs +++ b/crates/app/app.rs @@ -111,10 +111,11 @@ impl Config { } pub fn load_defs (&mut self, dsl: impl Dsl) -> Usually<()> { dsl.each(|item|{ - match item.exp().head() { - Ok(Some("keys")) if let Some(id) = item.exp().tail().head()? => + println!("{item:?} {:?}", item.expr().head()); + match item.expr().head() { + Ok(Some("keys")) if let Some(id) = item.expr().tail().head()? => self.load_bind(id.into(), item), - Ok(Some("mode")) if let Some(id) = item.exp().tail().head()? => + Ok(Some("mode")) if let Some(id) = item.expr().tail().head()? => self.load_mode(id.into(), item), _ => return Err(format!("load_defs: unexpected: {item:?}").into()) } @@ -122,48 +123,46 @@ impl Config { } pub fn load_bind (&mut self, id: Arc, item: impl Dsl) -> Usually<()> { let mut map = EventMap::new(); - item.exp().tail().tail()?.each(|item|Self::load_bind_one(&mut map, item))?; - self.binds.write().unwrap().insert(id, map); - Ok(()) - } - fn load_bind_one (map: &mut EventMap, Arc>, item: impl Dsl) -> Usually<()> { - if let Ok(Some(sym)) = item.exp().head().sym() { - map.add(TuiEvent::from_dsl(item.exp()?.head()?)?, Binding { - command: item.exp()?.tail()?.unwrap_or_default().into(), + item.expr().tail().tail()?.each(|item|if item.expr().head() == Ok(Some("see")) { + // TODO + Ok(()) + } else if let Ok(Some(word)) = item.expr().head().word() { + map.add(TuiEvent::from_dsl(item.expr()?.head()?)?, Binding { + commands: [item.expr()?.tail()?.unwrap_or_default().into()].into(), condition: None, description: None, source: None }); - } else if item.exp().head() == Ok(Some("see")) { - // TODO + Ok(()) } else { - return Err(format!("load_defs: unexpected: {item:?}").into()) - } + return Err(format!("load_bind: unexpected: {item:?}").into()) + })?; + self.binds.write().unwrap().insert(id, map); Ok(()) } pub fn load_mode (&mut self, id: Arc, item: impl Dsl) -> Usually<()> { let mut mode = Mode::default(); - item.exp().tail().tail()?.each(|item|Self::load_mode_one(&mut mode, item))?; + item.expr().tail().tail()?.each(|item|Self::load_mode_one(&mut mode, item))?; self.modes.write().unwrap().insert(id.into(), Arc::new(mode)); Ok(()) } pub fn load_mode_one (mode: &mut Mode>, item: impl Dsl) -> Usually<()> { - Ok(if let Ok(Some(key)) = item.exp().head() { + Ok(if let Ok(Some(key)) = item.expr().head() { match key { - "name" => mode.name.push(item.exp()?.tail()?.map(|x|x.trim()).unwrap_or("").into()), - "info" => mode.info.push(item.exp()?.tail()?.map(|x|x.trim()).unwrap_or("").into()), - "keys" => mode.keys.push(item.exp()?.tail()?.map(|x|x.trim()).unwrap_or("").into()), - "mode" => if let Some(id) = item.exp()?.tail()?.head()? { + "name" => mode.name.push(item.expr()?.tail()?.map(|x|x.trim()).unwrap_or("").into()), + "info" => mode.info.push(item.expr()?.tail()?.map(|x|x.trim()).unwrap_or("").into()), + "keys" => mode.keys.push(item.expr()?.tail()?.map(|x|x.trim()).unwrap_or("").into()), + "mode" => if let Some(id) = item.expr()?.tail()?.head()? { let mut submode = Mode::default(); - Self::load_mode_one(&mut submode, item.exp()?.tail()?.tail()?)?; + Self::load_mode_one(&mut submode, item.expr()?.tail()?.tail()?)?; mode.modes.insert(id.into(), submode); } else { return Err(format!("load_mode_one: incomplete: {item:?}").into()); }, - _ => mode.view.push(item.exp()?.unwrap().into()), + _ => mode.view.push(item.expr()?.unwrap().into()), } - } else if let Ok(Some(sym)) = item.sym() { - mode.view.push(sym.into()); + } else if let Ok(Some(word)) = item.word() { + mode.view.push(word.into()); } else { return Err(format!("load_mode_one: unexpected: {item:?}").into()); }) @@ -210,20 +209,24 @@ fn render_dsl <'t> ( state: &'t impl DslNs<'t, Box>>, src: &str ) -> Box> { - let sym_err: Option> = match state.from_sym(src) { + let err: Option> = match state.from(src) { Ok(Some(value)) => return value, Ok(None) => None, Err(e) => Some(e), }; - let exp_err = match state.from_exp(src) { - Ok(Some(value)) => return value, Ok(None) => None, Err(e) => Some(e), - }; - let (err_fg_1, err_bg_1) = (Color::Rgb(240, 160, 100), Color::Rgb(48, 0, 0)); - let (err_fg_2, err_bg_2) = (Color::Rgb(250, 200, 120), Color::Rgb(32, 0, 0)); - Box::new(Fill::x(col! { - Fill::x(Margin::x(1, Align::w(Tui::bold(true, Tui::fg_bg(err_fg_1, err_bg_1, "Could not render:"))))), - Fill::x(Margin::x(1, Tui::fg_bg(err_fg_2, err_bg_2, format!("{src}")))), - Fill::x(Margin::x(1, Tui::fg_bg(err_fg_2, err_bg_2, format!("{sym_err:?}")))), - Fill::x(Margin::x(1, Tui::fg_bg(err_fg_2, err_bg_2, format!("{exp_err:?}")))), - })) + let (fg_1, bg_1) = (Color::Rgb(240, 160, 100), Color::Rgb(48, 0, 0)); + let (fg_2, bg_2) = (Color::Rgb(250, 200, 180), Color::Rgb(48, 0, 0)); + let (fg_3, bg_3) = (Color::Rgb(250, 200, 120), Color::Rgb(0, 0, 0)); + let bg = Color::Rgb(24, 0, 0); + Box::new(col! { + Tui::fg(bg, Fixed::y(1, Fill::x(RepeatH("▄")))), + Tui::bg(bg, col! { + Fill::x(Bsp::e( + Tui::bold(true, Tui::fg_bg(fg_1, bg_1, " Render error: ")), + Tui::fg_bg(fg_2, bg_2, err.map(|e|format!(" {e} "))), + )), + Fill::x(Align::x(Tui::fg_bg(fg_3, bg_3, format!(" {src} ")))), + }), + Tui::fg(bg, Fixed::y(1, Fill::x(RepeatH("▀")))), + }) } fn wrap_dialog (dialog: impl Content) -> impl Content { @@ -253,11 +256,6 @@ handle!(TuiIn:|self: App, input|{ }); #[derive(Debug)] pub enum AppCommand { /* TODO */ } -impl App { - pub fn editor_focused (&self) -> bool { - false - } -} impl Dialog { pub fn menu_selected (&self) -> Option { if let Self::Menu(selected) = self { Some(*selected) } else { None } @@ -293,6 +291,9 @@ impl Dialog { } impl App { + pub fn editor_focused (&self) -> bool { + false + } pub fn toggle_dialog (&mut self, mut dialog: Dialog) -> Dialog { std::mem::swap(&mut self.dialog, &mut dialog); dialog @@ -371,27 +372,99 @@ impl App { } } -dsl_ns!(|app: App| +dsl_ns! { |app: App| - u8; + Box> => { + ("bold", value: bool, x: Box>) => Box::new(Tui::bold(value, x)), - isize; + ("fg", color: Color, x: Box>) => Box::new(Tui::fg(color, x)), + ("bg", color: Color, x: Box>) => Box::new(Tui::bg(color, x)), + ("fg/bg", fg: Color, bg: Color, x: Box>) => Box::new(Tui::fg_bg(fg, bg, x)), - ItemTheme => { - ":_theme_stub" => Default::default() - }; + ("bsp/n", a: Box>, b: Box>) => Box::new(Bsp::n(a, b)), + ("bsp/s", a: Box>, b: Box>) => Box::new(Bsp::s(a, b)), + ("bsp/e", a: Box>, b: Box>) => Box::new(Bsp::e(a, b)), + ("bsp/w", a: Box>, b: Box>) => Box::new(Bsp::w(a, b)), + ("bsp/a", a: Box>, b: Box>) => Box::new(Bsp::a(a, b)), + ("bsp/b", a: Box>, b: Box>) => Box::new(Bsp::b(a, b)), - u16 => { - ":w/sidebar" => app.project.w_sidebar(app.editor().is_some()), - ":h/sample-detail" => 6.max(app.height() as u16 * 3 / 9), - }; + ("align/n", x: Box>) => Box::new(Align::n(x)), + ("align/s", x: Box>) => Box::new(Align::s(x)), + ("align/e", x: Box>) => Box::new(Align::e(x)), + ("align/w", x: Box>) => Box::new(Align::w(x)), + ("align/x", x: Box>) => Box::new(Align::x(x)), + ("align/y", x: Box>) => Box::new(Align::y(x)), + ("align/c", x: Box>) => Box::new(Align::c(x)), - usize => { - ":scene-count" => app.scenes().len(), - ":track-count" => app.tracks().len(), - ":device-kind" => app.dialog.device_kind().unwrap_or(0), - ":device-kind/next" => app.dialog.device_kind_next().unwrap_or(0), - ":device-kind/prev" => app.dialog.device_kind_prev().unwrap_or(0), + ("fill/x", x: Box>) => Box::new(Fill::x(x)), + ("fill/y", x: Box>) => Box::new(Fill::y(x)), + ("fill/xy", x: Box>) => Box::new(Fill::xy(x)), + + ("fixed/x", x: u16, c: Box>) => Box::new(Fixed::x(x, c)), + ("fixed/y", y: u16, c: Box>) => Box::new(Fixed::y(y, c)), + ("fixed/xy", x: u16, y: u16, c: Box>) => Box::new(Fixed::xy(x, y, c)), + + ("min/x", x: u16, c: Box>) => Box::new(Min::x(x, c)), + ("min/y", y: u16, c: Box>) => Box::new(Min::y(y, c)), + ("min/xy", x: u16, y: u16, c: Box>) => Box::new(Min::xy(x, y, c)), + + ("max/x", x: u16, c: Box>) => Box::new(Max::x(x, c)), + ("max/y", y: u16, c: Box>) => Box::new(Max::y(y, c)), + ("max/xy", x: u16, y: u16, c: Box>) => Box::new(Max::xy(x, y, c)), + + ":view/menu" => app.view(stringify!( + (bg :color/bg (bsp/s :view/ports/outs (bsp/n :view/ports/ins :view/modes))) + )), + + ":view/modes" => Box::new({ + let modes = app.config.modes.clone(); + let height = (modes.read().unwrap().len() * 2) as u16; + Fixed::y(height, + Fill::x(Stack::south(move|add: &mut dyn FnMut(&dyn Render)|{ + for (index, (id, profile)) in modes.read().unwrap().iter().enumerate() { + let bg = if index == 0 { Rgb(48,64,32) } else { Rgb(16, 32, 24) }; + let name = profile.name.get(0).map(|x|x.as_ref()).unwrap_or(""); + let info = profile.info.get(0).map(|x|x.as_ref()).unwrap_or(""); + let fg1 = Rgb(224, 192, 128); + let fg2 = Rgb(224, 128, 32); + add(&Fixed::y(2, Fill::x(Tui::bg(bg, Bsp::s( + Fill::x(Bsp::a(Fill::x(Align::w(Tui::fg(fg1, name))), + Fill::x(Align::e(Tui::fg(fg2, id))))), + Fill::x(Align::w(info))))))); + } + })))}), + + ":view/ports/outs" => + app.view(stringify!((fill/x (fixed/y 3 + (bsp/a (fill/x (align/w "L AUDIO OUT") + (bsp/a "MIDI OUT" (fill/x (align/e "AUDIO OUT R"))))))))), + ":view/ports/ins" => + app.view(stringify!(fill/x (fixed/y 3 + (bsp/a (fill/x (align/w "L AUDIO IN ") + (bsp/a "MIDI IN " (fill/x (align/e "AUDIO IN R")))))))), + ":view/browse" => + app.view(stringify!(bsp/s + (padding/xy 3 1 :view/browse-title) (enclose (fg (g 96)) :view/browser))), + ":view/browse/title" => + Box::new(Fill::x(Align::w(FieldV(Default::default(), + match app.dialog.browser_target().unwrap() { + BrowserTarget::SaveProject => "Save project:", + BrowserTarget::LoadProject => "Load project:", + BrowserTarget::ImportSample(_) => "Import sample:", + BrowserTarget::ExportSample(_) => "Export sample:", + BrowserTarget::ImportClip(_) => "Import clip:", + BrowserTarget::ExportClip(_) => "Export clip:", + }, Shrink::x(3, Fixed::y(1, Tui::fg(Tui::g(96), RepeatH("🭻")))))))), + ":view/device" => { + let selected = app.dialog.device_kind().unwrap(); + Box::new(Bsp::s(Tui::bold(true, "Add device"), Map::south(1, + move||device_kinds().iter(), + move|label: &&'static str, i|{ + let bg = if i == selected { Rgb(64,128,32) } else { Rgb(0,0,0) }; + let lb = if i == selected { "[ " } else { " " }; + let rb = if i == selected { " ]" } else { " " }; + Fill::x(Tui::bg(bg, Bsp::e(lb, Bsp::w(rb, "FIXME device name")))) }))) }, + //(":view/options", view_options), }; bool => { @@ -414,6 +487,10 @@ dsl_ns!(|app: App| Selection::Mix), }; + ItemTheme => { + ":_theme_stub" => Default::default() + }; + Dialog => { ":dialog/none" => Dialog::None, ":dialog/options" => Dialog::Options, @@ -490,99 +567,28 @@ dsl_ns!(|app: App| Color => { ("g", n: u8) => Color::Rgb(n, n, n), - ("rgb", r: u8, g: u8, b: u8) => Color::Rgb(r, g, b), + ("rgb", red: u8, green: u8, blue: u8) => Color::Rgb(red, green, blue), + ":color/bg" => Color::Rgb(28, 32, 36), + }; - Box> => { - ("bold", value: bool, x: Box>) => Box::new(Tui::bold(value, x)), +} - ("fg", color: Color, x: Box>) => Box::new(Tui::fg(color, x)), - ("bg", color: Color, x: Box>) => Box::new(Tui::bg(color, x)), - ("fg/bg", fg: Color, bg: Color, x: Box>) => Box::new(Tui::fg_bg(fg, bg, x)), - - ("bsp/n", a: Box>, b: Box>) => Box::new(Bsp::n(a, b)), - ("bsp/s", a: Box>, b: Box>) => Box::new(Bsp::s(a, b)), - ("bsp/e", a: Box>, b: Box>) => Box::new(Bsp::e(a, b)), - ("bsp/w", a: Box>, b: Box>) => Box::new(Bsp::w(a, b)), - ("bsp/a", a: Box>, b: Box>) => Box::new(Bsp::a(a, b)), - ("bsp/b", a: Box>, b: Box>) => Box::new(Bsp::b(a, b)), - - ("align/n", x: Box>) => Box::new(Align::n(x)), - ("align/s", x: Box>) => Box::new(Align::s(x)), - ("align/e", x: Box>) => Box::new(Align::e(x)), - ("align/w", x: Box>) => Box::new(Align::w(x)), - ("align/x", x: Box>) => Box::new(Align::x(x)), - ("align/y", x: Box>) => Box::new(Align::y(x)), - ("align/c", x: Box>) => Box::new(Align::c(x)), - - ("fill/x", x: Box>) => Box::new(Fill::x(x)), - ("fill/y", x: Box>) => Box::new(Fill::y(x)), - ("fill/xy", x: Box>) => Box::new(Fill::xy(x)), - - ("fixed/x", x: u16, c: Box>) => Box::new(Fixed::x(x, c)), - ("fixed/y", y: u16, c: Box>) => Box::new(Fixed::y(y, c)), - ("fixed/xy", x: u16, y: u16, c: Box>) => Box::new(Fixed::xy(x, y, c)), - - ("min/x", x: u16, c: Box>) => Box::new(Min::x(x, c)), - ("min/y", y: u16, c: Box>) => Box::new(Min::y(y, c)), - ("min/xy", x: u16, y: u16, c: Box>) => Box::new(Min::xy(x, y, c)), - - ("max/x", x: u16, c: Box>) => Box::new(Max::x(x, c)), - ("max/y", y: u16, c: Box>) => Box::new(Max::y(y, c)), - ("max/xy", x: u16, y: u16, c: Box>) => Box::new(Max::xy(x, y, c)), - - ":view/menu" => - app.view(stringify!((bg (rgb 0 0 0) - (bsp/s :view/ports/outs (bsp/s (bg (rgb 33 33 33) (bold :true "tek 0.3.0-rc.0")) - (bsp/n :view/ports/ins (bsp/n - (bg (rgb 33 33 33) (bsp/e (fg (rgb 255 192 48) "[Enter]") " new session")) - (align/n (fill/xy :view/modes))))))))), - ":view/modes" => Box::new({ - let modes = app.config.modes.clone(); - Stack::south(move|add: &mut dyn FnMut(&dyn Render)|{ - for (index, (id, profile)) in modes.read().unwrap().iter().enumerate() { - let bg = if index == 0 { Rgb(64,64,64) } else { Rgb(32,32,32) }; - let name = profile.name.get(0).map(|x|x.as_ref()).unwrap_or(""); - let info = profile.info.get(0).map(|x|x.as_ref()).unwrap_or(""); - //add(&app.view(stringify!((fixed/y 2 (bg #bg (bsp/s - //(fill/x (bsp/a (fill/x (align/w (fg (rgb 224 192 128) #name))) - //(fill/x (align/e (fg (rgb 224 128 32) #id))))) - //(fill/x (align/w #info)))))))); - } - }) }), - ":view/ports/outs" => - app.view(stringify!((fill/x (fixed/y 3 - (bsp/a (fill/x (align/w "L AUDIO OUT") - (bsp/a "MIDI OUT" (fill/x (align/e "AUDIO OUT R"))))))))), - ":view/ports/ins" => - app.view(stringify!(fill/x (fixed/y 3 - (bsp/a (fill/x (align/w "L AUDIO IN ") - (bsp/a "MIDI IN " (fill/x (align/e "AUDIO IN R")))))))), - ":view/browse" => - app.view(stringify!(bsp/s - (padding/xy 3 1 :view/browse-title) (enclose (fg (g 96)) :view/browser))), - ":view/browse/title" => - Box::new(Fill::x(Align::w(FieldV(Default::default(), - match app.dialog.browser_target().unwrap() { - BrowserTarget::SaveProject => "Save project:", - BrowserTarget::LoadProject => "Load project:", - BrowserTarget::ImportSample(_) => "Import sample:", - BrowserTarget::ExportSample(_) => "Export sample:", - BrowserTarget::ImportClip(_) => "Import clip:", - BrowserTarget::ExportClip(_) => "Export clip:", - }, Shrink::x(3, Fixed::y(1, Tui::fg(Tui::g(96), RepeatH("🭻")))))))), - ":view/device" => { - let selected = app.dialog.device_kind().unwrap(); - Box::new(Bsp::s(Tui::bold(true, "Add device"), Map::south(1, - move||device_kinds().iter(), - move|label: &&'static str, i|{ - let bg = if i == selected { Rgb(64,128,32) } else { Rgb(0,0,0) }; - let lb = if i == selected { "[ " } else { " " }; - let rb = if i == selected { " ]" } else { " " }; - Fill::x(Tui::bg(bg, Bsp::e(lb, Bsp::w(rb, "FIXME device name")))) }))) }, - //(":view/options", view_options), +dsl_ns! { num |app: App| + u8; + u16 => { + ":w/sidebar" => app.project.w_sidebar(app.editor().is_some()), + ":h/sample-detail" => 6.max(app.height() as u16 * 3 / 9), }; -); + usize => { + ":scene-count" => app.scenes().len(), + ":track-count" => app.tracks().len(), + ":device-kind" => app.dialog.device_kind().unwrap_or(0), + ":device-kind/next" => app.dialog.device_kind_next().unwrap_or(0), + ":device-kind/prev" => app.dialog.device_kind_prev().unwrap_or(0), + }; + isize; +} /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/deps/tengri b/deps/tengri index 4fc0db57..1cc90548 160000 --- a/deps/tengri +++ b/deps/tengri @@ -1 +1 @@ -Subproject commit 4fc0db577737ad1ce2601aa99d45248dba9a2d5f +Subproject commit 1cc905485fe42f36be4802cec29fee155a3ae295