wip: new error
Some checks failed
/ build (push) Has been cancelled

This commit is contained in:
🪞👃🪞 2025-08-16 16:28:43 +03:00
parent 08730df042
commit c0add81ff4
3 changed files with 155 additions and 148 deletions

View file

@ -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 --"

View file

@ -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<str>, 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<Option<TuiEvent>, Arc<str>>, 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<str>, 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<Arc<str>>, 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<dyn Render<TuiOut>>>,
src: &str
) -> Box<dyn Render<TuiOut>> {
let sym_err: Option<Box<dyn Error>> = match state.from_sym(src) {
let err: Option<Box<dyn Error>> = 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<TuiOut>) -> impl Content<TuiOut> {
@ -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<usize> {
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<dyn Render<TuiOut>> => {
("bold", value: bool, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::bold(value, x)),
isize;
("fg", color: Color, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::fg(color, x)),
("bg", color: Color, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::bg(color, x)),
("fg/bg", fg: Color, bg: Color, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::fg_bg(fg, bg, x)),
ItemTheme => {
":_theme_stub" => Default::default()
};
("bsp/n", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::n(a, b)),
("bsp/s", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::s(a, b)),
("bsp/e", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::e(a, b)),
("bsp/w", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::w(a, b)),
("bsp/a", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::a(a, b)),
("bsp/b", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => 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<dyn Render<TuiOut>>) => Box::new(Align::n(x)),
("align/s", x: Box<dyn Render<TuiOut>>) => Box::new(Align::s(x)),
("align/e", x: Box<dyn Render<TuiOut>>) => Box::new(Align::e(x)),
("align/w", x: Box<dyn Render<TuiOut>>) => Box::new(Align::w(x)),
("align/x", x: Box<dyn Render<TuiOut>>) => Box::new(Align::x(x)),
("align/y", x: Box<dyn Render<TuiOut>>) => Box::new(Align::y(x)),
("align/c", x: Box<dyn Render<TuiOut>>) => 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<dyn Render<TuiOut>>) => Box::new(Fill::x(x)),
("fill/y", x: Box<dyn Render<TuiOut>>) => Box::new(Fill::y(x)),
("fill/xy", x: Box<dyn Render<TuiOut>>) => Box::new(Fill::xy(x)),
("fixed/x", x: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Fixed::x(x, c)),
("fixed/y", y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Fixed::y(y, c)),
("fixed/xy", x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Fixed::xy(x, y, c)),
("min/x", x: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Min::x(x, c)),
("min/y", y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Min::y(y, c)),
("min/xy", x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Min::xy(x, y, c)),
("max/x", x: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Max::x(x, c)),
("max/y", y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Max::y(y, c)),
("max/xy", x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => 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<TuiOut>)|{
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("<no name>");
let info = profile.info.get(0).map(|x|x.as_ref()).unwrap_or("<no info>");
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<dyn Render<TuiOut>> => {
("bold", value: bool, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::bold(value, x)),
}
("fg", color: Color, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::fg(color, x)),
("bg", color: Color, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::bg(color, x)),
("fg/bg", fg: Color, bg: Color, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::fg_bg(fg, bg, x)),
("bsp/n", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::n(a, b)),
("bsp/s", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::s(a, b)),
("bsp/e", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::e(a, b)),
("bsp/w", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::w(a, b)),
("bsp/a", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::a(a, b)),
("bsp/b", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::b(a, b)),
("align/n", x: Box<dyn Render<TuiOut>>) => Box::new(Align::n(x)),
("align/s", x: Box<dyn Render<TuiOut>>) => Box::new(Align::s(x)),
("align/e", x: Box<dyn Render<TuiOut>>) => Box::new(Align::e(x)),
("align/w", x: Box<dyn Render<TuiOut>>) => Box::new(Align::w(x)),
("align/x", x: Box<dyn Render<TuiOut>>) => Box::new(Align::x(x)),
("align/y", x: Box<dyn Render<TuiOut>>) => Box::new(Align::y(x)),
("align/c", x: Box<dyn Render<TuiOut>>) => Box::new(Align::c(x)),
("fill/x", x: Box<dyn Render<TuiOut>>) => Box::new(Fill::x(x)),
("fill/y", x: Box<dyn Render<TuiOut>>) => Box::new(Fill::y(x)),
("fill/xy", x: Box<dyn Render<TuiOut>>) => Box::new(Fill::xy(x)),
("fixed/x", x: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Fixed::x(x, c)),
("fixed/y", y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Fixed::y(y, c)),
("fixed/xy", x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Fixed::xy(x, y, c)),
("min/x", x: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Min::x(x, c)),
("min/y", y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Min::y(y, c)),
("min/xy", x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Min::xy(x, y, c)),
("max/x", x: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Max::x(x, c)),
("max/y", y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Max::y(y, c)),
("max/xy", x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => 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<TuiOut>)|{
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("<no name>");
let info = profile.info.get(0).map(|x|x.as_ref()).unwrap_or("<no info>");
//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;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

2
deps/tengri vendored

@ -1 +1 @@
Subproject commit 4fc0db577737ad1ce2601aa99d45248dba9a2d5f
Subproject commit 1cc905485fe42f36be4802cec29fee155a3ae295