aboutsummaryrefslogtreecommitdiff
path: root/files/.vim/bundle/CSApprox/plugin/CSApprox.vim
blob: 68f92d8b11c952dc750bfa98c970934d58dd7797 (plain) (blame)
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
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
" CSApprox:    Make gvim-only colorschemes Just Work terminal vim
" Maintainer:  Matthew Wozniski (godlygeek@gmail.com)
" Date:        Fri, 14 Sep 2012 01:12:13 -0400
" Version:     4.00
" History:     :help csapprox-changelog
"
" Long Description:
" It's hard to find colorschemes for terminal Vim.  Most colorschemes are
" written to only support GVim, and don't work at all in terminal Vim.
"
" This plugin makes GVim-only colorschemes Just Work in terminal Vim, as long
" as the terminal supports 88 or 256 colors - and most do these days.  This
" usually requires no user interaction (but see below for what to do if things
" don't Just Work).  After getting this plugin happily installed, any time you
" use :colorscheme it will do its magic and make the colorscheme Just Work.
"
" Whenever you change colorschemes using the :colorscheme command this script
" will be executed.  It will take the colors that the scheme specified for use
" in the GUI and use an approximation algorithm to try to gracefully degrade
" them to the closest color available in your terminal.  If you are running in
" a GUI or if your terminal doesn't support 88 or 256 colors, no changes are
" made.  Also, no changes will be made if the colorscheme seems to have been
" high color already.
"
" License:
" Copyright (c) 2012, Matthew J. Wozniski
" All rights reserved.
"
" Redistribution and use in source and binary forms, with or without
" modification, are permitted provided that the following conditions are met:
"     * Redistributions of source code must retain the above copyright
"       notice, this list of conditions and the following disclaimer.
"     * Redistributions in binary form must reproduce the above copyright
"       notice, this list of conditions and the following disclaimer in the
"       documentation and/or other materials provided with the distribution.
"     * The names of the contributors may not be used to endorse or promote
"       products derived from this software without specific prior written
"       permission.
"
" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
" EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
" DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

" {>1} Basic plugin setup

" {>2} Check preconditions
" Quit if the user doesn't want or need us or is missing the gui feature.  We
" need +gui to be able to check the gui color settings; vim doesn't bother to
" store them if it is not built with +gui.
if exists('g:CSApprox_loaded')
  finish
elseif !has('gui') && v:version < 703
  " Vim versions less than < 7.3.0 need +gui.
  " Warn unless the user set g:CSApprox_verbose_level to zero.
  if get(g:, 'CSApprox_verbose_level', 1)
    echomsg "CSApprox needs gui support - not loading."
    echomsg "  See :help |csapprox-+gui| for possible workarounds."
  endif

  finish
endif

" {1} Mark us as loaded, and disable all compatibility options for now.
let g:CSApprox_loaded = 1

let s:savecpo = &cpo
set cpo&vim

" {>1} Collect info for the set highlights

" {>2} Determine if synIDattr is usable
" synIDattr() couldn't support 'guisp' until 7.2.052.  This function returns
" true if :redir is needed to find the 'guisp' attribute, false if synIDattr()
" is functional.  This test can be overridden by setting the global variable
" g:CSApprox_redirfallback to 1 (to force use of :redir) or to 0 (to force use
" of synIDattr()).
function! s:NeedRedirFallback()
  if !exists("g:CSApprox_redirfallback")
    let g:CSApprox_redirfallback = (v:version == 702 && !has('patch52'))
                                 \  || v:version < 702
  endif
  return g:CSApprox_redirfallback
endfunction

" {>2} Collect and store the highlights
" Get a dictionary containing information for every highlight group not merely
" linked to another group.  Return value is a dictionary, with highlight group
" numbers for keys and values that are dictionaries with four keys each,
" 'name', 'term', 'cterm', and 'gui'.  'name' holds the group name, and each
" of the others holds highlight information for that particular mode.
function! s:Highlights(modes)
  let rv = {}

  let i = 0
  while 1
    let i += 1

    " Only interested in groups that exist and aren't linked
    if synIDtrans(i) == 0
      break
    endif

    " Handle vim bug allowing groups with name == "" to be created
    if synIDtrans(i) != i || len(synIDattr(i, "name")) == 0
      continue
    endif

    let rv[i] = {}
    let rv[i].name = synIDattr(i, "name")

    for where in a:modes
      let rv[i][where]  = {}
      for attr in s:PossibleAttributes()
        let rv[i][where][attr] = synIDattr(i, attr, where)
      endfor

      for attr in [ "fg", "bg" ]
        let rv[i][where][attr] = synIDattr(i, attr.'#', where)
      endfor

      if where == "gui"
        let rv[i][where]["sp"] = s:SynGuiSp(i, rv[i].name)
      else
        let rv[i][where]["sp"] = -1
      endif

      for attr in [ "fg", "bg", "sp" ]
        if rv[i][where][attr] == -1
          let rv[i][where][attr] = ''
        endif
      endfor
    endfor
  endwhile

  return rv
endfunction

" {>2} Retrieve guisp

" Get guisp using whichever method is specified by _redir_fallback
function! s:SynGuiSp(idx, name)
  if !s:NeedRedirFallback()
    return s:SynGuiSpAttr(a:idx)
  else
    return s:SynGuiSpRedir(a:name)
  endif
endfunction

" {>3} Implementation for retrieving guisp with redir hack
function! s:SynGuiSpRedir(name)
  redir => temp
  exe 'sil hi ' . a:name
  redir END
  let temp = matchstr(temp, 'guisp=\zs.*')
  if len(temp) == 0 || temp[0] =~ '\s'
    let temp = ""
  else
    " Make sure we can handle guisp='dark red'
    let temp = substitute(temp, '[\x00].*', '', '')
    let temp = substitute(temp, '\s*\(c\=term\|gui\).*', '', '')
    let temp = substitute(temp, '\s*$', '', '')
  endif
  return temp
endfunction

" {>3} Implementation for retrieving guisp with synIDattr()
function! s:SynGuiSpAttr(idx)
  return synIDattr(a:idx, 'sp#', 'gui')
endfunction

" {>1} Handle color names

" Place to store rgb.txt name to color mappings - lazy loaded if needed
let s:rgb = {}

" {>2} Builtin gui color names
" gui_x11.c and gui_gtk_x11.c have some default colors names that are searched
" if the x server doesn't know about a color.  If 'showrgb' is available,
" we'll default to using these color names and values, and overwrite them with
" other values if 'showrgb' tells us about those colors.
let s:rgb_defaults = { "lightred"     : "#FFBBBB",
                     \ "lightgreen"   : "#88FF88",
                     \ "lightmagenta" : "#FFBBFF",
                     \ "darkcyan"     : "#008888",
                     \ "darkblue"     : "#0000BB",
                     \ "darkred"      : "#BB0000",
                     \ "darkmagenta"  : "#BB00BB",
                     \ "darkgrey"     : "#BBBBBB",
                     \ "darkyellow"   : "#BBBB00",
                     \ "gray10"       : "#1A1A1A",
                     \ "grey10"       : "#1A1A1A",
                     \ "gray20"       : "#333333",
                     \ "grey20"       : "#333333",
                     \ "gray30"       : "#4D4D4D",
                     \ "grey30"       : "#4D4D4D",
                     \ "gray40"       : "#666666",
                     \ "grey40"       : "#666666",
                     \ "gray50"       : "#7F7F7F",
                     \ "grey50"       : "#7F7F7F",
                     \ "gray60"       : "#999999",
                     \ "grey60"       : "#999999",
                     \ "gray70"       : "#B3B3B3",
                     \ "grey70"       : "#B3B3B3",
                     \ "gray80"       : "#CCCCCC",
                     \ "grey80"       : "#CCCCCC",
                     \ "gray90"       : "#E5E5E5",
                     \ "grey90"       : "#E5E5E5" }

" {>2} Colors that vim will use by name in one of the default schemes, either
" for bg=light or for bg=dark.  This lets us avoid loading the entire rgb.txt
" database when the scheme itself doesn't ask for colors by name.
let s:rgb_presets = { "black"         : "#000000",
                     \ "blue"         : "#0000ff",
                     \ "brown"        : "#a52a2a",
                     \ "cyan"         : "#00ffff",
                     \ "darkblue"     : "#00008b",
                     \ "darkcyan"     : "#008b8b",
                     \ "darkgrey"     : "#a9a9a9",
                     \ "darkmagenta"  : "#8b008b",
                     \ "green"        : "#00ff00",
                     \ "grey"         : "#bebebe",
                     \ "grey40"       : "#666666",
                     \ "grey90"       : "#e5e5e5",
                     \ "lightblue"    : "#add8e6",
                     \ "lightcyan"    : "#e0ffff",
                     \ "lightgrey"    : "#d3d3d3",
                     \ "lightmagenta" : "#ffbbff",
                     \ "magenta"      : "#ff00ff",
                     \ "red"          : "#ff0000",
                     \ "seagreen"     : "#2e8b57",
                     \ "white"        : "#ffffff",
                     \ "yellow"       : "#ffff00" }

" {>2} Find available color names
" Find the valid named colors.  By default, use our own rgb list, but try to
" retrieve the system's list if g:CSApprox_use_showrgb is set to true.  Store
" the color names and color values to the dictionary s:rgb - the keys are
" color names (in lowercase), the values are strings representing color values
" (as '#rrggbb').
function! s:UpdateRgbHash()
  try
    if !exists("g:CSApprox_use_showrgb") || !g:CSApprox_use_showrgb
      throw "Not using showrgb"
    endif

    " We want to use the 'showrgb' program, if it's around
    let lines = split(system('showrgb'), '\n')

    if v:shell_error || !exists('lines') || empty(lines)
      throw "'showrgb' didn't give us an rgb.txt"
    endif

    let s:rgb = copy(s:rgb_defaults)

    " fmt is (blanks?)(red)(blanks)(green)(blanks)(blue)(blanks)(name)
    let parsepat  = '^\s*\(\d\+\)\s\+\(\d\+\)\s\+\(\d\+\)\s\+\(.*\)$'

    for line in lines
      let v = matchlist(line, parsepat)
      if len(v) < 0
        throw "CSApprox: Bad RGB line: " . string(line)
      endif
      let s:rgb[tolower(v[4])] = printf("#%02x%02x%02x", v[1], v[2], v[3])
    endfor
  catch
    try
      let s:rgb = csapprox#rgb()
    catch
      echohl ErrorMsg
      echomsg "Can't call rgb() from autoload/csapprox.vim"
      echomsg "Named colors will not be available!"
      echohl None
    endtry
  endtry

  return 0
endfunction

" {>1} Derive and set cterm attributes

" {>2} List of all possible attributes
function! s:PossibleAttributes()
  return [ "bold", "italic", "reverse", "underline", "undercurl" ]
endfunction

" {>2} Attribute overrides
" Allow the user to override a specified attribute with another attribute.
" For example, the default is to map 'italic' to 'underline' (since many
" terminals cannot display italic text, and gvim itself will replace italics
" with underlines where italicizing is impossible), and to replace 'sp' with
" 'fg' (since terminals can't use one color for the underline and another for
" the foreground, we color the entire word).  This default can of course be
" overridden by the user, by setting g:CSApprox_attr_map.  This map must be
" a dictionary of string keys, representing the same attributes that synIDattr
" can look up, to string values, representing the attribute mapped to or an
" empty string to disable the given attribute entirely.
function! s:attr_map(attr)
  let rv = get(g:CSApprox_attr_map, a:attr, a:attr)

  return rv
endfunction

function! s:NormalizeAttrMap(map)
  let old = copy(a:map)
  let new = filter(a:map, '0')

  let valid_attrs = [ 'bg', 'fg', 'sp' ] + s:PossibleAttributes()

  let colorattrs = [ 'fg', 'bg', 'sp' ]

  for olhs in keys(old)
    if olhs ==? 'inverse'
      let nlhs = 'reverse'
    endif

    let orhs = old[olhs]

    if orhs ==? 'inverse'
      let nrhs = 'reverse'
    endif

    let nlhs = tolower(olhs)
    let nrhs = tolower(orhs)

    try
      if index(valid_attrs, nlhs) == -1
        echomsg "CSApprox: Bad attr map (removing unrecognized attribute " . olhs . ")"
      elseif nrhs != '' && index(valid_attrs, nrhs) == -1
        echomsg "CSApprox: Bad attr map (removing unrecognized attribute " . orhs . ")"
      elseif nrhs != '' && !!(index(colorattrs, nlhs)+1) != !!(index(colorattrs, nrhs)+1)
        echomsg "CSApprox: Bad attr map (removing " . olhs . "; type mismatch with " . orhs . ")"
      elseif nrhs == 'sp'
        echomsg "CSApprox: Bad attr map (removing " . olhs . "; can't map to 'sp')"
      else
        let new[nlhs] = nrhs
      endif
    catch
      echo v:exception
    endtry
  endfor
endfunction

" {>2} Normalize the GUI settings of a highlight group
" If the Normal group is cleared, set it to gvim's default, black on white
" Though this would be a really weird thing for a scheme to do... *shrug*
function! s:FixupGuiInfo(highlights)
  if a:highlights[s:hlid_normal].gui.bg == ''
    let a:highlights[s:hlid_normal].gui.bg = 'white'
  endif

  if a:highlights[s:hlid_normal].gui.fg == ''
    let a:highlights[s:hlid_normal].gui.fg = 'black'
  endif
endfunction

" {>2} Map gui settings to cterm settings
" Given information about a highlight group, replace the cterm settings with
" the mapped gui settings, applying any attribute overrides along the way.  In
" particular, this gives special treatment to the 'reverse' attribute and the
" 'guisp' attribute.  In particular, if the 'reverse' attribute is set for
" gvim, we unset it for the terminal and instead set ctermfg to match guibg
" and vice versa, since terminals can consider a 'reverse' flag to mean using
" default-bg-on-default-fg instead of current-bg-on-current-fg.  We also
" ensure that the 'sp' attribute is never set for cterm, since no terminal can
" handle that particular highlight.  If the user wants to display the guisp
" color, he should map it to either 'fg' or 'bg' using g:CSApprox_attr_map.
function! s:FixupCtermInfo(highlights)
  for hl in values(a:highlights)

    if !has_key(hl, 'cterm')
      let hl["cterm"] = {}
    endif

    " Find attributes to be set in the terminal
    for attr in s:PossibleAttributes()
      let hl.cterm[attr] = ''
      if hl.gui[attr] == 1
        if s:attr_map(attr) != ''
          let hl.cterm[ s:attr_map(attr) ] = 1
        endif
      endif
    endfor

    for color in [ "bg", "fg" ]
      let eff_color = color
      if hl.cterm['reverse']
        let eff_color = (color == 'bg' ? 'fg' : 'bg')
      endif

      let hl.cterm[color] = get(hl.gui, s:attr_map(eff_color), '')
    endfor

    if hl.gui['sp'] != '' && s:attr_map('sp') != ''
      let hl.cterm[s:attr_map('sp')] = hl.gui['sp']
    endif

    if exists("g:CSApprox_fake_reverse") && g:CSApprox_fake_reverse
      if hl.cterm['reverse'] && hl.cterm.bg == ''
        let hl.cterm.bg = 'fg'
      endif

      if hl.cterm['reverse'] && hl.cterm.fg == ''
        let hl.cterm.fg = 'bg'
      endif

      if hl.cterm['reverse']
        let hl.cterm.reverse = ''
      endif
    endif
  endfor
endfunction

" {>2} Kludge around inability to reference autoload functions
function! s:DefaultApproximator(...)
  return call('csapprox#per_component#Approximate', a:000)
endfunction

" {>2} Set cterm colors for a highlight group
" Given the information for a single highlight group (ie, the value of
" one of the items in s:Highlights() already normalized with s:FixupCtermInfo
" and s:FixupGuiInfo), handle matching the gvim colors to the closest cterm
" colors by calling the appropriate approximator as specified with the
" g:CSApprox_approximator_function variable and set the colors and attributes
" appropriately to match the gui.
function! s:SetCtermFromGui(hl)
  let hl = a:hl

  " Set up the default approximator function, if needed
  if !exists("g:CSApprox_approximator_function")
    let g:CSApprox_approximator_function = function("s:DefaultApproximator")
  endif

  " Clear existing highlights
  exe 'hi ' . hl.name . ' cterm=NONE ctermbg=NONE ctermfg=NONE'

  for which in [ 'bg', 'fg' ]
    let val = hl.cterm[which]

    " Skip unset colors
    if val == -1 || val == ""
      continue
    endif

    " Try translating anything but 'fg', 'bg', #rrggbb, and rrggbb from an
    " rgb.txt color to a #rrggbb color
    if val !~? '^[fb]g$' && val !~ '^#\=\x\{6}$'
      try
        " First see if it is in our preset-by-vim rgb list
        let val = s:rgb_presets[tolower(val)]
      catch
        " Then try loading and checking our real rgb list
        if empty(s:rgb)
          call s:UpdateRgbHash()
        endif
        try
          let val = s:rgb[tolower(val)]
        catch
          " And then barf if we still haven't found it
          if &verbose
            echomsg "CSApprox: Colorscheme uses unknown color \"" . val . "\""
          endif
          continue
        endtry
      endtry
    endif

    if val =~? '^[fb]g$'
      exe 'hi ' . hl.name . ' cterm' . which . '=' . val
      let hl.cterm[which] = val
    elseif val =~ '^#\=\x\{6}$'
      let val = substitute(val, '^#', '', '')
      let r = str2nr(val[0:1], 16)
      let g = str2nr(val[2:3], 16)
      let b = str2nr(val[4:5], 16)
      let hl.cterm[which] = g:CSApprox_approximator_function(r, g, b)
      exe 'hi ' . hl.name . ' cterm' . which . '=' . hl.cterm[which]
    else
      throw "Internal error handling color: " . val
    endif
  endfor

  " Finally, set the attributes
  let attrs = s:PossibleAttributes()
  call filter(attrs, 'hl.cterm[v:val] == 1')

  if !empty(attrs)
    exe 'hi ' . hl.name . ' cterm=' . join(attrs, ',')
  endif
endfunction


" {>1} Top-level control

" Cache the highlight ID of the normal group; it's used often and won't change
let s:hlid_normal = hlID('Normal')

" {>2} Builtin cterm color names above 15
" Vim defines some color name to high color mappings internally (see
" syntax.c:do_highlight).  Since we don't want to overwrite a colorscheme that
" was actually written for a high color terminal with our choices, but have no
" way to tell if a colorscheme was written for a high color terminal, we fall
" back on guessing.  If any highlight group has a cterm color set to 16 or
" higher, we assume that the user has used a high color colorscheme - unless
" that color is one of the below, which vim can set internally when a color is
" requested by name.
let s:presets_88  = []
let s:presets_88 += [32] " Brown
let s:presets_88 += [72] " DarkYellow
let s:presets_88 += [84] " Gray
let s:presets_88 += [84] " Grey
let s:presets_88 += [82] " DarkGray
let s:presets_88 += [82] " DarkGrey
let s:presets_88 += [43] " LightBlue
let s:presets_88 += [61] " LightGreen
let s:presets_88 += [63] " LightCyan
let s:presets_88 += [74] " LightRed
let s:presets_88 += [75] " LightMagenta
let s:presets_88 += [78] " LightYellow

let s:presets_256  = []
let s:presets_256 += [130] " Brown
let s:presets_256 += [130] " DarkYellow
let s:presets_256 += [248] " Gray
let s:presets_256 += [248] " Grey
let s:presets_256 += [242] " DarkGray
let s:presets_256 += [242] " DarkGrey
let s:presets_256 += [ 81] " LightBlue
let s:presets_256 += [121] " LightGreen
let s:presets_256 += [159] " LightCyan
let s:presets_256 += [224] " LightRed
let s:presets_256 += [225] " LightMagenta
let s:presets_256 += [229] " LightYellow

" {>2} Wrapper around :exe to allow :executing multiple commands.
" "cmd" is the command to be :executed.
" If the variable is a String, it is :executed.
" If the variable is a List, each element is :executed.
function! s:exe(cmd)
  if type(a:cmd) == type('')
    exe a:cmd
  else
    for cmd in a:cmd
      call s:exe(cmd)
    endfor
  endif
endfunction

" {>2} Function to handle hooks
" Prototype: HandleHooks(type [, scheme])
" "type" is the type of hook to be executed, ie. "pre" or "post"
" "scheme" is the name of the colorscheme that is currently active, if known
"
" If the variables g:CSApprox_hook_{type} and g:CSApprox_hook_{scheme}_{type}
" exist, this will :execute them in that order.  If one does not exist, it
" will silently be ignored.
"
" If the scheme name contains characters that are invalid in a variable name,
" they will simply be removed.  Ie, g:colors_name = "123 foo_bar-baz456"
" becomes "foo_barbaz456"
"
" NOTE: Exceptions will be printed out, rather than end processing early.  The
" rationale is that it is worse for the user to fix the hook in an editor with
" broken colors.  :)
function! s:HandleHooks(type, ...)
  let type = a:type
  let scheme = (a:0 == 1 ? a:1 : "")
  let scheme = substitute(scheme, '[^[:alnum:]_]', '', 'g')
  let scheme = substitute(scheme, '^\d\+', '', '')

  for cmd in [ 'g:CSApprox_hook_' . type,
             \ 'g:CSApprox_' . scheme . '_hook_' . type,
             \ 'g:CSApprox_hook_' . scheme . '_' . type ]
    if exists(cmd)
      try
        call s:exe(eval(cmd))
      catch
        echomsg "Error processing " . cmd . ":"
        echomsg v:exception
      endtry
    endif
  endfor
endfunction

" {>2} Main function
" Wrapper around the actual implementation to make it easier to ensure that
" all temporary settings are restored by the time we return, whether or not
" something was thrown.  Additionally, sets the 'verbose' option to the max of
" g:CSApprox_verbose_level (default 1) and &verbose for the duration of the
" main function.  This allows us to default to a message whenever any error,
" even a recoverable one, occurs, meaning the user quickly finds out when
" something's wrong, but makes it very easy for the user to make us silent.
function! s:CSApprox(...)
  try
    if a:0 == 1 && a:1
      if !exists('s:inhibit_hicolor_test')
        let s:inhibit_hicolor_test = 0
      endif
      let s:inhibit_hicolor_test += 1
    endif

    let savelz  = &lz

    set lz

    if exists("g:CSApprox_attr_map") && type(g:CSApprox_attr_map) == type({})
      call s:NormalizeAttrMap(g:CSApprox_attr_map)
    else
      let g:CSApprox_attr_map = { 'italic' : 'underline', 'sp' : 'fg' }
    endif

    " colors_name must be unset and reset, or vim will helpfully reload the
    " colorscheme when we set the background for the Normal group.
    " See the help entries ':hi-normal-cterm' and 'g:colors_name'
    if exists("g:colors_name")
      let colors_name = g:colors_name
      unlet g:colors_name
    endif

    " Similarly, the global variable "syntax_cmd" must be set to something vim
    " doesn't recognize, lest vim helpfully switch all colors back to the
    " default whenever the Normal group is changed (in syncolor.vim)...
    if exists("g:syntax_cmd")
      let syntax_cmd = g:syntax_cmd
    endif
    let g:syntax_cmd = "PLEASE DON'T CHANGE ANY COLORS!!!"

    " Set up our verbosity level, if needed.
    " Default to 1, so the user can know if something's wrong.
    if !exists("g:CSApprox_verbose_level")
      let g:CSApprox_verbose_level = 1
    endif

    call s:HandleHooks("pre", (exists("colors_name") ? colors_name : ""))

    let old_bg = &bg

    " Set 'verbose' set to the maximum of &verbose and CSApprox_verbose_level
    exe max([&vbs, g:CSApprox_verbose_level]) 'verbose call s:CSApproxImpl()'

    let &bg = old_bg

    call s:HandleHooks("post", (exists("colors_name") ? colors_name : ""))
  finally
    if exists("colors_name")
      let g:colors_name = colors_name
    endif

    unlet g:syntax_cmd
    if exists("syntax_cmd")
      let g:syntax_cmd = syntax_cmd
    endif

    let &lz   = savelz

    if a:0 == 1 && a:1
      let s:inhibit_hicolor_test -= 1
      if s:inhibit_hicolor_test == 0
        unlet s:inhibit_hicolor_test
      endif
    endif
  endtry
endfunction

" {>2} CSApprox implementation
" Verifies that the user has not started the gui, and that vim recognizes his
" terminal as having enough colors for us to go on, then gathers the existing
" highlights and sets the cterm colors to match the gui colors for all those
" highlights (unless the colorscheme was already high-color).
function! s:CSApproxImpl()
  " Return if not running in an 88/256 color terminal
  if &t_Co != 256 && &t_Co != 88
    if &verbose && &t_Co != ''
      echomsg "CSApprox skipped; terminal only has" &t_Co "colors, not 88/256"
      echomsg "Try checking :help csapprox-terminal for workarounds"
    endif

    return
  endif

  " Get the current highlight colors
  let highlights = s:Highlights(["gui"])

  let hinums = keys(highlights)

  " Make sure that the script is not already 256 color by checking to make
  " sure that no groups are set to a value above 256, unless the color they're
  " set to can be set internally by vim (gotten by scraping
  " color_numbers_{88,256} in syntax.c:do_highlight)
  "
  " XXX: s:inhibit_hicolor_test allows this test to be skipped for snapshots
  if !exists("s:inhibit_hicolor_test") || !s:inhibit_hicolor_test
    for hlid in hinums
      for type in [ 'bg', 'fg' ]
        let color = synIDattr(hlid, type, 'cterm')

        if color > 15 && index(s:presets_{&t_Co}, str2nr(color)) < 0
          " The value is set above 15, and wasn't set by vim.
          if &verbose >= 2
            echomsg 'CSApprox: Exiting - high' type 'color found for' highlights[hlid].name
          endif
          return
        endif
      endfor
    endfor
  endif

  call s:FixupGuiInfo(highlights)
  call s:FixupCtermInfo(highlights)

  " We need to set the Normal group first so 'bg' and 'fg' work as colors
  call insert(hinums, remove(hinums, index(hinums, string(s:hlid_normal))))

  " then set each color's cterm attributes to match gui
  for hlid in hinums
    call s:SetCtermFromGui(highlights[hlid])
  endfor
endfunction

" {>2} Write out the current colors to an 88/256 color colorscheme file.
" "file" - destination filename
" "overwrite" - overwrite an existing file
function! s:CSApproxSnapshot(file, overwrite)
  let force = a:overwrite
  let file = fnamemodify(a:file, ":p")

  if empty(file)
    throw "Bad file name: \"" . file . "\""
  elseif (filewritable(fnamemodify(file, ':h')) != 2)
    throw "Cannot write to directory \"" . fnamemodify(file, ':h') . "\""
  elseif (glob(file) || filereadable(file)) && !force
    " TODO - respect 'confirm' here and prompt if it's set.
    echohl ErrorMsg
    echomsg "E13: File exists (add ! to override)"
    echohl None
    return
  endif

  " Sigh... This is basically a bug, but one that I have no chance of fixing.
  " Vim decides that Pmenu should be highlighted in 'LightMagenta' in terminal
  " vim and as 'Magenta' in gvim...  And I can't ask it what color it actually
  " *wants*.  As far as I can see, there's no way for me to learn that
  " I should output 'Magenta' when 'LightMagenta' is provided by vim for the
  " terminal.
  if !has('gui_running')
    echohl WarningMsg
    echomsg "Warning: The written colorscheme may have incorrect colors"
    echomsg "         when CSApproxSnapshot is used in terminal vim!"
    echohl None
  endif

  let save_t_Co = &t_Co
  let s:inhibit_hicolor_test = 1
  if exists("g:CSApprox_konsole")
    let save_CSApprox_konsole = g:CSApprox_konsole
  endif
  if exists("g:CSApprox_eterm")
    let save_CSApprox_eterm = g:CSApprox_eterm
  endif

  " Needed just like in CSApprox()
  if exists("g:colors_name")
    let colors_name = g:colors_name
    unlet g:colors_name
  endif

  " Needed just like in CSApprox()
  if exists("g:syntax_cmd")
    let syntax_cmd = g:syntax_cmd
  endif
  let g:syntax_cmd = "PLEASE DON'T CHANGE ANY COLORS!!!"

  try
    let lines = []
    let lines += [ '" This scheme was created by CSApproxSnapshot' ]
    let lines += [ '" on ' . strftime("%a, %d %b %Y") ]
    let lines += [ '' ]
    let lines += [ 'hi clear' ]
    let lines += [ 'if exists("syntax_on")' ]
    let lines += [ '    syntax reset' ]
    let lines += [ 'endif' ]
    let lines += [ '' ]
    let lines += [ 'if v:version < 700' ]
    let lines += [ '    let g:colors_name = expand("<sfile>:t:r")' ]
    let lines += [ '    command! -nargs=+ CSAHi exe "hi" substitute(substitute(<q-args>, "undercurl", "underline", "g"), "guisp\\S\\+", "", "g")' ]
    let lines += [ 'else' ]
    let lines += [ '    let g:colors_name = expand("<sfile>:t:r")' ]
    let lines += [ '    command! -nargs=+ CSAHi exe "hi" <q-args>' ]
    let lines += [ 'endif' ]
    let lines += [ '' ]
    let lines += [ 'function! s:old_kde()' ]
    let lines += [ '  " Konsole only used its own palette up til KDE 4.2.0' ]
    let lines += [ "  if executable('kde4-config') && system('kde4-config --kde-version') =~ '^4\.[10]\.'" ]
    let lines += [ '    return 1' ]
    let lines += [ "  elseif executable('kde-config') && system('kde-config --version') =~# 'KDE: 3\.'" ]
    let lines += [ '    return 1' ]
    let lines += [ '  else' ]
    let lines += [ '    return 0' ]
    let lines += [ '  endif' ]
    let lines += [ 'endfunction' ]
    let lines += [ '' ]


    let lines += [ 'if 0' ]
    for round in [ 'konsole', 'eterm', 'xterm', 'urxvt' ]
      sil! unlet g:CSApprox_eterm
      sil! unlet g:CSApprox_konsole

      if round == 'konsole'
        let g:CSApprox_konsole = 1
      elseif round == 'eterm'
        let g:CSApprox_eterm = 1
      endif

      if round == 'urxvt'
        set t_Co=88
      else
        set t_Co=256
      endif

      call s:CSApprox()

      let highlights = s:Highlights(["term", "cterm", "gui"])
      call s:FixupGuiInfo(highlights)

      if round == 'konsole' || round == 'eterm'
        if round == 'konsole'
          let term_matches_round = '(&term =~? "^konsole" && s:old_kde())'
        else
          let term_matches_round = '&term =~? "^' . round . '"'
        endif

        let lines += [ 'elseif has("gui_running") || (&t_Co == ' . &t_Co
                   \ . ' && (&term ==# "xterm" || &term =~# "^screen")'
                   \ . ' && exists("g:CSApprox_' . round . '")'
                   \ . ' && g:CSApprox_' . round . ')'
                   \ . ' || ' . term_matches_round ]
      else
        let lines += [ 'elseif has("gui_running") || &t_Co == ' . &t_Co ]
      endif

      let hinums = keys(highlights)

      call insert(hinums, remove(hinums, index(hinums, string(s:hlid_normal))))

      for hlnum in hinums
        let hl = highlights[hlnum]
        let line = '    CSAHi ' . hl.name
        for type in [ 'term', 'cterm', 'gui' ]
          let attrs = s:PossibleAttributes()
          call filter(attrs, 'hl[type][v:val] == 1')
          let line .= ' ' . type . '=' . (empty(attrs) ? 'NONE' : join(attrs, ','))
          if type != 'term'
            let line .= ' ' . type . 'bg=' . (len(hl[type].bg) ? hl[type].bg : 'bg')
            let line .= ' ' . type . 'fg=' . (len(hl[type].fg) ? hl[type].fg : 'fg')
            if type == 'gui' && hl.gui.sp !~ '^\s*$'
              let line .= ' ' . type . 'sp=' . hl[type].sp
            endif
          endif
        endfor
        let lines += [ line ]
      endfor
    endfor
    let lines += [ 'endif' ]
    let lines += [ '' ]
    let lines += [ 'if 1' ]
    let lines += [ '    delcommand CSAHi' ]
    let lines += [ 'endif' ]
    call writefile(lines, file)
  finally
    let &t_Co = save_t_Co

    if exists("save_CSApprox_konsole")
      let g:CSApprox_konsole = save_CSApprox_konsole
    endif
    if exists("save_CSApprox_eterm")
      let g:CSApprox_eterm = save_CSApprox_eterm
    endif

    if exists("colors_name")
      let g:colors_name = colors_name
    endif

    unlet g:syntax_cmd
    if exists("syntax_cmd")
      let g:syntax_cmd = syntax_cmd
    endif

    call s:CSApprox()

    unlet s:inhibit_hicolor_test
  endtry
endfunction

" {>2} Snapshot user command
command! -bang -nargs=1 -complete=file -bar CSApproxSnapshot
        \ call s:CSApproxSnapshot(<f-args>, strlen("<bang>"))

" {>2} Manual updates
command -bang -bar CSApprox call s:CSApprox(strlen("<bang>"))

" {>1} Autocmds
" Set up an autogroup to hook us on the completion of any :colorscheme command
augroup CSApprox
  au!
  au ColorScheme * call s:CSApprox()
  "au User CSApproxPost highlight Normal ctermbg=none | highlight NonText ctermbg=None
augroup END

" {>1} Restore compatibility options
let &cpo = s:savecpo
unlet s:savecpo


" {0} vim:sw=2:sts=2:et:fdm=expr:fde=substitute(matchstr(getline(v\:lnum),'^\\s*"\\s*{\\zs.\\{-}\\ze}'),'^$','=','')