Patch 8.2.2518
Problem:    'listchars' should be window-local.
Solution:   Make 'listchars' global-local. (Yegappan Lakshmanan, Marco Hinz,
            closes #5206, closes #7850)
Files:      runtime/doc/options.txt, src/buffer.c, src/charset.c,
            src/drawline.c, src/drawscreen.c, src/evalfunc.c, src/globals.h,
            src/indent.c, src/message.c, src/misc1.c, src/option.c,
            src/option.h, src/optiondefs.h, src/optionstr.c,
            src/proto/screen.pro, src/screen.c, src/structs.h,
            src/testdir/test_listchars.vim, src/testdir/test_listlbr.vim


*** ../vim-8.2.2517/runtime/doc/options.txt     2021-02-13 18:24:19.322119004 
+0100
--- runtime/doc/options.txt     2021-02-15 20:24:56.191194001 +0100
***************
*** 4850,4856 ****
  
                                                *'listchars'* *'lcs'*
  'listchars' 'lcs'     string  (default "eol:$")
!                       global
        Strings to use in 'list' mode and for the |:list| command.  It is a
        comma separated list of string settings.
                                                        *lcs-eol*
--- 4854,4860 ----
  
                                                *'listchars'* *'lcs'*
  'listchars' 'lcs'     string  (default "eol:$")
!                       global or local to window |global-local|
        Strings to use in 'list' mode and for the |:list| command.  It is a
        comma separated list of string settings.
                                                        *lcs-eol*
***************
*** 4884,4891 ****
                                                        *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"
                        setting for trailing spaces.
--- 4888,4897 ----
                                                        *lcs-lead*
          lead:c        Character to show for leading spaces.  When omitted,
                        leading spaces are blank.  Overrides the "space"
!                       setting for leading spaces.  You can combine it with
!                       "tab:", for example: >
!                               :set listchars+=tab:>-,lead:.
! <                                                     *lcs-trail*
          trail:c       Character to show for trailing spaces.  When omitted,
                        trailing spaces are blank.  Overrides the "space"
                        setting for trailing spaces.
*** ../vim-8.2.2517/src/buffer.c        2021-02-07 12:12:39.373215432 +0100
--- src/buffer.c        2021-02-15 20:24:56.191194001 +0100
***************
*** 4549,4555 ****
        case STL_VIRTCOL_ALT:
            // In list mode virtcol needs to be recomputed
            virtcol = wp->w_virtcol;
!           if (wp->w_p_list && lcs_tab1 == NUL)
            {
                wp->w_p_list = FALSE;
                getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
--- 4549,4555 ----
        case STL_VIRTCOL_ALT:
            // In list mode virtcol needs to be recomputed
            virtcol = wp->w_virtcol;
!           if (wp->w_p_list && wp->w_lcs_chars.tab1 == NUL)
            {
                wp->w_p_list = FALSE;
                getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
*** ../vim-8.2.2517/src/charset.c       2021-01-07 19:36:26.124739771 +0100
--- src/charset.c       2021-02-15 20:24:56.191194001 +0100
***************
*** 753,759 ****
  
  #ifdef FEAT_VARTABS
  # define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
!     if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) \
      { \
        return tabstop_padding(col, (buf)->b_p_ts, (buf)->b_p_vts_array); \
      } \
--- 753,759 ----
  
  #ifdef FEAT_VARTABS
  # define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
!     if (*(p) == TAB && (!(wp)->w_p_list || wp->w_lcs_chars.tab1)) \
      { \
        return tabstop_padding(col, (buf)->b_p_ts, (buf)->b_p_vts_array); \
      } \
***************
*** 761,767 ****
        return ptr2cells(p);
  #else
  # define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
!     if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) \
      { \
        int ts; \
        ts = (buf)->b_p_ts; \
--- 761,767 ----
        return ptr2cells(p);
  #else
  # define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
!     if (*(p) == TAB && (!(wp)->w_p_list || wp->w_lcs_chars.tab1)) \
      { \
        int ts; \
        ts = (buf)->b_p_ts; \
***************
*** 1153,1159 ****
  {
      int               n;
  
!     if (*s == TAB && (!wp->w_p_list || lcs_tab1))
      {
  # ifdef FEAT_VARTABS
        return tabstop_padding(col, wp->w_buffer->b_p_ts,
--- 1153,1159 ----
  {
      int               n;
  
!     if (*s == TAB && (!wp->w_p_list || wp->w_lcs_chars.tab1))
      {
  # ifdef FEAT_VARTABS
        return tabstop_padding(col, wp->w_buffer->b_p_ts,
***************
*** 1248,1254 ****
       * use a simple loop.
       * Also use this when 'list' is set but tabs take their normal size.
       */
!     if ((!wp->w_p_list || lcs_tab1 != NUL)
  #ifdef FEAT_LINEBREAK
            && !wp->w_p_lbr && *get_showbreak_value(wp) == NUL && !wp->w_p_bri
  #endif
--- 1248,1254 ----
       * use a simple loop.
       * Also use this when 'list' is set but tabs take their normal size.
       */
!     if ((!wp->w_p_list || wp->w_lcs_chars.tab1 != NUL)
  #ifdef FEAT_LINEBREAK
            && !wp->w_p_lbr && *get_showbreak_value(wp) == NUL && !wp->w_p_bri
  #endif
*** ../vim-8.2.2517/src/drawline.c      2021-02-10 17:19:52.614643725 +0100
--- src/drawline.c      2021-02-15 20:24:56.191194001 +0100
***************
*** 248,256 ****
      int               c_final = NUL;          // final char, mandatory if set
      int               extra_attr = 0;         // attributes when n_extra != 0
      static char_u *at_end_str = (char_u *)""; // used for p_extra when
!                                          // displaying lcs_eol at end-of-line
!     int               lcs_eol_one = lcs_eol;  // lcs_eol until it's been used
!     int               lcs_prec_todo = lcs_prec;   // lcs_prec until it's been 
used
  
      // saved "extra" items for when draw_state becomes WL_LINE (again)
      int               saved_n_extra = 0;
--- 248,256 ----
      int               c_final = NUL;          // final char, mandatory if set
      int               extra_attr = 0;         // attributes when n_extra != 0
      static char_u *at_end_str = (char_u *)""; // used for p_extra when
!                                       // displaying eol at end-of-line
!     int               lcs_eol_one = wp->w_lcs_chars.eol; // eol until it's 
been used
!     int               lcs_prec_todo = wp->w_lcs_chars.prec; // prec until 
it's been used
  
      // saved "extra" items for when draw_state becomes WL_LINE (again)
      int               saved_n_extra = 0;
***************
*** 735,745 ****
  
      if (wp->w_p_list)
      {
!       if (lcs_space || lcs_trail || lcs_lead || lcs_nbsp)
            extra_check = TRUE;
  
        // find start of trailing whitespace
!       if (lcs_trail)
        {
            trailcol = (colnr_T)STRLEN(ptr);
            while (trailcol > (colnr_T)0 && VIM_ISWHITE(ptr[trailcol - 1]))
--- 735,748 ----
  
      if (wp->w_p_list)
      {
!       if (wp->w_lcs_chars.space
!               || wp->w_lcs_chars.trail
!               || wp->w_lcs_chars.lead
!               || wp->w_lcs_chars.nbsp)
            extra_check = TRUE;
  
        // find start of trailing whitespace
!       if (wp->w_lcs_chars.trail)
        {
            trailcol = (colnr_T)STRLEN(ptr);
            while (trailcol > (colnr_T)0 && VIM_ISWHITE(ptr[trailcol - 1]))
***************
*** 747,753 ****
            trailcol += (colnr_T) (ptr - line);
        }
        // find end of leading whitespace
!       if (lcs_lead)
        {
            leadcol = 0;
            while (VIM_ISWHITE(ptr[leadcol]))
--- 750,756 ----
            trailcol += (colnr_T) (ptr - line);
        }
        // find end of leading whitespace
!       if (wp->w_lcs_chars.lead)
        {
            leadcol = 0;
            while (VIM_ISWHITE(ptr[leadcol]))
***************
*** 2000,2021 ****
                }
  #endif
  
!               // 'list': Change char 160 to lcs_nbsp and space to lcs_space.
!               // But not when the character is followed by a composing
!               // character (use mb_l to check that).
                if (wp->w_p_list
                        && ((((c == 160 && mb_l == 1)
                              || (mb_utf8
                                  && ((mb_c == 160 && mb_l == 2)
                                      || (mb_c == 0x202f && mb_l == 3))))
!                            && lcs_nbsp)
                            || (c == ' '
                                && mb_l == 1
!                               && lcs_space
                                && ptr - line >= leadcol
                                && ptr - line <= trailcol)))
                {
!                   c = (c == ' ') ? lcs_space : lcs_nbsp;
                    if (area_attr == 0 && search_attr == 0)
                    {
                        n_attr = 1;
--- 2003,2025 ----
                }
  #endif
  
!               // 'list': Change char 160 to 'nbsp' and space to 'space'
!               // setting in 'listchars'.  But not when the character is
!               // followed by a composing character (use mb_l to check that).
                if (wp->w_p_list
                        && ((((c == 160 && mb_l == 1)
                              || (mb_utf8
                                  && ((mb_c == 160 && mb_l == 2)
                                      || (mb_c == 0x202f && mb_l == 3))))
!                            && wp->w_lcs_chars.nbsp)
                            || (c == ' '
                                && mb_l == 1
!                               && wp->w_lcs_chars.space
                                && ptr - line >= leadcol
                                && ptr - line <= trailcol)))
                {
!                   c = (c == ' ') ? wp->w_lcs_chars.space :
!                                                       wp->w_lcs_chars.nbsp;
                    if (area_attr == 0 && search_attr == 0)
                    {
                        n_attr = 1;
***************
*** 2036,2042 ****
                if ((trailcol != MAXCOL && ptr > line + trailcol && c == ' ')
                        || (leadcol != 0 && ptr < line + leadcol && c == ' '))
                {
!                   c = (ptr > line + trailcol) ? lcs_trail : lcs_lead;
                    if (!attr_pri)
                    {
                        n_attr = 1;
--- 2040,2047 ----
                if ((trailcol != MAXCOL && ptr > line + trailcol && c == ' ')
                        || (leadcol != 0 && ptr < line + leadcol && c == ' '))
                {
!                   c = (ptr > line + trailcol) ? wp->w_lcs_chars.trail
!                                                       : wp->w_lcs_chars.lead;
                    if (!attr_pri)
                    {
                        n_attr = 1;
***************
*** 2061,2067 ****
                // when getting a character from the file, we may have to
                // turn it into something else on the way to putting it
                // into "ScreenLines".
!               if (c == TAB && (!wp->w_p_list || lcs_tab1))
                {
                    int tab_len = 0;
                    long vcol_adjusted = vcol; // removed showbreak length
--- 2066,2072 ----
                // when getting a character from the file, we may have to
                // turn it into something else on the way to putting it
                // into "ScreenLines".
!               if (c == TAB && (!wp->w_p_list || wp->w_lcs_chars.tab1))
                {
                    int tab_len = 0;
                    long vcol_adjusted = vcol; // removed showbreak length
***************
*** 2101,2118 ****
                            // there are characters to conceal
                            tab_len += vcol_off;
                        // boguscols before FIX_FOR_BOGUSCOLS macro from above
!                       if (wp->w_p_list && lcs_tab1 && old_boguscols > 0
!                                                        && n_extra > tab_len)
                            tab_len += n_extra - tab_len;
  #endif
  
                        // if n_extra > 0, it gives the number of chars, to
                        // use for a tab, else we need to calculate the width
                        // for a tab
!                       len = (tab_len * mb_char2len(lcs_tab2));
                        if (n_extra > 0)
                            len += n_extra - tab_len;
!                       c = lcs_tab1;
                        p = alloc(len + 1);
                        vim_memset(p, ' ', len);
                        p[len] = NUL;
--- 2106,2124 ----
                            // there are characters to conceal
                            tab_len += vcol_off;
                        // boguscols before FIX_FOR_BOGUSCOLS macro from above
!                       if (wp->w_p_list && wp->w_lcs_chars.tab1
!                                                       && old_boguscols > 0
!                                                       && n_extra > tab_len)
                            tab_len += n_extra - tab_len;
  #endif
  
                        // if n_extra > 0, it gives the number of chars, to
                        // use for a tab, else we need to calculate the width
                        // for a tab
!                       len = (tab_len * mb_char2len(wp->w_lcs_chars.tab2));
                        if (n_extra > 0)
                            len += n_extra - tab_len;
!                       c = wp->w_lcs_chars.tab1;
                        p = alloc(len + 1);
                        vim_memset(p, ' ', len);
                        p[len] = NUL;
***************
*** 2120,2126 ****
                        p_extra_free = p;
                        for (i = 0; i < tab_len; i++)
                        {
!                           int lcs = lcs_tab2;
  
                            if (*p == NUL)
                            {
--- 2126,2132 ----
                        p_extra_free = p;
                        for (i = 0; i < tab_len; i++)
                        {
!                           int lcs = wp->w_lcs_chars.tab2;
  
                            if (*p == NUL)
                            {
***************
*** 2128,2137 ****
                                break;
                            }
  
!                           // if lcs_tab3 is given, need to change the char
                            // for tab
!                           if (lcs_tab3 && i == tab_len - 1)
!                               lcs = lcs_tab3;
                            mb_char2bytes(lcs, p);
                            p += mb_char2len(lcs);
                            n_extra += mb_char2len(lcs)
--- 2134,2143 ----
                                break;
                            }
  
!                           // if tab3 is given, need to change the char
                            // for tab
!                           if (wp->w_lcs_chars.tab3 && i == tab_len - 1)
!                               lcs = wp->w_lcs_chars.tab3;
                            mb_char2bytes(lcs, p);
                            p += mb_char2len(lcs);
                            n_extra += mb_char2len(lcs)
***************
*** 2162,2182 ****
                        // correctly set further below (effectively reverts the
                        // FIX_FOR_BOGSUCOLS macro
                        if (n_extra == tab_len + vc_saved && wp->w_p_list
!                                                                 && lcs_tab1)
                            tab_len += vc_saved;
                    }
  #endif
                    mb_utf8 = FALSE;    // don't draw as UTF-8
                    if (wp->w_p_list)
                    {
!                       c = (n_extra == 0 && lcs_tab3) ? lcs_tab3 : lcs_tab1;
  #ifdef FEAT_LINEBREAK
                        if (wp->w_p_lbr)
                            c_extra = NUL; // using p_extra from above
                        else
  #endif
!                           c_extra = lcs_tab2;
!                       c_final = lcs_tab3;
                        n_attr = tab_len + 1;
                        extra_attr = hl_combine_attr(win_attr, HL_ATTR(HLF_8));
                        saved_attr2 = char_attr; // save current attr
--- 2168,2190 ----
                        // correctly set further below (effectively reverts the
                        // FIX_FOR_BOGSUCOLS macro
                        if (n_extra == tab_len + vc_saved && wp->w_p_list
!                                               && wp->w_lcs_chars.tab1)
                            tab_len += vc_saved;
                    }
  #endif
                    mb_utf8 = FALSE;    // don't draw as UTF-8
                    if (wp->w_p_list)
                    {
!                       c = (n_extra == 0 && wp->w_lcs_chars.tab3)
!                                                       ? wp->w_lcs_chars.tab3
!                                                       : wp->w_lcs_chars.tab1;
  #ifdef FEAT_LINEBREAK
                        if (wp->w_p_lbr)
                            c_extra = NUL; // using p_extra from above
                        else
  #endif
!                           c_extra = wp->w_lcs_chars.tab2;
!                       c_final = wp->w_lcs_chars.tab3;
                        n_attr = tab_len + 1;
                        extra_attr = hl_combine_attr(win_attr, HL_ATTR(HLF_8));
                        saved_attr2 = char_attr; // save current attr
***************
*** 2241,2248 ****
                            c_final = NUL;
                        }
                    }
!                   if (wp->w_p_list && lcs_eol > 0)
!                       c = lcs_eol;
                    else
                        c = ' ';
                    lcs_eol_one = -1;
--- 2249,2256 ----
                            c_final = NUL;
                        }
                    }
!                   if (wp->w_p_list && wp->w_lcs_chars.eol > 0)
!                       c = wp->w_lcs_chars.eol;
                    else
                        c = ' ';
                    lcs_eol_one = -1;
***************
*** 2344,2350 ****
                    // don't do search HL for the rest of the line
                    if (line_attr != 0 && char_attr == search_attr
                                        && (did_line_attr > 1
!                                           || (wp->w_p_list && lcs_eol > 0)))
                        char_attr = line_attr;
  # ifdef FEAT_DIFF
                    if (diff_hlf == HLF_TXD)
--- 2352,2359 ----
                    // don't do search HL for the rest of the line
                    if (line_attr != 0 && char_attr == search_attr
                                        && (did_line_attr > 1
!                                           || (wp->w_p_list &&
!                                               wp->w_lcs_chars.eol > 0)))
                        char_attr = line_attr;
  # ifdef FEAT_DIFF
                    if (diff_hlf == HLF_TXD)
***************
*** 2404,2411 ****
                        c = match_conc;
                    else if (syn_get_sub_char() != NUL)
                        c = syn_get_sub_char();
!                   else if (lcs_conceal != NUL)
!                       c = lcs_conceal;
                    else
                        c = ' ';
  
--- 2413,2420 ----
                        c = match_conc;
                    else if (syn_get_sub_char() != NUL)
                        c = syn_get_sub_char();
!                   else if (wp->w_lcs_chars.conceal != NUL)
!                       c = wp->w_lcs_chars.conceal;
                    else
                        c = ' ';
  
***************
*** 2548,2554 ****
                && draw_state > WL_NR
                && c != NUL)
        {
!           c = lcs_prec;
            lcs_prec_todo = NUL;
            if (has_mbyte && (*mb_char2cells)(mb_c) > 1)
            {
--- 2557,2563 ----
                && draw_state > WL_NR
                && c != NUL)
        {
!           c = wp->w_lcs_chars.prec;
            lcs_prec_todo = NUL;
            if (has_mbyte && (*mb_char2cells)(mb_c) > 1)
            {
***************
*** 2594,2600 ****
            // highlight match at end of line. If it's beyond the last
            // char on the screen, just overwrite that one (tricky!)  Not
            // needed when a '$' was displayed for 'list'.
!           if (lcs_eol == lcs_eol_one
                    && ((area_attr != 0 && vcol == fromcol
                            && (VIsual_mode != Ctrl_V
                                || lnum == VIsual.lnum
--- 2603,2609 ----
            // highlight match at end of line. If it's beyond the last
            // char on the screen, just overwrite that one (tricky!)  Not
            // needed when a '$' was displayed for 'list'.
!           if (wp->w_lcs_chars.eol == lcs_eol_one
                    && ((area_attr != 0 && vcol == fromcol
                            && (VIsual_mode != Ctrl_V
                                || lnum == VIsual.lnum
***************
*** 2764,2770 ****
  
        // Show "extends" character from 'listchars' if beyond the line end and
        // 'list' is set.
!       if (lcs_ext != NUL
                && wp->w_p_list
                && !wp->w_p_wrap
  #ifdef FEAT_DIFF
--- 2773,2779 ----
  
        // Show "extends" character from 'listchars' if beyond the line end and
        // 'list' is set.
!       if (wp->w_lcs_chars.ext != NUL
                && wp->w_p_list
                && !wp->w_p_wrap
  #ifdef FEAT_DIFF
***************
*** 2779,2785 ****
                    || (wp->w_p_list && lcs_eol_one > 0)
                    || (n_extra && (c_extra != NUL || *p_extra != NUL))))
        {
!           c = lcs_ext;
            char_attr = hl_combine_attr(win_attr, HL_ATTR(HLF_AT));
            mb_c = c;
            if (enc_utf8 && utf_char2len(c) > 1)
--- 2788,2794 ----
                    || (wp->w_p_list && lcs_eol_one > 0)
                    || (n_extra && (c_extra != NUL || *p_extra != NUL))))
        {
!           c = wp->w_lcs_chars.ext;
            char_attr = hl_combine_attr(win_attr, HL_ATTR(HLF_AT));
            mb_c = c;
            if (enc_utf8 && utf_char2len(c) > 1)
***************
*** 3036,3042 ****
  #ifdef FEAT_DIFF
                    || filler_todo > 0
  #endif
!                   || (wp->w_p_list && lcs_eol != NUL && p_extra != at_end_str)
                    || (n_extra != 0 && (c_extra != NUL || *p_extra != NUL)))
                )
        {
--- 3045,3052 ----
  #ifdef FEAT_DIFF
                    || filler_todo > 0
  #endif
!                   || (wp->w_p_list && wp->w_lcs_chars.eol != NUL
!                                               && p_extra != at_end_str)
                    || (n_extra != 0 && (c_extra != NUL || *p_extra != NUL)))
                )
        {
***************
*** 3161,3167 ****
  #endif
                saved_char_attr = 0;
            n_extra = 0;
!           lcs_prec_todo = lcs_prec;
  #ifdef FEAT_LINEBREAK
  # ifdef FEAT_DIFF
            if (filler_todo <= 0)
--- 3171,3177 ----
  #endif
                saved_char_attr = 0;
            n_extra = 0;
!           lcs_prec_todo = wp->w_lcs_chars.prec;
  #ifdef FEAT_LINEBREAK
  # ifdef FEAT_DIFF
            if (filler_todo <= 0)
*** ../vim-8.2.2517/src/drawscreen.c    2021-02-13 18:24:19.326118995 +0100
--- src/drawscreen.c    2021-02-15 20:24:56.191194001 +0100
***************
*** 696,702 ****
  
        // In list mode virtcol needs to be recomputed
        virtcol = wp->w_virtcol;
!       if (wp->w_p_list && lcs_tab1 == NUL)
        {
            wp->w_p_list = FALSE;
            getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
--- 696,702 ----
  
        // In list mode virtcol needs to be recomputed
        virtcol = wp->w_virtcol;
!       if (wp->w_p_list && wp->w_lcs_chars.tab1 == NUL)
        {
            wp->w_p_list = FALSE;
            getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
*** ../vim-8.2.2517/src/evalfunc.c      2021-02-10 22:23:36.398613504 +0100
--- src/evalfunc.c      2021-02-15 20:24:56.191194001 +0100
***************
*** 9736,9742 ****
            {
                cchar = syn_get_sub_char();
                if (cchar == NUL && curwin->w_p_cole == 1)
!                   cchar = (lcs_conceal == NUL) ? ' ' : lcs_conceal;
                if (cchar != NUL)
                {
                    if (has_mbyte)
--- 9736,9743 ----
            {
                cchar = syn_get_sub_char();
                if (cchar == NUL && curwin->w_p_cole == 1)
!                   cchar = (curwin->w_lcs_chars.conceal == NUL) ? ' '
!                                       : curwin->w_lcs_chars.conceal;
                if (cchar != NUL)
                {
                    if (has_mbyte)
*** ../vim-8.2.2517/src/globals.h       2021-02-13 18:24:19.326118995 +0100
--- src/globals.h       2021-02-15 20:24:56.195193992 +0100
***************
*** 1342,1362 ****
  // directory is not a local directory, globaldir is NULL.
  EXTERN char_u *globaldir INIT(= NULL);
  
- // Characters from 'listchars' option
- EXTERN int    lcs_eol INIT(= '$');
- EXTERN int    lcs_ext INIT(= NUL);
- EXTERN int    lcs_prec INIT(= NUL);
- EXTERN int    lcs_nbsp INIT(= NUL);
- EXTERN int    lcs_space INIT(= NUL);
- 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
- 
  // Characters from 'fillchars' option
  EXTERN int    fill_stl INIT(= ' ');
  EXTERN int    fill_stlnc INIT(= ' ');
--- 1342,1347 ----
*** ../vim-8.2.2517/src/indent.c        2021-02-03 19:44:20.756793885 +0100
--- src/indent.c        2021-02-15 20:24:56.195193992 +0100
***************
*** 432,438 ****
      {
        if (*ptr == TAB)
        {
!           if (!list || lcs_tab1)    // count a tab for what it is worth
                count += ts - (count % ts);
            else
                // In list mode, when tab is not set, count screen char width
--- 432,439 ----
      {
        if (*ptr == TAB)
        {
!           if (!list || curwin->w_lcs_chars.tab1)
!               // count a tab for what it is worth
                count += ts - (count % ts);
            else
                // In list mode, when tab is not set, count screen char width
***************
*** 462,468 ****
      {
        if (*ptr == TAB)    // count a tab for what it is worth
        {
!           if (!list || lcs_tab1)
                count += tabstop_padding(count, ts, vts);
            else
                // In list mode, when tab is not set, count screen char width
--- 463,469 ----
      {
        if (*ptr == TAB)    // count a tab for what it is worth
        {
!           if (!list || curwin->w_lcs_chars.tab1)
                count += tabstop_padding(count, ts, vts);
            else
                // In list mode, when tab is not set, count screen char width
*** ../vim-8.2.2517/src/message.c       2021-02-14 15:37:26.953614303 +0100
--- src/message.c       2021-02-15 20:24:56.195193992 +0100
***************
*** 1848,1861 ****
      if (list)
      {
        // 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]))
--- 1848,1861 ----
      if (list)
      {
        // find start of trailing whitespace
!       if (curwin->w_lcs_chars.trail)
        {
            trail = s + STRLEN(s);
            while (trail > s && VIM_ISWHITE(trail[-1]))
                --trail;
        }
        // find end of leading whitespace
!       if (curwin->w_lcs_chars.lead)
        {
            lead = s;
            while (VIM_ISWHITE(lead[0]))
***************
*** 1868,1874 ****
  
      // output a space for an empty line, otherwise the line will be
      // overwritten
!     if (*s == NUL && !(list && lcs_eol != NUL))
        msg_putchar(' ');
  
      while (!got_int)
--- 1868,1874 ----
  
      // output a space for an empty line, otherwise the line will be
      // overwritten
!     if (*s == NUL && !(list && curwin->w_lcs_chars.eol != NUL))
        msg_putchar(' ');
  
      while (!got_int)
***************
*** 1890,1900 ****
            {
                STRCPY(buf, "?");
            }
!           else if (lcs_nbsp != NUL && list
                    && (mb_ptr2char(s) == 160
                        || mb_ptr2char(s) == 0x202f))
            {
!               mb_char2bytes(lcs_nbsp, buf);
                buf[(*mb_ptr2len)(buf)] = NUL;
            }
            else
--- 1890,1900 ----
            {
                STRCPY(buf, "?");
            }
!           else if (curwin->w_lcs_chars.nbsp != NUL && list
                    && (mb_ptr2char(s) == 160
                        || mb_ptr2char(s) == 0x202f))
            {
!               mb_char2bytes(curwin->w_lcs_chars.nbsp, buf);
                buf[(*mb_ptr2len)(buf)] = NUL;
            }
            else
***************
*** 1910,1916 ****
        {
            attr = 0;
            c = *s++;
!           if (c == TAB && (!list || lcs_tab1))
            {
                // tab amount depends on current column
  #ifdef FEAT_VARTABS
--- 1910,1916 ----
        {
            attr = 0;
            c = *s++;
!           if (c == TAB && (!list || curwin->w_lcs_chars.tab1))
            {
                // tab amount depends on current column
  #ifdef FEAT_VARTABS
***************
*** 1927,1950 ****
                }
                else
                {
!                   c = (n_extra == 0 && lcs_tab3) ? lcs_tab3 : lcs_tab1;
!                   c_extra = lcs_tab2;
!                   c_final = lcs_tab3;
                    attr = HL_ATTR(HLF_8);
                }
            }
!           else if (c == 160 && list && lcs_nbsp != NUL)
            {
!               c = lcs_nbsp;
                attr = HL_ATTR(HLF_8);
            }
!           else if (c == NUL && list && lcs_eol != NUL)
            {
                p_extra = (char_u *)"";
                c_extra = NUL;
                c_final = NUL;
                n_extra = 1;
!               c = lcs_eol;
                attr = HL_ATTR(HLF_AT);
                --s;
            }
--- 1927,1952 ----
                }
                else
                {
!                   c = (n_extra == 0 && curwin->w_lcs_chars.tab3)
!                                               ? curwin->w_lcs_chars.tab3
!                                               : curwin->w_lcs_chars.tab1;
!                   c_extra = curwin->w_lcs_chars.tab2;
!                   c_final = curwin->w_lcs_chars.tab3;
                    attr = HL_ATTR(HLF_8);
                }
            }
!           else if (c == 160 && list && curwin->w_lcs_chars.nbsp != NUL)
            {
!               c = curwin->w_lcs_chars.nbsp;
                attr = HL_ATTR(HLF_8);
            }
!           else if (c == NUL && list && curwin->w_lcs_chars.eol != NUL)
            {
                p_extra = (char_u *)"";
                c_extra = NUL;
                c_final = NUL;
                n_extra = 1;
!               c = curwin->w_lcs_chars.eol;
                attr = HL_ATTR(HLF_AT);
                --s;
            }
***************
*** 1961,1977 ****
            }
            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;
                attr = HL_ATTR(HLF_8);
            }
!           else if (c == ' ' && list && lcs_space != NUL)
            {
!               c = lcs_space;
                attr = HL_ATTR(HLF_8);
            }
        }
--- 1963,1979 ----
            }
            else if (c == ' ' && lead != NULL && s <= lead)
            {
!               c = curwin->w_lcs_chars.lead;
                attr = HL_ATTR(HLF_8);
            }
            else if (c == ' ' && trail != NULL && s > trail)
            {
!               c = curwin->w_lcs_chars.trail;
                attr = HL_ATTR(HLF_8);
            }
!           else if (c == ' ' && list && curwin->w_lcs_chars.space != NUL)
            {
!               c = curwin->w_lcs_chars.space;
                attr = HL_ATTR(HLF_8);
            }
        }
*** ../vim-8.2.2517/src/misc1.c 2020-10-28 20:19:56.372057081 +0100
--- src/misc1.c 2021-02-15 20:24:56.195193992 +0100
***************
*** 403,409 ****
       * If list mode is on, then the '$' at the end of the line may take up one
       * extra column.
       */
!     if (wp->w_p_list && lcs_eol != NUL)
        col += 1;
  
      /*
--- 403,409 ----
       * If list mode is on, then the '$' at the end of the line may take up one
       * extra column.
       */
!     if (wp->w_p_list && wp->w_lcs_chars.eol != NUL)
        col += 1;
  
      /*
***************
*** 460,466 ****
       * from one screen line to the next (when 'columns' is not a multiple of
       * 'ts') -- webb.
       */
!     if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1))
        col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL) - 1;
  
      /*
--- 460,467 ----
       * from one screen line to the next (when 'columns' is not a multiple of
       * 'ts') -- webb.
       */
!     if (*s == TAB && (State & NORMAL) && (!wp->w_p_list ||
!                                                       wp->w_lcs_chars.tab1))
        col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL) - 1;
  
      /*
*** ../vim-8.2.2517/src/option.c        2021-02-02 21:09:57.962971282 +0100
--- src/option.c        2021-02-15 20:24:56.195193992 +0100
***************
*** 2337,2345 ****
      // Parse default for 'wildmode'
      check_opt_wim();
  
!     (void)set_chars_option(&p_lcs);
      // Parse default for 'fillchars'.
!     (void)set_chars_option(&p_fcs);
  
  #ifdef FEAT_CLIPBOARD
      // Parse default for 'clipboard'
--- 2337,2347 ----
      // Parse default for 'wildmode'
      check_opt_wim();
  
!     // Parse default for 'listchars'.
!     (void)set_chars_option(curwin, &curwin->w_p_lcs);
! 
      // Parse default for 'fillchars'.
!     (void)set_chars_option(curwin, &p_fcs);
  
  #ifdef FEAT_CLIPBOARD
      // Parse default for 'clipboard'
***************
*** 5063,5068 ****
--- 5065,5075 ----
        case PV_MENC:
            clear_string_option(&buf->b_p_menc);
            break;
+       case PV_LCS:
+           clear_string_option(&((win_T *)from)->w_p_lcs);
+           set_chars_option((win_T *)from, &((win_T *)from)->w_p_lcs);
+           redraw_later(NOT_VALID);
+           break;
      }
  }
  #endif
***************
*** 5121,5126 ****
--- 5128,5135 ----
  #endif
            case PV_BKC:  return (char_u *)&(curbuf->b_p_bkc);
            case PV_MENC: return (char_u *)&(curbuf->b_p_menc);
+           case PV_LCS: return (char_u *)&(curwin->w_p_lcs);
+ 
        }
        return NULL; // "cannot happen"
      }
***************
*** 5218,5223 ****
--- 5227,5234 ----
        case PV_ARAB:   return (char_u *)&(curwin->w_p_arab);
  #endif
        case PV_LIST:   return (char_u *)&(curwin->w_p_list);
+       case PV_LCS:    return *curwin->w_p_lcs != NUL
+                                   ? (char_u *)&(curwin->w_p_lcs) : p->var;
  #ifdef FEAT_SPELL
        case PV_SPELL:  return (char_u *)&(curwin->w_p_spell);
  #endif
***************
*** 5445,5450 ****
--- 5456,5462 ----
      fill_culopt_flags(NULL, wp);
      check_colorcolumn(wp);
  #endif
+     set_chars_option(wp, &wp->w_p_lcs);
  }
  
  /*
***************
*** 5460,5465 ****
--- 5472,5478 ----
      to->wo_arab = from->wo_arab;
  #endif
      to->wo_list = from->wo_list;
+     to->wo_lcs = vim_strsave(from->wo_lcs);
      to->wo_nu = from->wo_nu;
      to->wo_rnu = from->wo_rnu;
  #ifdef FEAT_LINEBREAK
***************
*** 5594,5599 ****
--- 5607,5613 ----
      check_string_option(&wop->wo_briopt);
  #endif
      check_string_option(&wop->wo_wcr);
+     check_string_option(&wop->wo_lcs);
  }
  
  /*
***************
*** 5639,5644 ****
--- 5653,5659 ----
      clear_string_option(&wop->wo_twk);
      clear_string_option(&wop->wo_tws);
  #endif
+     clear_string_option(&wop->wo_lcs);
  }
  
  #ifdef FEAT_EVAL
*** ../vim-8.2.2517/src/option.h        2020-12-31 17:40:57.532087880 +0100
--- src/option.h        2021-02-15 20:24:56.195193992 +0100
***************
*** 1231,1236 ****
--- 1231,1237 ----
  enum
  {
      WV_LIST = 0
+     , WV_LCS
  #ifdef FEAT_ARABIC
      , WV_ARAB
  #endif
*** ../vim-8.2.2517/src/optiondefs.h    2021-02-13 18:24:19.326118995 +0100
--- src/optiondefs.h    2021-02-15 20:24:56.195193992 +0100
***************
*** 185,190 ****
--- 185,191 ----
  #ifdef FEAT_LINEBREAK
  # define PV_LBR               OPT_WIN(WV_LBR)
  #endif
+ #define PV_LCS                OPT_BOTH(OPT_WIN(WV_LCS))
  #define PV_NU         OPT_WIN(WV_NU)
  #define PV_RNU                OPT_WIN(WV_RNU)
  #ifdef FEAT_LINEBREAK
***************
*** 1598,1604 ****
                            (char_u *)VAR_WIN, PV_LIST,
                            {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
      {"listchars",   "lcs",  P_STRING|P_VI_DEF|P_RALL|P_ONECOMMA|P_NODUP,
!                           (char_u *)&p_lcs, PV_NONE,
                            {(char_u *)"eol:$", (char_u *)0L} SCTX_INIT},
      {"loadplugins", "lpl",  P_BOOL|P_VI_DEF,
                            (char_u *)&p_lpl, PV_NONE,
--- 1599,1605 ----
                            (char_u *)VAR_WIN, PV_LIST,
                            {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
      {"listchars",   "lcs",  P_STRING|P_VI_DEF|P_RALL|P_ONECOMMA|P_NODUP,
!                           (char_u *)&p_lcs, PV_LCS,
                            {(char_u *)"eol:$", (char_u *)0L} SCTX_INIT},
      {"loadplugins", "lpl",  P_BOOL|P_VI_DEF,
                            (char_u *)&p_lpl, PV_NONE,
*** ../vim-8.2.2517/src/optionstr.c     2021-02-01 18:39:43.327401074 +0100
--- src/optionstr.c     2021-02-15 20:32:06.810198611 +0100
***************
*** 862,871 ****
      {
        if (check_opt_strings(p_ambw, p_ambw_values, FALSE) != OK)
            errmsg = e_invarg;
!       else if (set_chars_option(&p_lcs) != NULL)
!           errmsg = _("E834: Conflicts with value of 'listchars'");
!       else if (set_chars_option(&p_fcs) != NULL)
            errmsg = _("E835: Conflicts with value of 'fillchars'");
      }
  
      // 'background'
--- 862,885 ----
      {
        if (check_opt_strings(p_ambw, p_ambw_values, FALSE) != OK)
            errmsg = e_invarg;
!       else if (set_chars_option(curwin, &p_fcs) != NULL)
            errmsg = _("E835: Conflicts with value of 'fillchars'");
+       else
+       {
+           tabpage_T   *tp;
+           win_T       *wp;
+ 
+           FOR_ALL_TAB_WINDOWS(tp, wp)
+           {
+               if (set_chars_option(wp, &wp->w_p_lcs) != NULL)
+               {
+                   errmsg = _("E834: Conflicts with value of 'listchars'");
+                   goto ambw_end;
+               }
+           }
+       }
+ ambw_end:
+       {}
      }
  
      // 'background'
***************
*** 1292,1307 ****
        }
      }
  
!     // 'listchars'
      else if (varp == &p_lcs)
      {
!       errmsg = set_chars_option(varp);
      }
  
      // 'fillchars'
      else if (varp == &p_fcs)
      {
!       errmsg = set_chars_option(varp);
      }
  
  #ifdef FEAT_CMDWIN
--- 1306,1342 ----
        }
      }
  
!     // global 'listchars'
      else if (varp == &p_lcs)
      {
!       errmsg = set_chars_option(curwin, varp);
!       if (errmsg == NULL)
!       {
!           tabpage_T   *tp;
!           win_T               *wp;
! 
!           // The current window is set to use the global 'listchars' value.
!           // So clear the window-local value.
!           if (!(opt_flags & OPT_GLOBAL))
!               clear_string_option(&curwin->w_p_lcs);
!           FOR_ALL_TAB_WINDOWS(tp, wp)
!           {
!               errmsg = set_chars_option(wp, &wp->w_p_lcs);
!               if (errmsg)
!                   break;
!           }
!           redraw_all_later(NOT_VALID);
!       }
      }
  
+     // local 'listchars'
+     else if (varp == &curwin->w_p_lcs)
+       errmsg = set_chars_option(curwin, varp);
+ 
      // 'fillchars'
      else if (varp == &p_fcs)
      {
!       errmsg = set_chars_option(curwin, varp);
      }
  
  #ifdef FEAT_CMDWIN
*** ../vim-8.2.2517/src/proto/screen.pro        2020-07-08 23:09:24.599921761 
+0200
--- src/proto/screen.pro        2021-02-15 20:24:56.195193992 +0100
***************
*** 55,59 ****
  int number_width(win_T *wp);
  int screen_screencol(void);
  int screen_screenrow(void);
! char *set_chars_option(char_u **varp);
  /* vim: set ft=c : */
--- 55,59 ----
  int number_width(win_T *wp);
  int screen_screencol(void);
  int screen_screenrow(void);
! char *set_chars_option(win_T *wp, char_u **varp);
  /* vim: set ft=c : */
*** ../vim-8.2.2517/src/screen.c        2021-02-13 18:24:19.326118995 +0100
--- src/screen.c        2021-02-15 20:24:56.199193983 +0100
***************
*** 4745,4754 ****
  
  /*
   * Handle setting 'listchars' or 'fillchars'.
   * Returns error message, NULL if it's OK.
   */
      char *
! set_chars_option(char_u **varp)
  {
      int               round, i, len, entries;
      char_u    *p, *s;
--- 4745,4755 ----
  
  /*
   * Handle setting 'listchars' or 'fillchars'.
+  * Assume monocell characters.
   * Returns error message, NULL if it's OK.
   */
      char *
! set_chars_option(win_T *wp, char_u **varp)
  {
      int               round, i, len, entries;
      char_u    *p, *s;
***************
*** 4767,4794 ****
        {&fill_diff,    "diff"},
        {&fill_eob,     "eob"},
      };
!     static struct charstab lcstab[] =
      {
!       {&lcs_eol,      "eol"},
!       {&lcs_ext,      "extends"},
!       {&lcs_nbsp,     "nbsp"},
!       {&lcs_prec,     "precedes"},
!       {&lcs_space,    "space"},
!       {&lcs_tab2,     "tab"},
!       {&lcs_trail,    "trail"},
!       {&lcs_lead,     "lead"},
  #ifdef FEAT_CONCEAL
!       {&lcs_conceal,  "conceal"},
  #else
        {NULL,          "conceal"},
  #endif
      };
      struct charstab *tab;
  
!     if (varp == &p_lcs)
      {
        tab = lcstab;
        entries = sizeof(lcstab) / sizeof(struct charstab);
      }
      else
      {
--- 4768,4797 ----
        {&fill_diff,    "diff"},
        {&fill_eob,     "eob"},
      };
!     struct charstab lcstab[] =
      {
!       {&wp->w_lcs_chars.eol,  "eol"},
!       {&wp->w_lcs_chars.ext,  "extends"},
!       {&wp->w_lcs_chars.nbsp, "nbsp"},
!       {&wp->w_lcs_chars.prec, "precedes"},
!       {&wp->w_lcs_chars.space,"space"},
!       {&wp->w_lcs_chars.tab2, "tab"},
!       {&wp->w_lcs_chars.trail,"trail"},
!       {&wp->w_lcs_chars.lead, "lead"},
  #ifdef FEAT_CONCEAL
!       {&wp->w_lcs_chars.conceal,      "conceal"},
  #else
        {NULL,          "conceal"},
  #endif
      };
      struct charstab *tab;
  
!     if (varp == &p_lcs || varp == &wp->w_p_lcs)
      {
        tab = lcstab;
        entries = sizeof(lcstab) / sizeof(struct charstab);
+       if (varp == &wp->w_p_lcs && wp->w_p_lcs[0] == NUL)
+           varp = &p_lcs;
      }
      else
      {
***************
*** 4805,4816 ****
            // 'fillchars', NUL for 'listchars'
            for (i = 0; i < entries; ++i)
                if (tab[i].cp != NULL)
!                   *(tab[i].cp) = (varp == &p_lcs ? NUL : ' ');
  
!           if (varp == &p_lcs)
            {
!               lcs_tab1 = NUL;
!               lcs_tab3 = NUL;
            }
            else
            {
--- 4808,4820 ----
            // 'fillchars', NUL for 'listchars'
            for (i = 0; i < entries; ++i)
                if (tab[i].cp != NULL)
!                   *(tab[i].cp) =
!                       ((varp == &p_lcs || varp == &wp->w_p_lcs) ? NUL : ' ');
  
!           if (varp == &p_lcs || varp == &wp->w_p_lcs)
            {
!               wp->w_lcs_chars.tab1 = NUL;
!               wp->w_lcs_chars.tab3 = NUL;
            }
            else
            {
***************
*** 4833,4839 ****
                    c1 = mb_ptr2char_adv(&s);
                    if (mb_char2cells(c1) > 1)
                        continue;
!                   if (tab[i].cp == &lcs_tab2)
                    {
                        if (*s == NUL)
                            continue;
--- 4837,4843 ----
                    c1 = mb_ptr2char_adv(&s);
                    if (mb_char2cells(c1) > 1)
                        continue;
!                   if (tab[i].cp == &wp->w_lcs_chars.tab2)
                    {
                        if (*s == NUL)
                            continue;
***************
*** 4852,4862 ****
                    {
                        if (round)
                        {
!                           if (tab[i].cp == &lcs_tab2)
                            {
!                               lcs_tab1 = c1;
!                               lcs_tab2 = c2;
!                               lcs_tab3 = c3;
                            }
                            else if (tab[i].cp != NULL)
                                *(tab[i].cp) = c1;
--- 4856,4866 ----
                    {
                        if (round)
                        {
!                           if (tab[i].cp == &wp->w_lcs_chars.tab2)
                            {
!                               wp->w_lcs_chars.tab1 = c1;
!                               wp->w_lcs_chars.tab2 = c2;
!                               wp->w_lcs_chars.tab3 = c3;
                            }
                            else if (tab[i].cp != NULL)
                                *(tab[i].cp) = c1;
*** ../vim-8.2.2517/src/structs.h       2021-02-14 12:57:32.552655477 +0100
--- src/structs.h       2021-02-15 20:24:56.199193983 +0100
***************
*** 225,230 ****
--- 225,232 ----
  #endif
      int               wo_list;
  #define w_p_list w_onebuf_opt.wo_list // 'list'
+     char_u    *wo_lcs;
+ #define w_p_lcs w_onebuf_opt.wo_lcs   // 'listchars'
      int               wo_nu;
  #define w_p_nu w_onebuf_opt.wo_nu     // 'number'
      int               wo_rnu;
***************
*** 3333,3338 ****
--- 3335,3360 ----
  #endif
  
  /*
+  * Characters from the 'listchars' option
+  */
+ typedef struct
+ {
+     int               eol;
+     int               ext;
+     int               prec;
+     int               nbsp;
+     int               space;
+     int               tab1;
+     int               tab2;
+     int               tab3;
+     int               trail;
+     int               lead;
+ #ifdef FEAT_CONCEAL
+     int               conceal;
+ #endif
+ } lcs_chars_T;
+ 
+ /*
   * Structure which contains all information that belongs to a window
   *
   * All row numbers are relative to the start of the window, except w_winrow.
***************
*** 3380,3385 ****
--- 3402,3409 ----
      colnr_T   w_old_visual_col;   // last known start of visual part
      colnr_T   w_old_curswant;     // last known value of Curswant
  
+     lcs_chars_T       w_lcs_chars;        // 'listchars' characters
+ 
      /*
       * "w_topline", "w_leftcol" and "w_skipcol" specify the offsets for
       * displaying the buffer.
*** ../vim-8.2.2517/src/testdir/test_listchars.vim      2021-02-03 
15:58:09.088690884 +0100
--- src/testdir/test_listchars.vim      2021-02-15 20:24:56.199193983 +0100
***************
*** 234,237 ****
--- 234,343 ----
    set listchars& ff&
  endfunction
  
+ " Check for the value of the 'listchars' option
+ func s:CheckListCharsValue(expected)
+   call assert_equal(a:expected, &listchars)
+   call assert_equal(a:expected, getwinvar(0, '&listchars'))
+ endfunc
+ 
+ " Test for using a window local value for 'listchars'
+ func Test_listchars_window_local()
+   %bw!
+   set list listchars&
+   new
+   " set a local value for 'listchars'
+   setlocal listchars=tab:+-,eol:#
+   call s:CheckListCharsValue('tab:+-,eol:#')
+   " When local value is reset, global value should be used
+   setlocal listchars=
+   call s:CheckListCharsValue('eol:$')
+   " Use 'setlocal <' to copy global value
+   setlocal listchars=space:.,extends:>
+   setlocal listchars<
+   call s:CheckListCharsValue('eol:$')
+   " Use 'set <' to copy global value
+   setlocal listchars=space:.,extends:>
+   set listchars<
+   call s:CheckListCharsValue('eol:$')
+   " Changing global setting should not change the local setting
+   setlocal listchars=space:.,extends:>
+   setglobal listchars=tab:+-,eol:#
+   call s:CheckListCharsValue('space:.,extends:>')
+   " when split opening a new window, local value should be copied
+   split
+   call s:CheckListCharsValue('space:.,extends:>')
+   " clearing local value in one window should not change the other window
+   set listchars&
+   call s:CheckListCharsValue('eol:$')
+   close
+   call s:CheckListCharsValue('space:.,extends:>')
+ 
+   " use different values for 'listchars' items in two different windows
+   call setline(1, ["\t  one  two  "])
+   setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
+   split
+   setlocal listchars=tab:[.],lead:#,space:_,trail:.,eol:&
+   split
+   set listchars=tab:+-+,lead:^,space:>,trail:<,eol:%
+   call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
+   close
+   call assert_equal(['[......]##one__two..&'], ScreenLines(1, virtcol('$')))
+   close
+   call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
+   " changing the global setting should not change the local value
+   setglobal listchars=tab:[.],lead:#,space:_,trail:.,eol:&
+   call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
+   set listchars<
+   call assert_equal(['[......]##one__two..&'], ScreenLines(1, virtcol('$')))
+ 
+   " Using setglobal in a window with local setting should not affect the
+   " window. But should impact other windows using the global setting.
+   enew! | only
+   call setline(1, ["\t  one  two  "])
+   set listchars=tab:[.],lead:#,space:_,trail:.,eol:&
+   split
+   setlocal listchars=tab:+-+,lead:^,space:>,trail:<,eol:%
+   split
+   setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
+   setglobal listchars=tab:{.},lead:-,space:=,trail:#,eol:$
+   call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
+   close
+   call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
+   close
+   call assert_equal(['{......}--one==two##$'], ScreenLines(1, virtcol('$')))
+ 
+   " Setting the global setting to the default value should not impact a window
+   " using a local setting
+   split
+   setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
+   setglobal listchars&vim
+   call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
+   close
+   call assert_equal(['^I  one  two  $'], ScreenLines(1, virtcol('$')))
+ 
+   " Setting the local setting to the default value should not impact a window
+   " using a global setting
+   set listchars=tab:{.},lead:-,space:=,trail:#,eol:$
+   split
+   setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
+   call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
+   setlocal listchars&vim
+   call assert_equal(['^I  one  two  $'], ScreenLines(1, virtcol('$')))
+   close
+   call assert_equal(['{......}--one==two##$'], ScreenLines(1, virtcol('$')))
+ 
+   " Using set in a window with a local setting should change it to use the
+   " global setting and also impact other windows using the global setting
+   split
+   setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
+   call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
+   set listchars=tab:+-+,lead:^,space:>,trail:<,eol:%
+   call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
+   close
+   call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
+ 
+   %bw!
+   set list& listchars&
+ endfunc
+ 
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.2.2517/src/testdir/test_listlbr.vim        2020-08-12 
18:50:31.879655802 +0200
--- src/testdir/test_listlbr.vim        2021-02-15 20:24:56.199193983 +0100
***************
*** 43,48 ****
--- 43,49 ----
  endfunc
  
  func Test_linebreak_with_list()
+   set listchars=
    call s:test_windows('setl ts=4 sbr=+ list listchars=')
    call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ")
    let lines = s:screen_lines([1, 4], winwidth(0))
***************
*** 54,59 ****
--- 55,61 ----
  \ ]
    call s:compare_lines(expect, lines)
    call s:close_windows()
+   set listchars&vim
  endfunc
  
  func Test_linebreak_with_nolist()
*** ../vim-8.2.2517/src/version.c       2021-02-14 22:40:53.233969130 +0100
--- src/version.c       2021-02-15 20:25:50.023070853 +0100
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2518,
  /**/

-- 
The early bird gets the worm. If you want something else for
breakfast, get up later.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202102151939.11FJd3Z31741745%40masaka.moolenaar.net.

Raspunde prin e-mail lui