How to "bind -x" keyboard shortcut and refresh prompt

I'm trying to bind git branch toggling to a keystroke via following configuration:

bind -x '"\et":"git checkout -"'

This works like charm, however I also have following setup code for displaying branch name in PS1:

PS1='${debian_chroot:+($debian_chroot)}\u@:\W $(parse_git_branch)\$ '

The branch name is nor refreshed after calling my keyboard shortcut until I do, say, cd ..

The only known workaround I'm aware of is to update config as follows:

bind '"\et":"git checkout - \n"'

Not a big deal, but I wonder what would be the best way to achieve desirable behavior with '-x' key preserved. What I've googled so far is there's redraw-current-line function but the thing is it's not installed on my Ubuntu and I have no idea how to install it.


Solution 1:

redraw-current-line is a command in readline, you don't install it. Bash uses the readline library and that's it, the command is already there. You use it with the bind builtin.

My tests indicate redraw-current-line redraws the prompt without re-evaluating it. PROMPT_COMMAND is not triggered, so you cannot use it to update the prompt either. For comparison: clear-screen behaves similarly.

In other words Bash remembers your prompt and redraws it without running parse_git_branch again. It seems the only way to actually re-evaluate is to type a command (possibly empty) and hit Enter. Your "git checkout - \n" emulates this: git checkout - is the command and \n works like Enter.

I understand in at least one aspect bind -x '"\et":"git checkout -"' is better than bind '"\et":"git checkout - \n"'. The former binding can be used while typing a random command and it doesn't interfere with what you typed. The latter binding generates a mess in such case.

But only the latter binding re-evaluates the prompt.

There is a relatively complex way to have your cake and eat it:

_save_command_line() {
   READLINE_LINE_OLD="$READLINE_LINE"
   READLINE_POINT_OLD="$READLINE_POINT"
   READLINE_LINE=
   READLINE_POINT=0
}

_restore_command_line() {
   READLINE_LINE="$READLINE_LINE_OLD"
   READLINE_POINT="$READLINE_POINT_OLD"
}

bind -x '"\C-x\C-s":"_save_command_line; git checkout -"'
bind -x '"\C-x\C-r":"_restore_command_line"'
bind '"\et":"\C-x\C-s\n\C-x\C-r"'

Now if you type Esct then Bash will:

  • send Ctrl+x,Ctrl+s to itself, this will run _save_command_line; git checkout -; note _save_command_line not only saves but also clears the command line;
  • send \n to itself, i.e. Ctrl+j which works like Enter; so it's Enter in our now empty command line; this will re-evaluate PS1 and print a new prompt;
  • send Ctrl+x,Ctrl+r to itself, this will run _restore_command_line.

This way you can run git checkout - and re-evaluate the prompt without disrupting the current command line.