The macOS Dark Mode, your Terminal and Vim
Posted on
The new Dark Mode in macOS Mojave is a nice addition and is – especially in the night hours — more pleasing to your eyes than the light mode.
However, enabling Dark Mode will not change the Terminal profile, which is a little bit annoying – especially if your color theme has a light and a dark variant (like the infamous Solarized, Snow, One, or my own Rasta theme).
If you change your Terminal profile to something dark, Vim still doesn’t look
right because it uses its own mechanism for light/dark backgrounds (see :help
'background'
for details) and doesn’t know about the changes you made to the
Terminal profile.
If you execute :set background=dark
in Vim (and if you color scheme
supports it), Vim looks nice and dark now, too.
However, on the next day, the fun begins again when you want to switch everything back to light mode …
Wouldn’t it be nice if this could all be accomplished with a single command?
There are tools, that help you with switching to/from macOS Dark Mode (e.g., NightOwl or Shifty), but they can’t change your Terminal profile or notify Vim.
As it turns out, it’s not too hard to implement a little program that does exactly this:
You can uses the
defaults
command to get the current macOS Dark Theme mode:$ defaults read -g AppleInterfaceStyle Dark
You can use AppleScript (oh, how I love this language …) to set Dark Mode and update the Terminal profile:
# Set Dark Mode tell application "System Events" tell appearance preferences set dark mode to true # Can be one of: true, false, not dark end tell end tell # Update default settings (for new windows/tabs) tell application "Terminal" set default settings to settings set "Rasta" end tell # Update settings for exsting windows/tabs tell application "Terminal" set current settings of tabs of windows to settings set "Rasta" # Theme name end tell
You can wrap both things with a Python script:
# toggle-macos-dark-mode.py import subprocess OSASCRIPT = """ tell application "System Events" tell appearance preferences set dark mode to {mode} end tell end tell tell application "Terminal" set default settings to settings set "{theme}" end tell tell application "Terminal" set current settings of tabs of windows to settings set "{theme}" end tell """ TERMINAL_THEMES = { False: 'Rasta light', True: 'Rasta', } def is_dark_mode() -> bool: """Return the current Dark Mode status.""" result = subprocess.run( ['defaults', 'read', '-g', 'AppleInterfaceStyle'], text=True, capture_output=True, ) return result.returncode == 0 and result.stdout.strip() == 'Dark' def set_interface_style(dark: bool): """Enable/disable dark mode.""" mode = 'true' if dark else 'false' # mode can be {true, false, not dark} script = OSASCRIPT.format(mode=mode, theme=TERMINAL_THEMES[dark]) result = subprocess.run( ['osascript', '-e', script], text=True, capture_output=True, ) assert result.returncode == 0, result if __name__ == '__main__': set_interface_style(not is_dark_mode())
You can use the
timer_start()
function introduced in Vim 8 and neovim to regularly check for the current Dark Mode settings. Put this into your Vim config:function! SetBackgroundMode(...) let s:new_bg = "light" if $TERM_PROGRAM ==? "Apple_Terminal" let s:mode = systemlist("defaults read -g AppleInterfaceStyle")[0] if s:mode ==? "dark" let s:new_bg = "dark" else let s:new_bg = "light" endif else " This is for Linux where I use an environment variable for this: if $VIM_BACKGROUND ==? "dark" let s:new_bg = "dark" else let s:new_bg = "light" endif endif if &background !=? s:new_bg let &background = s:new_bg endif endfunction call SetBackgroundMode() call timer_start(3000, "SetBackgroundMode", {"repeat": -1})
You can create an Automator action that runs the Python script and that can be activated with a global shortcut. I use
⌥⌘D
(you need to deactivate this shortcut for showing/hiding the Dock first). This is the AppleScript I used:do shell script "/usr/local/bin/python3 ~/toggle-macos-dark-mode.py"
The drawback of this method is that the current application (at the time you
press ⌥⌘D
) is used as “source” of the action you get two dialogs asking you
to give that app permissions to remote control the System Settings and Terminal.
A better solution would be if the authors of NightOwl and Shifty would integrated this into their tools. I’m gonna contact them and see what happens. :-)
Update:
MacVim got an OSAppearanceChanged
event that is emitted every time MacVim changes its appearance.
Thanks to Frank for the heads up!