Jak naprawić owijanie monitu o kolor?

9

Zdefiniowałem monit bash (używając PROMPT_FUNCTION) w następujący sposób:

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

Ogólnie rzecz biorąc, daje mi to wieloliniowy monit, który wyświetla niektóre informacje o stanie hg, a także mój obecny virtualenv, wyglądający (bez koloru) w następujący sposób:

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

Problem polega na tym, że jest to niepoprawne w obliczaniu długości pytania (myślę!) I powoduje dziwne problemy z zawijaniem terminali i umieszczaniem kursora. Na przykład w 80-znakowym terminalu oto monit, który widzę (** - otoczony znak to położenie kursora):

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

W terminalach wystarczająco szerokich, aby wyświetlić monit, zawijanie linii następuje znacznie wcześniej niż powinno; oto jak najwięcej tekstu, jaki mogę zmieścić w pierwszym wierszu monitu w oknie terminala o szerokości 108 znaków (ponownie ** oznacza moją lokalizację kursora):

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

Gdy linia się zawija, zastępuje monit. Drugi wiersz danych wejściowych biegnie jednak do krawędzi terminala, a następnie jest poprawnie zawijany.

Widać więc, że coś jest nie tak z szerokością monitu. Jak sprawić, aby bash określił długość łańcucha PS1 nie według kodów specjalnych ANSI, ale na podstawie rzeczywistej wyświetlanej długości pytania?

Chris R.
źródło

Odpowiedzi:

21

bashsłuży \[ \]do określania „wyświetlanej długości”: tekst między tymi dwoma znakami zmiany znaczenia jest uważany za niedrukowalny i nie jest liczony w całkowitej długości; wszystko inne jest.

Wygląda na to, że występuje problem z twoimi zmiennymi: bright='\[[01m\]'tak naprawdę nie zawiera znaku ESC, więc [01mjest drukowany jako zwykły tekst, ale nie jest liczony w długości. Powinno być '\[\e[01m\]'. To samo dla wszystkich innych zmiennych.


Związane z:

  • w Bash możesz \$(hg_status)przejść $PS1bezpośrednio, bez potrzeby oddzielnego PROMPT_COMMAND.
użytkownik1686
źródło
1
To zapewnia częściową poprawkę; długość linii jest teraz poprawnie wykrywana, ale nie rozwiązuje problemu z zapisem zawijania pierwszej linii w wierszu polecenia.
Chris R
1
Chris R: Próbowałem szybką i po prostu zastąpienie wszystkich '\[[z '\[\e[pracowała w bash na Ubuntu 12.04. \[...\]Pomogło mi również unikanie kolorów (i innych części , które nie mają związku z pytaniem), ale mam znacznie bardziej złożony PS1 niż ty (z wyrównanym do prawej tekstem w tym samym wierszu co podpowiedź i tekst w prawym dolnym rogu kąt). Naprawiono zarówno problemy z wczesnym owijaniem, jak i nakładaniem się owijania.
TWiStErRob,