---+---+--XXX ~
Where "XXX" denotes the first non-blank characters in
the line.
+ *lcs-leadtab*
+ leadtab:xy[z]
+ Like |lcs-tab|, but only for leading tabs. When
+ omitted, the "tab" setting is used for leading tabs.
+ |lcs-tab| must also be set for this to work. *E1572*
+ You can combine it with "tab:", for example:
+ `:set listchars=tab:>-,leadtab:.\ `
+ This shows leading tabs as periods(.) and other tabs
+ as ">--".
*lcs-trail*
trail:c Character to show for trailing spaces. When omitted,
trailing spaces are blank. Overrides the "space" and
E157 sign.txt /*E157*
E1570 builtin.txt /*E1570*
E1571 builtin.txt /*E1571*
+E1572 options.txt /*E1572*
E158 sign.txt /*E158*
E159 sign.txt /*E159*
E16 cmdline.txt /*E16*
lcs-extends options.txt /*lcs-extends*
lcs-lead options.txt /*lcs-lead*
lcs-leadmultispace options.txt /*lcs-leadmultispace*
+lcs-leadtab options.txt /*lcs-leadtab*
lcs-multispace options.txt /*lcs-multispace*
lcs-nbsp options.txt /*lcs-nbsp*
lcs-precedes options.txt /*lcs-precedes*
- |ConPTY| support is considered stable as of Windows 11.
- Support for "dap" channel mode for the |debug-adapter-protocol|.
- |status-line| can use several lines, see 'statuslineopt'.
+- New "leadtab" value for the 'listchars' setting.
*changed-9.3*
Changed~
trailcol += (colnr_T)(ptr - line);
}
// find end of leading whitespace
- if (wp->w_lcs_chars.lead || wp->w_lcs_chars.leadmultispace != NULL)
+ if (wp->w_lcs_chars.lead || wp->w_lcs_chars.leadmultispace != NULL ||
+ wp->w_lcs_chars.leadtab1 != NUL)
{
leadcol = 0;
while (VIM_ISWHITE(ptr[leadcol]))
{
int tab_len = 0;
long vcol_adjusted = wlv.vcol; // removed showbreak len
+ int lcs_tab1 = wp->w_lcs_chars.tab1;
+ int lcs_tab2 = wp->w_lcs_chars.tab2;
+ int lcs_tab3 = wp->w_lcs_chars.tab3;
+
+ // check if leadtab is set in 'listchars'
+ if (wp->w_p_list && wp->w_lcs_chars.leadtab1 != NUL &&
+ (leadcol == 0 || ptr < line + leadcol))
+ {
+ lcs_tab1 = wp->w_lcs_chars.leadtab1;
+ lcs_tab2 = wp->w_lcs_chars.leadtab2;
+ lcs_tab3 = wp->w_lcs_chars.leadtab3;
+ }
#ifdef FEAT_LINEBREAK
char_u *sbr = get_showbreak_value(wp);
tab_len += wlv.vcol_off_co;
// boguscols before FIX_FOR_BOGUSCOLS macro from above
- if (wp->w_p_list && wp->w_lcs_chars.tab1
- && old_boguscols > 0
- && wlv.n_extra > tab_len)
+ if (wp->w_p_list && lcs_tab1 &&
+ old_boguscols > 0 &&
+ wlv.n_extra > tab_len)
tab_len += wlv.n_extra - tab_len;
# endif
if (tab_len > 0)
// If wlv.n_extra > 0, it gives the number of chars
// to use for a tab, else we need to calculate the
// width for a tab.
- int tab2_len = mb_char2len(wp->w_lcs_chars.tab2);
+ int tab2_len = mb_char2len(lcs_tab2);
len = tab_len * tab2_len;
- if (wp->w_lcs_chars.tab3)
- len += mb_char2len(wp->w_lcs_chars.tab3)
- - tab2_len;
+ if (lcs_tab3)
+ len += mb_char2len(lcs_tab3) - tab2_len;
if (wlv.n_extra > 0)
len += wlv.n_extra - tab_len;
- c = wp->w_lcs_chars.tab1;
+ c = lcs_tab1;
p = alloc(len + 1);
if (p == NULL)
wlv.n_extra = 0;
wlv.p_extra_free = p;
for (i = 0; i < tab_len; i++)
{
- int lcs = wp->w_lcs_chars.tab2;
+ int lcs = lcs_tab2;
if (*p == NUL)
{
// if tab3 is given, use it for the last
// char
- if (wp->w_lcs_chars.tab3
- && i == tab_len - 1)
- lcs = wp->w_lcs_chars.tab3;
+ if (lcs_tab3 && i == tab_len - 1)
+ lcs = lcs_tab3;
p += mb_char2bytes(lcs, p);
wlv.n_extra += mb_char2len(lcs)
- (saved_nextra > 0 ? 1 : 0);
mb_utf8 = FALSE; // don't draw as UTF-8
if (wp->w_p_list)
{
- c = (wlv.n_extra == 0 && wp->w_lcs_chars.tab3)
- ? wp->w_lcs_chars.tab3
- : wp->w_lcs_chars.tab1;
+ c = (wlv.n_extra == 0 && lcs_tab3) ? lcs_tab3
+ : lcs_tab1;
#ifdef FEAT_LINEBREAK
if (wp->w_p_lbr && wlv.p_extra != NULL
&& *wlv.p_extra != NUL)
wlv.c_extra = NUL; // using p_extra from above
else
#endif
- wlv.c_extra = wp->w_lcs_chars.tab2;
- wlv.c_final = wp->w_lcs_chars.tab3;
+ wlv.c_extra = lcs_tab2;
+ wlv.c_final = lcs_tab3;
n_attr = tab_len + 1;
wlv.extra_attr = hl_combine_attr(wlv.win_attr,
HL_ATTR(HLF_8));
EXTERN char e_no_redraw_listener_callbacks_defined[]
INIT(= N_("E1571: Must specify at least one callback for redraw_listener_add"));
#endif
+EXTERN char e_leadtab_requires_tab[]
+ INIT(= N_("E1572: 'listchars' field \"leadtab\" requires \"tab\" to be specified"));
CHARSTAB_ENTRY(&lcs_chars.prec, "precedes"),
CHARSTAB_ENTRY(&lcs_chars.space, "space"),
CHARSTAB_ENTRY(&lcs_chars.tab2, "tab"),
+ CHARSTAB_ENTRY(&lcs_chars.leadtab2, "leadtab"),
CHARSTAB_ENTRY(&lcs_chars.trail, "trail"),
CHARSTAB_ENTRY(&lcs_chars.lead, "lead"),
#ifdef FEAT_CONCEAL
*(tab[i].cp) = NUL;
lcs_chars.tab1 = NUL;
lcs_chars.tab3 = NUL;
+ lcs_chars.leadtab1 = NUL;
+ lcs_chars.leadtab3 = NUL;
if (multispace_len > 0)
{
return field_value_err(errbuf, errbuflen,
e_wrong_character_width_for_field_str,
tab[i].name.string);
- if (tab[i].cp == &lcs_chars.tab2)
+ if (tab[i].cp == &lcs_chars.tab2 ||
+ tab[i].cp == &lcs_chars.leadtab2)
{
if (*s == NUL)
return field_value_err(errbuf, errbuflen,
lcs_chars.tab2 = c2;
lcs_chars.tab3 = c3;
}
+ else if (tab[i].cp == &lcs_chars.leadtab2)
+ {
+ lcs_chars.leadtab1 = c1;
+ lcs_chars.leadtab2 = c2;
+ lcs_chars.leadtab3 = c3;
+ }
else if (tab[i].cp != NULL)
*(tab[i].cp) = c1;
-
}
p = s;
break;
}
}
+ if (is_listchars && lcs_chars.leadtab2 != NUL && lcs_chars.tab2 == NUL)
+ return e_leadtab_requires_tab;
+
if (apply)
{
if (is_listchars)
int tab3;
int trail;
int lead;
+ int leadtab1;
+ int leadtab2;
+ int leadtab3;
int *multispace;
int *leadmultispace;
#ifdef FEAT_CONCEAL
call Check_listchars(expected, 5, -1, 6)
call assert_equal(expected, split(execute("%list"), "\n"))
+ " Test leadtab basic functionality
+ normal ggdG
+ set listchars=tab:>-,leadtab:+*
+ set list
+ call append(0, [
+ \ "\ttext",
+ \ "\t\ttext",
+ \ "text\ttab"
+ \ ])
+ let expected = [
+ \ '+*******text ',
+ \ '+*******+*******text',
+ \ 'text>---tab '
+ \ ]
+ call Check_listchars(expected, 3, 20)
+
+ " Test leadtab with unicode characters
+ normal ggdG
+ set listchars=tab:>-,leadtab:├─┤
+ call append(0, ["\ttext"])
+ let expected = ['├──────┤text']
+ call Check_listchars(expected, 1, 12)
+
+ " Test leadtab with mixed indentation (spaces + tabs)
+ normal ggdG
+ set listchars=tab:>-,leadtab:+*,space:.
+ call append(0, [" \t text"])
+ let expected = ['.+******.text']
+ call Check_listchars(expected, 1, 13)
+
+ " Test leadtab with pipe character
+ normal ggdG
+ set listchars=tab:>-,leadtab:\|\
+ call append(0, ["\ttext"])
+ let expected = ['| text']
+ call Check_listchars(expected, 1, 12)
+
+ " Test leadtab with unicode bar
+ normal ggdG
+ set listchars=tab:>-,leadtab:│\
+ call append(0, ["\ttext"])
+ let expected = ['│ text']
+ call Check_listchars(expected, 1, 12)
+
+ " Test leadtab vs tab distinction (leading vs non-leading)
+ normal ggdG
+ set listchars=tab:>-,leadtab:+*
+ call append(0, [
+ \ "\tleading",
+ \ "text\tnot leading",
+ \ "\t\tmultiple leading"
+ \ ])
+ let expected = [
+ \ '+*******leading ',
+ \ 'text>---not leading ',
+ \ '+*******+*******multiple leading'
+ \ ]
+ call Check_listchars(expected, 3, 32)
+
+ " Test leadtab with trail and space
+ normal ggdG
+ set listchars=tab:>-,leadtab:+*,trail:<,space:.
+ call append(0, [
+ \ "\ttext ",
+ \ " \ttext",
+ \ "\t text "
+ \ ])
+ let expected = [
+ \ '+*******text<< ',
+ \ '..+*****text ',
+ \ '+*******..text<<'
+ \ ]
+ call Check_listchars(expected, 3, 16)
+
+ " Test leadtab with eol
+ normal ggdG
+ set listchars=tab:>-,leadtab:+*,eol:$
+ call append(0, ["\ttext", "text\ttab"])
+ let expected = [
+ \ '+*******text$',
+ \ 'text>---tab$ '
+ \ ]
+ call Check_listchars(expected, 2, 13)
+
+
" test nbsp
normal ggdG
set listchars=nbsp:X,trail:Y
call assert_equal('eol', getcompletion('set listchars+=', 'cmdline')[0])
call assert_equal(['multispace', 'leadmultispace'], getcompletion('set listchars+=', 'cmdline')[-2:])
+ call assert_equal(['tab', 'leadtab'], getcompletion('set listchars+=', 'cmdline')[5:6])
call assert_equal('eol', getcompletion('setl listchars+=', 'cmdline')[0])
+ call assert_equal(['tab', 'leadtab'], getcompletion('setl listchars+=', 'cmdline')[5:6])
call assert_equal(['multispace', 'leadmultispace'], getcompletion('setl listchars+=', 'cmdline')[-2:])
call assert_equal('stl', getcompletion('set fillchars+=', 'cmdline')[0])
call assert_equal('stl', getcompletion('setl fillchars+=', 'cmdline')[0])
\ 'langmap': [['', 'xX', 'aA,bB'], ['xxx']],
\ 'lispoptions': [['', 'expr:0', 'expr:1'], ['xxx', 'expr:x', 'expr:']],
\ 'listchars': [['', 'eol:x', 'tab:xy', 'tab:xyz', 'space:x',
- \ 'multispace:xxxy', 'lead:x', 'leadmultispace:xxxy', 'trail:x',
- \ 'extends:x', 'precedes:x', 'conceal:x', 'nbsp:x', 'eol:\\x24',
- \ 'eol:\\u21b5', 'eol:\\U000021b5', 'eol:x,space:y'],
- \ ['xxx', 'eol:']],
+ \ 'multispace:xxxy', 'lead:x', 'tab:xy,leadtab:xyz', 'leadmultispace:xxxy',
+ \ 'trail:x', 'extends:x', 'precedes:x', 'conceal:x', 'nbsp:x',
+ \ 'eol:\\x24', 'eol:\\u21b5', 'eol:\\U000021b5', 'eol:x,space:y'],
+ \ ['xxx', 'eol:', 'leadtab:xyz']],
\ 'matchpairs': [['', '(:)', '(:),<:>'], ['xxx']],
\ 'maxsearchcount': [[1, 10, 100, 1000], [0, -1, 10000]],
\ 'messagesopt': [['hit-enter,history:1', 'hit-enter,history:10000',
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 88,
/**/
87,
/**/