Como faço para corrigir meu quebra automática de linha do bash de cores?

9

Eu defini um prompt do bash (usando PROMPT_FUNCTION) assim:

function get_hg_prompt_prefix() {
    local APPLIED_COLOR=$1; shift
    local UNAPPLIED_COLOR=$1; shift
    local ALERT_COLOUR=$1; shift
    local TEXTCOLOR=$1; shift
    local mercurial_prompt_line="{{patches|join(:)|pre_applied(${APPLIED_COLOR})|post_applied(${TEXTCOLOR})|pre_unapplied(${UNAPPLIED_COLOR})|post_unapplied(${TEXTCOLOR})}\n\r}"
    local mercurial_status_prompt="{ ${ALERT_COLOUR}{status}${TEXTCOLOR}}"

    echo "$(hg prompt "${mercurial_prompt_line}" 2>/dev/null)$(hg prompt "${mercurial_status_prompt}" 2>/dev/null)"
}

function set_prompt() {
    bright='\[[01m\]'
    colors_reset='\[[00m\]'
    HOSTCOLOR=${colors_reset}='\[[34m\]'
    USERCOLOR=${colors_reset}='\[[01m\]'
    TEXTCOLOR=${colors_reset}='\[[32m\]'
    APPLIED_COLOR=${colors_reset}='\[[32m\]'
    UNAPPLIED_COLOR=${colors_reset}='\[[37m\]'
    ALERT_COLOUR=${colors_reset}='\[[31m\]'

    hg_status="$(get_hg_prompt_prefix $APPLIED_COLOR $UNAPPLIED_COLOR $ALERT_COLOUR $TEXTCOLOR)"
    ps1_prefix="${hg_status}$colors_reset($bright$(basename $VIRTUAL_ENV)$colors_reset) "
    PROMPTEND='$'
    PS1="${ps1_prefix}${USERCOLOR}\u${colors_reset}${TEXTCOLOR}@${colors_reset}${HOSTCOLOR}\h${colors_reset}${TEXTCOLOR} (\W) ${PROMPTEND}${colors_reset} "
}

PROMPT_COMMAND=set_prompt

Em geral, isso me dá um prompt de várias linhas que exibe algumas informações de status de hg, bem como meu virtualenv atual, parecendo (sem cor) assim:

buggy-wins.patch
 ! (saas) user@computer (~) $ 

O problema é que isso está atrapalhando o cálculo do comprimento do prompt (eu acho!) E causando problemas estranhos de quebra de terminal e posicionamento do cursor. Por exemplo, em um terminal de 80 caracteres, eis o prompt que vejo (o caractere ** - cercado é o local do cursor):

~) $ **a**nis) crose@chris-rose (~

Nos terminais com largura suficiente para exibir o prompt, a quebra de linha ocorre muito mais cedo do que deveria; aqui está o máximo de texto que eu posso ajustar na primeira linha do prompt em uma janela do terminal com 108 caracteres (novamente, o ** marca a localização do meu cursor):

 **(**advanis) crose@chris-rose (~) $ sdkfjlskdjflksdjff

Quando a linha passa, ela substitui o prompt. A segunda linha de entrada é executada até a borda do terminal, no entanto, e depois envolve corretamente.

Então, claramente algo está mexendo com a largura do prompt. Como posso fazer com que o bash determine o comprimento da sequência PS1 não de acordo com os códigos de escape ANSI, mas de acordo com o comprimento real exibido do prompt?

Chris R
fonte

Respostas:

21

bashusa \[ \]para determinar o "comprimento exibido": o texto entre essas duas fugas é considerado imprimível e não contado no comprimento total; tudo o resto é.

Parece haver um problema com suas variáveis: bright='\[[01m\]'na verdade, não inclui um caractere ESC, portanto [01mé impresso como texto normal, mas não é contado no comprimento. Deveria ser '\[\e[01m\]'. O mesmo para todas as outras variáveis.


Relacionado:

  • em Bash, você pode colocar \$(hg_status)a $PS1diretamente, sem a necessidade de um separado PROMPT_COMMAND.
user1686
fonte
11
Isso fornece uma correção parcial; o comprimento da linha é detectado corretamente agora, mas não corrige o problema com a primeira quebra de linha escrevendo sobre o prompt.
Chris R
11
Chris R: Eu tentei o seu prompt e apenas substituí todos '\[[por '\[\e[trabalhou no bash no Ubuntu 12.04. Escapar de cores (e outras partes irrelevantes do tamanho do prompt) \[...\]também me ajudou, mas eu tenho um PS1 muito mais complexo do que você (com o texto alinhado à direita na mesma linha que o prompt e o texto no canto inferior direito) canto). Ele corrigiu os problemas de empacotamento antecipado e de sobreposição.
TWiStErRob