Vim terminal mode config

Vim has a couple ways to run terminal commands from the editor. From ex command mode, :! will allow running a single command before breaking back to Vim, while :term will open a new terminal window within Vim, by default in a split, to run as many commands as you want. There are several settings in my vimrc that I add to make working with these terminal modes easier and nicer. I will share some of them below.

Quicker access

I’ve been using the leader key, which I have set to space, plus another character to make frequent or more verbose commands / tasks quicker and easier. :! and :term are just long enough to make me want a quicker key for, so I’ve used t and T with leader to access them respectively. That looks like this:

nnoremap <leader>t :!
nnoremap <leader>T :tab term<cr>

in my ~/.vimrc.

Shell aliases

With GUI Vim (eg MacVim / gVim), CLI aliases and other Bash shell settings don’t work from the ! shell by default. Vim uses a $BASH_ENV variable to define a file that will be loaded for Bash configuration for that shell. That looks like:

let $BASH_ENV = "~/.vim/bashenv"

in my ~/.vimrc. In that bashenv file, which is Bash syntax like a .bashrc, we must run shopt -s expand_aliases to force it to support aliases for Vim. We also need PS1='>' in there so that Bash believes we’re interactive, and then load my regular .bashrc. That looks like this in my ~/.vim/bashenv file:

PS1='>'
shopt -s expand_aliases
source ~/.bashrc

Now all my aliases and some other settings work fine.

Output colors

The output of ! commands by default doesn’t support colors in GUI Vim. When running commands that force output colors, they will instead output the strings that represent the colors, making the output hard to read. In Vim newer than 9.0.0100, we can set a guioptions option of ! to run those commands in a temporary terminal window shown at the bottom of the screen, supporting colors. In my ~/.vimrc, I have:

if has('patch-9.0.0100') && has('gui')
    set guioptions+=!
endif

Now colors show fine, including in less or the like. The only problem I’ve noticed is that sometimes an extra <return> press is required when it otherwise wouldn’t be. I’ve read that more significant problems can crop up with this option set, but that may be in older versions of Vim.

Colors are not an issue in CLI Vim, as it actually breaks out to the calling terminal, and thus has all of its capabilities.

:term normal mode

The :term terminal, to match Vim’s interface, has a Normal mode to edit the current line or copy previous output. It is accessed by pressing <C-\><C-n>. Since that is not easy to remember and I want something quicker, I have mapped pressing escape twice to do the same thing. That is not something I commonly do in the terminal normally, so it hasn’t caused me problems. That is done in my ~/.vimrc like:

tnoremap <esc><esc> <C-\><C-n>

This only works from insert mode and beeps otherwise, which is a minor annoyance that I don’t run into often. I don’t use Normal mode in the terminal often in general though. The quicker access is just nice on the occasion that I need it.

:term line numbers

In Vim, I normally have line numbers enabled for editing text files. They are not useful and are actually annoying in a terminal window, so I disable them with an autocmd. The event they are attached to is TerminalOpen. I have this in my ~/.vimrc:

augroup TJMTermNoNum
    autocmd!
    autocmd TerminalOpen * set nonu nornu
augroup END

Open in current Vim instance

When opening a file from the Vim terminal to be opened in Vim, by default, it will open a new instance of Vim. This can be annoying if working in a single project and trying to keep things together. Vim provides special escape sequences from its terminal to communicate with it. Those can be used to tell that instance to open the specified file. We can add something like:

thisvim() { echo -e "\033]51;[\"drop\", \"$1\"]\007" ; }
[[ -n "${VIM_TERMINAL}" ]] && alias vim=thisvim

to our Bash config to override the vim command when inside a Vim terminal. I have an ot command that I use for opening files in GUI Vim, so I put my solution in there. I also tend to use tabs in Vim, which would require tab drop instead. To support passing multiple files, I did it as a loop, which required using a function. So the relevant part of my shell command looks like:

if [[ -n $VIM_TERMINAL ]] && [[ -n "${@:2}" ]]; then
    for var in "${@:2}"; do
        printf '\033]51;["call", "Tapi_OpenInTab", ["'$var'"]]\07'
    done
    exit 0
else
    open
endif

which is in among some logic determining if we want to be using Vim to open the file(s). That requires the loop function that I mentioned to be in my ~/.vimrc, which looks like:

fun! Tapi_OpenInTab(bufnum, arglist)
    echo "opening " . a:arglist[0]
    for fn in a:arglist
        execute 'tab drop ' . fn
    endfor
endfun

The function needed the Tapi_ prefix to work. Apparently it’s a security feature to prevent arbitrary functions being called from the terminal.