General Software Architecture Uncategorized

The Multiple Monitor Vim IDE

I program in Vim, using a terminal application in fullscreen. I love immersing myself in an environment and not switching contexts. I spent a lot of time researching how people can use vim/linux as an IDE and span across multiple monitors effectively. Sadly, the search came back empty-handed.

I don’t want to have multiple vim instances talking to each other, and I don’t want to be forced into any particular layouts. I like remaining flexible. I switch between a 13″ laptop and three 27″ displays at the office.

I just wanted to document my general set-up without going into too many implementation details. Those will likely come in future posts.

Since I already use tmux to manage multiple windows, I wanted something that would fall in line with my current workflow. I’ve identified two main contexts of work:

  1. Programming: I’d like to stay in Vim, and ideally just run commands from the command mode. (ex: :!chmod +x % to make the current file executable). Compiling, running tests, and other programming-related tasks are secondary to simply editing.
  2. System Administration: For everything else, like working on remote servers, managing local services, and every other random task, I don’t really care as much since this context eats up less than 25% of my time.

It became evident that I want to optimize for programming inside of vim, while still enabling other tasks to be done effectively without being an expensive context switch. The big question then is: if you had more space, what could you use it for?

Within my programming context, I came up with a few different types of tasks I want to perform, which compete for screen focus & real-estate:

  • Text editing
  • Running tests
  • Viewing logs
  • Interacting with JIRA
  • Interacting with an API manually

I tried my best to split those into two groups: interactive and non-interactive. Interactive tasks I either need to trigger, or I need to constantly interact with.

Under non-interactive, it’s pretty much just watching log files. However, if I get creative and just trigger tests from Vim, I can treat them as non-interactive as well. Or, minimally interactive.

Under interactive, that leaves my CLI JIRA app, and then the random curl calls or similar things. However, as I iterate through a feature, I’m mostly just re-running a past task anyway. Let’s chalk that with running tests. Or, better put: running commands.

Having 1000 things on the screen is easy… the real trick is to deal with focus. How do you get to the window you want to without too much effort?

My solution to this is limit the tasks that require any focus. If something can be completely non-interactive, it reduces the effort required to benefit from it, and to maintain it.

The Layout

Let’s assume my current triple-monitor setup:

The regions are as follows:

  • Left: 2 regions for different log streams l1, and l2
  • Center: Huge Vim, and then the other tmux windows (zsh, git, running app, etc)
  • Right: 2 interactive regions: one large that receives commands r1, and one focused r2.

I found that trying to manage all of the different logs files I’m interested in is too much work, so I abstracted that away. Similarly, I never (or rarely) need to go anywhere except for Vim or the other focused window. This lets me switch focus between Vim and Focused Region with Alt+Tab or similar.

Each non-interactive region is named and tails a log file: tail -f /var/log/stream/[name] &. This lets me easily redirect any stream to that location via tee. some_process | tee -a /var/log/stream/l2 which I alias as tsome_process | t r1. This lets me easily aggregate logs to those areas that are related. I can manage those logs externally without having to nagivate tmux panes which ruins the focus.

If you don’t want a historical copy of your data, can further optimize this by using named pipes (aka FIFOs) instead of writing all of that data to disk.

# for each region:
rm /var/log/stream/$region
mkfifo /var/log/stream/$region

For sending commands, I won’t go into too much detail in this post, but the short answer is configuring Vim to be able to run arbitrary commands via tmux send-keys. Among other more complex cases, I added a :Run command and plenty of hot-keys per project:

  • :Run ls will run ls on the configured target session
  • T in normal mode will run my test suite
  • K in normal mode to send Ctrl+C to stop something bad
  • ! in normal mode will run the last command (sends !! in target session, hint, “retry”)
  • and just saving will trigger any relevant tests to run

The target session is easily changable at run-time, and points to a tmux pane, which can be on another session. The beauty of this is that session can be active on a remote computer over SSH. In my primary case, another monitor, but it leaves it flexible. You can use this to gain extra monitors your computer may not natively support, or to pair program.

Hopefully this helps give you some ideas or a template to base your own madness on. I’ll be following up with more specific tools on how to set this up.