2024-12-06 • 2 min
How to Sync Neovim's Color Scheme with OS Dark Mode
We’ll take advantage of Neovim’s sockets support to run a Lua function that updates the color scheme. Unfortunately, this guide doesn’t apply to vanilla Vim since it doesn’t support sockets at the moment.
If your Neovim config is written in Vimscript, you can embed a Lua function like this:
" vimscript code
lua << EOF
print("This is lua code")
EOF
" more vimscript code
Create the Socket Files
The following function creates a unique socked file for each Neovim process in /tmp/nvim
and makes the instance listen on it:
function createSocket()
pid = vim.fn.getpid()
socket_name = '/tmp/nvim/nvim' .. pid .. '.sock'
vim.fn.mkdir('/tmp/nvim', 'p')
vim.fn.serverstart(socket_name)
end
Neovim will delete the socket files automatically when you close it.
Update the Color Scheme
Now we’ll write another function that reads the current OS theme and updates Neovim’s color scheme accordingly.
macOS
function updateColorscheme()
exit_code = os.execute("defaults read -g AppleInterfaceStyle")
if exit_code == 0 then
-- Set dark color scheme
else
-- Set light color scheme
end
end
Linux
function updateColorscheme()
command = "dbus-send --session --dest=org.freedesktop.portal.Desktop --print-reply /org/freedesktop/portal/desktop org.freedesktop.portal.Settings.Read string:'org.freedesktop.appearance' string:'color-scheme' | grep -o 'uint32 .' | cut -d' ' -f2"
handle = io.popen(command)
output = handle:read("*a")
handle:close()
output = string.gsub(output, "\n", "")
if output == "1" then
-- Set dark color scheme
else
-- Set light color scheme
end
end
Call the Functions on Startup
Both functions we’ve created must run automatically when we launch a Neovim instance. We can achieve this with a custom augroup:
vim.api.nvim_create_augroup('custom_startup', {})
vim.api.nvim_create_autocmd('VimEnter', {
desc = 'Create a socket for every nvim process',
group = 'custom_startup',
once = true,
callback = createSocket
})
vim.api.nvim_create_autocmd('UIEnter', {
desc = 'Set the appropriate theme on startup',
group = 'custom_startup',
once = true,
callback = updateColorscheme
})
React to OS Theme Changes
We’ll use pynvim
to connect to each of the socket files and call our updateColorscheme
function. You can install pynvim
through Python’s pip
.
Create a script with the following contents:
#!/usr/bin/env python3
import glob
from pynvim import attach
nvim_sockets = (attach('socket', path=p) for p in glob.glob('/tmp/nvim/nvim*.sock'))
for nvim in nvim_sockets:
nvim.exec_lua('updateColorscheme()')
All that remains is to hook this Python script into your OS theme change event so it runs automatically every time. This is not so straightforward and will depend on your platform:
- On macOS, you can use my
abysswatcher
daemon, which I created specifically for this purpose. You can find instructions on how to set it up in the README file. - On Linux, if you’re using Gnome, you can use the Night Theme Switcher extension. Otherwise, you’re on your own, but you’ll figure it out 😉.
Although this guide was focused on Neovim, you can of course use this same approach to automate other things. For example, I use abysswatcher
to update themes in Neovim, the Fish shell and other utilities like bat
.