How to change cursor shape depending on VI mode in Bash?
Asked Answered
F

6

31

I have the following line in my .bashrc:

set -o vi

And I want my cursor to have a pipe shape when I am in insert mode and a block shape when I am in command mode, like what I would have in Vim if I placed the following in my .vimrc:

let &t_SI = "\e[6 q"
let &t_SR = "\e[4 q"
let &t_EI = "\e[2 q"

Except in this case I want to have the equivalent behavior on the command line.

I found a partial answer to my question here - https://unix.stackexchange.com/questions/22527/change-cursor-shape-or-color-to-indicate-vi-mode-in-bash - written by @gogolb.

Here is the answer, copied:

#!/bin/bash
# Script "kmtest.sh"

TEST=`bind -v | awk '/keymap/ {print $NF}'`
if [ "$TEST" = 'vi-insert' ]; then
    echo -ne "\033]12;Green\007"
else
    echo -ne "\033]12;Red\007"
fi

export PS1="\u@\h \$(kmtest.sh)> "

Unfortunately, though, as explained in the answer, the example script only changes the cursor shape after a carriage return, whereas, what I want is for the cursor shape to change when I hit <Esc> (i.e. when I change mode).

I am on Linux running the native terminal app, with Bash 4.4.7 and my $TERM variable set to xterm-256color. Also, I do not know if tmux has any effect on what I am asking for, but I, ideally, would like the solution to work both within and exterior to tmux sessions.


SOLUTION

I ended up discovering the answer to this question myself, which I describe here in another question I posted:

How to correctly link patched GNU readline library to all existing programs?

Don't worry, the solution does not require any patching. ;)

Freitas answered 14/6, 2017 at 2:59 Comment(0)
F
51

SOLUTION:

I am posting my answer to my own question here as recommended.

This solution works for Bash 4.4+, since, starting with that version of Bash, version 7.0 of the GNU readline library is used, which includes the necessary additions of the vi-cmd-mode-string and vi-ins-mode-string variables.

These variables can be set as follows in your .inputrc file in order to achieve the functionality I described above:

set show-mode-in-prompt on
set vi-cmd-mode-string "\1\e[2 q\2"
set vi-ins-mode-string "\1\e[6 q\2"

EXPLANATION:

For those who are actually interested in how the above solution works.

These two variables, vi-cmd-mode-string and vi-ins-mode-string, are printed to your terminal along with the command prompt in order to provide a sort of visual indicator as to which mode you are currently in (i.e. command mode vs. insert mode).

The defaults for these two variables are "(cmd)" and "(ins)" for command and insert modes, respectively. So if you were to just leave them as the defaults and had a command prompt of, say, PS1='>>>', then your prompt would look like the following:

  • Command mode:

      (cmd) >>>
    
  • Insert mode:

      (ins) >>>
    

According to the man-page for readline (see below), you can also specify non-printable characters, such as terminal control sequences, by embedding the sequences between the \1 and \2 escape characters.

vi-cmd-mode-string ((cmd))
       This  string  is  displayed immediately before the last line of the primary prompt when vi editing mode is active and in command mode.  The value is expanded like a key binding, so the
       standard set of meta- and control prefixes and backslash escape sequences is available.  Use the \1 and \2 escapes to begin and end sequences of non-printing characters, which  can  be
       used to embed a terminal control sequence into the mode string.
vi-ins-mode-string ((ins))
       This  string is displayed immediately before the last line of the primary prompt when vi editing mode is active and in insertion mode.  The value is expanded like a key binding, so the
       standard set of meta- and control prefixes and backslash escape sequences is available.  Use the \1 and \2 escapes to begin and end sequences of non-printing characters, which  can  be
       used to embed a terminal control sequence into the mode string.

Therefore, in my above solution, I am embedding the terminal control sequences, \e[2 q (make the cursor a vertical bar) and \e[6 q (make the cursor a pipe), between these \1 and \2 escape characters, resulting in my cursor having the shape of a vertical bar while in command mode and a pipe shape while in insert mode.

Freitas answered 25/1, 2018 at 17:53 Comment(5)
Cool, thanks for that! However, I have a problem with this solution: When I put those 3 lines into .inputrc, the cursor within vim (like, when I'm editing a file) changes to a pipe symbol as well. I don't want that - any idea how I can prevent that from happening?Sharmainesharman
I don't have that issue for some reason. I'm using NextJump/jarvis from Github, and within my vimrc.local file, I've written the let &t_SI = "\e[6 q", etc. commands (see the original problem description)... maybe adding that will fix your issues.Freitas
Thanks, it does, indeed.Sharmainesharman
This works in Git Bash on Windows but cursors are not blinking, do you know how to enable blinking?Intisar
If you want blinking you can try using the blinking versions of the above control sequences. E.g. blinking block cursor = "\e[1 q", blinking underline cursor = "\e[3 q", blinking vertical bar cursor = "\e[5 q".Freitas
C
5

This is awesome. I want to add that in addition to adjusting the cursor, it is still possible to have a textual mode state message as well. This code works:

set show-mode-in-prompt on
set vi-cmd-mode-string "\1\e[2 q\2cmd"
set vi-ins-mode-string "\1\e[6 q\2ins"

cmd and ins will show up on the left of the prompt based on the mode.

Cortie answered 9/6, 2019 at 6:8 Comment(0)
O
2

If you want to reset your cursor to normal before your other programs run, then you can use the environment variable PS0. From man bash:

The value of this parameter is expanded (see PROMPING below) and displayed by interactive shells after reading a command and before the command is executed.

Setting PS0 with this command will cause it to restore the cursor to a non-blinking block:

PS0="\e[2 q"
Oneal answered 14/1, 2020 at 5:57 Comment(0)
R
2

The answer by Alan Barnett re:

Setting PS0 with this command will cause it to restore the cursor to a non-blinking block:

PS0="\e[2 q"

Solved my Vim issue, but instead of PS0="\e[2 q", I wrote PS0="\e[2 q\2". Alan's answer somehow added a ] on my terminal output of any command.

Ruhnke answered 14/9, 2021 at 7:33 Comment(0)
G
0

This is what I'm using, yes unicode. The only downside is that when you're not running X server the unicode is borked! :-)

 set show-mode-in-prompt on
 set vi-ins-mode-string \1\e[34;1m\2└──[ins] \1\e[0m\2
 set vi-cmd-mode-string \1\e[33;1m\2└──[ cmd] \1\e[0m\2
Gunilla answered 14/1, 2020 at 11:13 Comment(0)
P
0

Terminal emulator

bikri@A-Boy-GMT-17:~$ echo $TERM
xterm-256color

It simply worked by:

bikri@A-Boy-GMT-17:~$ sudo apt install vim

then modify ~/.vimrc by adding those lines

" Customize the cursor type
let &t_SI = "\e[5 q"
let &t_EI = "\e[2 q"
Promptbook answered 8/5 at 6:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.