IDE in the terminal: meet vim and tmux


Integrated Development Environment. That is, an environment what integrates helper tools to help you with your development activities. Now that our environment variables are set up properly, let's get comfortable with the terminal multiplexer tmux and the main part of a development environment: the editor, in our case: vim.

After reading this post, you will probably be more comfortable using the terminal to carry out the task of editing files and make sure they are at least syntax-checked before committing them into the version control system.

Using multiple terminals with tmux

A terminal multiplexer gives you the ability to use (and easily navigate) multiple virtual terminals within the same terminal or terminal emulator window. Like your browser tabs! An additional advantage of using a multiplexer is that the multiplexed sessions don't exit when your SSH connection is terminated. That's why admins and devops people kinda like it.

Screen is most probably installed on your linux distribution / servers, however, it's kind of limited compared to what tmux offers. Feel free to read through the getting started guide, however, for now, it's enough if you learn the basics.

Install and then start it up by typing tmux! Create and navigate through tabs and splits. Whenever you create a new pane, your default shell will start up. As you can see, all the tmux-related shortcuts have a prefix, Ctrl-b. After pressing it, you type in the next letter and the magic happens.

Action Shortcut
Split current tab vertically Ctrl-b %
Split current tab horizontally Ctrl-b "
Switch to the pane left Ctrl-b
Switch to the pane below Ctrl-b
Switch to the pane above Ctrl-b
Switch to the pane right Ctrl-b
Create new tab Ctrl-b c
Switch to the next tab Ctrl-b n
Switch to the previous tab Ctrl-b p
Show all shorcuts (help) Ctrl-b ?
Search help for the word "resize" /``resize
Detach session Ctrl-b d

Now check the help for how to resize a pane. Split your terminal horizontally (prefix + "), resize until it's comfortable (prefix + C-↓ C-↓ C-↓ ‥), switch to the pane above (prefix + ↑), start up vim, and...

tmux and vim

Starts to look like an IDE isn't it? Well, there is a lot more to do and I won't show everything. However, at this stage, you can comfortably have a terminal environment running on your jumphost, server, desktop/laptop. Play around and then read along!

Edit in vim and lint on the command line

Reading this blog post means you already know vim somewhat. At least you can exit vim after editing a piece of text (:q). Now instead of quitting, press Tab after the colon. Chances are that you are presented with a lot of commands vim is capable to offer you. And this is only the tip of the iceberg.

Make sure you have a basic understanding of how vim is a modal editor, where you mostly spend your time in "Normal" mode (to navigate and edit files) and rarely go into "Insert" mode (to insert text). Esc puts you back in normal mode, i to insert mode, when you start typing a command with :, guess what, you are in command line mode. At any time you are lost, just press Esc a bunch of times (optimally you already remapped the useless Caps Lock key to Esc). A handy command would be :help so that whatever vim-related topic / shortcut / command / setting you are interested in, you can quickly check its documentation.

Let's start with 'Hello World!' - Press i for insert mode, and mindlessly type in our hello world program:

if __name__ == '__main__':
  print("Hello World!")

Esc back into normal mode, and save your work with :w hello.py. Navigate to your tmux bottom pane prefix + ↓ and check if your program runs:

$ python hello.py
# Output:
#   Hello World!

For linting we are usually not interested in actually running our little program. What we can do is to invoke the compiler only:

$ python -m py_compile hello.py
# No output

Now go back to your vim in the upper pane (prefix + ↑) and add an erroneous line in the python file. Press G to jump to the last line of your file, press o to open a new like for insertion and type in an obvious syntax error. Something like this:

if __name__ == '__main__':
  print("Hello World!")
  prin"This should not pass"

Go back to your shell below and observe the compiler's output:

$ python -m py_compile hello.py
# Output:
#   Traceback (most recent call last):
#     ...
#     File "hello.py", line 3
#       prin"This should not pass"
#           ^
#   SyntaxError: invalid syntax
#     ...

Now you can go back to vim, hello.py, 3rd line, and fix your syntax.

Summary

Some of these looks cumbersome at a first glance; however, all you needed is available on the command line, and a week's worth of muscle memory training you are going to be faster than clicking around in other IDEs.

This is NOT how I do daily development though. Of course, both vim and tmux can help you a bit here, and enhancing these tools with macros, plugins, proper configuration, the workflow gets unbeatable.

As a sneak peek, if you feel adventurous, read on!

Optional, advanced stuff: meet :make and the Quickfix list

Here be dragons!

Vim has built-in "error consoles" to help you navigate syntax errors. The flow is the following:

  1. Edit and save your code / script / descriptor (:w)
  2. Run the compiler or linter (:make, that invokes makeprg)
  3. Observe and acknowledge the compiler output and return to vim (Press Enter. Now vim parses the output according to errorformat)
  4. Navigate your file by jumping to the lines with syntax errors (:cfirst)

By default, vim can understand and parse gcc's error format. The python compiler's output is different, so, bear with me, we need to teach vim how to parse it.

Now this will sound scary, but let's follow this article on fandom to quickly configure errorformat and makeprg. Don't worry if you don't understand much for now. You can always ask the built-in :help if you want to dig deeper; here I only want to show you what vim is capable of.

Set makeprg to a python compiler. We need to invoke :set to, well, set some configuration variables. 🤫 If you are adventuorous, also try the :nmap command!

set makeprg=python\ -c\ \"import\ py_compile,sys;\ sys.stderr=sys.stdout;\ py_compile.compile(r'%')\"
set efm=%C\ %.%#,%A\ \ File\ \"%f\"\\,\ line\ %l%.%#,%Z%[%^\ ]%\\@=%m

" This is magic
nmap <F5> :!python %<CR>

Ready? Jump to the beginning of the file (gg). Now type in :make and press enter. The python compiler runs, and vim shows you it's output, asking you to press enter to continue. You might need to press enter again.

Your cursor is on the erroneous line now. That's comfy!

What happened here is that vim populated it's so-called Quickfix list with all the errors the compiler emitted. You can easily navigate this list:

Action Command
Jump to the first error :cfirst
Jump to the last error :clast
Jump to the previous error :cfirst
Jump to the next error :cfirst
Open the Quickfix list :copen

Well, this is much less cumbersome than switching terminals there and back, chcking line numbers manually. Also, this is already considered advanced vim features; so if you made it down here, you are an advanced vim user! 😉