use libtui; use libtui::widget; use libtui::widget::list; use libtui::layout; use encoding::utf8; use io; use fmt; use os; use strings; use unix::tty; use unix::signal; use unix::poll; use errors; use getopt; use ev; let searchterm: str = ""; let searchforward: bool = true; let nulterm: bool = false; let msg: str = ""; fn resize(l: *list::listwidget) void = { let sz = libtui::getwinsize(l.widget.ui)!; let rows: u16 = if (sz.rows - 1 < len(l.items)) { yield sz.rows - 1; } else { yield len(l.items): u16; }; const oldsz = list::ttysize { rows = l.sz.rows, cols = l.sz.cols, }; list::setsize(l, tty::ttysize { rows = rows, columns = sz.columns, }); list::resize(l, oldsz); }; type state = struct { ui: *libtui::ttyui, l: *list::listwidget, layout: libtui::layout::layout, }; fn handle_signal(file: *ev::file, sig: signal::sig) void = { if (sig == signal::sig::WINCH) { let st = ev::getuser(file): *state; resize(st.l); }; }; fn handle_ui(file: *ev::file) void = { let st = ev::getuser(file): *state; let r = match (libtui::scan(*st.ui)) { case let r: rune => yield r; case utf8::invalid => fmt::fprintln(os::stderr, "Invalid utf8 sequence")!; ev::stop(ev::getloop(file)); return; case io::EOF => fmt::fprintln(os::stderr, "EOF")!; ev::stop(ev::getloop(file)); return; case let e: io::error => fmt::fprintln(os::stderr, io::strerror(e))!; ev::stop(ev::getloop(file)); return; }; if (libtui::notify(st.ui, r)) { ev::stop(ev::getloop(file)); }; if (list::notify(st.l, r)) { ev::stop(ev::getloop(file)); }; ev::readable(file, &handle_ui); }; fn handle_print(loop: *ev::loop, user: nullable *opaque) void = { let st = user: *state; match (layout::print(st.layout)) { case void => yield; case let e: io::error => fmt::fprintln(os::stderr, io::strerror(e))!; ev::stop(loop); case let e: tty::error => fmt::fprintln(os::stderr, tty::strerror(e))!; ev::stop(loop); }; ev::do(loop, &handle_print, st); }; export fn main() void = { const cmd = getopt::parse(os::args, "interactive list", ('0', "NUL terminated output"), ('m', "msg", "string to print when 'h' is pressed (this can be used for custom help messages or building basic extra external features)"), ('s', "query", "search for a given item when opening the il"), ); defer getopt::finish(&cmd); for (let i = 0z; i < len(cmd.opts); i += 1) { const opt = cmd.opts[i]; switch (opt.0) { case '0' => nulterm = true; case 'm' => msg = opt.1; case 's' => searchterm = strings::dup(opt.1); case => yield; }; }; let in = match (io::drain(os::stdin)) { case let in: []u8 => yield in; case let e: io::error => fmt::fatal(io::strerror(e)); }; defer free(in); let sin = strings::fromutf8(in)!; sin = strings::trim(sin, '\n'); let items = strings::split(sin, "\n"); // freed by list::finish itemscopy = strings::dupall(items); defer strings::freeall(itemscopy); let ui = libtui::init(); defer libtui::finish(&ui); let sz = libtui::getwinsize(ui)!; let rows: u16 = if (sz.rows - 1u16 < len(items)) { yield sz.rows - 1u16; } else { yield len(items): u16; }; let l = list::newlistsz(ui, tty::ttysize { rows = rows, columns = sz.columns, }, items...); libtui::addlistener(&ui, &globalrunehandler); widget::addlistener(&l, &runehandler); let layout = layout::newlayout(&l); defer layout::finishall(&layout); if (searchterm != "") { list::search(&l, searchterm); }; //defer free(searchterm); //libtui::clear(l.widget.ui); let loop = ev::newloop()!; defer ev::finish(&loop); let st = state { ui = &ui, l = &l, layout = layout, }; const signal = ev::signal(&loop, &handle_signal, signal::sig::WINCH)!; defer ev::close(signal); ev::setuser(signal, &st); let mainf = ev::register(&loop, ui.f, &st)!; ev::readable(mainf, &handle_ui); ev::do(&loop, &handle_print, &st); for (ev::dispatch(&loop, -1)!) { void; }; }; @fini fn finish() void = { free(searchterm); };