2

I want to be able to change my background color mode throughout my tmux session with one command... I have a shell function bgl and bgd to set the background/colorschemes to be light and dark, and similar vim keybindings.

When I have a bunch of open panes, it becomes tedious to switch each pane to the different color mode.

Is it possible to write a script using tmux send-keys to send the appropriate commands to each pane (like if running vim, we'd have to do escape then <leader>bgd<enter>)? If so... (dear god) ...could you please help me with some direction towards that?

1 Answer 1

5

I pulled this answer from my blog

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

I had exactly the same problem as you, and I have the following setup:

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=colour235'
# 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()
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):

function! SetColorScheme()
    " check if tmux colorsheme is light or dark, and pick for vim accordingly
    if system('tmux show-environment THEME')[0:9] == 'THEME=dark'
        colorscheme zenburn
    else
        colorscheme seoul256-light
    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()

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 event in ~/.vimrc:

autocmd FocusGained * :call SetColorScheme()

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
6
  • thanks I've been looking at this code for a couple days and finally trying to implement now! Is the first codeblock shell-type code for a bashrc or similar file?
    – alec
    Jul 8, 2021 at 20:03
  • I'm struggling to understand that block... I'm wanting to simplify the logic but bash is ugly and I'm bad at it :(
    – alec
    Jul 8, 2021 at 20:05
  • The part that starts if command -v tmux>/dev/null; then would go into your .bashrc (usually in your home directory)
    – mattb
    Jul 8, 2021 at 20:08
  • 1
    The tmux environment variables were my missing piece, I wondered if (hoped) tmux has its own variables. Thanks for pointing this out!
    – alec
    Jul 8, 2021 at 20:39
  • 1
    @alec The first line checks if tmux is installed on the system (see most voted answer here: unix.stackexchange.com/questions/43601/…). You can read about the command in man bash, and search for the line that starts with 'command' using /command \[arg. From that man page: "...If the -V or -v option is supplied, the exit status is 0 if command was found, and 1 if not...."
    – mattb
    Jul 9, 2021 at 9:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .