changeset 23825:0bd44e94dd14 v8.2.2454

patch 8.2.2454: leading space can not be made visible Commit: https://github.com/vim/vim/commit/91478ae49a1b2dc1de63821db731a343e855dcc0 Author: Bram Moolenaar <Bram@vim.org> Date: Wed Feb 3 15:58:13 2021 +0100 patch 8.2.2454: leading space can not be made visible Problem: Leading space can not be made visible. Solution: Add "lead:" to 'listchars'. (closes https://github.com/vim/vim/issues/7772)
author Bram Moolenaar <Bram@vim.org>
date Wed, 03 Feb 2021 16:00:07 +0100
parents 8b7e3139961a
children 71d7c2500ee3
files runtime/doc/options.txt src/drawline.c src/globals.h src/message.c src/screen.c src/testdir/test_listchars.vim src/version.c
diffstat 7 files changed, 81 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -4877,6 +4877,10 @@ A jump table for the options with a shor
 							*lcs-space*
 	  space:c	Character to show for a space.  When omitted, spaces
 			are left blank.
+							*lcs-lead*
+	  lead:c	Character to show for leading spaces.  When omitted,
+			leading spaces are blank.  Overrides the "space"
+			setting for leading spaces.
 							*lcs-trail*
 	  trail:c	Character to show for trailing spaces.  When omitted,
 			trailing spaces are blank.  Overrides the "space"
--- a/src/drawline.c
+++ b/src/drawline.c
@@ -339,6 +339,7 @@ win_line(
     int		change_end = -1;	// last col of changed area
 #endif
     colnr_T	trailcol = MAXCOL;	// start of trailing spaces
+    colnr_T	leadcol = 0;		// start of leading spaces
 #ifdef FEAT_LINEBREAK
     int		need_showbreak = FALSE; // overlong line, skipping first x
 					// chars
@@ -734,8 +735,9 @@ win_line(
 
     if (wp->w_p_list)
     {
-	if (lcs_space || lcs_trail || lcs_nbsp)
+	if (lcs_space || lcs_trail || lcs_lead || lcs_nbsp)
 	    extra_check = TRUE;
+
 	// find start of trailing whitespace
 	if (lcs_trail)
 	{
@@ -744,6 +746,19 @@ win_line(
 		--trailcol;
 	    trailcol += (colnr_T) (ptr - line);
 	}
+	// find end of leading whitespace
+	if (lcs_lead)
+	{
+	    leadcol = 0;
+	    while (VIM_ISWHITE(ptr[leadcol]))
+		++leadcol;
+	    if (ptr[leadcol] == NUL)
+		// in a line full of spaces all of them are treated as trailing
+		leadcol = (colnr_T)0;
+	    else
+		// keep track of the first column not filled with spaces
+		leadcol += (colnr_T) (ptr - line) + 1;
+	}
     }
 
     wcr_attr = get_wcr_attr(wp);
@@ -1992,6 +2007,7 @@ win_line(
 			    || (c == ' '
 				&& mb_l == 1
 				&& lcs_space
+				&& ptr - line >= leadcol
 				&& ptr - line <= trailcol)))
 		{
 		    c = (c == ' ') ? lcs_space : lcs_nbsp;
@@ -2012,9 +2028,10 @@ win_line(
 			mb_utf8 = FALSE;
 		}
 
-		if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ')
+		if ((trailcol != MAXCOL && ptr > line + trailcol && c == ' ')
+			|| (leadcol != 0 && ptr < line + leadcol && c == ' '))
 		{
-		    c = lcs_trail;
+		    c = (ptr > line + trailcol) ? lcs_trail : lcs_lead;
 		    if (!attr_pri)
 		    {
 			n_attr = 1;
--- a/src/globals.h
+++ b/src/globals.h
@@ -1352,6 +1352,7 @@ EXTERN int	lcs_tab1 INIT(= NUL);
 EXTERN int	lcs_tab2 INIT(= NUL);
 EXTERN int	lcs_tab3 INIT(= NUL);
 EXTERN int	lcs_trail INIT(= NUL);
+EXTERN int	lcs_lead INIT(= NUL);
 #ifdef FEAT_CONCEAL
 EXTERN int	lcs_conceal INIT(= ' ');
 #endif
--- a/src/message.c
+++ b/src/message.c
@@ -1831,18 +1831,32 @@ msg_prt_line(char_u *s, int list)
     int		n;
     int		attr = 0;
     char_u	*trail = NULL;
+    char_u	*lead = NULL;
     int		l;
     char_u	buf[MB_MAXBYTES + 1];
 
     if (curwin->w_p_list)
 	list = TRUE;
 
-    // find start of trailing whitespace
-    if (list && lcs_trail)
+    if (list)
     {
-	trail = s + STRLEN(s);
-	while (trail > s && VIM_ISWHITE(trail[-1]))
-	    --trail;
+	// find start of trailing whitespace
+	if (lcs_trail)
+	{
+	    trail = s + STRLEN(s);
+	    while (trail > s && VIM_ISWHITE(trail[-1]))
+		--trail;
+	}
+	// find end of leading whitespace
+	if (lcs_lead)
+	{
+	    lead = s;
+	    while (VIM_ISWHITE(lead[0]))
+		lead++;
+	    // in a line full of spaces all of them are treated as trailing
+	    if (*lead == NUL)
+		lead = NULL;
+	}
     }
 
     // output a space for an empty line, otherwise the line will be
@@ -1938,6 +1952,11 @@ msg_prt_line(char_u *s, int list)
 		// the same in plain text.
 		attr = HL_ATTR(HLF_8);
 	    }
+	    else if (c == ' ' && lead != NULL && s <= lead)
+	    {
+		c = lcs_lead;
+		attr = HL_ATTR(HLF_8);
+	    }
 	    else if (c == ' ' && trail != NULL && s > trail)
 	    {
 		c = lcs_trail;
--- a/src/screen.c
+++ b/src/screen.c
@@ -4775,6 +4775,7 @@ set_chars_option(char_u **varp)
 	{&lcs_space,	"space"},
 	{&lcs_tab2,	"tab"},
 	{&lcs_trail,	"trail"},
+	{&lcs_lead,	"lead"},
 #ifdef FEAT_CONCEAL
 	{&lcs_conceal,	"conceal"},
 #else
--- a/src/testdir/test_listchars.vim
+++ b/src/testdir/test_listchars.vim
@@ -110,6 +110,35 @@ func Test_listchars()
 	      \ '.....h>-$',
 	      \ 'iii<<<<><<$', '$'], l)
 
+  " Test lead and trail
+  normal ggdG
+  set listchars&
+  set listchars+=lead:>,trail:<,space:x
+  set list
+
+  call append(0, [
+	      \ '    ffff    ',
+	      \ '          gg',
+	      \ 'h           ',
+	      \ '            ',
+	      \ '    0  0    ',
+	      \ ])
+
+  let expected = [
+	      \ '>>>>ffff<<<<$',
+	      \ '>>>>>>>>>>gg$',
+	      \ 'h<<<<<<<<<<<$',
+	      \ '<<<<<<<<<<<<$',
+	      \ '>>>>0xx0<<<<$',
+              \ '$'
+	      \ ]
+  redraw!
+  for i in range(1, 5)
+    call cursor(i, 1)
+    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
+  endfor
+
+  call assert_equal(expected, split(execute("%list"), "\n"))
 
   " test nbsp
   normal ggdG
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2454,
+/**/
     2453,
 /**/
     2452,