Как я могу исправить мою цветную оболочку подсказки?

9

Я определил приглашение bash (используя PROMPT_FUNCTION) следующим образом:

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

В общем, это дает мне многострочное приглашение, которое отображает некоторую информацию о статусе hg, а также мой текущий virtualenv, выглядящий (без цвета) примерно так:

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

Проблема в том, что это связано с вычислением длины приглашения (я думаю!) И вызывает странные проблемы с переносом терминалов и размещением курсора. Например, в терминале с 80 символами, я вижу подсказку (символ, заключенный в ** - это местоположение курсора):

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

В терминалах, достаточно широких для отображения приглашения, перенос строк происходит намного раньше, чем должен; вот большая часть текста, который я могу разместить в первой строке приглашения в окне терминала шириной 108 символов (опять же, ** обозначает мое местоположение курсора):

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

Когда строка заканчивается, она перезаписывает подсказку. Однако вторая строка ввода проходит прямо к краю терминала и затем корректно переносится.

Итак, явно что-то мешает с шириной подсказки. Как я могу заставить bash определять длину строки PS1 не в соответствии с управляющими кодами ANSI, а в соответствии с фактической отображаемой длиной приглашения?

Крис Р
источник

Ответы:

21

bashиспользуется \[ \]для определения «отображаемой длины»: текст между этими двумя экранированными символами считается непечатным и не учитывается в общей длине; все остальное есть.

Кажется, есть проблема с вашими переменными: на bright='\[[01m\]'самом деле не включает символ ESC, поэтому [01mпечатается как обычный текст, но не учитывается в длине. Так и должно быть '\[\e[01m\]'. То же самое для всех других переменных.


Связанные с:

  • в Bash, вы можете поместить \$(hg_status)в $PS1непосредственно, без необходимости отдельно PROMPT_COMMAND.
user1686
источник
1
Это обеспечивает частичное исправление; длина строки теперь правильно определена, но это не решает проблему с перезаписью первой строки над приглашением.
Крис Р
1
Крис Р: Я попробовал вашу подсказку и просто заменил все '\[[на '\[\e[работал в bash на Ubuntu 12.04. Экранирование цветов (и некоторых других частей, не относящихся к размеру подсказки) также \[...\]помогло мне, но у меня намного-намного более сложный PS1, чем у вас (с выровненным по правому краю текстом на той же строке, что и подсказка, и текстом в правом нижнем углу угол). Устранены проблемы как раннего, так и накладывающегося наложения.
TWiStErRob