patch 8.2.2518: 'listchars' should be window-local

Problem:    'listchars' should be window-local.
Solution:   Make 'listchars' global-local. (Yegappan Lakshmanan, Marco Hinz,
            closes #5206, closes #7850)
diff --git a/src/drawline.c b/src/drawline.c
index 077e7fc..2f86512 100644
--- a/src/drawline.c
+++ b/src/drawline.c
@@ -248,9 +248,9 @@
     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 @@
 
     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 @@
 	    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 @@
 		}
 #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 @@
 		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 @@
 		// 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 @@
 			    // 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 @@
 			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 @@
 				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 @@
 			// 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 @@
 			    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 @@
 		    // 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 @@
 			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 @@
 		&& 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 @@
 	    // 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 @@
 
 	// 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 @@
 		    || (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 @@
 #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 @@
 #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)