How do I fix my colour bash prompt wrapping?
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?
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 separatePROMPT_COMMAND
.