Terminal line resetting after about 50 characters - terminal

My command line prompt will reset after around 50 characters, including the prompt. For example, if the prompt looks like:
user#computer$
and I start to type
user#computer$ blah blah blah blah blah blah
I'll eventually get to the point where the command line starts to reset like so:
blah blmputer$ blah blah blah blah blah blah
Does anyone know why this is happening and how I can fix it? Originally I thought it was a problem with iTerm, but I looked at the default OS X terminal and was having the same problem.
Edit: Looking into it more I'm realizing that the line always resets a fixed amount of characters from the end of the window, not from the beginning.

The usual reason for this is due to not quoting escape sequences used to color or highlight your prompt in the bash shell. If you do not do that, bash counts the escape characters as "printing". The same would apply for zsh (different quoting), but bash happens to be the default shell with OSX.
According to the xterm manual:
bash treats characters within "[" and "]" as nonprinting (using no width on the screen).
zsh treats characters within "%{" and "%}" as nonprinting.
The place to change is your shell's definition of PS1. The Bash Prompt HOWTO is a good place to read more.

Related

How to interpret the PS1 variable in BASH?

I have a bash command prompt like this:
someone#some-host:~$
I checked the PS1:
echo $PS1
It shows this:
\[\e]0;\u#\h: \w\a\]${debian_chroot:+($debian_chroot)}\u#\h:\w\$
How to interpret it? Why is it so complex?
I thought "\u#\h:\w$" should be enough. And I tried to set PS1 to it. It works the same or at least looks so.
How to interpret it?
\[ ... \] is interpreted by Bash as not visible. It matters if you want for example Home key to jump to the beginning of the line but right after prompt. Bash has to know, how many characters to jump to place the cursor right after the prompt, so it has to know how many characters were printed when printing the prompt.
https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Controlling-the-Prompt
\e]0;\u#\h: \w\a sets the title of your terminal. https://en.wikipedia.org/wiki/ANSI_escape_code#OSC_(Operating_System_Command)_sequences
${debian_chroot:+($debian_chroot)} Expands to the string (<value of debian_chroot>) when variable debian_chroot is set, otherwise expands to nothing. Basically it adds ( ) to the value of debian_chroot, when it is set to display it nicely. https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Parameter-Expansion

What is the equivalent of \[ ... \] in zsh?

In my .bashrc file, when I colour the prompt, I use \[ ... \] to prevent new bash lines overwriting previous ones (also prevent writing buffer overflow/underflow or cases when text being typed in random locations). This only happens when using colours. In my .bashrc file I have:
WHITE="\[$(tty -s && tput setaf 254)\]";
PS1+="${WHITE} # "
But \[ escapes don't work on zsh in .zshrc
How can I fix this please?
The zsh equivalent is %{ ... %}. However, it's not needed as frequently because zsh has a much richer set of prompt escapes that eliminate the need to use raw escape sequences. For example, the %F escape lets you select a foreground color by name or number. zsh already knows how to query your terminal for the exact bytes to use, and zsh also knows that those bytes should not count towards the width of the prompt.
PS1+="%F{white} # "
or
PS1+="%F{254} # "

Why this two-line bash prompt is messing up the command history

I have my bash prompt as:
\u#\H: \w$(__git_ps1 "[\[\e[0;32m\]%s\[\e[0m\]\[\e[0;33m\]$(parse_git_dirty)\[\e[0m\]]")\n\e[1m\t\e[0m $
so the second line is to display current time.
However, I have found it is messing up the history - when use arrow key to move up, a port of command seems get "stuck" and won't change it anymore. The only way I get back is to press Enter again. How to fix it?
\u#\H: \w$(__git_ps1 "[\[\e[0;32m\]%s\[\e[0m\]\[\e[0;33m\]$(parse_git_dirty)\[\e[0m\]]")\n\e[1m\t\e[0m $
# ^^^^^ ^^^^^
All of the ANSI escape sequences on the first line are correctly surrounded by \[ and \], which tell Bash to not to count those characters when figuring out the visual length of the prompt. The ones on the second line are missing these delimiters.
PS1='\u#\H: \w$(__git_ps1 "[\[\e[0;32m\]%s\[\e[0m\]\[\e[0;33m\]$(parse_git_dirty)\[\e[0m\]]")\n\[\e[1m\]\t\[\e[0m\] $'
# ^^ ^^ ^^ ^^

zsh inserts extra spaces when performing searches and completion

I have written a small program that emits a command line prompt with some Git info. I use ANSI escape sequences to color it, and it looks something like this:
However, whenever I do tab completion or a search, zsh inserts several spaces after the prompt:
It seems to be inserting a space for each escape code character emitted by the prompt, since removing the color codes eliminates this behavior. Why is zsh doing so, and how can I stop this?
The actual character sequence emitted by my prompt program for this example is (assuming \e represents character 033)
~/s/promptd [\e[36mmaster \e[33m±\e[31m?\e[39m]
The relevant portion of my .zshrc is:
setopt PROMPT_SUBST
setopt PROMPT_PERCENT
PROMPT='%B$(promptd) %%%b '
After doing some additional research, the ZSH Prompt Expansion docs indicate that escape literals need to be enclosed in %{...%}.
This is bothersome since now I have to output those conditionally if I want the prompt program to work in other shells, but it seems to correct the behavior shown above.

Adding ANSI color escape sequences to a bash prompt results in bad cursor position when recalling/editing commands

If I set my command prompt like:
export PS1='\033[0;33m[\u#\h \w]\$ \033[00m'
The color of the prompt will be yellow and everything after the '$' character will
be the default terminal color. This is what I expect. However, If I recall a command line and attempt to edit it, moving the cursor -- either UpArrow/Ctrl-A (set -o emacs) or ESC K (set -o vi)
if the command line I'm trying to edit is long enough, the cursor is not positioned at the beginning of the command. Typing either Ctrl-A (set -o emacs) or ^ (set -o vi) will not move the cursor to what I'm seeing as the beginning of the recalled line on the screen. Similarly, attempting to position the cursor to the end of the line (Ctrl-E or $, depending) results in it being placed several characters past what appears to be the end of the line.
It looks like bash is getting confused by the escape characters I've added to the prompt.
Is this just something I'll have to deal with, changing my prompt to a monochromatic one when I wish to edit recalled lines, or is there a way to get bash to correctly allow the editing of recalled commands with a colorful prompt?
You need to enclose the non-printing characters in \[ ... \] so that bash knows to ignore them when computing the length of the prompt:
export PS1='\[\033[0;33m\][\u#\h \w]\$ \[\033[00m\]'

Resources