1" This script tests a color scheme for some errors and lists potential errors. 2" Load the scheme and source this script, like this: 3" :edit colors/desert.vim | :so colors/tools/check_colors.vim 4 5let s:save_cpo= &cpo 6set cpo&vim 7 8func! Test_check_colors() 9 let l:savedview = winsaveview() 10 call cursor(1,1) 11 let err = {} 12 13 " 1) Check g:colors_name is existing 14 if !search('\<\%(g:\)\?colors_name\>', 'cnW') 15 let err['colors_name'] = 'g:colors_name not set' 16 else 17 let err['colors_name'] = 'OK' 18 endif 19 20 " 2) Check for some well-defined highlighting groups 21 let hi_groups = [ 22 \ 'ColorColumn', 23 \ 'Comment', 24 \ 'Conceal', 25 \ 'Constant', 26 \ 'Cursor', 27 \ 'CursorColumn', 28 \ 'CursorLine', 29 \ 'CursorLineNr', 30 \ 'DiffAdd', 31 \ 'DiffChange', 32 \ 'DiffDelete', 33 \ 'DiffText', 34 \ 'Directory', 35 \ 'EndOfBuffer', 36 \ 'Error', 37 \ 'ErrorMsg', 38 \ 'FoldColumn', 39 \ 'Folded', 40 \ 'Identifier', 41 \ 'Ignore', 42 \ 'IncSearch', 43 \ 'LineNr', 44 \ 'MatchParen', 45 \ 'ModeMsg', 46 \ 'MoreMsg', 47 \ 'NonText', 48 \ 'Normal', 49 \ 'Pmenu', 50 \ 'PmenuSbar', 51 \ 'PmenuSel', 52 \ 'PmenuThumb', 53 \ 'PreProc', 54 \ 'Question', 55 \ 'QuickFixLine', 56 \ 'Search', 57 \ 'SignColumn', 58 \ 'Special', 59 \ 'SpecialKey', 60 \ 'SpellBad', 61 \ 'SpellCap', 62 \ 'SpellLocal', 63 \ 'SpellRare', 64 \ 'Statement', 65 \ 'StatusLine', 66 \ 'StatusLineNC', 67 \ 'StatusLineTerm', 68 \ 'StatusLineTermNC', 69 \ 'TabLine', 70 \ 'TabLineFill', 71 \ 'TabLineSel', 72 \ 'Title', 73 \ 'Todo', 74 \ 'ToolbarButton', 75 \ 'ToolbarLine', 76 \ 'Type', 77 \ 'Underlined', 78 \ 'VertSplit', 79 \ 'Visual', 80 \ 'VisualNOS', 81 \ 'WarningMsg', 82 \ 'WildMenu', 83 \ ] 84 let groups = {} 85 for group in hi_groups 86 if search('\c@suppress\s\+\<' .. group .. '\>', 'cnW') 87 " skip check, if the script contains a line like 88 " @suppress Visual: 89 continue 90 endif 91 if search('hi\%[ghlight]!\= \+link \+' .. group, 'cnW') " Linked group 92 continue 93 endif 94 if !search('hi\%[ghlight] \+\<' .. group .. '\>', 'cnW') 95 let groups[group] = 'No highlight definition for ' .. group 96 continue 97 endif 98 if !search('hi\%[ghlight] \+\<' .. group .. '\>.*[bf]g=', 'cnW') 99 let groups[group] = 'Missing foreground or background color for ' .. group 100 continue 101 endif 102 if search('hi\%[ghlight] \+\<' .. group .. '\>.*guibg=', 'cnW') && 103 \ !search('hi\%[ghlight] \+\<' .. group .. '\>.*ctermbg=', 'cnW') 104 \ && group != 'Cursor' 105 let groups[group] = 'Missing bg terminal color for ' .. group 106 continue 107 endif 108 if !search('hi\%[ghlight] \+\<' .. group .. '\>.*guifg=', 'cnW') 109 \ && group !~ '^Diff' 110 let groups[group] = 'Missing guifg definition for ' .. group 111 continue 112 endif 113 if !search('hi\%[ghlight] \+\<' .. group .. '\>.*ctermfg=', 'cnW') 114 \ && group !~ '^Diff' 115 \ && group != 'Cursor' 116 let groups[group] = 'Missing ctermfg definition for ' .. group 117 continue 118 endif 119 " do not check for background colors, they could be intentionally left out 120 call cursor(1,1) 121 endfor 122 let err['highlight'] = groups 123 124 " 3) Check, that it does not set background highlighting 125 " Doesn't ':hi Normal ctermfg=253 ctermfg=233' also set the background sometimes? 126 let bg_set = '\(set\?\|setl\(ocal\)\?\) .*\(background\|bg\)=\(dark\|light\)' 127 let bg_let = 'let \%([&]\%([lg]:\)\?\)\%(background\|bg\)\s*=\s*\([''"]\?\)\w\+\1' 128 let bg_pat = '\%(' .. bg_set .. '\|' .. bg_let .. '\)' 129 let line = search(bg_pat, 'cnW') 130 if search(bg_pat, 'cnW') 131 exe line 132 if search('hi \U\w\+\s\+\S', 'cbnW') 133 let err['background'] = 'Should not set background option after :hi statement' 134 endif 135 else 136 let err['background'] = 'OK' 137 endif 138 call cursor(1,1) 139 140 " 4) Check, that t_Co is checked 141 let pat = '[&]t_Co\s*[<>=]=\?\s*\d\+' 142 if !search(pat, 'ncW') 143 let err['t_Co'] = 'Does not check terminal for capable colors' 144 endif 145 146 " 5) Initializes correctly, e.g. should have a section like 147 " hi clear 148 " if exists("syntax_on") 149 " syntax reset 150 " endif 151 let pat = 'hi\%[ghlight]\s*clear\n\s*if\s*exists(\([''"]\)syntax_on\1)\n\s*syn\%[tax]\s*reset\n\s*endif' 152 if !search(pat, 'cnW') 153 let err['init'] = 'No initialization' 154 endif 155 156 " 6) Does not use :syn on 157 if search('syn\%[tax]\s\+on', 'cnW') 158 let err['background'] = 'Should not issue :syn on' 159 endif 160 161 " 7) Does not define filetype specific groups like vimCommand, htmlTag, 162 let hi_groups = filter(getcompletion('', 'filetype'), { _,v -> v !~# '\%[no]syn\%(color\|load\|tax\)' }) 163 let ft_groups = [] 164 " let group = '\%('.join(hi_groups, '\|').'\)' " More efficient than a for loop, but less informative 165 for group in hi_groups 166 let pat = '\Chi\%[ghlight]!\= *\%[link] \+\zs' .. group .. '\w\+\>\ze \+.' " Skips `hi clear` 167 if search(pat, 'cW') 168 call add(ft_groups, matchstr(getline('.'), pat)) 169 endif 170 call cursor(1,1) 171 endfor 172 if !empty(ft_groups) 173 let err['filetype'] = get(err, 'filetype', 'Should not define: ') . join(uniq(sort(ft_groups))) 174 endif 175 176 " 8) Were debugPC and debugBreakpoint defined? 177 for group in ['debugPC', 'debugBreakpoint'] 178 let pat = '\Chi\%[ghlight]!\= *\%[link] \+\zs' .. group .. '\>' 179 if search(pat, 'cnW') 180 let line = search(pat, 'cW') 181 let err['filetype'] = get(err, 'filetype', 'Should not define: ') . matchstr(getline('.'), pat). ' ' 182 endif 183 call cursor(1,1) 184 endfor 185 186 " 9) Normal should be defined first, not use reverse, fg or bg 187 call cursor(1,1) 188 let pat = 'hi\%[ghlight] \+\%(link\|clear\)\@!\w\+\>' 189 call search(pat, 'cW') " Look for the first hi def, skipping `hi link` and `hi clear` 190 if getline('.') !~# '\m\<Normal\>' 191 let err['highlight']['Normal'] = 'Should be defined first' 192 elseif getline('.') =~# '\m\%(=\%(fg\|bg\)\)' 193 let err['highlight']['Normal'] = "Should not use 'fg' or 'bg'" 194 elseif getline('.') =~# '\m=\%(inv\|rev\)erse' 195 let err['highlight']['Normal'] = 'Should not use reverse mode' 196 endif 197 198 call winrestview(l:savedview) 199 let g:err = err 200 201 " print Result 202 call Result(err) 203endfu 204 205fu! Result(err) 206 let do_groups = 0 207 echohl Title|echomsg "---------------"|echohl Normal 208 for key in sort(keys(a:err)) 209 if key is# 'highlight' 210 let do_groups = !empty(a:err[key]) 211 continue 212 else 213 if a:err[key] !~ 'OK' 214 echohl Title 215 endif 216 echomsg printf("%15s: %s", key, a:err[key]) 217 echohl Normal 218 endif 219 endfor 220 echohl Title|echomsg "---------------"|echohl Normal 221 if do_groups 222 echohl Title | echomsg "Groups" | echohl Normal 223 for v1 in sort(keys(a:err['highlight'])) 224 echomsg printf("%25s: %s", v1, a:err['highlight'][v1]) 225 endfor 226 endif 227endfu 228 229call Test_check_colors() 230 231let &cpo = s:save_cpo 232unlet s:save_cpo 233