let browser = |
#selected_style: Style = style(#bg:`Indexed(20), #fg:`Indexed(227)),
#header_style: Style = style(#fg:`Indexed(111)),
#style: Style = style(#fg:`Indexed(227), #bg:`Indexed(234)),
#cursor: MoveCursor = never(),
#selected_row: &string = &never(),
#selected_path: &string = &never(),
#flex: Flex = `SpaceBetween,
#rate: duration = duration:0.5s,
#size: Size,
path: string
| -> Tui {
let selected: {x: i64, y: i64} = { x: 1, y: 0 };
let viewport: {x: i64, y: i64} = { x: 0, y: 0 };
select uniq(path) {
p => {
selected <- p ~ { x: 1, y: 0 };
viewport <- p ~ { x: 0, y: 0 }
}
};
let (mode, structure) = select net::list_table(path)$ {
{ columns, rows } => {
let mode = uniq(select columns {
[] => `List,
_ => `Table
});
let columns: Array<string> = select array::map(columns, |(col, _)| col) {
[] => ["Value"],
cols => array::sort(#numeric:true, cols)
};
let rows: Array<string> = {
let base = select str::dirname(rows[0]$) {
null as _ => "/",
s => s
};
let names = array::filter_map(rows, |r| str::basename(r));
let names = array::sort(#numeric:true, names);
array::map(names, |name| select base {
"/" => "/[name]",
base => "[base]/[name]"
})
};
(mode, { columns, rows })
}
};
let visible_rows = select structure.rows {
[] => [],
rows => {
let len = array::len(rows);
let start = min(len - 1, viewport.y);
let end = min(len, viewport.y + size.height - 1);
(rows)[start .. end]$
}
};
let content_widths: Array<i64> = [];
let get_width = |i| try any(content_widths[i]?, i ~ 0) catch(e) => never(e);
let { total: _, cols: visible_columns, widths }:
{ total: i64, cols: Array<string>, widths: Array<Constraint> } = {
let name_width = max(4, get_width(0));
let max_width = size.width / 2;
let init = {
total: size.width - name_width,
cols: [],
widths: [`Min(name_width)]
};
let candidates = array::enumerate((structure.columns)[viewport.x ..]$);
let candidates = any(content_widths ~ candidates, candidates);
array::fold(candidates, init, |{total, cols, widths}, (i, col)| {
let len = min(max_width, max(str::len(col), get_width(i + 1)));
select total {
x if x > 0 => {
total: x - len - 4,
cols: array::push(cols, col),
widths: array::push(widths, `Min(len))
},
x => { total: x - len - 4, cols, widths }
}
})
};
let visible_columns: Array<string> = uniq(visible_columns);
*selected_row <- visible_rows[selected.y]$;
*selected_path <- select mode {
`List => "[*selected_row]",
`Table => "[*selected_row]/[visible_columns[selected.x - 1]$]"
};
let header = select mode {
`List => row(#style:header_style, [cell(line("Name")), cell(line("Value"))]),
`Table => {
let cols = array::map(visible_columns, |col| cell(line(col)));
row(#style:header_style, array::push_front(cols, cell(line("Name"))))
}
};
let rows = array::map(visible_rows, |row| {
let name = select str::basename(row) { null as _ => "", s => s };
select mode {
`List => {
let v: Result<Primitive, [`InvalidCast(string), `SubscribeError(string)]> =
throttle(#rate, net::subscribe("[row]"));
[name, "[any(uniq(row) ~ "#SUB", v)]"]
},
`Table => {
let a = array::map(visible_columns, |col| {
let path = "[row]/[col]";
let v: Result<Primitive, [`InvalidCast(string), `SubscribeError(string)]> =
throttle(#rate, net::subscribe(path));
"[any(uniq(path) ~ "#SUB", v)]"
});
array::push_front(a, name)
}
}
});
content_widths <- throttle(#rate:duration:1.0s,
array::fold(rows, [], |acc, row| {
let row = array::map(row, |s| str::len(s));
select acc {
[] => row,
acc => array::map(array::zip(row, acc), |(l0, l1)| max(l0, l1))
}
}));
let rows = array::map(rows, |row|
&tui::table::row(array::map(array::enumerate(row), |(i, s)| select i {
0 => cell(#style:header_style, line(s)),
_ => cell(line(s))
})));
select cursor {
`Left(n) => select n ~ selected.x {
e if e <= 1 =>
viewport <- uniq(e ~ { viewport with x: max(0, viewport.x - n) }),
e => selected <- e ~ { selected with x: max(1, selected.x - n) }
},
`Right(n) => {
let limit = array::len(visible_columns);
let clim = array::len(structure.columns) - array::len(visible_columns);
select n ~ selected.x {
x if x >= limit =>
viewport <- uniq(x ~ { viewport with x: min(clim, viewport.x + n) }),
e => selected <- e ~ {selected with x: min(limit, selected.x + n)}
}
},
`Down(n) => {
let rlim = array::len(structure.rows) - array::len(visible_rows);
let limit = array::len(visible_rows) - 1;
select n ~ selected.y {
x if x >= limit =>
viewport <- uniq(x ~ { viewport with y: min(rlim, viewport.y + n) }),
e => selected <- e ~ { selected with y: min(limit, selected.y + n) }
}
},
`Up(n) => select n ~ selected.y {
y if y <= 0 =>
viewport <- uniq(y ~ { viewport with y: max(0, viewport.y - n) }),
y => selected <- y ~ { selected with y: max(0, selected.y - n) }
}
};
table(
#cell_highlight_style:&selected_style,
#header:&header,
#widths:&widths,
#flex:&flex,
#selected_cell: &selected,
#style:&style,
&rows
)
}