9

I have defined a bash prompt (using PROMPT_FUNCTION) like so:

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

In general, this gives me a multi-line prompt that displays some hg status info as well as my current virtualenv, looking (without colour) like this:

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

The problem is, this is screwing with the calculation of the length of the prompt (I think!) and causing weird terminal wrapping issues and cursor placement. For example, in an 80-char terminal, here's the prompt I see (the **-surrounded character is the cursor location):

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

In terminals wide enough to display the prompt, line wrapping occurs much earlier than it should; here's the most text I can fit on the first line of the prompt in a 108-char-wide terminal window (again, the ** marks my cursor location):

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

When the line wraps over, it overwrites the prompt. The second line of input runs right to the edge of the terminal, however, and then wraps correctly.

So, clearly something is messing with the width of the prompt. How can I cause bash to determine the length of the PS1 string not according to the ANSI escape codes, but according to the actual displayed length of the prompt?

0

1 Answer 1

21

bash uses \[ \] to determine the "displayed length": text between those two escapes is considered unprintable and not counted in the total length; everything else is.

There seems to be a problem with your variables: bright='\[[01m\]' doesn't actually include an ESC character, so [01m gets printed as normal text but is not counted in the length. It should be '\[\e[01m\]'. Same for all other variables.


Related:

  • in Bash, you can put \$(hg_status) to $PS1 directly, without the need for a separate PROMPT_COMMAND.
2
  • 1
    That provides a partial fix; the line length is detected properly now, but it doesn't fix the problem with the first line wrap writing over the prompt.
    – Chris R
    Dec 17, 2010 at 17:46
  • 1
    Chris R: I tried your prompt and just replacing all '\[[ with '\[\e[ worked in bash on Ubuntu 12.04. Escaping colors (and some other irrelevant-to-prompt-size parts) with \[...\] helped me too, but I have much-much-much more complex PS1 than you (with right aligned text on the same line as prompt and text in the lower right corner). It fixed both the early wrapping and the overlapping wrapping problems.
    – TWiStErRob
    Dec 10, 2012 at 14:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .