vim-patch:8.1.0759: showing two characters for tab is limited

Problem:    Showing two characters for tab is limited.
Solution:   Allow for a third character for "tab:" in 'listchars'. (Nathaniel
            Braun, Ken Takata, closes vim/vim#3810)

83a52171ba
This commit is contained in:
Marco Hinz 2019-01-25 16:45:29 +01:00
parent 2418aa3a4a
commit 2ae97f3d4c
No known key found for this signature in database
GPG Key ID: 1C980A1B657B4A4F
6 changed files with 115 additions and 25 deletions

View File

@ -3662,11 +3662,26 @@ A jump table for the options with a short description can be found at |Q_op|.
omitted, there is no extra character at the end of the omitted, there is no extra character at the end of the
line. line.
*lcs-tab* *lcs-tab*
tab:xy Two characters to be used to show a tab. The first tab:xy[z] Two or three characters to be used to show a tab.
char is used once. The second char is repeated to The third character is optional.
fill the space that the tab normally occupies.
"tab:>-" will show a tab that takes four spaces as tab:xy The 'x' is always used, then 'y' as many times as will
">---". When omitted, a tab is show as ^I. fit. Thus "tab:>-" displays:
>
>-
>--
etc.
tab:xyz The 'z' is always used, then 'x' is prepended, and
then 'y' is used as many times as will fit. Thus
"tab:<->" displays:
>
<>
<->
<-->
etc.
When "tab:" is omitted, a tab is shown as ^I.
*lcs-space* *lcs-space*
space:c Character to show for a space. When omitted, spaces space:c Character to show for a space. When omitted, spaces
are left blank. are left blank.

View File

@ -1016,6 +1016,7 @@ struct window_S {
int space; int space;
int tab1; ///< first tab character int tab1; ///< first tab character
int tab2; ///< second tab character int tab2; ///< second tab character
int tab3; ///< third tab character
int trail; int trail;
int conceal; int conceal;
} w_p_lcs_chars; } w_p_lcs_chars;

View File

@ -1517,10 +1517,11 @@ void msg_prt_line(char_u *s, int list)
int col = 0; int col = 0;
int n_extra = 0; int n_extra = 0;
int c_extra = 0; int c_extra = 0;
char_u *p_extra = NULL; /* init to make SASC shut up */ int c_final = 0;
char_u *p_extra = NULL; // init to make SASC shut up
int n; int n;
int attr = 0; int attr = 0;
char_u *trail = NULL; char_u *trail = NULL;
int l; int l;
if (curwin->w_p_list) { if (curwin->w_p_list) {
@ -1543,7 +1544,9 @@ void msg_prt_line(char_u *s, int list)
while (!got_int) { while (!got_int) {
if (n_extra > 0) { if (n_extra > 0) {
n_extra--; n_extra--;
if (c_extra) { if (n_extra == 0 && c_final) {
c = c_final;
} else if (c_extra) {
c = c_extra; c = c_extra;
} else { } else {
assert(p_extra != NULL); assert(p_extra != NULL);
@ -1572,9 +1575,13 @@ void msg_prt_line(char_u *s, int list)
if (!list) { if (!list) {
c = ' '; c = ' ';
c_extra = ' '; c_extra = ' ';
c_final = NUL;
} else { } else {
c = curwin->w_p_lcs_chars.tab1; c = (n_extra == 0 && curwin->w_p_lcs_chars.tab3)
? curwin->w_p_lcs_chars.tab3
: curwin->w_p_lcs_chars.tab1;
c_extra = curwin->w_p_lcs_chars.tab2; c_extra = curwin->w_p_lcs_chars.tab2;
c_final = curwin->w_p_lcs_chars.tab3;
attr = HL_ATTR(HLF_8); attr = HL_ATTR(HLF_8);
} }
} else if (c == 160 && list && curwin->w_p_lcs_chars.nbsp != NUL) { } else if (c == 160 && list && curwin->w_p_lcs_chars.nbsp != NUL) {
@ -1583,6 +1590,7 @@ void msg_prt_line(char_u *s, int list)
} else if (c == NUL && list && curwin->w_p_lcs_chars.eol != NUL) { } else if (c == NUL && list && curwin->w_p_lcs_chars.eol != NUL) {
p_extra = (char_u *)""; p_extra = (char_u *)"";
c_extra = NUL; c_extra = NUL;
c_final = NUL;
n_extra = 1; n_extra = 1;
c = curwin->w_p_lcs_chars.eol; c = curwin->w_p_lcs_chars.eol;
attr = HL_ATTR(HLF_AT); attr = HL_ATTR(HLF_AT);
@ -1591,6 +1599,7 @@ void msg_prt_line(char_u *s, int list)
n_extra = n - 1; n_extra = n - 1;
p_extra = transchar_byte(c); p_extra = transchar_byte(c);
c_extra = NUL; c_extra = NUL;
c_final = NUL;
c = *p_extra++; c = *p_extra++;
/* Use special coloring to be able to distinguish <hex> from /* Use special coloring to be able to distinguish <hex> from
* the same in plain text. */ * the same in plain text. */

View File

@ -3405,7 +3405,7 @@ static char_u *set_chars_option(win_T *wp, char_u **varp)
{ {
int round, i, len, entries; int round, i, len, entries;
char_u *p, *s; char_u *p, *s;
int c1, c2 = 0; int c1 = 0, c2 = 0, c3 = 0;
struct chars_tab { struct chars_tab {
int *cp; ///< char value int *cp; ///< char value
@ -3462,6 +3462,7 @@ static char_u *set_chars_option(win_T *wp, char_u **varp)
} }
if (varp == &wp->w_p_lcs) { if (varp == &wp->w_p_lcs) {
wp->w_p_lcs_chars.tab1 = NUL; wp->w_p_lcs_chars.tab1 = NUL;
wp->w_p_lcs_chars.tab3 = NUL;
} }
} }
p = *varp; p = *varp;
@ -3471,6 +3472,7 @@ static char_u *set_chars_option(win_T *wp, char_u **varp)
if (STRNCMP(p, tab[i].name, len) == 0 if (STRNCMP(p, tab[i].name, len) == 0
&& p[len] == ':' && p[len] == ':'
&& p[len + 1] != NUL) { && p[len + 1] != NUL) {
c1 = c2 = c3 = 0;
s = p + len + 1; s = p + len + 1;
// TODO(bfredl): use schar_T representation and utfc_ptr2len // TODO(bfredl): use schar_T representation and utfc_ptr2len
@ -3488,12 +3490,20 @@ static char_u *set_chars_option(win_T *wp, char_u **varp)
if (mb_char2cells(c2) > 1 || (c2len == 1 && c2 > 127)) { if (mb_char2cells(c2) > 1 || (c2len == 1 && c2 > 127)) {
continue; continue;
} }
if (!(*s == ',' || *s == NUL)) {
int c3len = utf_ptr2len(s);
c3 = mb_cptr2char_adv((const char_u **)&s);
if (mb_char2cells(c3) > 1 || (c3len == 1 && c3 > 127)) {
continue;
}
}
} }
if (*s == ',' || *s == NUL) { if (*s == ',' || *s == NUL) {
if (round) { if (round) {
if (tab[i].cp == &wp->w_p_lcs_chars.tab2) { if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
wp->w_p_lcs_chars.tab1 = c1; wp->w_p_lcs_chars.tab1 = c1;
wp->w_p_lcs_chars.tab2 = c2; wp->w_p_lcs_chars.tab2 = c2;
wp->w_p_lcs_chars.tab3 = c3;
} else if (tab[i].cp != NULL) { } else if (tab[i].cp != NULL) {
*(tab[i].cp) = c1; *(tab[i].cp) = c1;
} }

View File

@ -2055,6 +2055,7 @@ win_line (
char_u *p_extra = NULL; // string of extra chars, plus NUL char_u *p_extra = NULL; // string of extra chars, plus NUL
char_u *p_extra_free = NULL; // p_extra needs to be freed char_u *p_extra_free = NULL; // p_extra needs to be freed
int c_extra = NUL; // extra chars, all the same int c_extra = NUL; // extra chars, all the same
int c_final = NUL; // final char, mandatory if set
int extra_attr = 0; // attributes when n_extra != 0 int extra_attr = 0; // attributes when n_extra != 0
static char_u *at_end_str = (char_u *)""; // used for p_extra when displaying static char_u *at_end_str = (char_u *)""; // used for p_extra when displaying
// curwin->w_p_lcs_chars.eol at // curwin->w_p_lcs_chars.eol at
@ -2066,6 +2067,7 @@ win_line (
int saved_n_extra = 0; int saved_n_extra = 0;
char_u *saved_p_extra = NULL; char_u *saved_p_extra = NULL;
int saved_c_extra = 0; int saved_c_extra = 0;
int saved_c_final = 0;
int saved_char_attr = 0; int saved_char_attr = 0;
int n_attr = 0; /* chars with special attr */ int n_attr = 0; /* chars with special attr */
@ -2644,6 +2646,7 @@ win_line (
/* Draw the cmdline character. */ /* Draw the cmdline character. */
n_extra = 1; n_extra = 1;
c_extra = cmdwin_type; c_extra = cmdwin_type;
c_final = NUL;
char_attr = win_hl_attr(wp, HLF_AT); char_attr = win_hl_attr(wp, HLF_AT);
} }
} }
@ -2662,6 +2665,7 @@ win_line (
p_extra_free[n_extra] = NUL; p_extra_free[n_extra] = NUL;
p_extra = p_extra_free; p_extra = p_extra_free;
c_extra = NUL; c_extra = NUL;
c_final = NUL;
char_attr = win_hl_attr(wp, HLF_FC); char_attr = win_hl_attr(wp, HLF_FC);
} }
} }
@ -2675,6 +2679,7 @@ win_line (
int text_sign; int text_sign;
// Draw cells with the sign value or blank. // Draw cells with the sign value or blank.
c_extra = ' '; c_extra = ' ';
c_final = NUL;
char_attr = win_hl_attr(wp, HLF_SC); char_attr = win_hl_attr(wp, HLF_SC);
n_extra = win_signcol_width(wp); n_extra = win_signcol_width(wp);
@ -2685,6 +2690,7 @@ win_line (
int symbol_blen = (int)STRLEN(p_extra); int symbol_blen = (int)STRLEN(p_extra);
if (p_extra != NULL) { if (p_extra != NULL) {
c_extra = NUL; c_extra = NUL;
c_final = NUL;
// symbol(s) bytes + (filling spaces) (one byte each) // symbol(s) bytes + (filling spaces) (one byte each)
n_extra = symbol_blen + n_extra = symbol_blen +
(win_signcol_width(wp) - mb_string2cells(p_extra)); (win_signcol_width(wp) - mb_string2cells(p_extra));
@ -2736,8 +2742,11 @@ win_line (
rl_mirror(extra); rl_mirror(extra);
p_extra = extra; p_extra = extra;
c_extra = NUL; c_extra = NUL;
} else c_final = NUL;
} else {
c_extra = ' '; c_extra = ' ';
c_final = NUL;
}
n_extra = number_width(wp) + 1; n_extra = number_width(wp) + 1;
char_attr = win_hl_attr(wp, HLF_N); char_attr = win_hl_attr(wp, HLF_N);
@ -2795,8 +2804,10 @@ win_line (
// draw "deleted" diff line(s) // draw "deleted" diff line(s)
if (char2cells(wp->w_p_fcs_chars.diff) > 1) { if (char2cells(wp->w_p_fcs_chars.diff) > 1) {
c_extra = '-'; c_extra = '-';
c_final = NUL;
} else { } else {
c_extra = wp->w_p_fcs_chars.diff; c_extra = wp->w_p_fcs_chars.diff;
c_final = NUL;
} }
if (wp->w_p_rl) { if (wp->w_p_rl) {
n_extra = col + 1; n_extra = col + 1;
@ -2809,6 +2820,7 @@ win_line (
/* Draw 'showbreak' at the start of each broken line. */ /* Draw 'showbreak' at the start of each broken line. */
p_extra = p_sbr; p_extra = p_sbr;
c_extra = NUL; c_extra = NUL;
c_final = NUL;
n_extra = (int)STRLEN(p_sbr); n_extra = (int)STRLEN(p_sbr);
char_attr = win_hl_attr(wp, HLF_AT); char_attr = win_hl_attr(wp, HLF_AT);
need_showbreak = false; need_showbreak = false;
@ -2830,6 +2842,7 @@ win_line (
/* Continue item from end of wrapped line. */ /* Continue item from end of wrapped line. */
n_extra = saved_n_extra; n_extra = saved_n_extra;
c_extra = saved_c_extra; c_extra = saved_c_extra;
c_final = saved_c_final;
p_extra = saved_p_extra; p_extra = saved_p_extra;
char_attr = saved_char_attr; char_attr = saved_char_attr;
} else { } else {
@ -3027,20 +3040,18 @@ win_line (
} }
} }
/* // Get the next character to put on the screen.
* Get the next character to put on the screen. //
*/ // The "p_extra" points to the extra stuff that is inserted to
/* // represent special characters (non-printable stuff) and other
* The "p_extra" points to the extra stuff that is inserted to // things. When all characters are the same, c_extra is used.
* represent special characters (non-printable stuff) and other // If c_final is set, it will compulsorily be used at the end.
* things. When all characters are the same, c_extra is used. // "p_extra" must end in a NUL to avoid mb_ptr2len() reads past
* "p_extra" must end in a NUL to avoid mb_ptr2len() reads past // "p_extra[n_extra]".
* "p_extra[n_extra]". // For the '$' of the 'list' option, n_extra == 1, p_extra == "".
* For the '$' of the 'list' option, n_extra == 1, p_extra == "".
*/
if (n_extra > 0) { if (n_extra > 0) {
if (c_extra != NUL) { if (c_extra != NUL || (n_extra == 1 && c_final != NUL)) {
c = c_extra; c = (n_extra == 1 && c_final != NUL) ? c_final : c_extra;
mb_c = c; // doesn't handle non-utf-8 multi-byte! mb_c = c; // doesn't handle non-utf-8 multi-byte!
if (enc_utf8 && utf_char2len(c) > 1) { if (enc_utf8 && utf_char2len(c) > 1) {
mb_utf8 = true; mb_utf8 = true;
@ -3147,6 +3158,7 @@ win_line (
mb_utf8 = (c >= 0x80); mb_utf8 = (c >= 0x80);
n_extra = (int)STRLEN(p_extra); n_extra = (int)STRLEN(p_extra);
c_extra = NUL; c_extra = NUL;
c_final = NUL;
if (area_attr == 0 && search_attr == 0) { if (area_attr == 0 && search_attr == 0) {
n_attr = n_extra + 1; n_attr = n_extra + 1;
extra_attr = win_hl_attr(wp, HLF_8); extra_attr = win_hl_attr(wp, HLF_8);
@ -3199,6 +3211,7 @@ win_line (
p_extra = extra; p_extra = extra;
n_extra = (int)STRLEN(extra) - 1; n_extra = (int)STRLEN(extra) - 1;
c_extra = NUL; c_extra = NUL;
c_final = NUL;
c = *p_extra++; c = *p_extra++;
if (area_attr == 0 && search_attr == 0) { if (area_attr == 0 && search_attr == 0) {
n_attr = n_extra + 1; n_attr = n_extra + 1;
@ -3233,6 +3246,7 @@ win_line (
if (n_skip > 0 && mb_l > 1 && n_extra == 0) { if (n_skip > 0 && mb_l > 1 && n_extra == 0) {
n_extra = 1; n_extra = 1;
c_extra = MB_FILLER_CHAR; c_extra = MB_FILLER_CHAR;
c_final = NUL;
c = ' '; c = ' ';
if (area_attr == 0 && search_attr == 0) { if (area_attr == 0 && search_attr == 0) {
n_attr = n_extra + 1; n_attr = n_extra + 1;
@ -3392,6 +3406,7 @@ win_line (
- vcol % (int)wp->w_buffer->b_p_ts - 1; - vcol % (int)wp->w_buffer->b_p_ts - 1;
} }
c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' ';
c_final = NUL;
if (ascii_iswhite(c)) { if (ascii_iswhite(c)) {
if (c == TAB) if (c == TAB)
/* See "Tab alignment" below. */ /* See "Tab alignment" below. */
@ -3523,12 +3538,15 @@ win_line (
mb_utf8 = false; // don't draw as UTF-8 mb_utf8 = false; // don't draw as UTF-8
if (wp->w_p_list) { if (wp->w_p_list) {
c = wp->w_p_lcs_chars.tab1; c = (n_extra == 0 && wp->w_p_lcs_chars.tab3)
? wp->w_p_lcs_chars.tab3
: wp->w_p_lcs_chars.tab1;
if (wp->w_p_lbr) { if (wp->w_p_lbr) {
c_extra = NUL; /* using p_extra from above */ c_extra = NUL; /* using p_extra from above */
} else { } else {
c_extra = wp->w_p_lcs_chars.tab2; c_extra = wp->w_p_lcs_chars.tab2;
} }
c_final = wp->w_p_lcs_chars.tab3;
n_attr = tab_len + 1; n_attr = tab_len + 1;
extra_attr = win_hl_attr(wp, HLF_0); extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = char_attr; // save current attr saved_attr2 = char_attr; // save current attr
@ -3539,6 +3557,7 @@ win_line (
c = 0xc0; c = 0xc0;
} }
} else { } else {
c_final = NUL;
c_extra = ' '; c_extra = ' ';
c = ' '; c = ' ';
} }
@ -3566,6 +3585,7 @@ win_line (
p_extra = at_end_str; p_extra = at_end_str;
n_extra = 1; n_extra = 1;
c_extra = NUL; c_extra = NUL;
c_final = NUL;
} }
} }
if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) { if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) {
@ -3593,6 +3613,7 @@ win_line (
if ((dy_flags & DY_UHEX) && wp->w_p_rl) if ((dy_flags & DY_UHEX) && wp->w_p_rl)
rl_mirror(p_extra); /* reverse "<12>" */ rl_mirror(p_extra); /* reverse "<12>" */
c_extra = NUL; c_extra = NUL;
c_final = NUL;
if (wp->w_p_lbr) { if (wp->w_p_lbr) {
char_u *p; char_u *p;
@ -3716,6 +3737,7 @@ win_line (
/* Double-width character being overwritten by the "precedes" /* Double-width character being overwritten by the "precedes"
* character, need to fill up half the character. */ * character, need to fill up half the character. */
c_extra = MB_FILLER_CHAR; c_extra = MB_FILLER_CHAR;
c_final = NUL;
n_extra = 1; n_extra = 1;
n_attr = 2; n_attr = 2;
extra_attr = win_hl_attr(wp, HLF_AT); extra_attr = win_hl_attr(wp, HLF_AT);
@ -4231,6 +4253,7 @@ win_line (
saved_n_extra = n_extra; saved_n_extra = n_extra;
saved_p_extra = p_extra; saved_p_extra = p_extra;
saved_c_extra = c_extra; saved_c_extra = c_extra;
saved_c_final = c_final;
saved_char_attr = char_attr; saved_char_attr = char_attr;
n_extra = 0; n_extra = 0;
lcs_prec_todo = wp->w_p_lcs_chars.prec; lcs_prec_todo = wp->w_p_lcs_chars.prec;

View File

@ -42,6 +42,38 @@ func Test_listchars()
call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$'))) call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
endfor endfor
" tab with 3rd character.
set listchars-=tab:>-
set listchars+=tab:<=>,trail:-
let expected = [
\ '<======>aa<====>$',
\ '..bb<==>--$',
\ '...cccc>-$',
\ 'dd........ee--<>$',
\ '-$'
\ ]
redraw!
for i in range(1, 5)
call cursor(i, 1)
call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
endfor
set listchars-=trail:-
let expected = [
\ '<======>aa<====>$',
\ '..bb<==>..$',
\ '...cccc>.$',
\ 'dd........ee..<>$',
\ '.$'
\ ]
redraw!
for i in range(1, 5)
call cursor(i, 1)
call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
endfor
set listchars-=tab:<=>
set listchars+=tab:>-
set listchars+=trail:< set listchars+=trail:<
set nolist set nolist
normal ggdG normal ggdG