aboutsummaryrefslogtreecommitdiff
path: root/.vim/autoload/airline/highlighter.vim
blob: 5d4a7d7e0382ed121e23a80d206c80758a6b585c (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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
" MIT License. Copyright (c) 2013-2018 Bailey Ling et al.
" vim: et ts=2 sts=2 sw=2

scriptencoding utf-8

let s:is_win32term = (has('win32') || has('win64')) &&
                   \ !has('gui_running') &&
                   \ (empty($CONEMUBUILD) || &term !=? 'xterm') &&
                   \ !(exists("+termguicolors") && &termguicolors)

let s:separators = {}
let s:accents = {}
let s:hl_groups = {}

function! s:gui2cui(rgb, fallback)
  if a:rgb == ''
    return a:fallback
  elseif match(a:rgb, '^\%(NONE\|[fb]g\)$') > -1
    return a:rgb
  endif
  let rgb = map(split(a:rgb[1:], '..\zs'), '0 + ("0x".v:val)')
  return airline#msdos#round_msdos_colors(rgb)
endfunction

function! s:group_not_done(list, name)
  if index(a:list, a:name) == -1
    call add(a:list, a:name)
    return 1
  else
    if &vbs
      echomsg printf("airline: group: %s already done, skipping", a:name)
    endif
    return 0
  endif
endfu

function! s:get_syn(group, what)
  if !exists("g:airline_gui_mode")
    let g:airline_gui_mode = airline#init#gui_mode()
  endif
  let color = ''
  if hlexists(a:group)
    let color = synIDattr(synIDtrans(hlID(a:group)), a:what, g:airline_gui_mode)
  endif
  if empty(color) || color == -1
    " should always exists
    let color = synIDattr(synIDtrans(hlID('Normal')), a:what, g:airline_gui_mode)
    " however, just in case
    if empty(color) || color == -1
      let color = 'NONE'
    endif
  endif
  return color
endfunction

function! s:get_array(fg, bg, opts)
  let opts=empty(a:opts) ? '' : join(a:opts, ',')
  return g:airline_gui_mode ==# 'gui'
        \ ? [ a:fg, a:bg, '', '', opts ]
        \ : [ '', '', a:fg, a:bg, opts ]
endfunction

function! airline#highlighter#reset_hlcache()
  let s:hl_groups = {}
endfunction

function! airline#highlighter#get_highlight(group, ...)
  let reverse = get(g:, 'airline_gui_mode', '') ==# 'gui'
      \ ? synIDattr(synIDtrans(hlID(a:group)), 'reverse', 'gui')
      \ : synIDattr(synIDtrans(hlID(a:group)), 'reverse', 'cterm')
      \|| synIDattr(synIDtrans(hlID(a:group)), 'reverse', 'term')
  if get(g:, 'airline_highlighting_cache', 0) && has_key(s:hl_groups, a:group)
    let res = s:hl_groups[a:group]
    return reverse ? [ res[1], res[0], res[3], res[2], res[4] ] : res
  else
    let fg = s:get_syn(a:group, 'fg')
    let bg = s:get_syn(a:group, 'bg')
    let bold = synIDattr(synIDtrans(hlID(a:group)), 'bold')
    if reverse
      let res = s:get_array(bg, fg, bold ? ['bold'] : a:000)
    else
      let res = s:get_array(fg, bg, bold ? ['bold'] : a:000)
    endif
  endif
  let s:hl_groups[a:group] = res
  return res
endfunction

function! airline#highlighter#get_highlight2(fg, bg, ...)
  let fg = s:get_syn(a:fg[0], a:fg[1])
  let bg = s:get_syn(a:bg[0], a:bg[1])
  return s:get_array(fg, bg, a:000)
endfunction

function! s:hl_group_exists(group)
  if !hlexists(a:group)
    return 0
  elseif empty(synIDattr(hlID(a:group), 'fg'))
    return 0
  endif
  return 1
endfunction

function! airline#highlighter#exec(group, colors)
  if pumvisible()
    return
  endif
  let colors = a:colors
  if s:is_win32term
    let colors[2] = s:gui2cui(get(colors, 0, ''), get(colors, 2, ''))
    let colors[3] = s:gui2cui(get(colors, 1, ''), get(colors, 3, ''))
  endif
  let old_hi = airline#highlighter#get_highlight(a:group)
  if len(colors) == 4
    call add(colors, '')
  endif
  if g:airline_gui_mode ==# 'gui'
    let new_hi = [colors[0], colors[1], '', '', colors[4]]
  else
    let new_hi = ['', '', printf("%s", colors[2]), printf("%s", colors[3]), colors[4]]
  endif
  let colors = s:CheckDefined(colors)
  if old_hi != new_hi || !s:hl_group_exists(a:group)
    let cmd = printf('hi %s%s', a:group, s:GetHiCmd(colors))
    exe cmd
    if has_key(s:hl_groups, a:group)
      let s:hl_groups[a:group] = colors
    endif
  endif
endfunction

function! s:CheckDefined(colors)
  " Checks, whether the definition of the colors is valid and is not empty or NONE
  " e.g. if the colors would expand to this:
  " hi airline_c ctermfg=NONE ctermbg=NONE
  " that means to clear that highlighting group, therefore, fallback to Normal
  " highlighting group for the cterm values

  " This only works, if the Normal highlighting group is actually defined, so
  " return early, if it has been cleared
  if !exists("g:airline#highlighter#normal_fg_hi")
    let g:airline#highlighter#normal_fg_hi = synIDattr(synIDtrans(hlID('Normal')), 'fg', 'cterm')
  endif
  if empty(g:airline#highlighter#normal_fg_hi) || g:airline#highlighter#normal_fg_hi < 0
    return a:colors
  endif

  for val in a:colors
    if !empty(val) && val !=# 'NONE'
      return a:colors
    endif
  endfor
  " this adds the bold attribute to the term argument of the :hi command,
  " but at least this makes sure, the group will be defined
  let fg = g:airline#highlighter#normal_fg_hi
  let bg = synIDattr(synIDtrans(hlID('Normal')), 'bg', 'cterm')
  if bg < 0
    " in case there is no background color defined for Normal
    let bg = a:colors[3]
  endif
  return a:colors[0:1] + [fg, bg] + [a:colors[4]]
endfunction

function! s:GetHiCmd(list)
  " a:list needs to have 5 items!
  let res = ''
  let i = -1
  while i < 4
    let i += 1
    let item = get(a:list, i, '')
    if item is ''
      continue
    endif
    if i == 0
      let res .= ' guifg='.item
    elseif i == 1
      let res .= ' guibg='.item
    elseif i == 2
      let res .= ' ctermfg='.item
    elseif i == 3
      let res .= ' ctermbg='.item
    elseif i == 4
      let res .= printf(' gui=%s cterm=%s term=%s', item, item, item)
    endif
  endwhile
  return res
endfunction

function! s:exec_separator(dict, from, to, inverse, suffix)
  if pumvisible()
    return
  endif
  let group = a:from.'_to_'.a:to.a:suffix
  let l:from = airline#themes#get_highlight(a:from.a:suffix)
  let l:to = airline#themes#get_highlight(a:to.a:suffix)
  if a:inverse
    let colors = [ l:from[1], l:to[1], l:from[3], l:to[3] ]
  else
    let colors = [ l:to[1], l:from[1], l:to[3], l:from[3] ]
  endif
  let a:dict[group] = colors
  call airline#highlighter#exec(group, colors)
endfunction

function! airline#highlighter#load_theme()
  if pumvisible()
    return
  endif
  for winnr in filter(range(1, winnr('$')), 'v:val != winnr()')
    call airline#highlighter#highlight_modified_inactive(winbufnr(winnr))
  endfor
  call airline#highlighter#highlight(['inactive'])
  if getbufvar( bufnr('%'), '&modified'  )
    call airline#highlighter#highlight(['normal', 'modified'])
  else
    call airline#highlighter#highlight(['normal'])
  endif
endfunction

function! airline#highlighter#add_separator(from, to, inverse)
  let s:separators[a:from.a:to] = [a:from, a:to, a:inverse]
  call <sid>exec_separator({}, a:from, a:to, a:inverse, '')
endfunction

function! airline#highlighter#add_accent(accent)
  let s:accents[a:accent] = 1
endfunction

function! airline#highlighter#highlight_modified_inactive(bufnr)
  if getbufvar(a:bufnr, '&modified')
    let colors = exists('g:airline#themes#{g:airline_theme}#palette.inactive_modified.airline_c')
          \ ? g:airline#themes#{g:airline_theme}#palette.inactive_modified.airline_c : []
  else
    let colors = exists('g:airline#themes#{g:airline_theme}#palette.inactive.airline_c')
          \ ? g:airline#themes#{g:airline_theme}#palette.inactive.airline_c : []
  endif

  if !empty(colors)
    call airline#highlighter#exec('airline_c'.(a:bufnr).'_inactive', colors)
  endif
endfunction

function! airline#highlighter#highlight(modes, ...)
  let bufnr = a:0 ? a:1 : ''
  let p = g:airline#themes#{g:airline_theme}#palette

  " draw the base mode, followed by any overrides
  let mapped = map(a:modes, 'v:val == a:modes[0] ? v:val : a:modes[0]."_".v:val')
  let suffix = a:modes[0] == 'inactive' ? '_inactive' : ''
  let airline_grouplist = []
  let buffers_in_tabpage = sort(tabpagebuflist())
  if exists("*uniq")
    let buffers_in_tabpage = uniq(buffers_in_tabpage)
  endif
  " mapped might be something like ['normal', 'normal_modified']
  " if a group is in both modes available, only define the second
  " that is how this was done previously overwrite the previous definition
  for mode in reverse(mapped)
    if exists('g:airline#themes#{g:airline_theme}#palette[mode]')
      let dict = g:airline#themes#{g:airline_theme}#palette[mode]
      for kvp in items(dict)
        let mode_colors = kvp[1]
        let name = kvp[0]
        if name is# 'airline_c' && !empty(bufnr) && suffix is# '_inactive'
          let name = 'airline_c'.bufnr
        endif
        " do not re-create highlighting for buffers that are no longer visible
        " in the current tabpage
        if name =~# 'airline_c\d\+'
          let bnr = matchstr(name, 'airline_c\zs\d\+') + 0
          if bnr > 0 && index(buffers_in_tabpage, bnr) == -1
            continue
          endif
        elseif (name =~# '_to_') || (name[0:10] is# 'airline_tab' && !empty(suffix))
          " group will be redefined below at exec_separator
          " or is not needed for tabline with '_inactive' suffix
          " since active flag is 1 for builder)
          continue
        endif
        if s:group_not_done(airline_grouplist, name.suffix)
          call airline#highlighter#exec(name.suffix, mode_colors)
        endif

        for accent in keys(s:accents)
          if !has_key(p.accents, accent)
            continue
          endif
          let colors = copy(mode_colors)
          if p.accents[accent][0] != ''
            let colors[0] = p.accents[accent][0]
          endif
          if p.accents[accent][2] != ''
            let colors[2] = p.accents[accent][2]
          endif
          if len(colors) >= 5
            let colors[4] = get(p.accents[accent], 4, '')
          else
            call add(colors, get(p.accents[accent], 4, ''))
          endif
          if s:group_not_done(airline_grouplist, name.suffix.'_'.accent)
            call airline#highlighter#exec(name.suffix.'_'.accent, colors)
          endif
        endfor
      endfor

      if empty(s:separators)
        " nothing to be done
        continue
      endif
      " TODO: optimize this
      for sep in items(s:separators)
        " we cannot check, that the group already exists, else the separators
        " might not be correctly defined. But perhaps we can skip above groups
        " that match the '_to_' name, because they would be redefined here...
        call <sid>exec_separator(dict, sep[1][0], sep[1][1], sep[1][2], suffix)
      endfor
    endif
  endfor
endfunction