changeset 29328:60977de70684 v9.0.0007

patch 9.0.0007: no support for double, dotted and dashed underlines Commit: https://github.com/vim/vim/commit/84f546363068e4ddfe14a8a2a2322bb8d3a25417 Author: Bram Moolenaar <Bram@vim.org> Date: Wed Jun 29 18:39:11 2022 +0100 patch 9.0.0007: no support for double, dotted and dashed underlines Problem: No support for double, dotted and dashed underlines. Solution: Add the termcap entries and highlight modes. (closes https://github.com/vim/vim/issues/9553)
author Bram Moolenaar <Bram@vim.org>
date Wed, 29 Jun 2022 19:45:03 +0200
parents 4675995a5b8a
children afd075dd5e04
files runtime/doc/options.txt runtime/doc/syntax.txt runtime/doc/term.txt src/evalfunc.c src/gui.c src/hardcopy.c src/highlight.c src/optiondefs.h src/screen.c src/term.c src/termdefs.h src/testdir/test_highlight.vim src/testdir/test_options.vim src/testdir/test_syn_attr.vim src/version.c src/vim.h
diffstat 16 files changed, 137 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -4211,7 +4211,10 @@ A jump table for the options with a shor
 		b	bold		(termcap entry "md" and "me")
 		s	standout	(termcap entry "so" and "se")
 		u	underline	(termcap entry "us" and "ue")
-		c	undercurl	(termcap entry "Cs" and "Ce")
+		c	undercurl	(termcap entry "Us" and "Ce")
+		2	double underline (termcap entry "Ds" and "Ce")
+		d	dotted underline (termcap entry "ds" and "Ce")
+		=	dashed underline (termcap entry "Ds" and "Ce")
 		t	strikethrough	(termcap entry "Ts" and "Te")
 		n	no highlighting
 		-	no highlighting
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -5002,14 +5002,18 @@ the same syntax file on all terminals, a
 1. highlight arguments for normal terminals
 
 					*bold* *underline* *undercurl*
-					*inverse* *italic* *standout*
-					*nocombine* *strikethrough*
+					*underdouble* *underdotted*
+					*underdashed* *inverse* *italic*
+					*standout* *nocombine* *strikethrough*
 term={attr-list}			*attr-list* *highlight-term* *E418*
 	attr-list is a comma-separated list (without spaces) of the
 	following items (in any order):
 		bold
 		underline
 		undercurl	not always available
+		underdouble	not always available
+		underdotted	not always available
+		underdashed	not always available
 		strikethrough	not always available
 		reverse
 		inverse		same as reverse
@@ -5020,6 +5024,7 @@ term={attr-list}			*attr-list* *highligh
 
 	Note that "bold" can be used here and by using a bold font.  They
 	have the same effect.
+							*underline-codes*
 	"undercurl" is a curly underline.  When "undercurl" is not possible
 	then "underline" is used.  In general "undercurl" and "strikethrough"
 	are only available in the GUI and some terminals.  The color is set
@@ -5028,6 +5033,17 @@ term={attr-list}			*attr-list* *highligh
 	    let &t_Cs = "\e[4:3m"
 	    let &t_Ce = "\e[4:0m"
 
+<	"underdouble" is a double underline, "underdotted" is a dotted
+	underline and "underdashed" is a dashed underline.  These are only
+	supported by some terminals.  If your terminal supports them you may
+	have to specify the codes like this: >
+	    let &t_Us = "\e[4:2m"
+	    let &t_ds = "\e[4:4m"
+	    let &t_Ds = "\e[4:5m"
+<	They are reset with |t_Ce|, the same as curly underline (undercurl).
+	When t_Us, t_ds or t_Ds is not set then underline will be used as a
+	fallback.
+
 
 start={term-list}				*highlight-start* *E422*
 stop={term-list}				*term-list* *highlight-stop*
--- a/runtime/doc/term.txt
+++ b/runtime/doc/term.txt
@@ -372,8 +372,11 @@ OUTPUT CODES						*terminal-output-codes
 
 Added by Vim (there are no standard codes for these):
 	t_AU	set underline color (ANSI)			*t_AU* *'t_AU'*
-	t_Ce	undercurl end					*t_Ce* *'t_Ce'*
-	t_Cs	undercurl mode					*t_Cs* *'t_Cs'*
+	t_Ce	undercurl and underline end			*t_Ce* *'t_Ce'*
+	t_Cs	undercurl (curly underline) mode		*t_Cs* *'t_Cs'*
+	t_Us	double underline mode				*t_Us* *'t_Us'*
+	t_ds	dotted underline mode				*t_ds* *'t_ds'*
+	t_Ds	dashed underline mode				*t_Ds* *'t_Ds'*
 	t_Te	strikethrough end				*t_Te* *'t_Te'*
 	t_Ts	strikethrough mode				*t_Ts* *'t_Ts'*
 	t_IS	set icon text start				*t_IS* *'t_IS'*
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -10104,14 +10104,27 @@ f_synIDattr(typval_T *argvars UNUSED, ty
 		break;
 
 	case 'u':
-		if (TOLOWER_ASC(what[1]) == 'l')	// ul
-		    p = highlight_color(id, what, modec);
-		else if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c')
+		if (STRLEN(what) >= 9)
+		{
+		    if (TOLOWER_ASC(what[5]) == 'l')
 							// underline
-		    p = highlight_has_attr(id, HL_UNDERLINE, modec);
+			p = highlight_has_attr(id, HL_UNDERLINE, modec);
+		    else if (TOLOWER_ASC(what[5]) != 'd')
+							// undercurl
+			p = highlight_has_attr(id, HL_UNDERCURL, modec);
+		    else if (TOLOWER_ASC(what[6]) != 'o')
+							// underdashed
+			p = highlight_has_attr(id, HL_UNDERDASHED, modec);
+		    else if (TOLOWER_ASC(what[7]) == 'u')
+							// underdouble
+			p = highlight_has_attr(id, HL_UNDERDOUBLE, modec);
+		    else
+							// underdotted
+			p = highlight_has_attr(id, HL_UNDERDOTTED, modec);
+		}
 		else
-							// undercurl
-		    p = highlight_has_attr(id, HL_UNDERCURL, modec);
+							// ul
+		    p = highlight_color(id, what, modec);
 		break;
     }
 
--- a/src/gui.c
+++ b/src/gui.c
@@ -2501,6 +2501,8 @@ gui_outstr_nowrap(
     if (hl_mask_todo & HL_UNDERCURL)
 	draw_flags |= DRAW_UNDERC;
 
+    // TODO: HL_UNDERDOUBLE, HL_UNDERDOTTED, HL_UNDERDASHED
+
     // Do we strikethrough the text?
     if (hl_mask_todo & HL_STRIKETHROUGH)
 	draw_flags |= DRAW_STRIKE;
--- a/src/hardcopy.c
+++ b/src/hardcopy.c
@@ -293,6 +293,7 @@ prt_get_attr(
     pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL);
     pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL);
     pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL);
+    // TODO: HL_UNDERDOUBLE, HL_UNDERDOTTED, HL_UNDERDASHED
 
 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
     if (USE_24BIT)
--- a/src/highlight.c
+++ b/src/highlight.c
@@ -25,10 +25,16 @@
  * following names, separated by commas (but no spaces!).
  */
 static char *(hl_name_table[]) =
-    {"bold", "standout", "underline", "undercurl",
-      "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
+    {"bold", "standout", "underline",
+	"undercurl", "underdouble", "underdotted", "underdashed",
+	"italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
 static int hl_attr_table[] =
-    {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
+    {HL_BOLD, HL_STANDOUT, HL_UNDERLINE,
+	HL_UNDERCURL, HL_UNDERDOUBLE, HL_UNDERDOTTED, HL_UNDERDASHED,
+	HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
+// length of all attribute names, plus commas, together (and a bit more)
+#define MAX_ATTR_LEN 120
+
 #define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? (attr_b) : (attr_a)) | (attr_b))
 
 /*
@@ -2963,7 +2969,7 @@ highlight_list_arg(
     char_u	*sarg,
     char	*name)
 {
-    char_u	buf[100];
+    char_u	buf[MAX_ATTR_LEN];
     char_u	*ts;
     int		i;
 
@@ -2984,8 +2990,8 @@ highlight_list_arg(
 		if (iarg & hl_attr_table[i])
 		{
 		    if (buf[0] != NUL)
-			vim_strcat(buf, (char_u *)",", 100);
-		    vim_strcat(buf, (char_u *)hl_name_table[i], 100);
+			vim_strcat(buf, (char_u *)",", MAX_ATTR_LEN);
+		    vim_strcat(buf, (char_u *)hl_name_table[i], MAX_ATTR_LEN);
 		    iarg &= ~hl_attr_table[i];	    // don't want "inverse"
 		}
 	    }
@@ -3287,7 +3293,8 @@ set_hl_attr(
 	at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
 	// Only use the underline/undercurl color when used, it may clear the
 	// background color if not supported.
-	if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
+	if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
+			   | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
 	    at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
 	else
 	    at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
@@ -3801,6 +3808,12 @@ highlight_changed(void)
 				break;
 		    case 'c':	attr |= HL_UNDERCURL;
 				break;
+		    case '2':	attr |= HL_UNDERDOUBLE;
+				break;
+		    case 'd':	attr |= HL_UNDERDOTTED;
+				break;
+		    case '=':	attr |= HL_UNDERDASHED;
+				break;
 		    case 't':	attr |= HL_STRIKETHROUGH;
 				break;
 		    case ':':	++p;		    // highlight group name
@@ -4362,9 +4375,9 @@ hlg_add_or_update(dict_T *dict)
 {
     char_u	*name;
     int		error;
-    char_u	term_attr[80];
-    char_u	cterm_attr[80];
-    char_u	gui_attr[80];
+    char_u	term_attr[MAX_ATTR_LEN];
+    char_u	cterm_attr[MAX_ATTR_LEN];
+    char_u	gui_attr[MAX_ATTR_LEN];
     char_u	*start;
     char_u	*stop;
     char_u	*ctermfg;
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -2893,9 +2893,9 @@ static struct vimoption options[] =
     p_term("t_BD", T_BD)
     p_term("t_cd", T_CD)
     p_term("t_ce", T_CE)
+    p_term("t_Ce", T_UCE)
     p_term("t_cl", T_CL)
     p_term("t_cm", T_CM)
-    p_term("t_Ce", T_UCE)
     p_term("t_Co", T_CCO)
     p_term("t_CS", T_CCS)
     p_term("t_Cs", T_UCS)
@@ -2905,6 +2905,8 @@ static struct vimoption options[] =
     p_term("t_db", T_DB)
     p_term("t_DL", T_CDL)
     p_term("t_dl", T_DL)
+    p_term("t_ds", T_DS)
+    p_term("t_Ds", T_CDS)
     p_term("t_EC", T_CEC)
     p_term("t_EI", T_CEI)
     p_term("t_fs", T_FS)
@@ -2952,6 +2954,7 @@ static struct vimoption options[] =
     p_term("t_u7", T_U7)
     p_term("t_ue", T_UE)
     p_term("t_us", T_US)
+    p_term("t_Us", T_USS)
     p_term("t_ut", T_UT)
     p_term("t_vb", T_VB)
     p_term("t_ve", T_VE)
--- a/src/screen.c
+++ b/src/screen.c
@@ -1855,8 +1855,17 @@ screen_start_highlight(int attr)
 		out_str(T_SO);
 	    if ((attr & HL_UNDERCURL) && *T_UCS != NUL) // undercurl
 		out_str(T_UCS);
-	    if (((attr & HL_UNDERLINE)	    // underline or undercurl
-			|| ((attr & HL_UNDERCURL) && *T_UCS == NUL))
+	    if ((attr & HL_UNDERDOUBLE) && *T_USS != NUL) // double underline
+		out_str(T_USS);
+	    if ((attr & HL_UNDERDOTTED) && *T_DS != NUL) // dotted underline
+		out_str(T_DS);
+	    if ((attr & HL_UNDERDASHED) && *T_CDS != NUL) // dashed underline
+		out_str(T_CDS);
+	    if (((attr & HL_UNDERLINE)	    // underline or undercurl, etc.
+			|| ((attr & HL_UNDERCURL) && *T_UCS == NUL)
+			|| ((attr & HL_UNDERDOUBLE) && *T_USS == NUL)
+			|| ((attr & HL_UNDERDOTTED) && *T_DS == NUL)
+			|| ((attr & HL_UNDERDASHED) && *T_CDS == NUL))
 		    && *T_US != NUL)
 		out_str(T_US);
 	    if ((attr & HL_ITALIC) && *T_CZH != NUL)	// italic
@@ -1951,6 +1960,8 @@ screen_stop_highlight(void)
 	else
 #endif
 	{
+	    int is_under;
+
 	    if (screen_attr > HL_ALL)			// special HL attr.
 	    {
 		attrentry_T *aep;
@@ -2030,15 +2041,16 @@ screen_stop_highlight(void)
 		else
 		    out_str(T_SE);
 	    }
-	    if ((screen_attr & HL_UNDERCURL) && *T_UCE != NUL)
+	    is_under = (screen_attr & (HL_UNDERCURL
+			  | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED));
+	    if (is_under && *T_UCE != NUL)
 	    {
 		if (STRCMP(T_UCE, T_ME) == 0)
 		    do_ME = TRUE;
 		else
 		    out_str(T_UCE);
 	    }
-	    if ((screen_attr & HL_UNDERLINE)
-			    || ((screen_attr & HL_UNDERCURL) && *T_UCE == NUL))
+	    if ((screen_attr & HL_UNDERLINE) || (is_under && *T_UCE == NUL))
 	    {
 		if (STRCMP(T_UE, T_ME) == 0)
 		    do_ME = TRUE;
--- a/src/term.c
+++ b/src/term.c
@@ -1187,6 +1187,9 @@ static struct builtin_term builtin_termc
     {(int)KS_US,	"[US]"},
     {(int)KS_UCE,	"[UCE]"},
     {(int)KS_UCS,	"[UCS]"},
+    {(int)KS_USS,	"[USS]"},
+    {(int)KS_DS,	"[DS]"},
+    {(int)KS_CDS,	"[CDS]"},
     {(int)KS_STE,	"[STE]"},
     {(int)KS_STS,	"[STS]"},
     {(int)KS_MS,	"[MS]"},
@@ -1669,6 +1672,7 @@ get_term_entries(int *height, int *width
 			{KS_MD, "md"}, {KS_SE, "se"}, {KS_SO, "so"},
 			{KS_CZH,"ZH"}, {KS_CZR,"ZR"}, {KS_UE, "ue"},
 			{KS_US, "us"}, {KS_UCE, "Ce"}, {KS_UCS, "Cs"},
+			{KS_USS, "Us"}, {KS_DS, "ds"}, {KS_CDS, "Ds"},
 			{KS_STE,"Te"}, {KS_STS,"Ts"},
 			{KS_CM, "cm"}, {KS_SR, "sr"},
 			{KS_CRI,"RI"}, {KS_VB, "vb"}, {KS_KS, "ks"},
--- a/src/termdefs.h
+++ b/src/termdefs.h
@@ -55,6 +55,9 @@ enum SpecialKey
     KS_US,	// underscore (underline) mode
     KS_UCE,	// exit undercurl mode
     KS_UCS,	// undercurl mode
+    KS_USS,	// double underline mode
+    KS_DS,	// dotted underline mode
+    KS_CDS,	// dashed underline mode
     KS_STE,	// exit strikethrough mode
     KS_STS,	// strikethrough mode
     KS_MS,	// save to move cur in reverse mode
@@ -160,6 +163,9 @@ extern char_u *(term_strings[]);    // c
 #define T_US	(TERM_STR(KS_US))	// underscore (underline) mode
 #define T_UCE	(TERM_STR(KS_UCE))	// exit undercurl mode
 #define T_UCS	(TERM_STR(KS_UCS))	// undercurl mode
+#define T_USS	(TERM_STR(KS_USS))	// double underline mode
+#define T_DS	(TERM_STR(KS_DS))	// dotted underline mode
+#define T_CDS	(TERM_STR(KS_CDS))	// dashed underline mode
 #define T_STE	(TERM_STR(KS_STE))	// exit strikethrough mode
 #define T_STS	(TERM_STR(KS_STS))	// strikethrough mode
 #define T_MS	(TERM_STR(KS_MS))	// save to move cur in reverse mode
--- a/src/testdir/test_highlight.vim
+++ b/src/testdir/test_highlight.vim
@@ -888,8 +888,8 @@ endfunc
 
 " Test for setting various 'term' attributes
 func Test_highlight_term_attr()
-  hi HlGrp3 term=bold,underline,undercurl,strikethrough,reverse,italic,standout
-  call assert_equal('hi HlGrp3          term=bold,standout,underline,undercurl,italic,reverse,strikethrough', HighlightArgs('HlGrp3'))
+  hi HlGrp3 term=bold,underline,undercurl,underdouble,underdotted,underdashed,strikethrough,reverse,italic,standout
+  call assert_equal('hi HlGrp3          term=bold,standout,underline,undercurl,underdouble,underdotted,underdashed,italic,reverse,strikethrough', HighlightArgs('HlGrp3'))
   hi HlGrp3 term=NONE
   call assert_equal('hi HlGrp3          cleared', HighlightArgs('HlGrp3'))
   hi clear
@@ -1174,12 +1174,14 @@ func Test_hlset()
   " Test for setting all the 'term', 'cterm' and 'gui' attributes of a
   " highlight group
   let lines =<< trim END
-    VAR attr = {'bold': v:true, 'underline': v:true, 'undercurl': v:true,
+    VAR attr = {'bold': v:true, 'underline': v:true,
+                \ 'undercurl': v:true, 'underdouble': v:true,
+                \ 'underdotted': v:true, 'underdashed': v:true,
                 \ 'strikethrough': v:true, 'reverse': v:true, 'italic': v:true,
                 \ 'standout': v:true, 'nocombine': v:true}
     call hlset([{'name': 'myhlg2', 'term': attr, 'cterm': attr, 'gui': attr}])
     VAR id2 = hlID('myhlg2')
-    VAR expected = "myhlg2 xxx term=bold,standout,underline,undercurl,italic,reverse,nocombine,strikethrough cterm=bold,standout,underline,undercurl,italic,reverse,nocombine,strikethrough gui=bold,standout,underline,undercurl,italic,reverse,nocombine,strikethrough"
+    VAR expected = "myhlg2 xxx term=bold,standout,underline,undercurl,underdouble,underdotted,underdashed,italic,reverse,nocombine,strikethrough cterm=bold,standout,underline,undercurl,underdouble,underdotted,underdashed,italic,reverse,nocombine,strikethrough gui=bold,standout,underline,undercurl,underdouble,underdotted,underdashed,italic,reverse,nocombine,strikethrough"
     VAR output = execute('highlight myhlg2')
     LET output = output->split("\n")->join()->substitute('\s\+', ' ', 'g')
     call assert_equal(expected, output)
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -955,6 +955,18 @@ func Test_opt_set_keycode()
   set <F9>=xyz
   call assert_equal('xyz', &t_k9)
   set <t_k9>&
+
+  " should we test all of them?
+  set t_Ce=testCe
+  set t_Cs=testCs
+  set t_Us=testUs
+  set t_ds=testds
+  set t_Ds=testDs
+  call assert_equal('testCe', &t_Ce)
+  call assert_equal('testCs', &t_Cs)
+  call assert_equal('testUs', &t_Us)
+  call assert_equal('testds', &t_ds)
+  call assert_equal('testDs', &t_Ds)
 endfunc
 
 " Test for changing options in a sandbox
--- a/src/testdir/test_syn_attr.vim
+++ b/src/testdir/test_syn_attr.vim
@@ -11,10 +11,17 @@ func Test_missing_attr()
   hi Mine term=reverse cterm=inverse
   call assert_equal('1', synIDattr(hlID("Mine"), "reverse", 'term'))
   call assert_equal('1', synIDattr(hlID("Mine"), "inverse", 'cterm'))
+
   hi Mine term=underline cterm=standout gui=undercurl
   call assert_equal('1', synIDattr(hlID("Mine"), "underline", 'term'))
   call assert_equal('1', synIDattr(hlID("Mine"), "standout", 'cterm'))
   call assert_equal('1', synIDattr("Mine"->hlID(), "undercurl", 'gui'))
+
+  hi Mine term=underdouble cterm=underdotted gui=underdashed
+  call assert_equal('1', synIDattr(hlID("Mine"), "underdouble", 'term'))
+  call assert_equal('1', synIDattr(hlID("Mine"), "underdotted", 'cterm'))
+  call assert_equal('1', synIDattr("Mine"->hlID(), "underdashed", 'gui'))
+
   hi Mine gui=strikethrough
   call assert_equal('1', synIDattr(hlID("Mine"), "strikethrough", 'gui'))
   hi Mine term=NONE cterm=NONE gui=NONE
--- a/src/version.c
+++ b/src/version.c
@@ -736,6 +736,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    7,
+/**/
     6,
 /**/
     5,
--- a/src/vim.h
+++ b/src/vim.h
@@ -659,10 +659,13 @@ extern int (*dyn_libintl_wputenv)(const 
 #define HL_ITALIC		0x04
 #define HL_UNDERLINE		0x08
 #define HL_UNDERCURL		0x10
-#define HL_STANDOUT		0x20
-#define HL_NOCOMBINE		0x40
-#define HL_STRIKETHROUGH	0x80
-#define HL_ALL			0xff
+#define HL_UNDERDOUBLE		0x20
+#define HL_UNDERDOTTED		0x40
+#define HL_UNDERDASHED		0x80
+#define HL_STANDOUT		0x100
+#define HL_NOCOMBINE		0x200
+#define HL_STRIKETHROUGH	0x400
+#define HL_ALL			0x7ff
 
 // special attribute addition: Put message in history
 #define MSG_HIST		0x1000