To: vim_dev@googlegroups.com Subject: Patch 8.2.2518 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 -- Bram@Moolenaar.net -- 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 ///