Vim Without Tmux
At GR Tech, we primarily use Clojure, but JavaScript is heavy in the mix too. I’ve worked on projects that use each and I want my editor to be as good with one as the other. To that end, I recently made the switch to develop full time in Vim, but have friends and colleagues who’ve been deftly using it for quite a long time. While everyone has their own unique setup, they all have one tool in common that they use on top of it: tmux
. Since I’ve only recently transitioned to using Vim, I had not yet added that tool to my workflow, and instead have been using a feature new to Vim 8, a terminal command that opens a shell inside a dedicated buffer.
Let’s explore this feature a bit and jump right in: open Vim and, to check if this is a capability, type
:echo has('terminal')
For those readers that a 1
appears in the Command line, the rest of this article is for you! Type
:terminal
This ought to create a horizontal split with a prompt like this
You should be able to immediately type at the prompt. Give it a whirl with
pwd
and you should see your current directory. Windows users, depending on which version of Vim you’re using you will either be able to use bash or Windows Command Prompt commands; the article will be using bash commands.
This is also the moment that you might experience a bit of terror mixed with regret as you realize that opening a Terminal inside a Vim buffer only adds to the number of meanings of quitting (in) Vim:
Typing :q
won’t work here because you will be entering that at the command prompt you just opened. To shut the terminal window down, you’ll have to type
exit
in at the prompt (and hit enter) which both stops the shell and closes its buffer.
But of course there’s lots of reasons you’ll want to leave the terminal running, and resume focus on a different buffer. To simply go to an open buffer, use the normal split movements; <Ctrl w> w
gets you back to your next (or, in this case, previous) buffer. And, it should go without saying that Vim will not let you close the session without exiting currently running shell buffers, treating them as though they are unsaved files.
Just Another Buffer (Sort of)
As we saw above, you can interact with this terminal buffer as any other: Moving it using normal split movements works just fine: <Ctrl w> L
will push it to the right and <Ctrl w> H
will push it to the left.
But terminal buffers have an additional mode,Terminal-Normal
. To see what this is all about, have the terminal buffer output something long, for example, if you're in a git initiated directory,
git log
Hitting spacebar a couple of times outputs more of the log and makes older lines vanish above the fold. However, even if you have mouse events on, this buffer stubbornly does not scroll. To be able to view the terminal’s older output, hit <Ctrl w> N
(note the capitalization) and this will put the buffer into something that's akin to Normal
Vim mode. If you have them turned on, you'll be able to see line numbers now as well as use scroll movements, yank lines, perform searches, even make selections with Visual
mode, to name a few regular Vim capabilities. But it is read-only, which you'll discover as soon as you try to do any manner of editing. To go back to being able to input commands at the shell prompt (called Terminal-Job
mode), hit i
.
Some Things to Know
- In many bash environments,
<Ctrl w>
deletes back one word, but (as we’ve been talking about) it won't in this case. Instead, it tells Vim to get ready to do something with windows. - When a buffer resizes and the terminal is in “Insert” (
Terminal-Job
) mode, characters will get cut off on the right, and they seem unrecoverable. This is just as true for manual resizes as it is for maximizing other buffers. However, if it’s inTerminal-Normal
mode, it will wrap the characters. You could have Vim automatically switch into Normal mode when losing focus, but that comes at the cost of not having updates print out from the running process as they happen. - If you cycle between buffers, you can end up in the Terminal buffer, even if you already have it open. If that happens, you’ll automatically be put into whatever mode the Terminal was in last, likely
Terminal-Job
mode. If that happens, you won’t be able to use buffer cycling or commands to move to the next buffer. Instead, you’ll need to<Ctrl w>
and then use your normal buffer cycling commands (like:bufnext
, or:b {name-of-buffer}
). - vim-airline users: The v0.10 update to hides the terminal buffer from the buffer list by default. This may not be exactly what you want due to the issue above. If you’d like to see any Terminal buffers displayed in that list, add the following to your
.vimrc
:let g:airline#extensions#tabline#ignore_buffadd_pat='gundo|undotree|vimfiler|tagbar|nerd_tree|startify'
This will add the buffer to the list and visually indicate that it has unsaved changes in it.
Use Cases
While the above are some things to be aware of there are plenty of helpful things one can do with a Terminal shell in a buffer. They include:
REPL
s- Watch commands
- Dependency installs
git
with regular terminal interactions- Anything you might have used
<Ctrl z>
for
And then there are the more vim-powered benefits:
- Vim-managed splits on multiple terminals:
tmux has its own keyboard commands for moving from window to window and of course long time users have these in muscle memory, but with this feature, you may not need them. You can open multiple terminal buffers and hop between them using only Vim movements and commands. - Ability to have more than one view into the same shell:
While perhaps rare or unusual in needing such an ability, this is where Vim could be adding something special to your workflow. When in a Terminal buffer,<Ctrl w> :split
creates a new split with the same content as your last buffer, as usual. If you now enterTerminal-Normal
mode, both splits will reflect it, but you’ll be able to jump around to previous output without it changing for the other for those circumstances where you might like to see multiple older states at once. When you’re done, close it up like you would any other buffer, with a<Ctrl w> :q
— in other words, don’texit
the terminal, just close the extra window(s) into the buffer. Typingexit
at the prompt closes all currently opened buffers with views into that shell. - Incepting (Terminal opens Vim which opens Terminal which opens Vim…)
While this does work, it’s probably best not to go even 2 levels deep with this capability…
but instead just hop into other Terminal utilities, which might be ordinary little things like ls
, rsync
, orping
, but could also include larger applications like mysql
orssh
(and, yes, even nano
or pico
if you’re feeling rococo).
Preferences
As with all other Vim features, you may want to customize this one to suit your needs and tastes. I have a shortcut in my .vimrc
that opens a terminal window on the right with
" split to the right
set splitright" open a terminal in a vertical window
nnoremap <silent> <leader>t :vert term<CR>
TL;DR
Vim 8 got a pretty amazing new feature in its ability to open Terminal windows inside buffers. This can allow a development workflow that used to depend on tmux to potentially exist without it. Of course, tmux has uses other than adding terminal windows adjacent to a Vim session, but for me, Vim’s Terminal feature has been a great addition that I use daily. As always, the help file has more and great information and can be viewed online or by :help terminal
.