Skip to content

Commit 04958cb

Browse files
committed
patch 8.1.0105: all tab stops are the same
Problem: All tab stops are the same. Solution: Add the variable tabstop feature. (Christian Brabandt, closes #2711)
1 parent 5ec7414 commit 04958cb

31 files changed

+1751
-174
lines changed

runtime/doc/change.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,11 @@ This replaces each 'E' character with a euro sign. Read more in |<Char->|.
987987
this (that's a good habit anyway).
988988
`:retab!` may also change a sequence of spaces by
989989
<Tab> characters, which can mess up a printf().
990+
If the |+vartabs| feature is enabled then a list of
991+
tab widths separated by commas may be used in place of
992+
a single tabstop. Each value in the list represents
993+
the width of one tabstop, except the final value which
994+
applies to all following tabstops.
990995
{not in Vi}
991996

992997
*retab-example*

runtime/doc/options.txt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7172,6 +7172,10 @@ A jump table for the options with a short description can be found at |Q_op|.
71727172
set.
71737173
NOTE: This option is set to 0 when 'compatible' is set.
71747174

7175+
If Vim is compiled with the |+vartabs| feature then the value of
7176+
'softtabstop' will be ignored if |'varsofttabstop'| is set to
7177+
anything other than an empty string.
7178+
71757179
*'spell'* *'nospell'*
71767180
'spell' boolean (default off)
71777181
local to window
@@ -7723,6 +7727,10 @@ A jump table for the options with a short description can be found at |Q_op|.
77237727
though. Otherwise aligned comments will be wrong when 'tabstop' is
77247728
changed.
77257729

7730+
If Vim is compiled with the |+vartabs| feature then the value of
7731+
'tabstop' will be ignored if |'vartabstop'| is set to anything other
7732+
than an empty string.
7733+
77267734
*'tagbsearch'* *'tbs'* *'notagbsearch'* *'notbs'*
77277735
'tagbsearch' 'tbs' boolean (default on)
77287736
global
@@ -8468,6 +8476,44 @@ A jump table for the options with a short description can be found at |Q_op|.
84688476
written to disk (see |crash-recovery|). Also used for the
84698477
|CursorHold| autocommand event.
84708478

8479+
*'varsofttabstop'* *'vsts'*
8480+
'varsofttabstop' 'vsts' string (default "")
8481+
local to buffer
8482+
{only available when compiled with the |+vartabs|
8483+
feature}
8484+
{not in Vi}
8485+
A list of the number of spaces that a <Tab> counts for while editing,
8486+
such as inserting a <Tab> or using <BS>. It "feels" like variable-
8487+
width <Tab>s are being inserted, while in fact a mixture of spaces
8488+
and <Tab>s is used. Tab widths are separated with commas, with the
8489+
final value applying to all subsequent tabs.
8490+
8491+
For example, when editing assembly language files where statements
8492+
start in the 8th column and comments in the 40th, it may be useful
8493+
to use the following: >
8494+
:set varsofttabstop=8,32,8
8495+
< This will set soft tabstops at the 8th and 40th columns, and at every
8496+
8th column thereafter.
8497+
8498+
Note that the value of |'softtabstop'| will be ignored while
8499+
'varsofttabstop' is set.
8500+
8501+
*'vartabstop'* *'vts'*
8502+
'vartabstop' 'vts' string (default "")
8503+
local to buffer
8504+
{only available when compiled with the |+vartabs|
8505+
feature}
8506+
{not in Vi}
8507+
A list of the number of spaces that a <Tab> in the file counts for,
8508+
separated by commas. Each value corresponds to one tab, with the
8509+
final value applying to all subsequent tabs. For example: >
8510+
:set vartabstop=4,20,10,8
8511+
< This will make the first tab 4 spaces wide, the second 20 spaces,
8512+
the third 10 spaces, and all following tabs 8 spaces.
8513+
8514+
Note that the value of |'tabstop'| will be ignored while 'vartabstop'
8515+
is set.
8516+
84718517
*'verbose'* *'vbs'*
84728518
'verbose' 'vbs' number (default 0)
84738519
global

runtime/doc/various.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ N *+timers* the |timer_start()| function
453453
N *+title* Setting the window 'title' and 'icon'
454454
N *+toolbar* |gui-toolbar|
455455
N *+user_commands* User-defined commands. |user-commands|
456+
B *+vartabs* Variable-width tabstops. |'vartabstop'|
456457
N *+viminfo* |'viminfo'|
457458
*+vertsplit* Vertically split windows |:vsplit|; Always enabled
458459
since 8.0.1118.

runtime/optwin.vim

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,14 @@ call <SID>OptionL("ts")
856856
call append("$", "shiftwidth\tnumber of spaces used for each step of (auto)indent")
857857
call append("$", "\t(local to buffer)")
858858
call <SID>OptionL("sw")
859+
if has("vartabs")
860+
call append("$", "vartabstop\tlist of number of spaces a tab counts for")
861+
call append("$", "\t(local to buffer)")
862+
call <SID>OptionL("vts")
863+
call append("$", "varsofttabstop\tlist of number of spaces a soft tabsstop counts for")
864+
call append("$", "\t(local to buffer)")
865+
call <SID>OptionL("vsts")
866+
endif
859867
call append("$", "smarttab\ta <Tab> in an indent inserts 'shiftwidth' spaces")
860868
call <SID>BinOptionG("sta", &sta)
861869
call append("$", "softtabstop\tif non-zero, number of spaces to insert for a <Tab>")

src/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,6 +2305,7 @@ test_arglist \
23052305
test_usercommands \
23062306
test_utf8 \
23072307
test_utf8_comparisons \
2308+
test_vartabs \
23082309
test_viminfo \
23092310
test_vimscript \
23102311
test_virtualedit \

src/beval.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ get_beval_info(
124124
*lnump = lnum;
125125
*textp = lbuf;
126126
*colp = col;
127+
#ifdef FEAT_VARTABS
128+
if (beval->vts)
129+
vim_free(beval->vts);
130+
beval->vts = tabstop_copy(wp->w_buffer->b_p_vts_array);
131+
#endif
127132
beval->ts = wp->w_buffer->b_p_ts;
128133
return OK;
129134
}

src/beval.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ typedef struct BalloonEvalStruct
7171
void *clientData; /* For callback */
7272
#endif
7373

74-
int ts; /* tabstop setting for this buffer */
74+
int ts; // tabstop setting for this buffer
75+
#ifdef FEAT_VARTABS
76+
int *vts; // vartabstop setting for this buffer
77+
#endif
7578
char_u *msg;
7679
} BalloonEval;
7780

src/buffer.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ open_buffer(
271271
/*
272272
* Set/reset the Changed flag first, autocmds may change the buffer.
273273
* Apply the automatic commands, before processing the modelines.
274-
* So the modelines have priority over auto commands.
274+
* So the modelines have priority over autocommands.
275275
*/
276276
/* When reading stdin, the buffer contents always needs writing, so set
277277
* the changed flag. Unless in readonly mode: "ls | gview -".
@@ -2159,6 +2159,19 @@ free_buf_options(
21592159
clear_string_option(&buf->b_p_fo);
21602160
clear_string_option(&buf->b_p_flp);
21612161
clear_string_option(&buf->b_p_isk);
2162+
#ifdef FEAT_VARTABS
2163+
clear_string_option(&buf->b_p_vsts);
2164+
if (buf->b_p_vsts_nopaste)
2165+
vim_free(buf->b_p_vsts_nopaste);
2166+
buf->b_p_vsts_nopaste = NULL;
2167+
if (buf->b_p_vsts_array)
2168+
vim_free(buf->b_p_vsts_array);
2169+
buf->b_p_vsts_array = NULL;
2170+
clear_string_option(&buf->b_p_vts);
2171+
if (buf->b_p_vts_array)
2172+
vim_free(buf->b_p_vts_array);
2173+
buf->b_p_vts_array = NULL;
2174+
#endif
21622175
#ifdef FEAT_KEYMAP
21632176
clear_string_option(&buf->b_p_keymap);
21642177
keymap_clear(&buf->b_kmap_ga);
@@ -5190,7 +5203,7 @@ ex_buffer_all(exarg_T *eap)
51905203
win_close(wp, FALSE);
51915204
wpnext = firstwin; /* just in case an autocommand does
51925205
something strange with windows */
5193-
tpnext = first_tabpage; /* start all over...*/
5206+
tpnext = first_tabpage; /* start all over... */
51945207
open_wins = 0;
51955208
}
51965209
else
@@ -5650,8 +5663,8 @@ bt_prompt(buf_T *buf)
56505663
}
56515664

56525665
/*
5653-
* Return TRUE if "buf" is a "nofile", "acwrite" or "terminal" buffer.
5654-
* This means the buffer name is not a file name.
5666+
* Return TRUE if "buf" is a "nofile", "acwrite", "terminal" or "prompt"
5667+
* buffer. This means the buffer name is not a file name.
56555668
*/
56565669
int
56575670
bt_nofile(buf_T *buf)
@@ -5663,7 +5676,8 @@ bt_nofile(buf_T *buf)
56635676
}
56645677

56655678
/*
5666-
* Return TRUE if "buf" is a "nowrite", "nofile" or "terminal" buffer.
5679+
* Return TRUE if "buf" is a "nowrite", "nofile", "terminal" or "prompt"
5680+
* buffer.
56675681
*/
56685682
int
56695683
bt_dontwrite(buf_T *buf)

src/charset.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,16 @@ vim_strnsize(char_u *s, int len)
812812
* Also see getvcol() below.
813813
*/
814814

815-
#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
815+
#ifdef FEAT_VARTABS
816+
# define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
817+
if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) \
818+
{ \
819+
return tabstop_padding(col, (buf)->b_p_ts, (buf)->b_p_vts_array); \
820+
} \
821+
else \
822+
return ptr2cells(p);
823+
#else
824+
# define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
816825
if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) \
817826
{ \
818827
int ts; \
@@ -821,6 +830,7 @@ vim_strnsize(char_u *s, int len)
821830
} \
822831
else \
823832
return ptr2cells(p);
833+
#endif
824834

825835
int
826836
chartabsize(char_u *p, colnr_T col)
@@ -1221,8 +1231,13 @@ win_nolbr_chartabsize(
12211231

12221232
if (*s == TAB && (!wp->w_p_list || lcs_tab1))
12231233
{
1234+
# ifdef FEAT_VARTABS
1235+
return tabstop_padding(col, wp->w_buffer->b_p_ts,
1236+
wp->w_buffer->b_p_vts_array);
1237+
# else
12241238
n = wp->w_buffer->b_p_ts;
12251239
return (int)(n - (col % n));
1240+
# endif
12261241
}
12271242
n = ptr2cells(s);
12281243
/* Add one cell for a double-width character in the last column of the
@@ -1282,6 +1297,9 @@ getvcol(
12821297
char_u *line; /* start of the line */
12831298
int incr;
12841299
int head;
1300+
#ifdef FEAT_VARTABS
1301+
int *vts = wp->w_buffer->b_p_vts_array;
1302+
#endif
12851303
int ts = wp->w_buffer->b_p_ts;
12861304
int c;
12871305

@@ -1332,7 +1350,11 @@ getvcol(
13321350
}
13331351
/* A tab gets expanded, depending on the current column */
13341352
if (c == TAB)
1353+
#ifdef FEAT_VARTABS
1354+
incr = tabstop_padding(vcol, ts, vts);
1355+
#else
13351356
incr = ts - (vcol % ts);
1357+
#endif
13361358
else
13371359
{
13381360
#ifdef FEAT_MBYTE

src/edit.c

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,14 @@ edit(
742742
mincol = curwin->w_wcol;
743743
validate_cursor_col();
744744

745-
if ((int)curwin->w_wcol < mincol - curbuf->b_p_ts
745+
if (
746+
#ifdef FEAT_VARTABS
747+
(int)curwin->w_wcol < mincol - tabstop_at(
748+
get_nolist_virtcol(), curbuf->b_p_ts,
749+
curbuf->b_p_vts_array)
750+
#else
751+
(int)curwin->w_wcol < mincol - curbuf->b_p_ts
752+
#endif
746753
&& curwin->w_wrow == W_WINROW(curwin)
747754
+ curwin->w_height - 1 - p_so
748755
&& (curwin->w_cursor.lnum != curwin->w_topline
@@ -9329,23 +9336,31 @@ ins_bs(
93299336
*/
93309337
if ( mode == BACKSPACE_CHAR
93319338
&& ((p_sta && in_indent)
9332-
|| (get_sts_value() != 0
9339+
|| ((get_sts_value() != 0
9340+
#ifdef FEAT_VARTABS
9341+
|| tabstop_count(curbuf->b_p_vsts_array)
9342+
#endif
9343+
)
93339344
&& curwin->w_cursor.col > 0
93349345
&& (*(ml_get_cursor() - 1) == TAB
93359346
|| (*(ml_get_cursor() - 1) == ' '
93369347
&& (!*inserted_space_p
93379348
|| arrow_used))))))
93389349
{
9350+
#ifndef FEAT_VARTABS
93399351
int ts;
9352+
#endif
93409353
colnr_T vcol;
93419354
colnr_T want_vcol;
93429355
colnr_T start_vcol;
93439356

93449357
*inserted_space_p = FALSE;
9358+
#ifndef FEAT_VARTABS
93459359
if (p_sta && in_indent)
93469360
ts = (int)get_sw_value(curbuf);
93479361
else
93489362
ts = (int)get_sts_value();
9363+
#endif
93499364
/* Compute the virtual column where we want to be. Since
93509365
* 'showbreak' may get in the way, need to get the last column of
93519366
* the previous character. */
@@ -9354,7 +9369,15 @@ ins_bs(
93549369
dec_cursor();
93559370
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &want_vcol);
93569371
inc_cursor();
9372+
#ifdef FEAT_VARTABS
9373+
if (p_sta && in_indent)
9374+
want_vcol = (want_vcol / curbuf->b_p_sw) * curbuf->b_p_sw;
9375+
else
9376+
want_vcol = tabstop_start(want_vcol, curbuf->b_p_sts,
9377+
curbuf->b_p_vsts_array);
9378+
#else
93579379
want_vcol = (want_vcol / ts) * ts;
9380+
#endif
93589381

93599382
/* delete characters until we are at or before want_vcol */
93609383
while (vcol > want_vcol
@@ -10144,10 +10167,22 @@ ins_tab(void)
1014410167
#endif
1014510168

1014610169
/*
10147-
* When nothing special, insert TAB like a normal character
10170+
* When nothing special, insert TAB like a normal character.
1014810171
*/
1014910172
if (!curbuf->b_p_et
10173+
#ifdef FEAT_VARTABS
10174+
&& !(p_sta && ind
10175+
/* These five lines mean 'tabstop' != 'shiftwidth' */
10176+
&& ((tabstop_count(curbuf->b_p_vts_array) > 1)
10177+
|| (tabstop_count(curbuf->b_p_vts_array) == 1
10178+
&& tabstop_first(curbuf->b_p_vts_array)
10179+
!= get_sw_value(curbuf))
10180+
|| (tabstop_count(curbuf->b_p_vts_array) == 0
10181+
&& curbuf->b_p_ts != get_sw_value(curbuf))))
10182+
&& tabstop_count(curbuf->b_p_vsts_array) == 0
10183+
#else
1015010184
&& !(p_sta && ind && curbuf->b_p_ts != get_sw_value(curbuf))
10185+
#endif
1015110186
&& get_sts_value() == 0)
1015210187
return TRUE;
1015310188

@@ -10162,13 +10197,28 @@ ins_tab(void)
1016210197
#endif
1016310198
AppendToRedobuff((char_u *)"\t");
1016410199

10200+
#ifdef FEAT_VARTABS
10201+
if (p_sta && ind) /* insert tab in indent, use 'shiftwidth' */
10202+
{
10203+
temp = (int)curbuf->b_p_sw;
10204+
temp -= get_nolist_virtcol() % temp;
10205+
}
10206+
else if (tabstop_count(curbuf->b_p_vsts_array) > 0 || curbuf->b_p_sts > 0)
10207+
/* use 'softtabstop' when set */
10208+
temp = tabstop_padding(get_nolist_virtcol(), curbuf->b_p_sts,
10209+
curbuf->b_p_vsts_array);
10210+
else /* otherwise use 'tabstop' */
10211+
temp = tabstop_padding(get_nolist_virtcol(), curbuf->b_p_ts,
10212+
curbuf->b_p_vts_array);
10213+
#else
1016510214
if (p_sta && ind) /* insert tab in indent, use 'shiftwidth' */
1016610215
temp = (int)get_sw_value(curbuf);
1016710216
else if (curbuf->b_p_sts != 0) /* use 'softtabstop' when set */
1016810217
temp = (int)get_sts_value();
1016910218
else /* otherwise use 'tabstop' */
1017010219
temp = (int)curbuf->b_p_ts;
1017110220
temp -= get_nolist_virtcol() % temp;
10221+
#endif
1017210222

1017310223
/*
1017410224
* Insert the first space with ins_char(). It will delete one char in
@@ -10193,7 +10243,13 @@ ins_tab(void)
1019310243
/*
1019410244
* When 'expandtab' not set: Replace spaces by TABs where possible.
1019510245
*/
10246+
#ifdef FEAT_VARTABS
10247+
if (!curbuf->b_p_et && (tabstop_count(curbuf->b_p_vsts_array) > 0
10248+
|| get_sts_value() > 0
10249+
|| (p_sta && ind)))
10250+
#else
1019610251
if (!curbuf->b_p_et && (get_sts_value() || (p_sta && ind)))
10252+
#endif
1019710253
{
1019810254
char_u *ptr;
1019910255
#ifdef FEAT_VREPLACE

0 commit comments

Comments
 (0)