WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP WIP
Looking for the perfect colorscheme is no doubt part of everyone's experience with text editors and IDEs. For the luckiest among us, that perfect colorscheme will be the one enabled by default or the one that everyone on the internet seems to use. For the less lucky, the quest can be long and eventually lead them to actually build their own "perfect" colorscheme.
As a budding vimmer, it did not take long for me to start looking around for a nice colorscheme. I started my journey with that huge colorscheme test page that crashed so many browsers for so many years. Then, noticing that it did not have every colorscheme, I turned to the scripts section of vim.org and spent too much time looking at too many screenshots and installing too many colorschemes…
Except it was GUI-only and I used my shell a lot.
For a while, I tried a compromise: Sorcerer in GVim/Macvim and Torte in Vim but there were too many difference and I soon tried two famous plugins that purported to "adapt" GUI-only colorschemes to 256/88/16-colors terminals. Needless to say, the result was far from perfect but that new compromise was far more acceptable.
What must happen unsurprisingly happened: a hack here, another there… and, slowly but surely, my Sorcerer started to deviate from the original in lesser and lesser subtle ways. After a good year of usage, small tweaks and CSApprox-related annoyances I took it upon myself to consolidate all my changes into a "better" Sorcerer that would look the same in GVim/MacVim and in 256-colors terminals.
Step one: consolidation
One thing that I noticed very early — way before I started work on my own colorscheme — was that Sorcerer's author used many colors. As a graphic designer, I could very well understand how all those colors ended up in there but it certainly made maintenance harder than necessary… and it lacked organization. Sorcerer definitely had a "mood", which is what attracted me in the first place, but it did not have much internal consistency. That was where I had to start.
For starters, I needed to list all the colors used in the colorscheme so I naturally turned to Vim:
put every color code on its own line
delete every line that doesn't contain a color code
get rid of the extraneous stuff
:%norm! f D
sort and keep only unique lines
:%!sort | uniq
After that little dance I ended up with fifty five different color codes, which was way too much.
My bad experience with CSApprox and Foobar lead me to photoshop where I created a document with a dark background and added every color patch, one by one:
Next step was to group those colors by proximity, forming clusters that would hopefully make the whole job easier:
But all those clusters didn't do anything to reduce the number of colors. I had to find a "median" color for each cluster using Photoshop's sgfdjkghf filter and a bit of hand-tuning:
At that point the number of colors was down to thirty five, which was still too high for me so I went a little further and reduced those mini-palettes from three colors to only two.
Twenty five colors. Not bad.
So… it looks like I reached my first goal but what measures should I take to make those colors look good in a 256-colors terminal?
While it is certainly possible to change the xterm palette, not many people know about colorcoke or its underlying magic so it is generally safe to assume that your user's terminal will render color
#87af87. From there, I took every single color of my reduced palette and painstakingly chose an xterm counterpart from this extremelly useful document.
The well know limitations of the xterm palette made me throw away a few colors that were not heavily used in the original colorscheme but I managed to make most of my initial palette fit in the xterm color space:
and, except for the greys, nicely fit into the traditional ANSI pattern which was quite a feat in and of itself:
MADE-UP NAME HEX RGB XTERM ANSI ======================================================================== almost black #1c1c1c rgb(28, 28, 28) 234 0 darker grey #262626 rgb(38, 38, 38) 235 background color dark grey #303030 rgb(48, 48, 48) 236 8 grey #444444 rgb(68, 68, 68) 238 8 medium grey #585858 rgb(88, 88, 88) 240 8 light grey #6c6c6c rgb(108, 108, 108) 242 7 lighter grey #bcbcbc rgb(188, 188, 188) 250 foreground color white #ffffff rgb(255, 255, 255) 231 15 purple #5f5f87 rgb(95, 95, 135) 60 5 light purple #8787af rgb(135, 135, 175) 103 13 green #5f875f rgb(95, 135, 95) 65 2 light green #87af87 rgb(135, 175, 135) 108 10 aqua #5f8787 rgb(95, 135, 135) 66 6 light aqua #5fafaf rgb(95, 175, 175) 73 14 blue #5f87af rgb(95, 135, 175) 67 4 light blue #8fafd7 rgb(143, 175, 215) 110 12 red #af5f5f rgb(175, 95, 95) 131 1 orange #ff8700 rgb(255, 135, 0) 208 9 ocre #87875f rgb(135, 135, 95) 101 3 yellow #ffffaf rgb(255, 255, 175) 229 11
Step 2: when the boring gets tough
Now comes the "boring" part…
"Boring" as in "I won't get to play with colors and be creative and all" but… that's text editing and I'm a Vimmer!
Substituting every original color with its new and improved self
So I started by realizing that I didn't keep a conversion table while zipping through the consolidation step. This meant that I had to go back to Photoshop and sample every original color if I wanted to finish the job. Dumb me…
000000 000000 000000 ffffff
Whatever. Once I had the table above, it was very easy to turn it into a list of substitution commands using a bunch of macros and substitutions:
:%norm! I%s/\( :%norm! A/g :%norm! $F s\)/ :%s/ /\\|/g " result %s/\(000000\|cccccc\)/ffffff %s/666666/foobar
and run them on my buffer:
I could certainly have used a single substitution but I firmly beieve that an intuitive, non-elegant method is almost always better than an elegant, hard-to-get-right-the-first-time one. But that's just me.
Fixing styling and sorting the whole mess in logical "blocks"
Then it was time to sort the highlight commands and "fix" the haphazard styling. There was not much of a pre-existing order in there but I didn't really have a precise idea either so I went with this mostly random process:
- move to some line,
- fix the
guiattribute if needed,
- bubble it up or down next to a related line,
- add empty lines to separate "blocks",
Adding the terminal/GUI logic
Globally make the code more coherent
Step 3: maintenance
Step 4: automatization