Matthew Bennett

Logo

I am a data scientist working on time series forecasting (using R and Python 3) at the London Ambulance Service NHS Trust. I earned my PhD in cognitive neuroscience at the University of Glasgow working with fmri data and neural networks. I favour linux machines, and working in the terminal with Vim as my editor of choice.

View my GitHub Profile

View my LinkedIn Profile

View my CV

Switch between light and dark mode in Vim and Tmux with one command

I always have one of my monitors taken up with Vim and/or Tmux as I work, and have up until now used dark themes. Here I implement a way to toggle between a light and dark colour scheme for both Vim and Tmux in one go with a simple shortcut (entered into either Vim or a Tmux pane).

In my case I can do either:

From a Tmux pane:
1a) The alias 'ol' switches both Vim and Tmux to light mode.
1b) The alias 'od' switches both Vim and Tmux to dark mode.

From Vim:
2) 'Leader-o' toggles both Vim and Tmux between light and dark colour schemes.

dark theme light theme

The way it works is that I define a Tmux environmental variable that keeps track of whether we have a light or dark colour scheme. Anytime I switch, be it from Vim or a Tmux pane, the variable will be updated. Existing and new Tmux panes and existing and new instances of Vim will check this variable and follow the scheme.

Code implementation

Step 1: Switching colours from inside a Tmux pane

For the purpose of this guide, I'll assume my various dot files are in my home directory. In reality I keep them all in a single git repository with symbolic links from the home directory to allow me to keep my working environment synchronised across machines as described on this git repo.

First, I've configured my ~/.bashrc such that it will automatically launch Tmux and attempt to connect to a session called 'main', or create it if it doesn't exist. I find this works for me, but you may want to alter this step.

Once I've launched Tmux, I query the environment variable called 'THEME', if it's not equal to 'THEME=light' (or just doesn't exist), then we go with the dark theme and set the THEME variable accordingly. This means that when we first launch a Tmux session, we will default to a dark theme:

if command -v tmux>/dev/null; then
    [[ ! $TERM =~ screen ]] && [ -z $TMUX ] && tmux new-session -A -s main

    # check if we have been switched to light, else go dark
    [[ ! $(tmux show-environment | grep THEME) =~ 'THEME=light' ]] && 
    tmux set-environment THEME dark
fi

At the beginning of my ~/tmux.conf file, I start by sourcing a secondary Tmux file that contains the dark colours I've chosen. These values may get overridden by a light scheme later:

# source colorscheme
set -g default-terminal 'screen-256color'
source-file ~/.tmux_dark.conf

These are the dark theme colours I have:

# dark colours
# fg = thin line
set -g pane-border-style "bg=colour234 fg=colour244"
set -g pane-active-border-style "bg=colour234 fg=colour208"
# fg = text
set -g window-style 'fg=colour248,bg=colour234'
set -g window-active-style 'fg=colour252,bg=colour237'
# Customize the status line
set -g status-fg colour208
set -g status-bg colour234

In addition to ~/.tmux_dark.conf, I have ~/.tmux_light.conf:

# light colours
# fg = thin line
set -g pane-border-style "bg=colour253 fg=colour244"
set -g pane-active-border-style "bg=colour253 fg=colour208"
# fg = text
set -g window-style 'fg=colour238,bg=colour253'
set -g window-active-style 'fg=colour238,bg=colour231'
# Customize the status line
set -g status-fg colour208
set -g status-bg colour253

If I'm in a Tmux pane, and want to switch colour schemes, I just source the relevant file, and update the THEME variable. I have two aliases in my ~/.bashrc to do this. I remember them as ol for 'ON/Light', and od for 'OFF/Dark':

# switch between light and dark themes
alias ol="tmux source-file ~/.tmux_light.conf; tmux set-environment THEME 'light'"
alias od="tmux source-file ~/.tmux_dark.conf; tmux set-environment THEME 'dark'"

Step 2: Switching colours from inside Vim

In my ~/.vimrc, I've defined two functions, the first handles the updating of the Tmux THEME variable and toggles the Tmux colours, and the second sets Vim's colours.

We read the Tmux THEME variable with a system call to Tmux. This returns the THEME variable, as well as a message saying 'Press ENTER or type a command to continue'. Obviously we're only interested in the variable. Importantly, since I'm checking a match with 'THEME=dark', we must take only the first 10 characters of the message returned by the system call. Whichever scheme the variable indicates that we're using, we source the alternate theme and update the THEME variable. Once the THEME variable is updated, we call the SetColorScheme function to change Vim's colours:

function! Toggle_Light_Dark_Colorscheme()
    if system('tmux show-environment THEME')[0:9] == 'THEME=dark'
        :silent :!tmux set-environment THEME 'light'
        :silent :!tmux source-file ~/.tmux_light.conf
    else
        :silent :!tmux set-environment THEME 'dark'
        :silent :!tmux source-file ~/.tmux_dark.conf
    endif
    :call SetColorScheme(1)
endfunction

The colour scheme we pick is dictated by the Tmux THEME variable. If the THEME is 'THEME=dark', we choose a dark colour scheme (in my case zenburn), otherwise we go with a light one (in my case seoul256-light). In addition, I also tweak the theme's background color depending on whether Vim is the pane that I'm 'focused' on (see below):

function! SetColorScheme(focus)
    " check if tmux colorscheme is light or dark, and pick for vim accordingly
    if system('tmux show-environment THEME')[0:9] ==# 'THEME=dark'
        colorscheme zenburn
        let $BAT_THEME=''
        if a:focus == 0
            highlight Normal ctermbg=none
        endif
    else
        colorscheme seoul256-light
        let $BAT_THEME='Monokai Extended Light'
        if a:focus == 1
            highlight Normal ctermbg=white
            highlight LineNr ctermbg=lightgrey
        endif
    endif
endfunction

We can create a mapping (or a command) to quickly toggle the colour scheme like so:

nnoremap <Leader>o :call Toggle_Light_Dark_Colorscheme()<cr>

When we open a new instance of Vim, the Tmux THEME variable will have already been defined, and so we choose the colour scheme using the SetColorScheme function (note that this must come after the SetColorScheme function in your ~/.vimrc):

call SetColorScheme(1)

We could stop there, but in the case where we have an instance of Vim running, and change the scheme using one of our aliases from within in a Tmux pane, Vim won't automatically re-run the SetColorScheme function:

toggle in tmux, with vim open

We can use an autocmd to check and reset the colour scheme whenever Vim is re-focused. Unfortunately, this doesn't work for Vim in the terminal, but luckily there is a plugin that resolves it for us:
Plugin 'tmux-plugins/vim-tmux-focus-events'

This plugin requires the following line in your ~/.tmux.conf (or that you've installed the tmux-sensible plugin):

set -g focus-events on

With one of the above options (I just have the line in my ~/.tmux.conf), we can use the FocusGained and FocusLost events in ~/.vimrc to make a call to our SetColorScheme function and update the colorscheme accordingly:

autocmd FocusGained * :call SetColorScheme(1)
autocmd FocusLost * :call SetColorScheme(0)

This will mean that as soon as you return to vim from the Tmux pane, Vim's colour scheme will update automatically:

light theme

It would probably be easy to coerce Vim to constantly check the Tmux THEME variable, but I only switch from light to dark in the evening (i.e. once a day) so I don't want to make Vim do a million checks in the background for such a rare event:

One last remark is that I opted to change the colour of my command prompt to be visible under both colour schemes. In my ~/.bashrc I put 35m to specify a purple colour in bold font. How these numbers specify a colour is mysterious to me, so I just googled around a bit:

PS1='${debian_chroot:+($debian_chroot)}\[\033[01;35m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\n\$ '

However, choosing colours in the ~/.tmux.conf file is easy with this code which can be pasted into the terminal to display a colour grid of the 0-255 range:

for i in {0..255} ; do \
printf "\x1b[48;5;%sm%3d\e[0m " "$i" "$i"; \
if (( i == 15 )) || (( i > 15 )) && (( (i-15) % 6 == 0 )); then \
printf "\n"; \
fi; done

back to home