Now that 88 and 256 color terminals are so ubiquitous, I find it
frustrating that very few colorschemes support 256 color terminals.
Unfortunately, writing a colorscheme that properly supports gvim, 88
color terminals, and 256 color terminals requires looking up the color
cube number that you want on at least one colorcube and often
including scripting logic in the colorscheme itself to handle
converting to the other cube (a la desert256 and inkpot).

This patch intends to make it much easier to write 88 and 256 color
schemes.  This will allow a colorscheme author to write, for instance
hi Normal cterm=none ctermfg=black ctermbg=#fffdfa
and have vim behave as though the user had typed
hi Normal cterm=none ctermfg=0 ctermbg=231
(or, for 88 colors, hi Normal cterm=none ctermfg=0 ctermbg=79)
based on the value of the t_Co setting.

Not only does this take out the entire intermediate step of looking up
colorcube values, it also will report the color that it chose as
a cterm colorcube number when queried, making it very easy to tweak
a single value to something the author feels more appropriate.

Also, it is quite easy to convert an existing colorscheme to work with
this patch; it usually is as simple as running a substitute:
:%s/cterm.\{-}=.\{-}\>//g
:%s/gui\zs\(fg\|bg\)\?=.\{-}\>/& cterm&/g

I hope that others find this useful enough to include in future Vim
releases.  To that end, it also includes a patch to the relevant
documentation, as well as a patch to the vim.vim syntax file to no
longer highlight ctermfg=#rrggbb as an error.

Feedback greatly appreciated.

~Matt Wozniski

--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---

Index: runtime/doc/syntax.txt
===================================================================
--- runtime/doc/syntax.txt	(revision 647)
+++ runtime/doc/syntax.txt	(working copy)
@@ -3940,6 +3940,30 @@
 	Unfortunately this means that it's not possible to get the same colors
 	for each user.	See |xterm-color| for info about color xterms.
 
+					*E805* *ctermfg-hex* *ctermbg-hex*
+	Many modern terminals, including recent versions of XTerm, URxvt,
+	PuTTY, GNOME Terminal, and Konsole,  support 88 or 256 colors.  If
+	your terminal supports at least 88 colors, you can also specify
+	a color by its Red, Green and Blue values.
+	The format is "#rrggbb", where
+		"rr"	is the Red value
+		"gg"	is the Green value
+		"bb"	is the Blue value
+	All values are hexadecimal, range from "00" to "ff".  Example: >
+	    :highlight Comment ctermfg=#11f0c3 ctermbg=#ff00ff
+<									*E804*
+	This works by attempting to gracefully degrade the specified color to
+	a similar available color.  In practice, you will likely not get
+	exactly the color you ask for; you will get the closest color that
+	your terminal emulator supports.  If your terminal emulator does not
+	report supporting at least 88 colors, this will fail.  If you are
+	certain that your terminal emulator does support 88 or more colors,
+	a possible workaround might be to set t_Co in your .vimrc, for
+	instance: >
+	    if $TERM =~ 'xterm'
+		set t_Co=256
+	    endif
+<
 	The MSDOS standard colors are fixed (in a console window), so these
 	have been used for the names.  But the meaning of color names in X11
 	are fixed, so these color settings have been used, to make the
Index: runtime/syntax/vim.vim
===================================================================
--- runtime/syntax/vim.vim	(revision 647)
+++ runtime/syntax/vim.vim	(working copy)
@@ -437,10 +437,10 @@
 syn case match
 syn match   vimHiFontname	contained	"[a-zA-Z\-*]\+"
 syn match   vimHiGuiFontname	contained	"'[a-zA-Z\-* ]\+'"
-syn match   vimHiGuiRgb	contained	"#\x\{6}"
 if !exists("g:vimsyntax_noerror")
  syn match   vimHiCtermError	contained	"[^0-9]\i*"
 endif
+syn match   vimHiRgb	contained	"#\x\{6}"
 
 " Highlighting: hi group key=arg ... {{{2
 syn cluster vimHiCluster contains=vimHiGroup,vimHiTerm,vimHiCTerm,vimHiStartStop,vimHiCtermFgBg,vimHiGui,vimHiGuiFont,vimHiGuiFgBg,vimHiKeyError,vimNotation
@@ -451,10 +451,10 @@
 syn match  vimHiTerm	contained	"\cterm="he=e-1		nextgroup=vimHiAttribList
 syn match  vimHiStartStop	contained	"\c\(start\|stop\)="he=e-1	nextgroup=vimHiTermcap,vimOption
 syn match  vimHiCTerm	contained	"\ccterm="he=e-1		nextgroup=vimHiAttribList
-syn match  vimHiCtermFgBg	contained	"\ccterm[fb]g="he=e-1	nextgroup=vimNumber,vimHiCtermColor,vimFgBgAttrib,vimHiCtermError
+syn match  vimHiCtermFgBg	contained	"\ccterm[fb]g="he=e-1	nextgroup=vimNumber,vimHiCtermColor,vimHiRgb,vimFgBgAttrib,vimHiCtermError
 syn match  vimHiGui	contained	"\cgui="he=e-1		nextgroup=vimHiAttribList
 syn match  vimHiGuiFont	contained	"\cfont="he=e-1		nextgroup=vimHiFontname
-syn match  vimHiGuiFgBg	contained	"\cgui\%([fb]g\|sp\)="he=e-1	nextgroup=vimHiGroup,vimHiGuiFontname,vimHiGuiRgb,vimFgBgAttrib
+syn match  vimHiGuiFgBg	contained	"\cgui\%([fb]g\|sp\)="he=e-1	nextgroup=vimHiGroup,vimHiGuiFontname,vimHiRgb,vimFgBgAttrib
 syn match  vimHiTermcap	contained	"\S\+"		contains=vimNotation
 
 " Highlight: clear {{{2
@@ -602,7 +602,7 @@
 hi def link vimHiGroup	vimGroupName
 hi def link vimHiGuiFgBg	vimHiTerm
 hi def link vimHiGuiFont	vimHiTerm
-hi def link vimHiGuiRgb	vimNumber
+hi def link vimHiRgb	vimNumber
 hi def link vimHiGui	vimHiTerm
 hi def link vimHiStartStop	vimHiTerm
 hi def link vimHLGroup	vimGroup
Index: src/syntax.c
===================================================================
--- src/syntax.c	(revision 647)
+++ src/syntax.c	(working copy)
@@ -448,6 +448,9 @@
 static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
 static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
 static void syn_incl_toplevel __ARGS((int id, int *flagsp));
+static int cube_nr_is_grey __ARGS((char_u nr));
+static char_u closest_cube_step __ARGS((short size, char_u component, int skip_grey));
+static int approximate_cterm_from_gui __ARGS((char_u* hex));
 
 /*
  * Start the syntax recognition for a line.  This function is normally called
@@ -6952,6 +6955,13 @@
 
 	    if (VIM_ISDIGIT(*arg))
 		color = atoi((char *)arg);
+	    else if (*arg == '#')
+	    {
+		if ((color = approximate_cterm_from_gui(arg)) == -1) {
+		    error = TRUE;
+		    break;
+		}
+	    }
 	    else if (STRICMP(arg, "fg") == 0)
 	    {
 		if (cterm_normal_fg_color)
@@ -9118,6 +9128,186 @@
 }
 #endif
 
+/*
+ * The majority of colors in both the 256 color colorcube and the 88 color
+ * colorcube can only be used in greys.  That is, even though 0xEE might be
+ * valid to use in a color, it can only be used as 0xEEEEEE.  This function
+ * returns true if a particular r/g/b val can only be combined with itself.
+ */
+    static int
+cube_nr_is_grey(nr)
+    char_u	nr;
+{
+    /*
+     * Only the 8 colors below can be used outside of greys, including both
+     * 256 color and 88 color cubes.  There is no place where a grey-only color
+     * on one cube can be mix-and-matched on the other, so this simplistic
+     * solution works.
+     */
+    return (nr != 0x00 && nr != 0x8B
+         && nr != 0xCD && nr != 0xFF
+         && nr != 0x5F && nr != 0x87
+         && nr != 0xAF && nr != 0xD7);
+}
+
+/*
+ * Given an r, g, or b component and a colorcube size to use, this function
+ * will return a number representing the closest colorcube value to that
+ * component.  If skip_grey is true, it will not return a grey.  If the return
+ * value is greater than or equal to 80, it is the colorcube value of a grey,
+ * otherwise it will be in the range [0-5] for a 256 color colorcube, and
+ * [0-3] for an 88 color colorcube; an index along a side of the cube.
+ */
+    static char_u
+closest_cube_step(size, component, skip_grey)
+    short	size;
+    char_u	component;
+    int		skip_grey;
+{
+    /* All possible steps on an 88 color colorcube */
+    static char_u	vals_88[12] = { 0x00, 0x2E, 0x5C, 0x73, 0x8B, 0xA2,
+                                        0xB9, 0xCD, 0xD0, 0xE7, 0xFF };
+
+    /* And on a 256 color colorcube */
+    static char_u	vals_256[30] = { 0x00, 0x08, 0x12, 0x1C, 0x26, 0x30,
+                                         0x3A, 0x44, 0x4E, 0x58, 0x5F, 0x62,
+                                         0x6C, 0x76, 0x80, 0x87, 0x8A, 0x94,
+                                         0x9E, 0xA8, 0xAF, 0xB2, 0xBC, 0xC6,
+                                         0xD0, 0xD7, 0xDA, 0xE4, 0xEE, 0xFF };
+
+    unsigned	i, end, n_color, n_grey, grey;
+    char_u	*list;
+
+    n_color = n_grey = 0;
+
+    if (size == 88)
+    {
+	end  = 11;
+	list = vals_88;
+    }
+    else
+    {
+	end  = 30;
+	list = vals_256;
+    }
+
+    for (i = 0; i < end - 1; /*NOP*/)
+    {
+	int j = 1;
+
+	if (cube_nr_is_grey(list[i]))
+	    ++n_grey;
+	else
+	    ++n_color;
+
+	while (skip_grey && cube_nr_is_grey(list[i+j]))
+	{
+	    ++n_grey;
+	    ++j;
+	}
+
+        /* The component is closest to list[i] if it is less than or equal to
+         * the midpt of list[i] and list[i+1] */
+	if (component <= (list[i] + list[i+j]) / 2) {
+	    grey = cube_nr_is_grey(list[i]);
+	    break;
+	}
+
+	i += j;
+    }
+
+
+    if (i == end - 1)
+        /* Last one is a color on both cubes, and it wasn't yet counted so we
+         * don't subtract 1 */
+	return n_color;
+    else if (!grey)
+	return n_color - 1;
+    else
+	return (size == 88 ? 80 : 232) + n_grey - 1;
+}
+
+/*
+ * Given a hex color of the form "#rrggbb", returns the closest cterm color on
+ * the currently available colorcube (based on  &t_Co).  This will only work if
+ * the current number of colors is greater than or equal to 88; it returns an
+ * error code of -1 upon failure.
+ */
+    static int
+approximate_cterm_from_gui(hex)
+    char_u*	hex;
+{
+    int		i;
+    char_u	rgb[3] = { 0, 0, 0 };  // These each store { rr, gg, bb }
+    char_u	vals[3];
+    unsigned	colors;
+
+    colors = t_colors;
+#ifdef FEAT_GUI
+    /*
+     * Don't ever give E805 error in gui, just pick a reasonable colorcube.
+     * Otherwise, colorschemes that set ctermfg=#rrggbb will give errors when
+     * used in the gui.
+     */
+    if (colors < 88 && gui.in_use)
+	colors = 256;
+#endif
+
+    if (colors < 88)
+    {
+	EMSG(_("E804: Too few terminal colors"));
+	return -1;
+    }
+
+    if (*hex == '#')
+	++hex;
+
+    for (i = 0; i < 6; ++i) {
+	int order = (i%2 ? 1 : 16);
+
+	if (hex[i] >= '0' && hex[i] <= '9')
+	    *(rgb+i/2) += (hex[i] - '0') * order;
+	else if (hex[i] >= 'A' && hex[i] <= 'F')
+	    *(rgb+i/2) += (hex[i] - 'A' + 10) * order;
+	else if (hex[i] >= 'a' && hex[i] <= 'f')
+	    *(rgb+i/2) += (hex[i] - 'a' + 10) * order;
+	else
+	    break;
+    }
+
+    /* Too few characters, too many, or not all in [0-9A-Fa-f] */
+    if (i != 6 || hex[6] != NUL) {
+	/* Should this get a new error number, or should I reuse E254? */
+	EMSG(_("E805: Bad color spec provided."));
+	return -1;
+    }
+
+    vals[0] = closest_cube_step(colors, rgb[0], FALSE);
+    vals[1] = closest_cube_step(colors, rgb[1], FALSE);
+    vals[2] = closest_cube_step(colors, rgb[2], FALSE);
+
+    if (vals[0] != vals[1] || vals[1] != vals[2])
+    {
+	/*
+	 * The author obviously didn't want a grey, and having #003000 rounded
+	 * to #121212 rather than #005F00 bothers me.  Search again, skipping
+	 * grey-only values.  This extra search just guarantees that unless the
+	 * requested color is sufficiently close to a grey, we will use
+	 * a color, even if it's further away.
+	 */
+	vals[0] = closest_cube_step(colors, rgb[0], TRUE);
+	vals[1] = closest_cube_step(colors, rgb[1], TRUE);
+	vals[2] = closest_cube_step(colors, rgb[2], TRUE);
+    }
+
+    if (vals[0] == vals[1] && vals[1] == vals[2] && vals[0] >= 80)
+	return vals[0];
+    else if (colors == 88)
+	return (vals[0] * 16 + vals[1] * 4 + vals[2] + 16);
+    else
+	return (vals[0] * 36 + vals[1] * 6 + vals[2] + 16);
+}
+
 #if defined(FEAT_GUI) || defined(PROTO)
 /*
  * Free all the highlight group fonts.

Raspunde prin e-mail lui