//! The line a long action draws while it works (§4, "never leave the person //! wondering"): one line on stderr, redrawn in place and cleared when the step //! ends, and only when stderr is a terminal, so pipes, logs and `--json` never //! see it. module cli import os import src.fmt import src.remote import src.view fn working(text string) { if os.is_atty(2) == 0 { return } width := line_width() shown := fmt.fit(' ${text}', width) // Padded with spaces, not cleared with an escape code, so a plain terminal // and NO_COLOR stay free of control sequences. eprint('\r${fmt.dim(shown)}${' '.repeat(width - fmt.visible_width(shown))}') flush_stderr() } fn done_working() { if os.is_atty(2) != 0 { eprint('\r${' '.repeat(line_width())}\r') flush_stderr() } } // line_width stays one column short of the terminal, so the line never wraps. fn line_width() int { _, columns := view.terminal_size() return if columns > 1 { columns - 1 } else { 79 } } // Watched counts what an offer, steal or strip moves through a backend, so the // line moves with the transfer. Exchange stays unaware of it. struct Watched { label string mut: inner remote.Backend received int sent int } // watched_backend is this machine's backend, counted on the line under `label`. fn watched_backend(label string) !remote.Backend { return remote.Backend(watch(label, this_machine_backend()!)) } fn watch(label string, backend remote.Backend) Watched { return Watched{ label: label inner: backend } } fn (mut w Watched) show() { mut text := w.label if w.sent > 0 { text += ' · ${w.sent} sent' } if w.received > 0 { text += ' · ${w.received} received' } working(text) } fn (mut w Watched) get(key string) !remote.Object { object := w.inner.get(key)! w.received++ w.show() return object } fn (mut w Watched) list(prefix string) ![]string { return w.inner.list(prefix)! } fn (mut w Watched) put_if_absent(key string, body []u8) !bool { put := w.inner.put_if_absent(key, body)! w.sent++ w.show() return put } fn (mut w Watched) put_if_match(key string, body []u8, tag string) !bool { put := w.inner.put_if_match(key, body, tag)! w.sent++ w.show() return put } fn (mut w Watched) delete(key string) ! { w.inner.delete(key)! w.sent++ w.show() }