Você pode fazer meu terminal menos chato?

11

Os terminais são tão chatos hoje em dia. Eles costumavam ficar assim:

Terminal 1 Terminal 2

Agora eles são apenas sem graça, sem graça e em preto e branco. Quero que você me escreva um programa que torne meu terminal colorido de novo!

Descrição

Veja este exemplo de código Ruby:

Código de exemplo

A maioria dos terminais Linux suporta essas seqüências de escape ( \esignifica o caractere de escape) e o Windows pode suportá-los com o ANSICON . Aqui está a sintaxe da sequência de escape específica que pode alterar o texto ou a cor de fundo de uma string:

\e[{{COLOR}}m

onde \erepresenta o caractere de escape ( 0x1Bem ASCII) e {{COLOR}}é substituído pelo número da cor que você deseja usar (mais detalhes sobre isso posteriormente). O texto que vem após essa sequência de escape será formatado conforme indicado e um valor de 0redefinirá toda a formatação.

Seu desafio é pegar uma sequência que especifique algum texto que possa conter cores e gerar uma versão colorida.

Entrada / Saída

O texto normal funciona exatamente como o normal e é impresso literalmente. Por exemplo, a entrada wafflesproduz a mesma saída, sem cor especial.

A sintaxe para especificar uma cor é semelhante à sintaxe da Wikipedia . Por exemplo, para colorir as palavras "a cor vermelha" em vermelho na frase This is the color red!, a entrada seria:

This is {{red|the color red}}!

As cores de fundo também funcionam. Se você quisesse letras pretas sobre fundo branco, usaria o seguinte:

{{black|white|This text is black on white}}

Para obter apenas uma cor de fundo, omita o primeiro plano:

{{|red|This text has a red background}}

Especificação

Dois colchetes abertos sempre especificam o início de uma diretiva de cores . Dois colchetes de fechamento especificam o final. Parênteses sempre coincidem; nunca haverá a {{sem um correspondente }}e a }}nunca virá antes do seu correspondente {{. Essas diretivas de cores não serão aninhadas e a {{nunca aparecerá em uma diretiva de cores.

Dentro de uma diretiva de cores, sempre haverá um ou dois |símbolos. Se houver, o texto anterior à cor do primeiro plano e o texto posterior serão a sequência a ser mostrada nessa cor. Se houver dois, o texto antes do primeiro é a cor do primeiro plano, o texto após o primeiro, mas antes do segundo, a cor do plano de fundo e o texto após o segundo, a sequência a ser exibida. Essas barras verticais podem existir fora de uma diretiva de cores e devem ser impressas literalmente.

A cor do primeiro plano ou da cor do plano de fundo (mas não as duas) pode estar vazia; nesse caso, você deve deixá-las como padrão. A string final (a que será impressa) nunca estará vazia.

Aqui estão as instruções para gerar texto de uma determinada cor:

  • Uma sequência de cores é definida na seção "Descrição". Por exemplo, uma sequência de cores de 42 seria "\e[42m".

  • Para definir uma cor, imprima a sequência de cores do número determinado abaixo:

     Color name   | Color sequence number (foreground / background)
    --------------+----------
     black        | 30 / 40
     red          | 31 / 41
     green        | 32 / 42
     yellow       | 33 / 43
     blue         | 34 / 44
     magenta      | 35 / 45
     cyan         | 36 / 46
     lightgray    | 37 / 47
     darkgray     | 90 / 100
     lightred     | 91 / 101
     lightgreen   | 92 / 102
     lightyellow  | 93 / 103
     lightblue    | 94 / 104
     lightmagenta | 95 / 105
     lightcyan    | 96 / 106
     white        | 97 / 107
    
  • Os nomes de cores diferenciam maiúsculas de minúsculas e nunca será fornecido um nome de cor inválido. Você não precisa lidar com coisas como REDou lightgrey(soletrado com um e).

  • Depois de imprimir uma sequência de cores, ela será aplicada a todo o texto a seguir. Para finalizar uma sequência de cores (redefinir para a cor padrão), imprima uma sequência de cores de 0( "\e[0m").

Caso de teste

 {{|yellow|     }}
{{|yellow| }}     {{|yellow| }}
{{|yellow| }} {{red|'}} {{red|'}} {{|yellow| }}
{{|yellow| }} \_/ {{|yellow| }}
{{|yellow| }}     {{|yellow| }}
 {{|yellow|     }}

Isso deve dar um rosto sorridente ... com maus olhos vermelhos.

Regras

  • Você não pode usar nenhuma biblioteca ou função da sua linguagem de programação para analisar automaticamente uma cor. Isso significa que você deve ser o único a determinar o que "red"significa; você não pode ter uma biblioteca automaticamente fazer isso por você.

  • Isso é , então o código mais curto em bytes vencerá!

Maçaneta da porta
fonte
Na verdade, é para ser um terminal? Ou apenas um visualizador de texto colorido? É suposto executar comandos?
Nathan Merrill
Estou achando difícil testar isso. Tudo o que envio para STDOUT usando a sintaxe especificada é fornecido em texto sem formatação. Meu perfil do bash usa um prompt colorido, para roubar o que eu tentei \n\[\e[32m\]\w\n\[\e[0m\]> (nome do diretório verde, prompt simples na próxima linha), mas não consigo fazê-lo funcionar em um programa (tentei python e Java até agora). Alguma ideia?
Geobits
@Geobits Try echo -e "\e[31mtest\e[0m".
Maçaneta
4
Eu acho que você iria gostar lolcat.
Anko
1
Eu acho que youele quer dizer figurativamente your program(em oposição a uma chamada para uma função de biblioteca), e que ele assume determineno sentido de figure out, não como em choose. Ou seja, é o seu programa que deve lidar com o mapeamento: String ("red") | -> Inteiro (31). redé somente 31porque ele diz isso, que as informações precisam ser integradas ao programa. Embora possa ser discutido exatamente o que seria considerado your program- podemos usar funções de manipulação de String de uso geral? - não trapaceie / abuse descaradamente.
Blotange

Respostas:

6

Ruby, 205 189 188 186 185 182 174 170 165 161 159 154 bytes

Colocar longas seqüências de nomes de cores no seu código não parece suficientemente nerd.

Até 170 em parte graças a rubik. Agora as barras de rolagem se foram!

Uma melhoria óbvia e outra não tão óbvia, graças à resposta flexível, sem a melhoria, eu não teria revisitado isso!

Não é mais, salvei 4 bytes com #sum. Eu não pretendia isso, mas percebi que essa solução também não diferencia maiúsculas de minúsculas. Felizmente processa {{RED|Red text}}.

Despejo hexagonal:

0000000: 7a3d 2d3e 6a7b 693d 2240 3054 2d44 1547  z=->j{i="@0T-D.G
0000010: 5155 0034 3256 2f46 1749 0b22 2e69 6e64  QU.42V/F.I.".ind
0000020: 6578 2028 415b 6a5d 2e74 6f5f 732e 7375  ex (A[j].to_s.su
0000030: 6d25 3839 292e 6368 723b 692b 3d69 3e39  m%89).chr;i+=i>9
0000040: 3f38 303a 3330 7d0a 243e 3c3c 243c 2e72  ?80:30}.$><<$<.r
0000050: 6561 642e 6773 7562 282f 7b7b 282e 2a3f  ead.gsub(/{{(.*?
0000060: 297d 7d2f 297b 413d 2431 2e73 706c 6974  )}}/){A=$1.split
0000070: 277c 273b 221b 5b25 693b 2569 6d23 7b41  '|';".[%i;%im#{A
0000080: 2e70 6f70 7d1b 5b30 6d22 255b 7a5b 305d  .pop}.[0m"%[z[0]
0000090: 2c31 302b 7a5b 315d 5d7d                 ,10+z[1]]}

Você pode convertê-lo com xxd -r hex.dump.

O programa com todos os caracteres não imprimíveis escapou para fins de referência:

z=->j{i="@0T-D\x15GQU\x0042V/F\x17I\v".index (A[j].to_s.sum%89).chr;i+=i>9?80:30}
$><<$<.read.gsub(/{{(.*?)}}/){A=$1.split'|';"\x1b[%i;%im#{A.pop}\x1b[0m"%[z[0],10+z[1]]}

Essa é uma linha. Use-o assim

ruby colors.rb -W0 < input.txt

O -W0sinalizador suprime avisos que seriam enviados para o stderrcontrário. No entanto, o programa funciona bem sem nenhum sinalizador.

Resultado:

resultado

blutorange
fonte
1
Ah, eu tive a mesma ideia, mas você me venceu! Eu acho que você poderia salvar um char com a base 35, o módulo 98 e xor 1. A seqüência seria: '1?IYU_N[(\x0c\x16&",\x1f\x01'. Minha corda é de 16 no entanto. Vejo que o seu é 18, então você provavelmente precisa se ajustar.
rubik
Obrigado. Os dois bytes adicionais existem para oferecer suporte ao código de cores 39/49, que define a cor padrão de retorno / retorno. Mas, graças à dica, atualmente estou no trabalho e vou pensar um pouco mais quando voltar para casa.
Bloqueange #
1
Bem, observei que as únicas bases que você pode usar são 35 e 36 (pelo menos a int()função do Python não pode ultrapassar 36). Depois, tentei todas as combinações para o módulo (de 2 para 10000, mas, em teoria, era possível ampliar a pesquisa para todo o Unicode) e para o xor, que eu mantive pequeno (1 a 9). Então, considerei resultados aceitáveis ​​apenas aqueles que não continham caracteres duplicados.
rubik
Sim, foi mais ou menos o que eu fiz também. Originalmente, eu estava me limitando a caracteres imprimíveis, porque isso lhe dá menos dor de cabeça e parece mais agradável. Mas como já estou usando o byte 0x1e em vez da sequência de escape, é melhor usar mais caracteres não imprimíveis. Para obter caracteres imprimíveis, eu usei x.to_i(base)%mod+offset. Eu então substituí o +com ^, porque bem, parece mais legal. Fora isso, é desnecessário. Soltando ^99e alterando <<para +salvo por mais bytes. Obrigado pela dica, eu não teria notado o contrário!
Blotange1 de
4

Ruby, 329 bytes.

h={};(y=0..15).each{|i|h[%w(black red green yellow blue magenta cyan lightgray darkgray lightred lightgreen lightyellow lightblue lightmagenta lightcyan white)[i]]=y.map{|j|[30,40].map{|k|k+i%8+i/8*60}}[i]}
loop{puts gets.gsub(/{{.+?}}/){|x|"\e[#{h[(g=x.scan(/[^{}|]+/))[0]][0]}m#{(g[2]? "\e[#{h[g[1]][1]}m":'')}#{g.last}\e[0m"}}
Alex Deva
fonte
Qual versão do Ruby eu preciso para executar isso? Eu usei ruby 2.1.2p95e jogue erro: undefined method 'gsub' for nil:NilClass (NoMethodError) .
Ray
Oi @ Ray, ele funciona em 2.0.0-p451. Eu não tentei no 2.1.2. Aqui funciona como um script e aqui funciona no irb .
Alex Deva
Funciona quando você digita o texto manualmente. Se você o fizer ruby colors.rb < input.txt, ele continuará em loop após toda a entrada ter sido lida. Então getsretorna nil, que não possui um #gsubmétodo, gerando um erro. Use em $><<$<.readvez de loop{puts gets, também é mais curto; )
blutorange 31/08
Acabei de testar esse script com o rosto sorridente (veja a pergunta e a imagem do meu post), e não há borda amarela ao redor do sorriso?
precisa saber é o seguinte
4

Flex (léxico) - 226 197 192 182 168 (ou 166)

Para reduzi-lo para 166, altere \33para um caractere de escape real.

 int z;p(i){printf("\33[%dm",i);}
%%
"{{" z=2;
[a-z]*\| if(!z)REJECT;~-yyleng&&p("062q00t03058ns7uo0p90r4"[*(int*)&yytext[yyleng>7?4:0]%131%27]-10*z);z--;
"}}" p(z=0);

Compile e execute:

$ flex -o colour.c colour.l
$ gcc -o colour colour.c -lfl
$ ./colour < input
rici
fonte
3

Python - 351

import re,sys
R=range
E=lambda n,d=0:'\033[%dm'%(T[n]+d)if n else''
def P(m):f,b,t=m.groups();return'%s%s%s\033[0m'%(E(f),E(b,10),t)
x='!red!green!yellow!blue!magenta!cyan'.replace
T=dict(zip(('black'+x('!',' ')+' lightgray darkgray'+x('!',' light')+' white').split(),R(30,38)+R(90,98)))
print re.sub(r'{{(\w+)?\|?(\w+)?\|?(.+?)}}',P,sys.stdin.read())
Raio
fonte
1

Cobra - 496

Quase poderia ser uma única declaração impressa.

use System.Text.RegularExpressions
class P
    def main
        print Regex.replace(Console.readLine,r'\{\{('+(l=List<of String>(((m=' black red green yellow blue magenta cyan'.split).join(' ')+' lightgray darkgray'+m.join(' light')+' white').split))[1:].join('|')+r')?\|?('+l[1:].join('|')+r')?\|(.*?)\}\}',do(a as Match))
            return if(x=l.indexOf('[a.groups[1]]'),r'\e['+'[if(x>8,x+81,x+29)]m','')+if(y=l.indexOf('[a.groups[2]]'),r'\e['+'[if(y>8,y+91,y+39)]m','')+'[a.groups[3]]'+if(x+y,r'\e[0m','')
Furioso
fonte
1

Python, 561

Lê o texto para formatar a partir de stdin.

import re,sys
def p(f,b,t):
    p=''
    m='\033[%dm'
    if f!=0:p+=m%f
    if b!=0:p+=m%b
    return p+t+m%0
def c(n,b=0):
    s='black:30#red:31#green:32#yellow:33#blue:34#magenta:35#cyan:36#lightgray:37#darkgray:90#lightred:91#lightgreen:92#lightyellow:93#lightblue:94#lightmagenta:95#lightcyan:96#white:97'
    r=0
    for i in s.split('#'):
        (t,c)=i.split(':')
        if t==n:
            r=int(c)
            if b==1:r+=10
    return r
def r(m):
    i=m.groups()
    f=b=0
    if i[0]!='':f=c(i[0])
    if i[1]!=None:b=c(i[1],1)
    return p(f,b,i[2])
print re.sub('{{(\w*)\|(?:(\w*)\|)?([^}]+)}}',r,sys.stdin.read())
Sammitch
fonte
2
É muito detalhado para ter is not Noneem um codegolf. Você pode usar !=None, por exemplo.
Ray
Além disso, em def p(f,b,t), seu código lançará a ZeroDivisionError. Qualquer coisa mod 0 é impossível.
Beta Decay
@BetaDecay que não é um número inteiro sendo modulado, é uma string que está sendo formatada.
Sammitch 2/09/2014
Estou recebendo erros de sintaxe inválidos em re.subexecutar este
artofcode
(comentário tardio) Este código de 499 bytes funciona?
Erik the Outgolfer