let s:path = get(g:, 'vim_dir', expand('~/.vim')) . '/theme.json' let s:defaults = { \ 'background': 'dark', \ 'dark': {'theme': 'everforest', 'transparent': v:false}, \ 'light': {'theme': 'PaperColor', 'transparent': v:false}} let s:config = deepcopy(s:defaults) let s:applying = v:false let s:transparent_groups = [ \ 'Normal', 'NormalNC', 'EndOfBuffer', 'Folded', 'LineNr', 'SignColumn', \ 'StatusLine', 'StatusLineNC', 'VertSplit', 'WinSeparator', 'NormalFloat'] function! s:Merge(defaults, override) abort let l:result = deepcopy(a:defaults) if type(a:override) != v:t_dict return l:result endif for [l:key, l:value] in items(a:override) if type(l:value) == v:t_dict && type(get(l:result, l:key)) == v:t_dict let l:result[l:key] = s:Merge(l:result[l:key], l:value) else let l:result[l:key] = l:value endif endfor return l:result endfunction function! s:Available(name) abort return !empty(globpath(&runtimepath, 'colors/' . a:name . '.vim', 1, 1)) endfunction function! s:ApplyTransparency() abort if !s:config[&background].transparent return endif for l:group in s:transparent_groups execute 'highlight ' . l:group . ' guibg=NONE ctermbg=NONE' endfor endfunction function! s:Load() abort if !filereadable(s:path) return deepcopy(s:defaults) endif try let l:saved = json_decode(join(readfile(s:path), "\n")) return s:Merge(s:defaults, l:saved) catch return deepcopy(s:defaults) endtry endfunction function! theme#save() abort try call mkdir(fnamemodify(s:path, ':h'), 'p') call writefile([json_encode(s:config)], s:path) catch " A read-only home directory should not prevent Vim from exiting cleanly. endtry endfunction function! theme#apply(name) abort let l:background = &background let l:name = s:Available(a:name) ? a:name : 'sunbather' let s:applying = v:true try execute 'colorscheme ' . l:name catch " A scheme that fails to load (for example, one that needs true color in a " terminal without it) must not leave Vim without a color scheme. The " saved preference is kept so the next capable session restores it. colorscheme sunbather finally let s:applying = v:false endtry let &background = l:background let s:config.background = l:background let s:config[l:background].theme = l:name call s:ApplyTransparency() endfunction function! theme#toggleBg() abort let l:background = &background ==# 'dark' ? 'light' : 'dark' let l:theme = s:config[l:background].theme let s:applying = v:true try let &background = l:background finally let s:applying = v:false endtry let s:config.background = l:background call theme#apply(l:theme) endfunction function! theme#toggleTransparent() abort let s:config[&background].transparent = !s:config[&background].transparent if s:config[&background].transparent call s:ApplyTransparency() else call theme#apply(s:config[&background].theme) endif endfunction function! theme#onColorScheme(name) abort if s:applying return endif let s:config.background = &background let s:config[&background].theme = a:name call s:ApplyTransparency() endfunction function! theme#Init() abort let s:config = s:Load() let &background = get(s:config, 'background', 'dark') call theme#apply(s:config[&background].theme) augroup DotfilesTheme autocmd! autocmd ColorScheme * call theme#onColorScheme(expand('')) autocmd VimLeavePre * call theme#save() augroup END endfunction