colorizer.vim 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. " colorizer.vim Colorize all text in the form #rrggbb or #rgb; autoload functions
  2. " Maintainer: lilydjwg <lilydjwg@gmail.com>
  3. " Version: 1.4.2
  4. " License: Vim License (see vim's :help license)
  5. "
  6. " See plugin/colorizer.vim for more info.
  7. let s:keepcpo = &cpo
  8. set cpo&vim
  9. function! s:FGforBG(bg) "{{{1
  10. " takes a 6hex color code and returns a matching color that is visible
  11. let pure = substitute(a:bg,'^#','','')
  12. let r = str2nr(pure[0:1], 16)
  13. let g = str2nr(pure[2:3], 16)
  14. let b = str2nr(pure[4:5], 16)
  15. let fgc = g:colorizer_fgcontrast
  16. if r*30 + g*59 + b*11 > 12000
  17. return s:predefined_fgcolors['dark'][fgc]
  18. else
  19. return s:predefined_fgcolors['light'][fgc]
  20. end
  21. endfunction
  22. function! s:Rgb2xterm(color) "{{{1
  23. " selects the nearest xterm color for a rgb value like #FF0000
  24. let best_match=0
  25. let smallest_distance = 10000000000
  26. let r = str2nr(a:color[1:2], 16)
  27. let g = str2nr(a:color[3:4], 16)
  28. let b = str2nr(a:color[5:6], 16)
  29. let colortable = s:GetXterm2rgbTable()
  30. for c in range(0,254)
  31. let d = pow(colortable[c][0]-r,2) + pow(colortable[c][1]-g,2) + pow(colortable[c][2]-b,2)
  32. if d<smallest_distance
  33. let smallest_distance = d
  34. let best_match = c
  35. endif
  36. endfor
  37. return best_match
  38. endfunction
  39. "" the 6 value iterations in the xterm color cube {{{1
  40. let s:valuerange = [0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF]
  41. "" 16 basic colors {{{1
  42. let s:basic16 = [
  43. \ [0x00, 0x00, 0x00], [0xCD, 0x00, 0x00],
  44. \ [0x00, 0xCD, 0x00], [0xCD, 0xCD, 0x00],
  45. \ [0x00, 0x00, 0xEE], [0xCD, 0x00, 0xCD],
  46. \ [0x00, 0xCD, 0xCD], [0xE5, 0xE5, 0xE5],
  47. \ [0x7F, 0x7F, 0x7F], [0xFF, 0x00, 0x00],
  48. \ [0x00, 0xFF, 0x00], [0xFF, 0xFF, 0x00],
  49. \ [0x5C, 0x5C, 0xFF], [0xFF, 0x00, 0xFF],
  50. \ [0x00, 0xFF, 0xFF], [0xFF, 0xFF, 0xFF]]
  51. function! s:Xterm2rgb(color) "{{{1
  52. " 16 basic colors
  53. let r = 0
  54. let g = 0
  55. let b = 0
  56. if a:color<16
  57. let r = s:basic16[a:color][0]
  58. let g = s:basic16[a:color][1]
  59. let b = s:basic16[a:color][2]
  60. endif
  61. " color cube color
  62. if a:color>=16 && a:color<=232
  63. let l:color=a:color-16
  64. let r = s:valuerange[(l:color/36)%6]
  65. let g = s:valuerange[(l:color/6)%6]
  66. let b = s:valuerange[l:color%6]
  67. endif
  68. " gray tone
  69. if a:color>=233 && a:color<=253
  70. let r=8+(a:color-232)*0x0a
  71. let g=r
  72. let b=r
  73. endif
  74. let rgb=[r,g,b]
  75. return rgb
  76. endfunction
  77. function! s:SetMatcher(color, pat) "{{{1
  78. " "color" is the converted color and "pat" is what to highlight
  79. let group = 'Color' . strpart(a:color, 1)
  80. if !hlexists(group) || s:force_group_update
  81. let fg = g:colorizer_fgcontrast < 0 ? a:color : s:FGforBG(a:color)
  82. if &t_Co == 256
  83. exe 'hi '.group.' ctermfg='.s:Rgb2xterm(fg).' ctermbg='.s:Rgb2xterm(a:color)
  84. endif
  85. " Always set gui* as user may switch to GUI version and it's cheap
  86. exe 'hi '.group.' guifg='.fg.' guibg='.a:color
  87. endif
  88. if !exists("w:colormatches[a:pat]")
  89. let w:colormatches[a:pat] = matchadd(group, a:pat)
  90. endif
  91. endfunction
  92. " Color Converters {{{1
  93. function! s:RgbBgColor() "{{{2
  94. let bg = synIDattr(synIDtrans(hlID("Normal")), "bg")
  95. let r = str2nr(bg[1:2], 16)
  96. let g = str2nr(bg[3:4], 16)
  97. let b = str2nr(bg[5:6], 16)
  98. return [r,g,b]
  99. endfunction
  100. function! s:Hexa2Rgba(hex,alpha) "{{{2
  101. let r = str2nr(a:hex[1:2], 16)
  102. let g = str2nr(a:hex[3:4], 16)
  103. let b = str2nr(a:hex[5:6], 16)
  104. let alpha = printf("%.2f", str2float(str2nr(a:alpha,16)) / 255.0)
  105. return [r,g,b,alpha]
  106. endfunction
  107. function! s:Rgba2Rgb(r,g,b,alpha,percent,rgb_bg) "{{{2
  108. " converts matched r,g,b values and percentages to [0:255]
  109. " if possible, overlays r,g,b with alpha on given rgb_bg color
  110. if a:percent
  111. let r = a:r * 255 / 100
  112. let g = a:g * 255 / 100
  113. let b = a:b * 255 / 100
  114. else
  115. let r = a:r
  116. let g = a:g
  117. let b = a:b
  118. endif
  119. if r > 255 || g > 255 || b > 255
  120. return []
  121. endif
  122. if empty(a:rgb_bg)
  123. return [r,g,b]
  124. endif
  125. let alpha = str2float(a:alpha)
  126. if alpha < 0
  127. let alpha = 0.0
  128. elseif alpha > 1
  129. let alpha = 1.0
  130. endif
  131. if alpha == 1.0
  132. return [r,g,b]
  133. endif
  134. let r = float2nr(ceil(r * alpha) + ceil(a:rgb_bg[0] * (1 - alpha)))
  135. let g = float2nr(ceil(g * alpha) + ceil(a:rgb_bg[1] * (1 - alpha)))
  136. let b = float2nr(ceil(b * alpha) + ceil(a:rgb_bg[2] * (1 - alpha)))
  137. if r > 255
  138. let r = 255
  139. endif
  140. if g > 255
  141. let g = 255
  142. endif
  143. if b > 255
  144. let b = 255
  145. endif
  146. return [r,g,b]
  147. endfunction
  148. "ColorFinders {{{1
  149. function! s:HexCode(str, lineno) "{{{2
  150. " finds RGB: #00f #0000ff and RGBA: #00f8 #0000ff88 (or ARGB: #800f #880000ff)
  151. if has("gui_running")
  152. let rgb_bg = s:RgbBgColor()
  153. else
  154. " translucent colors would display incorrectly, so ignore the alpha value
  155. let rgb_bg = []
  156. endif
  157. let ret = []
  158. let place = 0
  159. let colorpat = '#[0-9A-Fa-f]\{3\}\>\|#[0-9A-Fa-f]\{6\}\>\|#[0-9A-Fa-f]\{8\}\>\|#[0-9A-Fa-f]\{4\}\>'
  160. while 1
  161. let foundcolor = matchstr(a:str, colorpat, place)
  162. if foundcolor == ''
  163. break
  164. endif
  165. let place = matchend(a:str, colorpat, place)
  166. let pat = foundcolor . '\>'
  167. let colorlen = len(foundcolor)
  168. if get(g:, 'colorizer_hex_alpha_first') == 1
  169. if colorlen == 4 || colorlen == 5
  170. let ha = tolower(foundcolor[1])
  171. let hr = tolower(foundcolor[2])
  172. let hg = tolower(foundcolor[3])
  173. let hb = tolower(foundcolor[4])
  174. let foundcolor = substitute(foundcolor, '[[:xdigit:]]', '&&', 'g')
  175. else
  176. let ha = tolower(foundcolor[1:2])
  177. let hr = tolower(foundcolor[3:4])
  178. let hg = tolower(foundcolor[5:6])
  179. let hb = tolower(foundcolor[7:8])
  180. endif
  181. if len(foundcolor) == 9
  182. let alpha = foundcolor[1:2]
  183. let foundcolor = '#'.foundcolor[3:8]
  184. else
  185. let alpha = 'ff'
  186. endif
  187. if empty(rgb_bg)
  188. if colorlen == 5
  189. let pat = printf('\c#\x\zs%s%s%s\ze\>', hr,hg,hb)
  190. elseif colorlen == 9
  191. let pat = printf('\c#\x\x\zs%s%s%s\ze\>', hr,hg,hb)
  192. endif
  193. endif
  194. else
  195. if colorlen == 4 || colorlen == 5
  196. let hr = tolower(foundcolor[1])
  197. let hg = tolower(foundcolor[2])
  198. let hb = tolower(foundcolor[3])
  199. let ha = tolower(foundcolor[4])
  200. let foundcolor = substitute(foundcolor, '[[:xdigit:]]', '&&', 'g')
  201. else
  202. let hr = tolower(foundcolor[1:2])
  203. let hg = tolower(foundcolor[3:4])
  204. let hb = tolower(foundcolor[5:6])
  205. let ha = tolower(foundcolor[7:8])
  206. endif
  207. if len(foundcolor) == 9
  208. let alpha = foundcolor[7:8]
  209. let foundcolor = foundcolor[0:6]
  210. else
  211. let alpha = 'ff'
  212. endif
  213. if empty(rgb_bg)
  214. if colorlen == 5
  215. let pat = printf('\c#%s%s%s\ze\x\>', hr,hg,hb)
  216. elseif colorlen == 9
  217. let pat = printf('\c#%s%s%s\ze\x\x\>', hr,hg,hb)
  218. endif
  219. endif
  220. endif
  221. if empty(rgb_bg) || tolower(alpha) == 'ff'
  222. call add(ret, [foundcolor, pat])
  223. else
  224. let rgba = s:Hexa2Rgba(foundcolor, alpha)
  225. let rgb = s:Rgba2Rgb(rgba[0], rgba[1], rgba[2], rgba[3], 0, rgb_bg)
  226. let l:color = printf('#%02x%02x%02x', rgb[0], rgb[1], rgb[2])
  227. call add(ret, [l:color, pat])
  228. endif
  229. endwhile
  230. return ret
  231. endfunction
  232. function! s:RgbColor(str, lineno) "{{{2
  233. let ret = []
  234. let place = 0
  235. let colorpat = '\<rgb(\v\s*(\d+(\%)?)\s*,\s*(\d+%(\2))\s*,\s*(\d+%(\2))\s*\)'
  236. while 1
  237. let foundcolor = matchlist(a:str, colorpat, place)
  238. if empty(foundcolor)
  239. break
  240. endif
  241. let place = matchend(a:str, colorpat, place)
  242. if foundcolor[2] == '%'
  243. let r = foundcolor[1] * 255 / 100
  244. let g = foundcolor[3] * 255 / 100
  245. let b = foundcolor[4] * 255 / 100
  246. else
  247. let r = foundcolor[1]
  248. let g = foundcolor[3]
  249. let b = foundcolor[4]
  250. endif
  251. if r > 255 || g > 255 || b > 255
  252. break
  253. endif
  254. let pat = printf('\<rgb(\v\s*%s\s*,\s*%s\s*,\s*%s\s*\)', foundcolor[1], foundcolor[3], foundcolor[4])
  255. if foundcolor[2] == '%'
  256. let pat = substitute(pat, '%', '\\%', 'g')
  257. endif
  258. let l:color = printf('#%02x%02x%02x', r, g, b)
  259. call add(ret, [l:color, pat])
  260. endwhile
  261. return ret
  262. endfunction
  263. function! s:RgbaColor(str, lineno) "{{{2
  264. if has("gui_running")
  265. let rgb_bg = s:RgbBgColor()
  266. else
  267. " translucent colors would display incorrectly, so ignore the alpha value
  268. let rgb_bg = []
  269. endif
  270. let ret = []
  271. let place = 0
  272. let percent = 0
  273. let colorpat = '\<rgba(\v\s*(\d+(\%)?)\s*,\s*(\d+%(\2))\s*,\s*(\d+%(\2))\s*,\s*(-?[.[:digit:]]+)\s*\)'
  274. while 1
  275. let foundcolor = matchlist(a:str, colorpat, place)
  276. if empty(foundcolor)
  277. break
  278. endif
  279. if foundcolor[2] == '%'
  280. let percent = 1
  281. endif
  282. let rgb = s:Rgba2Rgb(foundcolor[1], foundcolor[3], foundcolor[4], foundcolor[5], percent, rgb_bg)
  283. if empty(rgb)
  284. break
  285. endif
  286. let place = matchend(a:str, colorpat, place)
  287. if empty(rgb_bg)
  288. let pat = printf('\<rgba(\v\s*%s\s*,\s*%s\s*,\s*%s\s*,\ze\s*(-?[.[:digit:]]+)\s*\)', foundcolor[1], foundcolor[3], foundcolor[4])
  289. else
  290. let pat = printf('\<rgba(\v\s*%s\s*,\s*%s\s*,\s*%s\s*,\s*%s0*\s*\)', foundcolor[1], foundcolor[3], foundcolor[4], foundcolor[5])
  291. endif
  292. if percent
  293. let pat = substitute(pat, '%', '\\%', 'g')
  294. endif
  295. let l:color = printf('#%02x%02x%02x', rgb[0], rgb[1], rgb[2])
  296. call add(ret, [l:color, pat])
  297. endwhile
  298. return ret
  299. endfunction
  300. function! s:PreviewColorInLine(where) "{{{1
  301. let line = getline(a:where)
  302. for Func in s:ColorFinder
  303. let ret = Func(line, a:where)
  304. " returned a list of a list: color as #rrggbb, text pattern to highlight
  305. for r in ret
  306. call s:SetMatcher(r[0], r[1])
  307. endfor
  308. endfor
  309. endfunction
  310. function! s:CursorMoved() "{{{1
  311. if !exists('w:colormatches')
  312. return
  313. endif
  314. if exists('b:colorizer_last_update')
  315. if b:colorizer_last_update == b:changedtick
  316. " Nothing changed
  317. return
  318. endif
  319. endif
  320. call s:PreviewColorInLine('.')
  321. let b:colorizer_last_update = b:changedtick
  322. endfunction
  323. function! s:TextChanged() "{{{1
  324. if !exists('w:colormatches')
  325. return
  326. endif
  327. echomsg "TextChanged"
  328. call s:PreviewColorInLine('.')
  329. endfunction
  330. function! colorizer#ColorHighlight(update, ...) "{{{1
  331. if exists('w:colormatches')
  332. if !a:update
  333. return
  334. endif
  335. call s:ClearMatches()
  336. endif
  337. if (g:colorizer_maxlines > 0) && (g:colorizer_maxlines <= line('$'))
  338. return
  339. end
  340. let w:colormatches = {}
  341. if g:colorizer_fgcontrast != s:saved_fgcontrast || (exists("a:1") && a:1 == '!')
  342. let s:force_group_update = 1
  343. endif
  344. for i in range(1, line("$"))
  345. call s:PreviewColorInLine(i)
  346. endfor
  347. let s:force_group_update = 0
  348. let s:saved_fgcontrast = g:colorizer_fgcontrast
  349. augroup Colorizer
  350. au!
  351. if exists('##TextChanged')
  352. autocmd TextChanged * silent call s:TextChanged()
  353. if v:version > 704 || v:version == 704 && has('patch143')
  354. autocmd TextChangedI * silent call s:TextChanged()
  355. else
  356. " TextChangedI does not work as expected
  357. autocmd CursorMovedI * silent call s:CursorMoved()
  358. endif
  359. else
  360. autocmd CursorMoved,CursorMovedI * silent call s:CursorMoved()
  361. endif
  362. " rgba handles differently, so need updating
  363. autocmd GUIEnter * silent call colorizer#ColorHighlight(1)
  364. autocmd BufEnter * silent call colorizer#ColorHighlight(1)
  365. autocmd WinEnter * silent call colorizer#ColorHighlight(1)
  366. autocmd ColorScheme * let s:force_group_update=1 | silent call colorizer#ColorHighlight(1)
  367. augroup END
  368. endfunction
  369. function! colorizer#ColorClear() "{{{1
  370. augroup Colorizer
  371. au!
  372. augroup END
  373. augroup! Colorizer
  374. let save_tab = tabpagenr()
  375. let save_win = winnr()
  376. tabdo windo call s:ClearMatches()
  377. exe 'tabn '.save_tab
  378. exe save_win . 'wincmd w'
  379. endfunction
  380. function! s:ClearMatches() "{{{1
  381. if !exists('w:colormatches')
  382. return
  383. endif
  384. for i in values(w:colormatches)
  385. try
  386. call matchdelete(i)
  387. catch /.*/
  388. " matches have been cleared in other ways, e.g. user has called clearmatches()
  389. endtry
  390. endfor
  391. unlet w:colormatches
  392. endfunction
  393. function! colorizer#ColorToggle() "{{{1
  394. if exists('#Colorizer')
  395. call colorizer#ColorClear()
  396. echomsg 'Disabled color code highlighting.'
  397. else
  398. call colorizer#ColorHighlight(0)
  399. echomsg 'Enabled color code highlighting.'
  400. endif
  401. endfunction
  402. function! colorizer#AlphaPositionToggle() "{{{1
  403. if exists('#Colorizer')
  404. if get(g:, 'colorizer_hex_alpha_first') == 1
  405. let g:colorizer_hex_alpha_first = 0
  406. else
  407. let g:colorizer_hex_alpha_first = 1
  408. endif
  409. call colorizer#ColorHighlight(1)
  410. endif
  411. endfunction
  412. function! s:GetXterm2rgbTable() "{{{1
  413. if !exists('s:table_xterm2rgb')
  414. let s:table_xterm2rgb = []
  415. for c in range(0, 254)
  416. let s:color = s:Xterm2rgb(c)
  417. call add(s:table_xterm2rgb, s:color)
  418. endfor
  419. endif
  420. return s:table_xterm2rgb
  421. endfun
  422. " Setups {{{1
  423. let s:ColorFinder = [function('s:HexCode'), function('s:RgbColor'), function('s:RgbaColor')]
  424. let s:force_group_update = 0
  425. let s:predefined_fgcolors = {}
  426. let s:predefined_fgcolors['dark'] = ['#444444', '#222222', '#000000']
  427. let s:predefined_fgcolors['light'] = ['#bbbbbb', '#dddddd', '#ffffff']
  428. if !exists("g:colorizer_fgcontrast")
  429. " Default to black / white
  430. let g:colorizer_fgcontrast = len(s:predefined_fgcolors['dark']) - 1
  431. elseif g:colorizer_fgcontrast >= len(s:predefined_fgcolors['dark'])
  432. echohl WarningMsg
  433. echo "g:colorizer_fgcontrast value invalid, using default"
  434. echohl None
  435. let g:colorizer_fgcontrast = len(s:predefined_fgcolors['dark']) - 1
  436. endif
  437. let s:saved_fgcontrast = g:colorizer_fgcontrast
  438. " Restoration and modelines {{{1
  439. let &cpo = s:keepcpo
  440. unlet s:keepcpo
  441. " vim:ft=vim:fdm=marker:fmr={{{,}}}:ts=8:sw=2:sts=2:et