use tui; use tui::widget::list; use tui::layout; use bufio; use os; use strings; use fmt; use io; export fn main() void = { const scanner = bufio::newscanner(os::stdin); defer bufio::finish(&scanner); let items: []str = []; for (let line: const str => bufio::scan_line(&scanner)!) { append(items, strings::dup(line)); }; defer strings::freeall(items); // static slice allocation //let buf: [1024]str = [""...]; //let items = buf[..0]; //for (let line: const str => bufio::scan_line(&scanner)!) { // match (static append(items, strings::dup(line))) { // case void => // void; // case nomem => // fmt::fatal("Error: too many items"); // }; //}; //defer for (let s .. items) free(s); const state = tui::init()!; defer tui::finish(&state); let li = list::newscrolllist( &state, (1, 1), void, void, items... )!; let vl = layout::newvlayout(&li); defer layout::finishvlayout(&vl); vl.layout.print(&vl); let term: (str | void) = void; defer if (term is str) free(term as str) else void; let revsearch = false; for (true) { const r = tui::read(&state)!; if (r == 'j') { list::down(&li); }; if (r == 'k') { list::up(&li); }; if (r == 'J') { list::framedown(&li); }; if (r == 'K') { list::frameup(&li); }; if (r == 'g') { list::top(&li); }; if (r == 'G') { list::bottom(&li); }; if (r == 'q') { break; }; if (r == 'l') { fmt::println(li.items[li.cursor])!; break; }; if (r == '/' || r == '?') { revsearch = r == '?'; term = search(&state, &li, r as rune); if (revsearch) prevsearch(&li, term) else nextsearch(&li, term); }; if (r == 'n') { if (revsearch) prevsearch(&li, term) else nextsearch(&li, term); }; if (r == 'N') { if (!revsearch) prevsearch(&li, term) else nextsearch(&li, term); }; vl.layout.print(&vl); }; }; fn search(state: *tui::tui, li: *list::scrolllist, prefix: (str | rune) = '/') (str | void) = { tui::unraw(state); defer tui::raw(state)!; fmt::fprint(state.out, prefix)!; const uline = match (bufio::read_line(state.out)!) { case let u: []u8 => yield u; case io::EOF => return; }; defer free(uline); return strings::dup(strings::fromutf8(uline)!); }; fn nextsearch(li: *list::scrolllist, term: (str | void)) void = { const term = match (term) { case let term: str => yield term; case void => return; }; for (let i = li.cursor + 1; i < len(li.items): int; i += 1) { if (strings::contains(li.items[i], term)) { list::setcursor(li, i); return; }; }; }; fn prevsearch(li: *list::scrolllist, term: (str | void)) void = { const term = match (term) { case let term: str => yield term; case void => return; }; for (let i: int = li.cursor: int - 1; i >= 0; i -= 1) { if (strings::contains(li.items[i], term)) { list::setcursor(li, i); return; }; }; };