Emphasizing the active pane in Vim using ColorColumn

Published on May 25, 2018 under Blog

Abstract

Around a month ago I switched to using Vim full-time. I've already been using Vim for all of my command-line editing, but now I've enabled Vim emulation in all of the other editors I use (namely those coming from JetBrains).

To keep my Vim skills versatile, my Vim configuration uses vanilla key mappings. That said, I've still made a lot of quality-of-life adjustments to the config, including colour scheme tweaks. One of such tweaks is changing the style of the active pane to make it more obvious - which is also the focus of this article.

Getting it to work

There are several steps needed to get the whole thing working. Jump to the end of this article if you want to see the final code.

Initial setup

For the sake of this article, I will use red for my colour column (from here on I will call it just "column"). To make screenshots more compact I will use values 20 and 40. Let's assume I start off with this .vimrc:

" Make cc red
highlight ColorColumn ctermbg=Red

" Set global cc default to 20
set colorcolumn=20

" Set cc in Markdown files to 40
autocmd FileType markdown setlocal colorcolumn=40   

Now, if I open files config.ini (left pane) and text.md (right pane) I will get this:

Vim with a vertical split

Note that the column in the left pane is draw at position 20, and in the right column and position 40.

Problem statement

We want the column to only appear in the active window. Here's the result we want to get:

Color column in active window in Vim

Naive solution (doesn't work)

A naive solution, that uses only autocmd, could look like this:

augroup BgHighlight
    autocmd!
    autocmd WinEnter * set colorcolumn=40
    autocmd WinLeave * set colorcolumn=0
augroup END

This will indeed only show the colour column in the active window, but there is one drawback - the position at which it is drawn is always 40. This defeats the purpose of using different colour column settings for different file types. We can do better!

Better solution

Instead of using one-liners with autocmd Win<Enter/Leave>, let's define some functions:

function! OnWinEnter()
    " We will put some code here soon
endfunction
function! OnWinLeave()
    " We will put some code here soon
endfunction
augroup BgHighlight
    autocmd!
    autocmd WinEnter * call OnWinEnter()
    autocmd WinLeave * call OnWinLeave()
augroup END

By now, you have probably guessed what we will do: We will hide the colour column in OnWinLeave() and reveal it in OnWinEnter(). Doing this is pretty straight-forward, but there are some things we have to consider.

We will "remember" the initial colorcolumn setting of a window using a window-scoped variable. Window-scoped variables are created by prefixing their names with w: (check out Vim variable scoping for more info). To remember the value, we'll need to execute the let w:initial_cc=&colorcolumn command at some point.

The question is, where do we put it? There's a catch: If we put it inside OnWinEnter(), we'll actually remember the colorcolumn of the window that we're leaving. This makes the logic somewhat complicated, so, instead, we're gonna put it inside OnWinLeave(). We're also gonna add some code to hide the colour column (since the window we're leaving will become inactive):

function! OnWinLeave()
    " Define w:initial_cc if it isn't already defined
    if !exists('w:initial_cc')
        let w:initial_cc=&colorcolumn
    endif

    " Hide colour column
    let &colorcolumn = 0
endfunction

Now, variable w:initial_cc holds the colorcolumn value of the window to whose scope it belongs. All we have left to do is use this value in OnWinEnter():

function! OnWinEnter()
    " Use w:initial_cc if it's defined
    " (default value will be used otherwise)
    if exists('w:initial_cc')
        let &colorcolumn = w:initial_cc
    endif
endfunction

And we're done! Putting everything into our Vim config we'll get the desired behaviour. I know that some of my explanations lack detail - there are just too many small things to explain here. If you want to understand why (and how) this works, try implementing it yourself.

Final code

Below you can see the full Vimscript snippet that you can put into your .vimrc.json. Note that this technique can be applied to other Vim parameters too - go make something cool with it!

" Global default
set colorcolumn=80

" Specific length for different filetypes
autocmd FileType javascript setlocal colorcolumn=120

" Logic for entering/leaving a pane
function! OnWinEnter()
    if exists('w:initial_cc')
        let &colorcolumn = w:initial_cc
    endif
endfunction
function! OnWinLeave()
    if !exists('w:initial_cc')
        let w:initial_cc=&colorcolumn
    endif
    let &colorcolumn = 0
endfunction
augroup BgHighlight
    autocmd!
    autocmd WinEnter * call OnWinEnter()
    autocmd WinLeave * call OnWinLeave()
augroup END

End of Article

Timur Kuzhagaliyev Author

I'm a Computer Science student at UCL, spending a year in Caltech. Check out my GitHub to see what I'm up to. Feel free to like and share: