mirror of
https://github.com/neovim/neovim.git
synced 2026-03-31 04:42:03 +00:00
vim-patch:9.2.0088: cannot display tabs for indentation
Problem: cannot display tabs for indentation
Solution: Add the "leadtab" value to the 'listchars' option to
distinguish between tabs used for indentation and tabs used
for alignment (HarshK97).
closes: vim/vim#19094
8526d32647
Co-authored-by: HarshK97 <harshkapse1234@gmail.com>
This commit is contained in:
@@ -354,6 +354,7 @@ OPTIONS
|
||||
• 'diffopt' `inline:` configures diff highlighting for changes within a line.
|
||||
• 'fillchars' has new flag "foldinner".
|
||||
• 'fsync' and 'grepformat' are now |global-local| options.
|
||||
• 'listchars' has new flag "leadtab".
|
||||
• 'jumpoptions' flag "view" now applies when popping the |tagstack|.
|
||||
• 'maxsearchcount' sets maximum value for |searchcount()| and defaults to 999.
|
||||
• 'pummaxwidth' sets maximum width for the completion popup menu.
|
||||
|
||||
@@ -4170,6 +4170,15 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
<
|
||||
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
|
||||
|
||||
9
runtime/lua/vim/_meta/options.lua
generated
9
runtime/lua/vim/_meta/options.lua
generated
@@ -4180,6 +4180,15 @@ vim.wo.list = vim.o.list
|
||||
---
|
||||
--- 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
|
||||
|
||||
@@ -1056,6 +1056,9 @@ typedef struct {
|
||||
schar_T tab1; ///< first tab character
|
||||
schar_T tab2; ///< second tab character
|
||||
schar_T tab3; ///< third tab character
|
||||
schar_T leadtab1;
|
||||
schar_T leadtab2;
|
||||
schar_T leadtab3;
|
||||
schar_T lead;
|
||||
schar_T trail;
|
||||
schar_T *multispace;
|
||||
|
||||
@@ -1462,7 +1462,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
trailcol += (colnr_T)(ptr - line);
|
||||
}
|
||||
// find end of leading whitespace
|
||||
if (wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.leadmultispace != NULL) {
|
||||
if (wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.leadmultispace != NULL
|
||||
|| wp->w_p_lcs_chars.leadtab1 != NUL) {
|
||||
leadcol = 0;
|
||||
while (ascii_iswhite(ptr[leadcol])) {
|
||||
leadcol++;
|
||||
@@ -2420,6 +2421,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
if (mb_c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
|
||||
int tab_len = 0;
|
||||
colnr_T vcol_adjusted = wlv.vcol; // removed showbreak length
|
||||
schar_T lcs_tab1 = wp->w_p_lcs_chars.tab1;
|
||||
schar_T lcs_tab2 = wp->w_p_lcs_chars.tab2;
|
||||
schar_T lcs_tab3 = wp->w_p_lcs_chars.tab3;
|
||||
// check if leadtab is set in 'listchars'
|
||||
if (wp->w_p_list && wp->w_p_lcs_chars.leadtab1 != NUL
|
||||
&& (leadcol == 0 || ptr < line + leadcol)) {
|
||||
lcs_tab1 = wp->w_p_lcs_chars.leadtab1;
|
||||
lcs_tab2 = wp->w_p_lcs_chars.leadtab2;
|
||||
lcs_tab3 = wp->w_p_lcs_chars.leadtab3;
|
||||
}
|
||||
char *const sbr = get_showbreak_value(wp);
|
||||
|
||||
// Only adjust the tab_len, when at the first column after the
|
||||
@@ -2442,8 +2453,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
tab_len += wlv.vcol_off_co;
|
||||
}
|
||||
// boguscols before fix_for_boguscols() from above.
|
||||
if (wp->w_p_lcs_chars.tab1 && wlv.old_boguscols > 0
|
||||
&& wlv.n_extra > tab_len) {
|
||||
if (lcs_tab1 && wlv.old_boguscols > 0 && wlv.n_extra > tab_len) {
|
||||
tab_len += wlv.n_extra - tab_len;
|
||||
}
|
||||
|
||||
@@ -2451,15 +2461,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
// 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.
|
||||
size_t tab2_len = schar_len(wp->w_p_lcs_chars.tab2);
|
||||
size_t tab2_len = schar_len(lcs_tab2);
|
||||
size_t len = (size_t)tab_len * tab2_len;
|
||||
if (wp->w_p_lcs_chars.tab3) {
|
||||
len += schar_len(wp->w_p_lcs_chars.tab3) - tab2_len;
|
||||
if (lcs_tab3) {
|
||||
len += schar_len(lcs_tab3) - tab2_len;
|
||||
}
|
||||
if (wlv.n_extra > 0) {
|
||||
len += (size_t)(wlv.n_extra - tab_len);
|
||||
}
|
||||
mb_schar = wp->w_p_lcs_chars.tab1;
|
||||
mb_schar = lcs_tab1;
|
||||
mb_c = schar_get_first_codepoint(mb_schar);
|
||||
char *p = get_extra_buf(len + 1);
|
||||
memset(p, ' ', len);
|
||||
@@ -2470,11 +2480,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
tab_len = i;
|
||||
break;
|
||||
}
|
||||
schar_T lcs = wp->w_p_lcs_chars.tab2;
|
||||
schar_T lcs = lcs_tab2;
|
||||
|
||||
// if tab3 is given, use it for the last char
|
||||
if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) {
|
||||
lcs = wp->w_p_lcs_chars.tab3;
|
||||
if (lcs_tab3 && i == tab_len - 1) {
|
||||
lcs = lcs_tab3;
|
||||
}
|
||||
size_t slen = schar_get_adv(&p, lcs);
|
||||
wlv.n_extra += (int)slen - (saved_nextra > 0 ? 1 : 0);
|
||||
@@ -2509,14 +2519,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
}
|
||||
|
||||
if (wp->w_p_list) {
|
||||
mb_schar = (wlv.n_extra == 0 && wp->w_p_lcs_chars.tab3)
|
||||
? wp->w_p_lcs_chars.tab3 : wp->w_p_lcs_chars.tab1;
|
||||
mb_schar = (wlv.n_extra == 0 && lcs_tab3) ? lcs_tab3 : lcs_tab1;
|
||||
if (wp->w_p_lbr && wlv.p_extra != NULL && *wlv.p_extra != NUL) {
|
||||
wlv.sc_extra = NUL; // using p_extra from above
|
||||
} else {
|
||||
wlv.sc_extra = wp->w_p_lcs_chars.tab2;
|
||||
wlv.sc_extra = lcs_tab2;
|
||||
}
|
||||
wlv.sc_final = wp->w_p_lcs_chars.tab3;
|
||||
wlv.sc_final = lcs_tab3;
|
||||
wlv.n_attr = tab_len + 1;
|
||||
wlv.extra_attr = win_hl_attr(wp, HLF_0);
|
||||
saved_attr2 = wlv.char_attr; // save current attr
|
||||
|
||||
@@ -219,6 +219,7 @@ EXTERN const char e_cannot_switch_to_a_closing_buffer[] INIT( = N_("E1546: Canno
|
||||
EXTERN const char e_cannot_have_more_than_nr_diff_anchors[] INIT( = N_("E1549: Cannot have more than %d diff anchors"));
|
||||
EXTERN const char e_failed_to_find_all_diff_anchors[] INIT( = N_("E1550: Failed to find all diff anchors"));
|
||||
EXTERN const char e_diff_anchors_with_hidden_windows[] INIT( = N_("E1562: Diff anchors cannot be used with hidden diff windows"));
|
||||
EXTERN const char e_leadtab_requires_tab[] INIT( = N_("E1572: 'listchars' field \"leadtab\" requires \"tab\" to be specified"));
|
||||
|
||||
EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s"));
|
||||
EXTERN const char e_cannot_read_from_str_2[] INIT(= N_("E282: Cannot read from \"%s\""));
|
||||
|
||||
@@ -5464,6 +5464,15 @@ local options = {
|
||||
<
|
||||
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
|
||||
|
||||
@@ -2252,17 +2252,18 @@ static const struct chars_tab fcs_tab[] = {
|
||||
|
||||
static lcs_chars_T lcs_chars;
|
||||
static const struct chars_tab lcs_tab[] = {
|
||||
CHARSTAB_ENTRY(&lcs_chars.eol, "eol", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.ext, "extends", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.nbsp, "nbsp", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.prec, "precedes", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.space, "space", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.tab2, "tab", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.lead, "lead", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.trail, "trail", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.conceal, "conceal", NULL, NULL),
|
||||
CHARSTAB_ENTRY(NULL, "multispace", NULL, NULL),
|
||||
CHARSTAB_ENTRY(NULL, "leadmultispace", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.eol, "eol", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.ext, "extends", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.nbsp, "nbsp", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.prec, "precedes", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.space, "space", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.tab2, "tab", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.leadtab2, "leadtab", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.lead, "lead", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.trail, "trail", NULL, NULL),
|
||||
CHARSTAB_ENTRY(&lcs_chars.conceal, "conceal", NULL, NULL),
|
||||
CHARSTAB_ENTRY(NULL, "multispace", NULL, NULL),
|
||||
CHARSTAB_ENTRY(NULL, "leadmultispace", NULL, NULL),
|
||||
};
|
||||
|
||||
#undef CHARSTAB_ENTRY
|
||||
@@ -2326,6 +2327,8 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
|
||||
if (what == kListchars) {
|
||||
lcs_chars.tab1 = NUL;
|
||||
lcs_chars.tab3 = NUL;
|
||||
lcs_chars.leadtab1 = NUL;
|
||||
lcs_chars.leadtab3 = NUL;
|
||||
|
||||
if (multispace_len > 0) {
|
||||
lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(schar_T));
|
||||
@@ -2433,7 +2436,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
|
||||
}
|
||||
schar_T c2 = 0;
|
||||
schar_T c3 = 0;
|
||||
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,
|
||||
e_wrong_number_of_characters_for_field_str,
|
||||
@@ -2461,6 +2464,10 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
|
||||
lcs_chars.tab1 = c1;
|
||||
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;
|
||||
}
|
||||
@@ -2484,6 +2491,10 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
|
||||
}
|
||||
}
|
||||
|
||||
if (what == kListchars && lcs_chars.leadtab2 != NUL && lcs_chars.tab2 == NUL) {
|
||||
return e_leadtab_requires_tab;
|
||||
}
|
||||
|
||||
if (apply) {
|
||||
if (what == kListchars) {
|
||||
xfree(wp->w_p_lcs_chars.multispace);
|
||||
|
||||
@@ -277,10 +277,10 @@ let test_values = {
|
||||
\ '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',
|
||||
|
||||
@@ -349,6 +349,91 @@ func Test_listchars()
|
||||
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
|
||||
|
||||
@@ -628,7 +628,9 @@ func Test_set_completion_string_values()
|
||||
|
||||
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])
|
||||
|
||||
Reference in New Issue
Block a user