summaryrefslogtreecommitdiffstats
path: root/runtime/colors/tools/check_colors.vim
blob: de77a382b072818d2f330682866f93c2076b0d16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
" This script tests a color scheme for some errors and lists potential errors.
" Load the scheme and source this script, like this:
"    :edit colors/desert.vim | :so colors/tools/check_colors.vim

let s:save_cpo= &cpo
set cpo&vim

func! Test_check_colors()
  let l:savedview = winsaveview()
  call cursor(1,1)
  let err = {}

  " 1) Check g:colors_name is existing
  if !search('\<\%(g:\)\?colors_name\>', 'cnW')
    let err['colors_name'] = 'g:colors_name not set'
  else
    let err['colors_name'] = 'OK'
  endif

  " 2) Check for some well-defined highlighting groups
  let hi_groups = [
        \ 'ColorColumn',
        \ 'Comment',
        \ 'Conceal',
        \ 'Constant',
        \ 'Cursor',
        \ 'CursorColumn',
        \ 'CursorLine',
        \ 'CursorLineNr',
        \ 'DiffAdd',
        \ 'DiffChange',
        \ 'DiffDelete',
        \ 'DiffText',
        \ 'Directory',
        \ 'EndOfBuffer',
        \ 'Error',
        \ 'ErrorMsg',
        \ 'FoldColumn',
        \ 'Folded',
        \ 'Identifier',
        \ 'Ignore',
        \ 'IncSearch',
        \ 'LineNr',
        \ 'MatchParen',
        \ 'ModeMsg',
        \ 'MoreMsg',
        \ 'NonText',
        \ 'Normal',
        \ 'Pmenu',
        \ 'PmenuSbar',
        \ 'PmenuSel',
        \ 'PmenuThumb',
        \ 'PreProc',
        \ 'Question',
        \ 'QuickFixLine',
        \ 'Search',
        \ 'SignColumn',
        \ 'Special',
        \ 'SpecialKey',
        \ 'SpellBad',
        \ 'SpellCap',
        \ 'SpellLocal',
        \ 'SpellRare',
        \ 'Statement',
        \ 'StatusLine',
        \ 'StatusLineNC',
        \ 'StatusLineTerm',
        \ 'StatusLineTermNC',
        \ 'TabLine',
        \ 'TabLineFill',
        \ 'TabLineSel',
        \ 'Title',
        \ 'Todo',
        \ 'ToolbarButton',
        \ 'ToolbarLine',
        \ 'Type',
        \ 'Underlined',
        \ 'VertSplit',
        \ 'Visual',
        \ 'VisualNOS',
        \ 'WarningMsg',
        \ 'WildMenu',
        \ ]
  let groups = {}
  for group in hi_groups
    if search('\c@suppress\s\+\<' .. group .. '\>', 'cnW')
      " skip check, if the script contains a line like
      " @suppress Visual:
      continue
    endif
    if search('hi\%[ghlight]!\= \+link \+' .. group, 'cnW') " Linked group
      continue
    endif
    if !search('hi\%[ghlight] \+\<' .. group .. '\>', 'cnW')
      let groups[group] = 'No highlight definition for ' .. group
      continue
    endif
    if !search('hi\%[ghlight] \+\<' .. group .. '\>.*[bf]g=', 'cnW')
      let groups[group] = 'Missing foreground or background color for ' .. group
      continue
    endif
    if search('hi\%[ghlight] \+\<' .. group .. '\>.*guibg=', 'cnW') &&
        \ !search('hi\%[ghlight] \+\<' .. group .. '\>.*ctermbg=', 'cnW')
	\ && group != 'Cursor'
      let groups[group] = 'Missing bg terminal color for ' .. group
      continue
    endif
    if !search('hi\%[ghlight] \+\<' .. group .. '\>.*guifg=', 'cnW')
	  \ && group !~ '^Diff'
      let groups[group] = 'Missing guifg definition for ' .. group
      continue
    endif
    if !search('hi\%[ghlight] \+\<' .. group .. '\>.*ctermfg=', 'cnW')
	  \ && group !~ '^Diff'
	  \ && group != 'Cursor'
      let groups[group] = 'Missing ctermfg definition for ' .. group
      continue
    endif
    " do not check for background colors, they could be intentionally left out
    call cursor(1,1)
  endfor
  let err['highlight'] = groups

  " 3) Check, that it does not set background highlighting
  " Doesn't ':hi Normal ctermfg=253 ctermfg=233' also set the background sometimes?
  let bg_set = '\(set\?\|setl\(ocal\)\?\) .*\(background\|bg\)=\(dark\|light\)'
  let bg_let = 'let \%([&]\%([lg]:\)\?\)\%(background\|bg\)\s*=\s*\([''"]\?\)\w\+\1'
  let bg_pat = '\%(' .. bg_set .. '\|' .. bg_let .. '\)'
  let line = search(bg_pat, 'cnW')
  if search(bg_pat, 'cnW')
    exe line
    if search('hi \U\w\+\s\+\S', 'cbnW')
      let err['background'] = 'Should not set background option after :hi statement'
    endif
  else
    let err['background'] = 'OK'
  endif
  call cursor(1,1)

  " 4) Check, that t_Co is checked
  let pat = '[&]t_Co\s*[<>=]=\?\s*\d\+'
  if !search(pat, 'ncW')
    let err['t_Co'] = 'Does not check terminal for capable colors'
  endif

  " 5) Initializes correctly, e.g. should have a section like
  " hi clear
  " if exists("syntax_on")
  " syntax reset
  " endif
  let pat = 'hi\%[ghlight]\s*clear\n\s*if\s*exists(\([''"]\)syntax_on\1)\n\s*syn\%[tax]\s*reset\n\s*endif'
  if !search(pat, 'cnW')
    let err['init'] = 'No initialization'
  endif

  " 6) Does not use :syn on
  if search('syn\%[tax]\s\+on', 'cnW')
    let err['background'] = 'Should not issue :syn on'
  endif

  " 7) Does not define filetype specific groups like vimCommand, htmlTag,
  let hi_groups = filter(getcompletion('', 'filetype'), { _,v -> v !~# '\%[no]syn\%(color\|load\|tax\)' })
  let ft_groups = []
  " let group = '\%('.join(hi_groups, '\|').'\)' " More efficient than a for loop, but less informative
  for group in hi_groups
    let pat = '\Chi\%[ghlight]!\= *\%[link] \+\zs' .. group .. '\w\+\>\ze \+.' " Skips `hi clear`
    if search(pat, 'cW')
      call add(ft_groups, matchstr(getline('.'), pat))
    endif
    call cursor(1,1)
  endfor
  if !empty(ft_groups)
    let err['filetype'] = get(err, 'filetype', 'Should not define: ') . join(uniq(sort(ft_groups)))
  endif

  " 8) Were debugPC and debugBreakpoint defined?
  for group in ['debugPC', 'debugBreakpoint']
    let pat = '\Chi\%[ghlight]!\= *\%[link] \+\zs' .. group .. '\>'
    if search(pat, 'cnW')
      let line = search(pat, 'cW')
      let err['filetype'] = get(err, 'filetype', 'Should not define: ') . matchstr(getline('.'), pat). ' '
    endif
    call cursor(1,1)
  endfor

  " 9) Normal should be defined first, not use reverse, fg or bg
  call cursor(1,1)
  let pat = 'hi\%[light] \+\%(link\|clear\)\@!\w\+\>'
  call search(pat, 'cW') " Look for the first hi def, skipping `hi link` and `hi clear`
  if getline('.') !~# '\m\<Normal\>'
    let err['highlight']['Normal'] = 'Should be defined first'
  elseif getline('.') =~# '\m\%(=\%(fg\|bg\)\)'
    let err['highlight']['Normal'] = "Should not use 'fg' or 'bg'"
  elseif getline('.') =~# '\m=\%(inv\|rev\)erse'
    let err['highlight']['Normal'] = 'Should not use reverse mode'
  endif

  call winrestview(l:savedview)
  let g:err = err

  " print Result
  call Result(err)
endfu

fu! Result(err)
  let do_groups = 0
  echohl Title|echomsg "---------------"|echohl Normal
  for key in sort(keys(a:err))
    if key is# 'highlight'
      let do_groups = !empty(a:err[key])
      continue
    else
      if a:err[key] !~ 'OK'
        echohl Title
      endif
      echomsg printf("%15s: %s", key, a:err[key])
      echohl Normal
    endif
  endfor
  echohl Title|echomsg "---------------"|echohl Normal
  if do_groups
    echohl Title | echomsg "Groups" | echohl Normal
    for v1 in sort(keys(a:err['highlight']))
      echomsg printf("%25s: %s", v1, a:err['highlight'][v1])
    endfor
  endif
endfu

call Test_check_colors()

let &cpo = s:save_cpo
unlet s:save_cpo