# HG changeset patch # User Bram Moolenaar # Date 1612364407 -3600 # Node ID 0bd44e94dd14c892d260b397c8f55d57f0b49139 # Parent 8b7e3139961aa9ee99e6f98923329ff8dd98fc4d patch 8.2.2454: leading space can not be made visible Commit: https://github.com/vim/vim/commit/91478ae49a1b2dc1de63821db731a343e855dcc0 Author: Bram Moolenaar 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) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- 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" diff --git a/src/drawline.c b/src/drawline.c --- 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; diff --git a/src/globals.h b/src/globals.h --- 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 diff --git a/src/message.c b/src/message.c --- 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; diff --git a/src/screen.c b/src/screen.c --- 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 diff --git a/src/testdir/test_listchars.vim b/src/testdir/test_listchars.vim --- 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 diff --git a/src/version.c b/src/version.c --- 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,