I’m trialing a move to MacVim, and with it, moving to Vim 8’s new built-in terminal emulator.

My purpose in doing this is to see how GUI Vim is looking these days and to see if GUI Vim running a terminal emulator can actually compete with a terminal emulator running Vim for my development uses.

Why use GUI Vim?

  • I like that tab management looks nice and I can re-order tabs easily
  • No dealing with terminal color limitations
  • Less keybinding weirdness, want to bind to Meta/Option/Alt? Easy, set macmeta
  • Proper bold/italics

Why have a terminal inside GUI Vim?

  • Navigating terminal output using Vim bindings
  • Easily yank/put between buffers and terminal, no system clipboard involved

Honestly this argument is kind of weak. If you like tinkering with your tools though, carry on!

Launching Terminal

:terminal

This launches a horizontal split with a terminal taking up precious vertical space.

Let’s define some keybindings that are more comfortable. I use <space> as my leader, and tend to group related commands under a prefix. I chose <leader>t to be my terminal prefix.

" terminal in tab
nnoremap <leader>tt :tab term<CR>
" terminal in current window
nnoremap <leader>tc :term++curwin<CR>
" terminal in vertical split
nnoremap <leader>tv :vert term<CR>

These cover my use cases pretty well, I never use horizontal split terminals.

Getting Out

How do you get out of terminal windows? :help Terminal-Mode shows that CTRL-W basically works as expected. You can move to different windows, tabs, or escape normal mode commands by prefixing with CTRL-W. You can escape to terminal normal mode using CTRL-W N, but I found this annoying.

If you don’t really need escape inside your terminal sessions you can bind it to escape terminal mode (this is from the Vim docs!).

tnoremap <Esc> <C-W>N

Passing through special keys

The most annoying thing I find with these built-in terminals is how they always seem to break shell bindings.

I’ve learned bits and pieces of emacs-ish bindings available in common shells. For example, <M-b> goes backwards a word in most shells and <M-f> goes forward a word. Also, <CTRL-W> deletes backwards one word.

These are totally broken in Vim’s built-in terminal. Obviously <CTRL-W> gets stolen by Vim since it is using <CTRL-W> to escape to normal mode. In addition, <M-b> and <M-f> will leave you frustrated with a bunch of special characters like so ∫∫ƒƒƒ.

I don’t really have a great solution for <CTRL-W>, I don’t want to make drastic changes to how Vim sets up these bindings by default, so I’m just getting used to hitting <M-BS> to delete a word while avoiding Vim’s bindings.

I do have a solution for my <M-b> and <M-f> woes though! Vim’s docs have a little function definition to help with passing special keys.

" Allow us to create <M-*> bindings
set macmeta
" Remap to call send_term function
tmap <expr> <M-b> SendToTerm("\<Esc>b")
tmap <expr> <M-f> SendToTerm("\<Esc>f")
func SendToTerm(what)
    call term_sendkeys('', a:what)
    return ''
endfunc

This lets Vim actually pass the correct terminal escape code to get these shortcuts working as expected again.

Thoughts

I’m sure I’ll run into some more annoyances soon, but so far this is working out pretty well for me, and there’s a clear path to adding additional escape sequences for other broken shortcuts. I will say that being able to navigate terminal output and rapidly yank/put has really been an improvement.

Protip

Want to know what keycode is being sent by a terminal to your application? Run cat then hit the key combination, you’ll see the keycode output.