rmyorston / busybox-w32

WIN32 native port of BusyBox.
https://frippery.org/busybox
Other
677 stars 124 forks source link

A PS1 variable with colors prevents backspacing to the beginning of input #344

Closed doctorpangloss closed 1 year ago

doctorpangloss commented 1 year ago

ConEmu64_FdZZPMzENd

  1. Create a .profile like below which generates a large PS1 string and uses colors.

Contents of .profile:

parse_git_branch() {
  BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
  if [ "$BRANCH" != "" ]; then
    GIT_STATUS=$(git status -uno 2>/dev/null)
    if echo "$GIT_STATUS" | grep -q 'Your branch is up to date'; then
      # Green color
      printf " (\033[0;32m%s\033[0m)" $BRANCH
    else
      # No color
      printf " (%s)" $BRANCH
    fi
  fi
}

PS1="\w \$(date +'%Y-%m-%d %H:%M:%S')\$(parse_git_branch) \$ "
  1. Write a long line that wraps to the next line.
  2. Hold backspace.
  3. Observe you cannot backspace to the beginning of the reading side of the prompt.
  4. Remove the color formatting from the .profile
  5. Repeat 2-3
  6. Observe you can correctly backspace to the beginning of the prompt.

It's possible the color control codes and similar have been responsible for a lot of the layout and positioning wonkiness that I've observed here and there. Many applications emit them, like git, and they seem to curse the session permanently.

rmyorston commented 1 year ago

The issue with PS1 is also present in upstream BusyBox. There's a comment in the line editing code which mentions a workaround:

 * lineedit does not know that the terminal escape sequences do not
 * take up space on the screen. The redisplay code assumes, unless
 * told otherwise, that each character in the prompt is a printable
 * character that takes up one character position on the screen.
 * You need to tell lineedit that some sequences of characters
 * in the prompt take up no screen space. Compatibly with readline,
 * use the \[ escape to begin a sequence of non-printing characters,
 * and the \] escape to signal the end of such a sequence. Example:
 *
 * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
doctorpangloss commented 1 year ago

This is very illuminating, thanks. I don't really comprehend how to modify my prompt in response to the comment, adding backslashes just glitches things for me. I will keep trying to reliable reproduce linedit glitch.

avih commented 1 year ago

I don't really comprehend how to modify my prompt in response to the comment

The \[ and \] to wrap escape sequences is the same with bash (which uses readline).

You need to modify this line:

      printf " (\033[0;32m%s\033[0m)" $BRANCH

into this:

      printf " (\\[\033[0;32m\\]%s\\[\033[0m\\])" $BRANCH

it uses double-backslash (\\[) because printf interprets backslashes too, so if you want literal \[ to be part of the output, then each of those backslashes need to be escaped with another backslash.