Pular para o conteúdo

Cor, tabela e o terminal que não é terminal

Colorir sem estragar o | grep, e desenhar tabela, árvore e barra sem dependência.

A regra que quase todo programa de terminal erra: cor só quando a saída é um terminal. Canalizado para um arquivo ou para o grep, o código de escape vira lixo no meio do dado — e um grep "erro" deixa de casar porque há um \x1b[31m grudado na palavra.

dataforge
adopt Arcane.Color as Cor
adopt Arcane.OS as OS

// 'auto' decide pelo terminal; 'supports' responde o que ele aceita
assert Cor.supports() is yes or Cor.supports() is no

// E 'strip' tira a cor de um texto já colorido — é o que um teste faz.
pintado := Cor.red("falhou")
assert Cor.strip(pintado) is "falhou"
out Cor.strip(Cor.bold(Cor.green("ok")))

Tabela#

dataforge
adopt Arcane.Color as Cor

linhas := [
    ["produto", "qtd", "preço"],
    ["café", "12", "R$ 32,90"],
    ["filtro", "3", "R$ 8,50"],
]
desenho := Cor.table(linhas)
assert "produto" in desenho
out desenho

Árvore, régua e barra#

dataforge
adopt Arcane.Color as Cor

out Cor.rule("Relatório")
out Cor.tree({"projeto": {"src": ["main.df", "util.df"], "tests": ["main_test.df"]}})
out Cor.bar(0.72, 30)
out Cor.box("Concluído em 1,2 s")

Progresso que não polui um log#

Uma barra que se reescreve com \r é ótima num terminal e é uma linha por quadro num arquivo de log. A saída é perguntar antes:

dataforge
adopt Arcane.Color as Cor
adopt Arcane.OS as OS

action progresso(feito, total):
    given not OS.is_tty():
        // canalizado: uma linha por marco, e só
        given feito % 50 is 0:
            out $"  {feito}/{total}"
        yield void
    out $"\r  {Cor.bar(feito / total, 24)} {feito}/{total}"
    yield void

cycle i from 1 to 100:
    given i % 50 is 0:
        progresso(i, 100)
out ""

A largura do terminal#

dataforge
adopt Arcane.Cli as Cli
adopt Arcane.Color as Cor

largura := Cli.largura()
assert largura > 0
// Sem terminal, ela devolve um padrão — e é por isso que ela nunca é
// zero: uma divisão por largura no meio do desenho estouraria.
out $"largura: {largura} colunas"