summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-02-15 20:38:25 +0100
committerBram Moolenaar <Bram@vim.org>2021-02-15 20:38:25 +0100
commiteed9d46293f0842aad0d50ff3a526f9a48b12421 (patch)
treeef3730ca3c0a7ede44fade0ea638975f43a65d5f
parent7c5b3c03699a4ab31f47c24290852d441ea8c12a (diff)
patch 8.2.2518: 'listchars' should be window-localv8.2.2518
Problem: 'listchars' should be window-local. Solution: Make 'listchars' global-local. (Yegappan Lakshmanan, Marco Hinz, closes #5206, closes #7850)
-rw-r--r--runtime/doc/options.txt2
-rw-r--r--src/buffer.c2
-rw-r--r--src/charset.c8
-rw-r--r--src/drawline.c84
-rw-r--r--src/drawscreen.c2
-rw-r--r--src/evalfunc.c3
-rw-r--r--src/globals.h15
-rw-r--r--src/indent.c5
-rw-r--r--src/message.c36
-rw-r--r--src/misc1.c5
-rw-r--r--src/option.c19
-rw-r--r--src/option.h1
-rw-r--r--src/optiondefs.h3
-rw-r--r--src/optionstr.c47
-rw-r--r--src/proto/screen.pro2
-rw-r--r--src/screen.c46
-rw-r--r--src/structs.h24
-rw-r--r--src/testdir/test_listchars.vim106
-rw-r--r--src/testdir/test_listlbr.vim2
-rw-r--r--src/version.c2
20 files changed, 302 insertions, 112 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 08d205ceff..d5264ecff9 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -4854,7 +4854,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'listchars'* *'lcs'*
'listchars' 'lcs' string (default "eol:$")
- global
+ 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*
diff --git a/src/buffer.c b/src/buffer.c
index ca1fd36efe..4e4f87c926 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -4549,7 +4549,7 @@ build_stl_str_hl(
case STL_VIRTCOL_ALT:
// In list mode virtcol needs to be recomputed
virtcol = wp->w_virtcol;
- if (wp->w_p_list && lcs_tab1 == NUL)
+ if (wp->w_p_list && wp->w_lcs_chars.tab1 == NUL)
{
wp->w_p_list = FALSE;
getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
diff --git a/src/charset.c b/src/charset.c
index 4289360e47..d06a273643 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -753,7 +753,7 @@ vim_strnsize(char_u *s, int len)
#ifdef FEAT_VARTABS
# define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
- if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) \
+ 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,7 +761,7 @@ vim_strnsize(char_u *s, int len)
return ptr2cells(p);
#else
# define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
- if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) \
+ if (*(p) == TAB && (!(wp)->w_p_list || wp->w_lcs_chars.tab1)) \
{ \
int ts; \
ts = (buf)->b_p_ts; \
@@ -1153,7 +1153,7 @@ win_nolbr_chartabsize(
{
int n;
- if (*s == TAB && (!wp->w_p_list || lcs_tab1))
+ 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,7 +1248,7 @@ getvcol(
* 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)
+ 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
diff --git a/src/drawline.c b/src/drawline.c
index 077e7fcde3..2f865127f7 100644
--- a/src/drawline.c
+++ b/src/drawline.c
@@ -248,9 +248,9 @@ win_line(
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
+ // 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,11 +735,14 @@ win_line(
if (wp->w_p_list)
{
- if (lcs_space || lcs_trail || lcs_lead || lcs_nbsp)
+ 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 (lcs_trail)
+ if (wp->w_lcs_chars.trail)
{
trailcol = (colnr_T)STRLEN(ptr);
while (trailcol > (colnr_T)0 && VIM_ISWHITE(ptr[trailcol - 1]))
@@ -747,7 +750,7 @@ win_line(
trailcol += (colnr_T) (ptr - line);
}
// find end of leading whitespace
- if (lcs_lead)
+ if (wp->w_lcs_chars.lead)
{
leadcol = 0;
while (VIM_ISWHITE(ptr[leadcol]))
@@ -2000,22 +2003,23 @@ win_line(
}
#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).
+ // '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))))
- && lcs_nbsp)
+ && wp->w_lcs_chars.nbsp)
|| (c == ' '
&& mb_l == 1
- && lcs_space
+ && wp->w_lcs_chars.space
&& ptr - line >= leadcol
&& ptr - line <= trailcol)))
{
- c = (c == ' ') ? lcs_space : lcs_nbsp;
+ c = (c == ' ') ? wp->w_lcs_chars.space :
+ wp->w_lcs_chars.nbsp;
if (area_attr == 0 && search_attr == 0)
{
n_attr = 1;
@@ -2036,7 +2040,8 @@ win_line(
if ((trailcol != MAXCOL && ptr > line + trailcol && c == ' ')
|| (leadcol != 0 && ptr < line + leadcol && c == ' '))
{
- c = (ptr > line + trailcol) ? lcs_trail : lcs_lead;
+ c = (ptr > line + trailcol) ? wp->w_lcs_chars.trail
+ : wp->w_lcs_chars.lead;
if (!attr_pri)
{
n_attr = 1;
@@ -2061,7 +2066,7 @@ win_line(
// 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))
+ if (c == TAB && (!wp->w_p_list || wp->w_lcs_chars.tab1))
{
int tab_len = 0;
long vcol_adjusted = vcol; // removed showbreak length
@@ -2101,18 +2106,19 @@ win_line(
// 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)
+ 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(lcs_tab2));
+ len = (tab_len * mb_char2len(wp->w_lcs_chars.tab2));
if (n_extra > 0)
len += n_extra - tab_len;
- c = lcs_tab1;
+ c = wp->w_lcs_chars.tab1;
p = alloc(len + 1);
vim_memset(p, ' ', len);
p[len] = NUL;
@@ -2120,7 +2126,7 @@ win_line(
p_extra_free = p;
for (i = 0; i < tab_len; i++)
{
- int lcs = lcs_tab2;
+ int lcs = wp->w_lcs_chars.tab2;
if (*p == NUL)
{
@@ -2128,10 +2134,10 @@ win_line(
break;
}
- // if lcs_tab3 is given, need to change the char
+ // if tab3 is given, need to change the char
// for tab
- if (lcs_tab3 && i == tab_len - 1)
- lcs = lcs_tab3;
+ 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,21 +2168,23 @@ win_line(
// correctly set further below (effectively reverts the
// FIX_FOR_BOGSUCOLS macro
if (n_extra == tab_len + vc_saved && wp->w_p_list
- && lcs_tab1)
+ && 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 && lcs_tab3) ? lcs_tab3 : lcs_tab1;
+ 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 = lcs_tab2;
- c_final = lcs_tab3;
+ 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,8 +2249,8 @@ win_line(
c_final = NUL;
}
}
- if (wp->w_p_list && lcs_eol > 0)
- c = lcs_eol;
+ if (wp->w_p_list && wp->w_lcs_chars.eol > 0)
+ c = wp->w_lcs_chars.eol;
else
c = ' ';
lcs_eol_one = -1;
@@ -2344,7 +2352,8 @@ win_line(
// 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)))
+ || (wp->w_p_list &&
+ wp->w_lcs_chars.eol > 0)))
char_attr = line_attr;
# ifdef FEAT_DIFF
if (diff_hlf == HLF_TXD)
@@ -2404,8 +2413,8 @@ win_line(
c = match_conc;
else if (syn_get_sub_char() != NUL)
c = syn_get_sub_char();
- else if (lcs_conceal != NUL)
- c = lcs_conceal;
+ else if (wp->w_lcs_chars.conceal != NUL)
+ c = wp->w_lcs_chars.conceal;
else
c = ' ';
@@ -2548,7 +2557,7 @@ win_line(
&& draw_state > WL_NR
&& c != NUL)
{
- c = lcs_prec;
+ c = wp->w_lcs_chars.prec;
lcs_prec_todo = NUL;
if (has_mbyte && (*mb_char2cells)(mb_c) > 1)
{
@@ -2594,7 +2603,7 @@ win_line(
// 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
+ if (wp->w_lcs_chars.eol == lcs_eol_one
&& ((area_attr != 0 && vcol == fromcol
&& (VIsual_mode != Ctrl_V
|| lnum == VIsual.lnum
@@ -2764,7 +2773,7 @@ win_line(
// Show "extends" character from 'listchars' if beyond the line end and
// 'list' is set.
- if (lcs_ext != NUL
+ if (wp->w_lcs_chars.ext != NUL
&& wp->w_p_list
&& !wp->w_p_wrap
#ifdef FEAT_DIFF
@@ -2779,7 +2788,7 @@ win_line(
|| (wp->w_p_list && lcs_eol_one > 0)
|| (n_extra && (c_extra != NUL || *p_extra != NUL))))
{
- c = lcs_ext;
+ 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,7 +3045,8 @@ win_line(
#ifdef FEAT_DIFF
|| filler_todo > 0
#endif
- || (wp->w_p_list && lcs_eol != NUL && p_extra != at_end_str)
+ || (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,7 +3171,7 @@ win_line(
#endif
saved_char_attr = 0;
n_extra = 0;
- lcs_prec_todo = lcs_prec;
+ lcs_prec_todo = wp->w_lcs_chars.prec;
#ifdef FEAT_LINEBREAK
# ifdef FEAT_DIFF
if (filler_todo <= 0)
diff --git a/src/drawscreen.c b/src/drawscreen.c
index 694b9dac31..3fe1c0c797 100644
--- a/src/drawscreen.c
+++ b/src/drawscreen.c
@@ -696,7 +696,7 @@ win_redr_ruler(win_T *wp, int always, int ignore_pum)
// In list mode virtcol needs to be recomputed
virtcol = wp->w_virtcol;
- if (wp->w_p_list && lcs_tab1 == NUL)
+ if (wp->w_p_list && wp->w_lcs_chars.tab1 == NUL)
{
wp->w_p_list = FALSE;
getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
diff --git a/src/evalfunc.c b/src/evalfunc.c
index b3da9f6cf6..bee76c6eaf 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -9736,7 +9736,8 @@ f_synconcealed(typval_T *argvars UNUSED, typval_T *rettv)
{
cchar = syn_get_sub_char();
if (cchar == NUL && curwin->w_p_cole == 1)
- cchar = (lcs_conceal == NUL) ? ' ' : lcs_conceal;
+ cchar = (curwin->w_lcs_chars.conceal == NUL) ? ' '
+ : curwin->w_lcs_chars.conceal;
if (cchar != NUL)
{
if (has_mbyte)
diff --git a/src/globals.h b/src/globals.h
index 3ad4227ebc..bc4a3d5d53 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1342,21 +1342,6 @@ EXTERN char_u *homedir INIT(= NULL);
// 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(= ' ');
diff --git a/src/indent.c b/src/indent.c
index 6ac6182df4..e1c6f522a9 100644
--- a/src/indent.c
+++ b/src/indent.c
@@ -432,7 +432,8 @@ get_indent_str(
{
if (*ptr == TAB)
{
- if (!list || lcs_tab1) // count a tab for what it is worth
+ 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,7 +463,7 @@ get_indent_str_vtab(char_u *ptr, int ts, int *vts, int list)
{
if (*ptr == TAB) // count a tab for what it is worth
{
- if (!list || lcs_tab1)
+ 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
diff --git a/src/message.c b/src/message.c
index 012811f9cf..07c3943ab4 100644
--- a/src/message.c
+++ b/src/message.c
@@ -1848,14 +1848,14 @@ msg_prt_line(char_u *s, int list)
if (list)
{
// find start of trailing whitespace
- if (lcs_trail)
+ if (curwin->w_lcs_chars.trail)
{
trail = s + STRLEN(s);
while (trail > s && VIM_ISWHITE(trail[-1]))
--trail;
}
// find end of leading whitespace
- if (lcs_lead)
+ if (curwin->w_lcs_chars.lead)
{
lead = s;
while (VIM_ISWHITE(lead[0]))
@@ -1868,7 +1868,7 @@ msg_prt_line(char_u *s, int list)
// output a space for an empty line, otherwise the line will be
// overwritten
- if (*s == NUL && !(list && lcs_eol != NUL))
+ if (*s == NUL && !(list && curwin->w_lcs_chars.eol != NUL))
msg_putchar(' ');
while (!got_int)
@@ -1890,11 +1890,11 @@ msg_prt_line(char_u *s, int list)
{
STRCPY(buf, "?");
}
- else if (lcs_nbsp != NUL && list
+ else if (curwin->w_lcs_chars.nbsp != NUL && list
&& (mb_ptr2char(s) == 160
|| mb_ptr2char(s) == 0x202f))
{
- mb_char2bytes(lcs_nbsp, buf);
+ mb_char2bytes(curwin->w_lcs_chars.nbsp, buf);
buf[(*mb_ptr2len)(buf)] = NUL;
}
else
@@ -1910,7 +1910,7 @@ msg_prt_line(char_u *s, int list)
{
attr = 0;
c = *s++;
- if (c == TAB && (!list || lcs_tab1))
+ if (c == TAB && (!list || curwin->w_lcs_chars.tab1))
{
// tab amount depends on current column
#ifdef FEAT_VARTABS
@@ -1927,24 +1927,26 @@ msg_prt_line(char_u *s, int list)
}
else
{
- c = (n_extra == 0 && lcs_tab3) ? lcs_tab3 : lcs_tab1;
- c_extra = lcs_tab2;
- c_final = lcs_tab3;
+ 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 && lcs_nbsp != NUL)
+ else if (c == 160 && list && curwin->w_lcs_chars.nbsp != NUL)
{
- c = lcs_nbsp;
+ c = curwin->w_lcs_chars.nbsp;
attr = HL_ATTR(HLF_8);
}
- else if (c == NUL && list && lcs_eol != NUL)
+ 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 = lcs_eol;
+ c = curwin->w_lcs_chars.eol;
attr = HL_ATTR(HLF_AT);
--s;
}
@@ -1961,17 +1963,17 @@ msg_prt_line(char_u *s, int list)
}
else if (c == ' ' && lead != NULL && s <= lead)
{
- c = lcs_lead;
+ c = curwin->w_lcs_chars.lead;
attr = HL_ATTR(HLF_8);
}
else if (c == ' ' && trail != NULL && s > trail)
{
- c = lcs_trail;
+ c = curwin->w_lcs_chars.trail;
attr = HL_ATTR(HLF_8);
}
- else if (c == ' ' && list && lcs_space != NUL)
+ else if (c == ' ' && list && curwin->w_lcs_chars.space != NUL)
{
- c = lcs_space;
+ c = curwin->w_lcs_chars.space;
attr = HL_ATTR(HLF_8);
}
}
diff --git a/src/misc1.c b/src/misc1.c
index 010622ee5c..a72dc9bf21 100644
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -403,7 +403,7 @@ plines_win_nofold(win_T *wp, linenr_T lnum)
* 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)
+ if (wp->w_p_list && wp->w_lcs_chars.eol != NUL)
col += 1;
/*
@@ -460,7 +460,8 @@ plines_win_col(win_T *wp, linenr_T lnum, long column)
* 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))
+ 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;
/*
diff --git a/src/option.c b/src/option.c
index bba467e5e9..9db9c2633f 100644
--- a/src/option.c
+++ b/src/option.c
@@ -2337,9 +2337,11 @@ didset_options2(void)
// Parse default for 'wildmode'
check_opt_wim();
- (void)set_chars_option(&p_lcs);
+ // Parse default for 'listchars'.
+ (void)set_chars_option(curwin, &curwin->w_p_lcs);
+
// Parse default for 'fillchars'.
- (void)set_chars_option(&p_fcs);
+ (void)set_chars_option(curwin, &p_fcs);
#ifdef FEAT_CLIPBOARD
// Parse default for 'clipboard'
@@ -5063,6 +5065,11 @@ unset_global_local_option(char_u *name, void *from)
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,6 +5128,8 @@ get_varp_scope(struct vimoption *p, int opt_flags)
#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,6 +5227,8 @@ get_varp(struct vimoption *p)
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,6 +5456,7 @@ after_copy_winopt(win_T *wp UNUSED)
fill_culopt_flags(NULL, wp);
check_colorcolumn(wp);
#endif
+ set_chars_option(wp, &wp->w_p_lcs);
}
/*
@@ -5460,6 +5472,7 @@ copy_winopt(winopt_T *from, winopt_T *to)
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,6 +5607,7 @@ check_winopt(winopt_T *wop UNUSED)
check_string_option(&wop->wo_briopt);
#endif
check_string_option(&wop->wo_wcr);
+ check_string_option(&wop->wo_lcs);
}
/*
@@ -5639,6 +5653,7 @@ clear_winopt(winopt_T *wop UNUSED)
clear_string_option(&wop->wo_twk);
clear_string_option(&wop->wo_tws);
#endif
+ clear_string_option(&wop->wo_lcs);
}
#ifdef FEAT_EVAL
diff --git a/src/option.h b/src/option.h
index 4bc189ca73..349268c3d4 100644
--- a/src/option.h
+++ b/src/option.h
@@ -1231,6 +1231,7 @@ enum
enum
{
WV_LIST = 0
+ , WV_LCS
#ifdef FEAT_ARABIC
, WV_ARAB
#endif
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 3e76c8ace7..dcd195d7ef 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -185,6 +185,7 @@
#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,7 +1599,7 @@ static struct vimoption options[] =
(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 *)&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,
diff --git a/src/optionstr.c b/src/optionstr.c
index c8a5b0c658..a1cbe262f4 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -862,10 +862,24 @@ did_set_string_option(
{
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)
+ 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,16 +1306,37 @@ did_set_string_option(
}
}
- // 'listchars'
+ // global 'listchars'
else if (varp == &p_lcs)
{
- errmsg = set_chars_option(varp);
+ 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(varp);
+ errmsg = set_chars_option(curwin, varp);
}
#ifdef FEAT_CMDWIN
diff --git a/src/proto/screen.pro b/src/proto/screen.pro
index 3f475ec12c..1ab40df0c9 100644
--- a/src/proto/screen.pro
+++ b/src/proto/screen.pro
@@ -55,5 +55,5 @@ void comp_col(void);
int number_width(win_T *wp);
int screen_screencol(void);
int screen_screenrow(void);
-char *set_chars_option(char_u **varp);
+char *set_chars_option(win_T *wp, char_u **varp);
/* vim: set ft=c : */
diff --git a/src/screen.c b/src/screen.c
index b3944a71da..176f98d571 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -4745,10 +4745,11 @@ screen_screenrow(void)
/*
* Handle setting 'listchars' or 'fillchars'.
+ * Assume monocell characters.
* Returns error message, NULL if it's OK.
*/
char *
-set_chars_option(char_u **varp)
+set_chars_option(win_T *wp, char_u **varp)
{
int round, i, len, entries;
char_u *p, *s;
@@ -4767,28 +4768,30 @@ set_chars_option(char_u **varp)
{&fill_diff, "diff"},
{&fill_eob, "eob"},
};
- static struct charstab lcstab[] =
+ 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"},
+ {&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
- {&lcs_conceal, "conceal"},
+ {&wp->w_lcs_chars.conceal, "conceal"},
#else
{NULL, "conceal"},
#endif
};
struct charstab *tab;
- if (varp == &p_lcs)
+ 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,12 +4808,13 @@ set_chars_option(char_u **varp)
// 'fillchars', NUL for 'listchars'
for (i = 0; i < entries; ++i)
if (tab[i].cp != NULL)
- *(tab[i].cp) = (varp == &p_lcs ? NUL : ' ');
+ *(tab[i].cp) =
+ ((varp == &p_lcs || varp == &wp->w_p_lcs) ? NUL : ' ');
- if (varp == &p_lcs)
+ if (varp == &p_lcs || varp == &wp->w_p_lcs)
{
- lcs_tab1 = NUL;
- lcs_tab3 = NUL;
+ wp->w_lcs_chars.tab1 = NUL;
+ wp->w_lcs_chars.tab3 = NUL;
}
else
{
@@ -4833,7 +4837,7 @@ set_chars_option(char_u **varp)
c1 = mb_ptr2char_adv(&s);
if (mb_char2cells(c1) > 1)
continue;
- if (tab[i].cp == &lcs_tab2)
+ if (tab[i].cp == &wp->w_lcs_chars.tab2)
{
if (*s == NUL)
continue;
@@ -4852,11 +4856,11 @@ set_chars_option(char_u **varp)
{
if (round)
{
- if (tab[i].cp == &lcs_tab2)
+ if (tab[i].cp == &wp->w_lcs_chars.tab2)
{
- lcs_tab1 = c1;
- lcs_tab2 = c2;
- lcs_tab3 = c3;
+ 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;
diff --git a/src/structs.h b/src/structs.h
index 7bcbc389e8..0ca9203406 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -225,6 +225,8 @@ typedef struct
#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,6 +3335,26 @@ typedef struct {
#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,6 +3402,8 @@ struct window_S
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.
diff --git a/src/testdir/test_listchars.vim b/src/testdir/test_listchars.vim
index 69c98b9269..ca47d5283a 100644
--- a/src/testdir/test_listchars.vim
+++ b/src/testdir/test_listchars.vim
@@ -234,4 +234,110 @@ func Test_listchars_composing()
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:>