On Di, 21 Mai 2013, Matthew Winn wrote:

> On Tue, 21 May 2013 09:51:37 -0700 (PDT), Kirill Serkh <[email protected]>
> wrote:
> 
> > In 2007 Matthew Winn published a patch against vim 7.1.135 for non-uniform 
> > tabstops (i.e. setting tabstops at user-defined columns) [1]. It was voted 
> > on in 2008 [2] but never made its way into vim (?).
> [snip]
> > I'm trying to figure out
> > 1) Where can the most recent version of the patch be found?
> 
> I don't have the latest version. I wrote it while out of work and someone
> else took it over some considerable time later when I didn't have the time
> to maintain it any more, and then I lost my compilation environment so I
> couldn't have maintained it anyway.
> 
> > 2) Has there been any recent interest in this feature?
> 
> I'm still interested in it.
> 
> > 3) Was there a particular reason this patch never made its way into vim?
> 
> Beats me. I'm still rather annoyed about that, as I thought the whole point
> of having a TODO list was that things on it were to be done.

I've extracted the patch from the vim_extended repository and updated it 
for Vim 7.3.1004. Attached you'll find it. I don't really know how to 
test it though, but the provided test case (test100.in) passes. So if 
anybody is interested in that patch and likes to try it out, here is it.

(I don't know how to convince Bram to include it.)

regards,
Christian

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.


diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -888,6 +888,11 @@
 			{not in Vi}
 			Not available when |+ex_extra| feature was disabled at
 			compile time.
+			If the |+vartabs| feature is enabled then a list of
+			tab widths separated by commas may be used in place of
+			a single tabstop.  Each value in the list represents
+			the width of one tabstop, except the final value which
+			applies to all following tabstops.
 
 							*retab-example*
 Example for using autocommands and ":retab" to edit a file which is stored
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -6442,6 +6442,10 @@
 	set.
 	NOTE: This option is set to 0 when 'compatible' is set.
 
+	If Vim is compiled with the |+vartabs| feature then the value of
+	'softtabstop' will be ignored if |'varsofttabstop'| is set to
+	anything other than an empty string.
+
 						*'spell'* *'nospell'*
 'spell'			boolean	(default off)
 			local to window
@@ -6979,6 +6983,10 @@
 	   though.  Otherwise aligned comments will be wrong when 'tabstop' is
 	   changed.
 
+	If Vim is compiled with the |+vartabs| feature then the value of
+	'tabstop' will be ignored if |'vartabstop'| is set to anything other
+	than an empty string.
+
 			*'tagbsearch'* *'tbs'* *'notagbsearch'* *'notbs'*
 'tagbsearch' 'tbs'	boolean	(default on)
 			global
@@ -7608,6 +7616,44 @@
 	written to disk (see |crash-recovery|).  Also used for the
 	|CursorHold| autocommand event.
 
+					*'varsofttabstop'* *'vsts'*
+'varsofttabstop' 'vsts'	string	(default "")
+			local to buffer
+			{only available when compiled with the |+vartabs|
+			feature}
+			{not in Vi}
+	A list of the number of spaces that a <Tab> counts for while editing,
+	such as inserting a <Tab> or using <BS>.  It "feels" like variable-
+	width <Tab>s are being inserted, while in fact a mixture of spaces
+	and <Tab>s is used.  Tab widths are separated with commas, with the
+	final value applying to all subsequent tabs.
+	
+	For example, when editing assembly language files where statements
+	start in the 8th column and comments in the 40th, it may be useful
+	to use the following: >
+		:set varsofttabstop=8,32,8
+<	This will set soft tabstops at the 8th and 40th columns, and at every
+	8th column thereafter.
+
+	Note that the value of |'softtabstop'| will be ignored while
+	'varsofttabstop' is set.
+
+						*'vartabstop'* *'vts'*
+'vartabstop' 'vts'	string	(default 8)
+			local to buffer
+			{only available when compiled with the |+vartabs|
+			feature}
+			{not in Vi}
+	A list of the number of spaces that a <Tab> in the file counts for,
+	separated by commas.  Each value corresponds to one tab, with the
+	final value applying to all subsequent tabs. For example: >
+		:set vartabstop=4,20,10,8
+<	This will make the first tab 4 spaces wide, the second 20 spaces,
+	the third 10 spaces, and all following tabs 8 spaces.
+
+	Note that the value of |'tabstop'| will be ignored while 'vartabstop'
+	is set.
+
 						*'verbose'* *'vbs'*
 'verbose' 'vbs'		number	(default 0)
 			global
diff --git a/runtime/doc/tags b/runtime/doc/tags
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -1048,6 +1048,8 @@
 'updatetime'	options.txt	/*'updatetime'*
 'ur'	options.txt	/*'ur'*
 'ut'	options.txt	/*'ut'*
+'varsofttabstop'	options.txt	/*'varsofttabstop'*
+'vartabstop'	options.txt	/*'vartabstop'*
 'vb'	options.txt	/*'vb'*
 'vbs'	options.txt	/*'vbs'*
 'vdir'	options.txt	/*'vdir'*
@@ -1062,6 +1064,8 @@
 'virtualedit'	options.txt	/*'virtualedit'*
 'visualbell'	options.txt	/*'visualbell'*
 'vop'	options.txt	/*'vop'*
+'vsts'	options.txt	/*'vsts'*
+'vts'	options.txt	/*'vts'*
 'w1200'	vi_diff.txt	/*'w1200'*
 'w300'	vi_diff.txt	/*'w300'*
 'w9600'	vi_diff.txt	/*'w9600'*
@@ -1242,6 +1246,7 @@
 +title	various.txt	/*+title*
 +toolbar	various.txt	/*+toolbar*
 +user_commands	various.txt	/*+user_commands*
++vartabs	various.txt	/*+vartabs*
 +vertsplit	various.txt	/*+vertsplit*
 +viminfo	various.txt	/*+viminfo*
 +virtualedit	various.txt	/*+virtualedit*
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -414,6 +414,7 @@
 N  *+title*		Setting the window 'title' and 'icon'
 N  *+toolbar*		|gui-toolbar|
 N  *+user_commands*	User-defined commands. |user-commands|
+B  *+vartabs*		Variable-width tabstops. |'vartabstop'|
 N  *+viminfo*		|'viminfo'|
 N  *+vertsplit*		Vertically split windows |:vsplit|
 N  *+virtualedit*	|'virtualedit'|
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -806,6 +806,14 @@
 call append("$", "shiftwidth\tnumber of spaces used for each step of (auto)indent")
 call append("$", "\t(local to buffer)")
 call <SID>OptionL("sw")
+if has("vartabs")
+  call append("$", "vartabstop\tlist of number of spaces a tab counts for")
+  call append("$", "\t(local to buffer)")
+  call <SID>OptionL("vts")
+  call append("$", "varsofttabstop\tlist of number of spaces a soft tabsstop counts for")
+  call append("$", "\t(local to buffer)")
+  call <SID>OptionL("vsts")
+endif
 call append("$", "smarttab\ta <Tab> in an indent inserts 'shiftwidth' spaces")
 call <SID>BinOptionG("sta", &sta)
 call append("$", "softtabstop\tif non-zero, number of spaces to insert for a <Tab>")
diff --git a/src/buffer.c b/src/buffer.c
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1881,6 +1881,19 @@
     clear_string_option(&buf->b_p_fo);
     clear_string_option(&buf->b_p_flp);
     clear_string_option(&buf->b_p_isk);
+#ifdef FEAT_VARTABS
+    clear_string_option(&buf->b_p_vsts);
+    if (buf->b_p_vsts_nopaste)
+	vim_free(buf->b_p_vsts_nopaste);
+    buf->b_p_vsts_nopaste = 0;
+    if (buf->b_p_vsts_ary)
+	vim_free(buf->b_p_vsts_ary);
+    buf->b_p_vsts_ary = 0;
+    clear_string_option(&buf->b_p_vts);
+    if (buf->b_p_vts_ary)
+	vim_free(buf->b_p_vts_ary);
+    buf->b_p_vts_ary = 0;
+#endif
 #ifdef FEAT_KEYMAP
     clear_string_option(&buf->b_p_keymap);
     ga_clear(&buf->b_kmap_ga);
diff --git a/src/charset.c b/src/charset.c
--- a/src/charset.c
+++ b/src/charset.c
@@ -815,6 +815,15 @@
  * Also see getvcol() below.
  */
 
+#ifdef FEAT_VARTABS
+#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
+    if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) \
+    { \
+	return tabstop_padding(col, (buf)->b_p_ts, (buf)->b_p_vts_ary); \
+    } \
+    else \
+	return ptr2cells(p);
+#else
 #define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
     if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) \
     { \
@@ -824,6 +833,7 @@
     } \
     else \
 	return ptr2cells(p);
+#endif
 
 #if defined(FEAT_VREPLACE) || defined(FEAT_EX_EXTRA) || defined(FEAT_GUI) \
 	|| defined(FEAT_VIRTUALEDIT) || defined(PROTO)
@@ -1075,6 +1085,9 @@
     colnr_T	col2;
     colnr_T	colmax;
     int		added;
+# ifdef FEAT_VARTABS
+    colnr_T	orig_col = col;
+# endif
 # ifdef FEAT_MBYTE
     int		mb_added = 0;
 # else
@@ -1182,7 +1195,15 @@
 	{
 	    added = vim_strsize(p_sbr);
 	    if (tab_corr)
+	    {
+# ifdef FEAT_VARTABS
+		int ts = tabstop_at(orig_col, wp->w_buffer->b_p_ts,
+					      wp->w_buffer->b_p_vts_ary);
+		size += (added / ts) * ts;
+# else
 		size += (added / wp->w_buffer->b_p_ts) * wp->w_buffer->b_p_ts;
+# endif
+	    }
 	    else
 		size += added;
 	    if (col != 0)
@@ -1212,8 +1233,13 @@
 
     if (*s == TAB && (!wp->w_p_list || lcs_tab1))
     {
+# ifdef FEAT_VARTABS
+	return tabstop_padding(col, wp->w_buffer->b_p_ts,
+				    wp->w_buffer->b_p_vts_ary);
+# else
 	n = wp->w_buffer->b_p_ts;
 	return (int)(n - (col % n));
+# endif
     }
     n = ptr2cells(s);
     /* Add one cell for a double-width character in the last column of the
@@ -1276,6 +1302,9 @@
     char_u	*posptr;	/* points to char at pos->col */
     int		incr;
     int		head;
+#ifdef FEAT_VARTABS
+    int		*vts = wp->w_buffer->b_p_vts_ary;
+#endif
     int		ts = wp->w_buffer->b_p_ts;
     int		c;
 
@@ -1314,7 +1343,11 @@
 	    }
 	    /* A tab gets expanded, depending on the current column */
 	    if (c == TAB)
+#ifdef FEAT_VARTABS
+		incr = tabstop_padding(vcol, ts, vts);
+#else
 		incr = ts - (vcol % ts);
+#endif
 	    else
 	    {
 #ifdef FEAT_MBYTE
diff --git a/src/edit.c b/src/edit.c
--- a/src/edit.c
+++ b/src/edit.c
@@ -694,7 +694,13 @@
 	    mincol = curwin->w_wcol;
 	    validate_cursor_col();
 
-	    if ((int)curwin->w_wcol < mincol - curbuf->b_p_ts
+	    if (
+#ifdef FEAT_VARTABS
+		(int)curwin->w_wcol < mincol
+		    - tabstop_at(get_nolist_virtcol(), curbuf->b_p_ts, curbuf->b_p_vts_ary)
+#else
+		(int)curwin->w_wcol < mincol - curbuf->b_p_ts
+#endif
 		    && curwin->w_wrow == W_WINROW(curwin)
 						 + curwin->w_height - 1 - p_so
 		    && (curwin->w_cursor.lnum != curwin->w_topline
@@ -8926,23 +8932,33 @@
 	 */
 	if (	   mode == BACKSPACE_CHAR
 		&& ((p_sta && in_indent)
-		    || (get_sts_value() != 0
+		    || (
+#ifdef FEAT_VARTABS
+			(tabstop_count(curbuf->b_p_vsts_ary) != 0
+			    || curbuf->b_p_sts != 0)
+#else
+			curbuf->b_p_sts != 0
+#endif
 			&& curwin->w_cursor.col > 0
 			&& (*(ml_get_cursor() - 1) == TAB
 			    || (*(ml_get_cursor() - 1) == ' '
 				&& (!*inserted_space_p
 				    || arrow_used))))))
 	{
+#ifndef FEAT_VARTABS
 	    int		ts;
+#endif
 	    colnr_T	vcol;
 	    colnr_T	want_vcol;
 	    colnr_T	start_vcol;
 
 	    *inserted_space_p = FALSE;
+#ifndef FEAT_VARTABS
 	    if (p_sta && in_indent)
 		ts = (int)get_sw_value();
 	    else
 		ts = (int)get_sts_value();
+#endif
 	    /* Compute the virtual column where we want to be.  Since
 	     * 'showbreak' may get in the way, need to get the last column of
 	     * the previous character. */
@@ -8951,7 +8967,15 @@
 	    dec_cursor();
 	    getvcol(curwin, &curwin->w_cursor, NULL, NULL, &want_vcol);
 	    inc_cursor();
+#ifdef FEAT_VARTABS
+	    if (p_sta && in_indent)
+		want_vcol = (want_vcol / curbuf->b_p_sw) * curbuf->b_p_sw;
+	    else
+		want_vcol = tabstop_start(want_vcol, curbuf->b_p_sts,
+						     curbuf->b_p_vsts_ary);
+#else
 	    want_vcol = (want_vcol / ts) * ts;
+#endif
 
 	    /* delete characters until we are at or before want_vcol */
 	    while (vcol > want_vcol
@@ -9629,8 +9653,21 @@
      * When nothing special, insert TAB like a normal character
      */
     if (!curbuf->b_p_et
+#ifdef FEAT_VARTABS
+	    && !(p_sta && ind
+		/* These five lines mean 'tabstop' != 'shiftwidth' */
+		&& ((tabstop_count(curbuf->b_p_vts_ary) > 1)
+		    || (tabstop_count(curbuf->b_p_vts_ary) == 1
+		        && tabstop_first(curbuf->b_p_vts_ary) != get_sw_value())
+	            || (tabstop_count(curbuf->b_p_vts_ary) == 0
+		        && curbuf->b_p_ts != get_sw_value())))
+	    && tabstop_count(curbuf->b_p_vsts_ary) == 0
+	    && get_sts_value() == 0
+#else
 	    && !(p_sta && ind && curbuf->b_p_ts != get_sw_value())
-	    && get_sts_value() == 0)
+	    && get_sts_value() == 0
+#endif
+	    )
 	return TRUE;
 
     if (stop_arrow() == FAIL)
@@ -9644,6 +9681,20 @@
 #endif
     AppendToRedobuff((char_u *)"\t");
 
+#ifdef FEAT_VARTABS
+    if (p_sta && ind)		/* insert tab in indent, use 'shiftwidth' */
+    {
+	temp = (int)curbuf->b_p_sw;
+	temp -= get_nolist_virtcol() % temp;
+    }
+    else if (tabstop_count(curbuf->b_p_vsts_ary) > 0 || curbuf->b_p_sts > 0)
+	                        /* use 'softtabstop' when set */
+	temp = tabstop_padding(get_nolist_virtcol(), curbuf->b_p_sts,
+						     curbuf->b_p_vsts_ary);
+    else			/* otherwise use 'tabstop' */
+	temp = tabstop_padding(get_nolist_virtcol(), curbuf->b_p_ts,
+						     curbuf->b_p_vts_ary);
+#else
     if (p_sta && ind)		/* insert tab in indent, use 'shiftwidth' */
 	temp = (int)get_sw_value();
     else if (curbuf->b_p_sts != 0) /* use 'softtabstop' when set */
@@ -9651,6 +9702,7 @@
     else			/* otherwise use 'tabstop' */
 	temp = (int)curbuf->b_p_ts;
     temp -= get_nolist_virtcol() % temp;
+#endif
 
     /*
      * Insert the first space with ins_char().	It will delete one char in
@@ -9675,7 +9727,13 @@
     /*
      * When 'expandtab' not set: Replace spaces by TABs where possible.
      */
+#ifdef FEAT_VARTABS
+    if (!curbuf->b_p_et && (tabstop_count(curbuf->b_p_vsts_ary) > 0
+                            || get_sts_value() > 0
+			    || (p_sta && ind)))
+#else
     if (!curbuf->b_p_et && (get_sts_value() || (p_sta && ind)))
+#endif
     {
 	char_u		*ptr;
 #ifdef FEAT_VREPLACE
diff --git a/src/eval.c b/src/eval.c
--- a/src/eval.c
+++ b/src/eval.c
@@ -12452,6 +12452,9 @@
 #ifdef FEAT_VIMINFO
 	"viminfo",
 #endif
+#ifdef FEAT_VARTABS
+	"vartabs",
+#endif
 #ifdef FEAT_VERTSPLIT
 	"vertsplit",
 #endif
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -592,12 +592,17 @@
     long	vcol;
     long	start_col = 0;		/* For start of white-space string */
     long	start_vcol = 0;		/* For start of white-space string */
-    int		temp;
     long	old_len;
     char_u	*ptr;
     char_u	*new_line = (char_u *)1;    /* init to non-NULL */
     int		did_undo;		/* called u_save for current line */
+#ifdef FEAT_VARTABS
+    int		*new_ts = 0;
+    char_u	*new_ts_str;		/* string value of tab argument */
+#else
+    int		temp;
     int		new_ts;
+#endif
     int		save_list;
     linenr_T	first_line = 0;		/* first changed line */
     linenr_T	last_line = 0;		/* last changed line */
@@ -605,6 +610,27 @@
     save_list = curwin->w_p_list;
     curwin->w_p_list = 0;	    /* don't want list mode here */
 
+#ifdef FEAT_VARTABS
+    new_ts_str = eap->arg;
+    if (vim_isdigit(*(eap->arg)) && !tabstop_set(eap->arg, &new_ts))
+    {
+	EMSG(_(e_invarg));
+	return;
+    }
+    while (vim_isdigit(*(eap->arg)) || *(eap->arg) == ',')
+	++(eap->arg);
+
+    /* This ensures that either new_ts and new_ts_str are freshly allocated,
+     * or new_ts points to an existing array and new_ts_str is null.
+     */
+    if (new_ts == 0)
+    {
+	new_ts = curbuf->b_p_vts_ary;
+	new_ts_str = NULL;
+    }
+    else
+	new_ts_str = vim_strnsave(new_ts_str, eap->arg - new_ts_str);
+#else
     new_ts = getdigits(&(eap->arg));
     if (new_ts < 0)
     {
@@ -613,6 +639,7 @@
     }
     if (new_ts == 0)
 	new_ts = curbuf->b_p_ts;
+#endif
     for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum)
     {
 	ptr = ml_get(lnum);
@@ -645,6 +672,15 @@
 		    num_tabs = 0;
 		    if (!curbuf->b_p_et)
 		    {
+#ifdef FEAT_VARTABS
+			int t, s;
+			tabstop_fromto(start_vcol, vcol,
+				       tabstop_count(new_ts)? 0: curbuf->b_p_ts,
+				       new_ts,
+				       &t, &s);
+			num_tabs = t;
+			num_spaces = s;
+#else
 			temp = new_ts - (start_vcol % new_ts);
 			if (num_spaces >= temp)
 			{
@@ -653,6 +689,7 @@
 			}
 			num_tabs += num_spaces / new_ts;
 			num_spaces -= (num_spaces / new_ts) * new_ts;
+#endif
 		    }
 		    if (curbuf->b_p_et || got_tab ||
 					(num_spaces + num_tabs < len))
@@ -710,14 +747,54 @@
     if (got_int)
 	EMSG(_(e_interr));
 
+#ifdef FEAT_VARTABS
+    /* If a single value was given then it can be considered equal to
+     * either the value of 'tabstop' or the value of 'vartabstop'.
+     */
+    if (tabstop_count(curbuf->b_p_vts_ary) == 0
+	&& tabstop_count(new_ts) == 1
+	&& curbuf->b_p_ts == tabstop_first(new_ts))
+	; /* not changed */
+    else if (tabstop_count(curbuf->b_p_vts_ary) > 0
+        && tabstop_eq(curbuf->b_p_vts_ary, new_ts))
+	; /* not changed */
+    else
+	redraw_curbuf_later(NOT_VALID);
+#else
     if (curbuf->b_p_ts != new_ts)
 	redraw_curbuf_later(NOT_VALID);
+#endif
     if (first_line != 0)
 	changed_lines(first_line, 0, last_line + 1, 0L);
 
     curwin->w_p_list = save_list;	/* restore 'list' */
 
+#ifdef FEAT_VARTABS
+    if (new_ts_str != NULL)		/* set the new tabstop */
+    {
+	/* If 'vartabstop' is in use or if the value given to retab has more
+	 * than one tabstop then update 'vartabstop'.
+	 */
+	int* old_vts_ary = curbuf->b_p_vts_ary;
+	if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_ts) > 1) {
+	    set_string_option_direct((char_u *)"vts", -1, new_ts_str,
+						    OPT_FREE|OPT_LOCAL, 0);
+	    vim_free(new_ts_str);
+	    curbuf->b_p_vts_ary = new_ts;
+	    vim_free(old_vts_ary);
+	}
+	/* If 'vartabstop' wasn't in use and a single value was given to
+	 * retab then update 'tabstop'.
+	 */
+	else
+	{
+	    curbuf->b_p_ts = tabstop_first(new_ts);
+	    vim_free(new_ts);
+	}
+    }
+#else
     curbuf->b_p_ts = new_ts;
+#endif
     coladvance(curwin->w_curswant);
 
     u_clearline();
diff --git a/src/feature.h b/src/feature.h
--- a/src/feature.h
+++ b/src/feature.h
@@ -847,6 +847,13 @@
 #endif
 
 /*
+ * +vartabs		'vartabstop' and 'varsofttabstop' options.
+ */
+#ifdef FEAT_BIG
+# define FEAT_VARTABS
+#endif
+
+/*
  * Preferences:
  * ============
  */
diff --git a/src/gui_beval.c b/src/gui_beval.c
--- a/src/gui_beval.c
+++ b/src/gui_beval.c
@@ -219,6 +219,9 @@
 	beval->msg = mesg;
 	beval->msgCB = mesgCB;
 	beval->clientData = clientData;
+#ifdef FEAT_VARTABS
+	beval->vts = 0;
+#endif
 
 	/*
 	 * Set up event handler which will keep its eyes on the pointer,
@@ -262,6 +265,10 @@
 # else
     XtDestroyWidget(beval->balloonShell);
 # endif
+# ifdef FEAT_VARTABS
+    if (beval->vts)
+	vim_free(beval->vts);
+# endif
     vim_free(beval);
 }
 #endif
@@ -401,6 +408,11 @@
 		*lnump = lnum;
 		*textp = lbuf;
 		*colp = col;
+#ifdef FEAT_VARTABS
+		if (beval->vts)
+		    vim_free(beval->vts);
+		beval->vts = tabstop_copy(wp->w_buffer->b_p_vts_ary);
+#endif
 		beval->ts = wp->w_buffer->b_p_ts;
 		return OK;
 	    }
diff --git a/src/gui_beval.h b/src/gui_beval.h
--- a/src/gui_beval.h
+++ b/src/gui_beval.h
@@ -58,6 +58,9 @@
     BeState		showState;	/* tells us whats currently going on */
 # endif
 #endif
+#ifdef FEAT_VARTABS
+    int			*vts;		/* vartabstop setting for this buffer */
+#endif
     int			ts;		/* tabstop setting for this buffer */
     char_u		*msg;
     void		(*msgCB)__ARGS((struct BalloonEvalStruct *, int));
diff --git a/src/gui_w32.c b/src/gui_w32.c
--- a/src/gui_w32.c
+++ b/src/gui_w32.c
@@ -4728,6 +4728,9 @@
 	beval->msg = mesg;
 	beval->msgCB = mesgCB;
 	beval->clientData = clientData;
+#ifdef FEAT_VARTABS
+	beval->vts = 0;
+#endif
 
 	InitCommonControls();
 	cur_beval = beval;
@@ -4786,6 +4789,10 @@
 gui_mch_destroy_beval_area(beval)
     BalloonEval	*beval;
 {
+#ifdef FEAT_VARTABS
+    if (beval->vts)
+	vim_free(beval->vts);
+#endif
     vim_free(beval);
 }
 #endif /* FEAT_BEVAL */
diff --git a/src/hardcopy.c b/src/hardcopy.c
--- a/src/hardcopy.c
+++ b/src/hardcopy.c
@@ -900,7 +900,12 @@
 	if (line[col] == TAB || tab_spaces != 0)
 	{
 	    if (tab_spaces == 0)
+#ifdef FEAT_VARTABS
+		tab_spaces = tabstop_padding(print_pos, curbuf->b_p_ts,
+							curbuf->b_p_vts_ary);
+#else
 		tab_spaces = (int)(curbuf->b_p_ts - (print_pos % curbuf->b_p_ts));
+#endif
 
 	    while (tab_spaces > 0)
 	    {
diff --git a/src/message.c b/src/message.c
--- a/src/message.c
+++ b/src/message.c
@@ -1707,7 +1707,11 @@
 	    if (c == TAB && (!list || lcs_tab1))
 	    {
 		/* tab amount depends on current column */
+#ifdef FEAT_VARTABS
+		n_extra = tabstop_padding(col, curbuf->b_p_ts, curbuf->b_p_vts_ary) - 1;
+#else
 		n_extra = curbuf->b_p_ts - col % curbuf->b_p_ts - 1;
+#endif
 		if (!list)
 		{
 		    c = ' ';
diff --git a/src/misc1.c b/src/misc1.c
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -29,7 +29,11 @@
     int
 get_indent()
 {
+#ifdef FEAT_VARTABS
+    return get_indent_str_vtab(ml_get_curline(), curbuf->b_p_ts, curbuf->b_p_vts_ary);
+#else
     return get_indent_str(ml_get_curline(), (int)curbuf->b_p_ts);
+#endif
 }
 
 /*
@@ -39,7 +43,11 @@
 get_indent_lnum(lnum)
     linenr_T	lnum;
 {
+#ifdef FEAT_VARTABS
+    return get_indent_str_vtab(ml_get(lnum), curbuf->b_p_ts, curbuf->b_p_vts_ary);
+#else
     return get_indent_str(ml_get(lnum), (int)curbuf->b_p_ts);
+#endif
 }
 
 #if defined(FEAT_FOLDING) || defined(PROTO)
@@ -52,7 +60,11 @@
     buf_T	*buf;
     linenr_T	lnum;
 {
+#ifdef FEAT_VARTABS
+    return get_indent_str_vtab(ml_get_buf(buf, lnum, FALSE), curbuf->b_p_ts, buf->b_p_vts_ary);
+#else
     return get_indent_str(ml_get_buf(buf, lnum, FALSE), (int)buf->b_p_ts);
+#endif
 }
 #endif
 
@@ -79,6 +91,32 @@
     return count;
 }
 
+#ifdef FEAT_VARTABS
+/*
+ * count the size (in window cells) of the indent in line "ptr", using
+ * variable tabstops
+ */
+    int
+get_indent_str_vtab(ptr, ts, vts)
+    char_u	*ptr;
+    int		ts;
+    int		*vts;
+{
+    int		count = 0;
+
+    for ( ; *ptr; ++ptr)
+    {
+	if (*ptr == TAB)    /* count a tab for what it is worth */
+	    count += tabstop_padding(count, ts, vts);
+	else if (*ptr == ' ')
+	    ++count;		/* count a space for one */
+	else
+	    break;
+    }
+    return count;
+}
+#endif
+
 /*
  * Set the indent of the current line.
  * Leaves the cursor on the first non-blank in the line.
@@ -103,6 +141,9 @@
     int		line_len;
     int		doit = FALSE;
     int		ind_done = 0;	    /* measured in spaces */
+#ifdef FEAT_VARTABS
+    int		ind_col = 0;
+#endif
     int		tab_pad;
     int		retval = FALSE;
     int		orig_char_len = -1; /* number of initial whitespace chars when
@@ -135,8 +176,13 @@
 	    {
 		if (*p == TAB)
 		{
+#ifdef FEAT_VARTABS
+		    tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts,
+							curbuf->b_p_vts_ary);
+#else
 		    tab_pad = (int)curbuf->b_p_ts
 					   - (ind_done % (int)curbuf->b_p_ts);
+#endif
 		    /* stop if this tab will overshoot the target */
 		    if (todo < tab_pad)
 			break;
@@ -153,23 +199,50 @@
 		++p;
 	    }
 
+#ifdef FEAT_VARTABS
+	    /* These diverge from this point. */
+	    ind_col = ind_done;
+#endif
 	    /* Set initial number of whitespace chars to copy if we are
 	     * preserving indent but expandtab is set */
 	    if (curbuf->b_p_et)
 		orig_char_len = ind_len;
 
 	    /* Fill to next tabstop with a tab, if possible */
+#ifdef FEAT_VARTABS
+	    tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts,
+						curbuf->b_p_vts_ary);
+#else
 	    tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
+#endif
 	    if (todo >= tab_pad && orig_char_len == -1)
 	    {
 		doit = TRUE;
 		todo -= tab_pad;
 		++ind_len;
 		/* ind_done += tab_pad; */
+#ifdef FEAT_VARTABS
+		ind_col += tab_pad;
+#endif
 	    }
 	}
 
 	/* count tabs required for indent */
+#ifdef FEAT_VARTABS
+	for (;;)
+	{
+	    tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts, curbuf->b_p_vts_ary);
+	    if (todo < tab_pad)
+		break;
+	    if (*p != TAB)
+		doit = TRUE;
+	    else
+		++p;
+	    todo -= tab_pad;
+	    ++ind_len;
+	    ind_col += tab_pad;
+	}
+#else
 	while (todo >= (int)curbuf->b_p_ts)
 	{
 	    if (*p != TAB)
@@ -180,6 +253,7 @@
 	    ++ind_len;
 	    /* ind_done += (int)curbuf->b_p_ts; */
 	}
+#endif
     }
     /* count spaces required for indent */
     while (todo > 0)
@@ -254,8 +328,13 @@
 	    {
 		if (*p == TAB)
 		{
+#ifdef FEAT_VARTABS
+		    tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts,
+							curbuf->b_p_vts_ary);
+#else
 		    tab_pad = (int)curbuf->b_p_ts
 					   - (ind_done % (int)curbuf->b_p_ts);
+#endif
 		    /* stop if this tab will overshoot the target */
 		    if (todo < tab_pad)
 			break;
@@ -271,21 +350,41 @@
 	    }
 
 	    /* Fill to next tabstop with a tab, if possible */
+#ifdef FEAT_VARTABS
+	    tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts,
+						curbuf->b_p_vts_ary);
+#else
 	    tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
+#endif
 	    if (todo >= tab_pad)
 	    {
 		*s++ = TAB;
 		todo -= tab_pad;
+#ifdef FEAT_VARTABS
+		ind_done += tab_pad;
+#endif
 	    }
 
 	    p = skipwhite(p);
 	}
 
+#ifdef FEAT_VARTABS
+	for (;;)
+	{
+	    tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, curbuf->b_p_vts_ary);
+	    if (todo < tab_pad)
+		break;
+	    *s++ = TAB;
+	    todo -= tab_pad;
+	    ind_done += tab_pad;
+	}
+#else
 	while (todo >= (int)curbuf->b_p_ts)
 	{
 	    *s++ = TAB;
 	    todo -= (int)curbuf->b_p_ts;
 	}
+#endif
     }
     while (todo > 0)
     {
@@ -332,6 +431,9 @@
     int		tab_pad;
     int		ind_done;
     int		round;
+#ifdef FEAT_VARTABS
+    int		ind_col;
+#endif
 
     /* Round 1: compute the number of characters needed for the indent
      * Round 2: copy the characters. */
@@ -340,6 +442,9 @@
 	todo = size;
 	ind_len = 0;
 	ind_done = 0;
+#ifdef FEAT_VARTABS
+	ind_col = 0;
+#endif
 	s = src;
 
 	/* Count/copy the usable portion of the source line */
@@ -347,18 +452,28 @@
 	{
 	    if (*s == TAB)
 	    {
+#ifdef FEAT_VARTABS
+		tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, curbuf->b_p_vts_ary);
+#else
 		tab_pad = (int)curbuf->b_p_ts
 					   - (ind_done % (int)curbuf->b_p_ts);
+#endif
 		/* Stop if this tab will overshoot the target */
 		if (todo < tab_pad)
 		    break;
 		todo -= tab_pad;
 		ind_done += tab_pad;
+#ifdef FEAT_VARTABS
+		ind_col += tab_pad;
+#endif
 	    }
 	    else
 	    {
 		--todo;
 		++ind_done;
+#ifdef FEAT_VARTABS
+		++ind_col;
+#endif
 	    }
 	    ++ind_len;
 	    if (p != NULL)
@@ -367,16 +482,36 @@
 	}
 
 	/* Fill to next tabstop with a tab, if possible */
+#ifdef FEAT_VARTABS
+	tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, curbuf->b_p_vts_ary);
+#else
 	tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
+#endif
 	if (todo >= tab_pad && !curbuf->b_p_et)
 	{
 	    todo -= tab_pad;
 	    ++ind_len;
+#ifdef FEAT_VARTABS
+	    ind_col += tab_pad;
+#endif
 	    if (p != NULL)
 		*p++ = TAB;
 	}
 
 	/* Add tabs required for indent */
+#ifdef FEAT_VARTABS
+	for (;;)
+	{
+	    tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts, curbuf->b_p_vts_ary);
+	    if (todo < tab_pad)
+		break;
+	    todo -= tab_pad;
+	    ++ind_len;
+	    ind_col += tab_pad;
+	    if (p != NULL)
+		*p++ = TAB;
+	}
+#else
 	while (todo >= (int)curbuf->b_p_ts && !curbuf->b_p_et)
 	{
 	    todo -= (int)curbuf->b_p_ts;
@@ -384,6 +519,7 @@
 	    if (p != NULL)
 		*p++ = TAB;
 	}
+#endif
 
 	/* Count/add spaces required for indent */
 	while (todo > 0)
@@ -667,7 +803,12 @@
 	/*
 	 * count white space on current line
 	 */
+#ifdef FEAT_VARTABS
+	newindent = get_indent_str_vtab(saved_line, curbuf->b_p_ts,
+						    curbuf->b_p_vts_ary);
+#else
 	newindent = get_indent_str(saved_line, (int)curbuf->b_p_ts);
+#endif
 	if (newindent == 0 && !(flags & OPENLINE_COM_LIST))
 	    newindent = second_line_indent; /* for ^^D command in insert mode */
 
@@ -1190,7 +1331,12 @@
 					|| do_si
 #endif
 							   )
+#ifdef FEAT_VARTABS
+			newindent = get_indent_str_vtab(leader, curbuf->b_p_ts,
+								curbuf->b_p_vts_ary);
+#else
 			newindent = get_indent_str(leader, (int)curbuf->b_p_ts);
+#endif
 
 		    /* Add the indent offset */
 		    if (newindent + off < 0)
diff --git a/src/ops.c b/src/ops.c
--- a/src/ops.c
+++ b/src/ops.c
@@ -389,6 +389,9 @@
     char_u		*newp, *oldp;
     int			oldcol = curwin->w_cursor.col;
     int			p_sw = (int)get_sw_value();
+#ifdef FEAT_VARTABS
+    int			*p_vts = curbuf->b_p_vts_ary;
+#endif
     int			p_ts = (int)curbuf->b_p_ts;
     struct block_def	bd;
     int			incr;
@@ -437,12 +440,19 @@
 	}
 	/* OK, now total=all the VWS reqd, and textstart points at the 1st
 	 * non-ws char in the block. */
+#ifdef FEAT_VARTABS
+	if (!curbuf->b_p_et)
+	    tabstop_fromto(ws_vcol, ws_vcol + total, p_ts, p_vts, &i, &j);
+	else
+	    j = total;
+#else
 	if (!curbuf->b_p_et)
 	    i = ((ws_vcol % p_ts) + total) / p_ts; /* number of tabs */
 	if (i)
 	    j = ((ws_vcol % p_ts) + total) % p_ts; /* number of spp */
 	else
 	    j = total;
+#endif
 	/* if we're splitting a TAB, allow for it */
 	bd.textcol -= bd.pre_whitesp_c - (bd.startspaces != 0);
 	len = (int)STRLEN(bd.textstart) + 1;
@@ -3519,10 +3529,18 @@
 	{
 	    /* Don't need to insert spaces when "p" on the last position of a
 	     * tab or "P" on the first position. */
+#ifdef FEAT_VARTABS
+	    int viscol = getviscol();
+	    if (dir == FORWARD
+		    ? tabstop_padding(viscol, curbuf->b_p_ts, curbuf->b_p_vts_ary) != 1
+						: curwin->w_cursor.coladd > 0)
+		coladvance_force(viscol);
+#else
 	    if (dir == FORWARD
 		    ? (int)curwin->w_cursor.coladd < curbuf->b_p_ts - 1
 						: curwin->w_cursor.coladd > 0)
 		coladvance_force(getviscol());
+#endif
 	    else
 		curwin->w_cursor.coladd = 0;
 	}
diff --git a/src/option.c b/src/option.c
--- a/src/option.c
+++ b/src/option.c
@@ -178,6 +178,10 @@
 # define PV_UDF		OPT_BUF(BV_UDF)
 #endif
 #define PV_WM		OPT_BUF(BV_WM)
+#ifdef FEAT_VARTABS
+#define PV_VSTS		OPT_BUF(BV_VSTS)
+#define PV_VTS		OPT_BUF(BV_VTS)
+#endif
 
 /*
  * Definition of the PV_ values for window-local options.
@@ -367,6 +371,10 @@
 static int	p_udf;
 #endif
 static long	p_wm;
+#ifdef FEAT_VARTABS
+static char_u	*p_vsts;
+static char_u	*p_vts;
+#endif
 #ifdef FEAT_KEYMAP
 static char_u	*p_keymap;
 #endif
@@ -382,6 +390,9 @@
 static long	p_wm_nopaste;
 static long	p_sts_nopaste;
 static int	p_ai_nopaste;
+#ifdef FEAT_VARTABS
+static char_u	*p_vsts_nopaste;
+#endif
 
 struct vimoption
 {
@@ -2682,6 +2693,14 @@
     {"updatetime",  "ut",   P_NUM|P_VI_DEF,
 			    (char_u *)&p_ut, PV_NONE,
 			    {(char_u *)4000L, (char_u *)0L} SCRIPTID_INIT},
+#ifdef FEAT_VARTABS
+    {"varsofttabstop", "vsts",  P_STRING|P_VI_DEF|P_VIM|P_COMMA,
+			    (char_u *)&p_vsts, PV_VSTS,
+			    {(char_u *)"", (char_u *)0L}},
+    {"vartabstop",  "vts",  P_STRING|P_VI_DEF|P_VIM|P_RBUF|P_COMMA,
+			    (char_u *)&p_vts, PV_VTS,
+			    {(char_u *)"8", (char_u *)0L} SCRIPTID_INIT},
+#endif
     {"verbose",	    "vbs",  P_NUM|P_VI_DEF,
 			    (char_u *)&p_verbose, PV_NONE,
 			    {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT},
@@ -5272,6 +5291,10 @@
     /* set cedit_key */
     (void)check_cedit();
 #endif
+#ifdef FEAT_VARTABS
+    tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_ary);
+    tabstop_set(curbuf->b_p_vts,  &curbuf->b_p_vts_ary);
+#endif
 }
 
 /*
@@ -5383,6 +5406,10 @@
     check_string_option(&buf->b_p_dict);
     check_string_option(&buf->b_p_tsr);
 #endif
+#ifdef FEAT_VARTABS
+    check_string_option(&buf->b_p_vsts);
+    check_string_option(&buf->b_p_vts);
+#endif
 }
 
 /*
@@ -6972,6 +6999,88 @@
     }
 #endif
 
+#ifdef FEAT_VARTABS
+    /* 'varsofttabstop' */
+    else if (varp == &(curbuf->b_p_vsts))
+    {
+	char_u *cp;
+
+	if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1]))
+	{
+	    if (curbuf->b_p_vsts_ary)
+	    {
+		vim_free(curbuf->b_p_vsts_ary);
+		curbuf->b_p_vsts_ary = 0;
+	    }
+	}
+	else
+	{
+	    for (cp = *varp; *cp; ++cp)
+	    {
+		if (vim_isdigit(*cp))
+		    continue;
+		if (*cp == ',' && cp > *varp && *(cp-1) != ',')
+		    continue;
+		errmsg = e_invarg;
+		break;
+	    }
+	    if (errmsg == NULL)
+	    {
+		int *oldarray = curbuf->b_p_vsts_ary;
+		if (tabstop_set(*varp, &(curbuf->b_p_vsts_ary)))
+		{
+		    if (oldarray)
+			vim_free(oldarray);
+		}
+		else
+		    errmsg = e_invarg;
+	    }
+	}
+    }
+
+    /* 'vartabstop' */
+    else if (varp == &(curbuf->b_p_vts))
+    {
+	char_u *cp;
+
+	if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1]))
+	{
+	    if (curbuf->b_p_vts_ary)
+	    {
+		vim_free(curbuf->b_p_vts_ary);
+		curbuf->b_p_vts_ary = 0;
+	    }
+	}
+	else
+	{
+	    for (cp = *varp; *cp; ++cp)
+	    {
+		if (vim_isdigit(*cp))
+		    continue;
+		if (*cp == ',' && cp > *varp && *(cp-1) != ',')
+		    continue;
+		errmsg = e_invarg;
+		break;
+	    }
+	    if (errmsg == NULL)
+	    {
+		int *oldarray = curbuf->b_p_vts_ary;
+		if (tabstop_set(*varp, &(curbuf->b_p_vts_ary)))
+		{
+		    if (oldarray)
+			vim_free(oldarray);
+#ifdef FEAT_FOLDING
+		    if (foldmethodIsIndent(curwin))
+			foldUpdateAll(curwin);
+#endif /* FEAT_FOLDING */
+		}
+		else
+		    errmsg = e_invarg;
+	    }
+	}
+    }
+#endif
+
     /* Options that are a list of flags. */
     else
     {
@@ -8168,7 +8277,14 @@
     if (curbuf->b_p_sw < 0)
     {
 	errmsg = e_positive;
+#ifdef FEAT_VARTABS
+	/* Use the first 'vartabstop' value, or 'tabstop' if vts isn't in use. */
+	curbuf->b_p_sw = tabstop_count(curbuf->b_p_vts_ary) > 0
+	               ? tabstop_first(curbuf->b_p_vts_ary)
+		       : curbuf->b_p_ts;
+#else
 	curbuf->b_p_sw = curbuf->b_p_ts;
+#endif
     }
 
     /*
@@ -10038,6 +10154,10 @@
 #ifdef FEAT_KEYMAP
 	case PV_KMAP:	return (char_u *)&(curbuf->b_p_keymap);
 #endif
+#ifdef FEAT_VARTABS
+	case PV_VSTS:	return (char_u *)&(curbuf->b_p_vsts);
+	case PV_VTS:	return (char_u *)&(curbuf->b_p_vts);
+#endif
 	default:	EMSG(_("E356: get_varp ERROR"));
     }
     /* always return a valid pointer to avoid a crash! */
@@ -10326,6 +10446,15 @@
 #endif
 	    buf->b_p_sts = p_sts;
 	    buf->b_p_sts_nopaste = p_sts_nopaste;
+#ifdef FEAT_VARTABS
+	    buf->b_p_vsts = vim_strsave(p_vsts);
+	    if (p_vsts && p_vsts != empty_option)
+		tabstop_set(p_vsts, &buf->b_p_vsts_ary);
+	    else
+		buf->b_p_vsts_ary = 0;
+	    buf->b_p_vsts_nopaste = p_vsts_nopaste
+				 ? vim_strsave(p_vsts_nopaste) : NULL;
+#endif
 #ifndef SHORT_FNAME
 	    buf->b_p_sn = p_sn;
 #endif
@@ -10435,12 +10564,27 @@
 	     * or to a help buffer.
 	     */
 	    if (dont_do_help)
+	    {
 		buf->b_p_isk = save_p_isk;
+#ifdef FEAT_VARTABS
+		if (p_vts && p_vts != empty_option && !buf->b_p_vts_ary)
+		    tabstop_set(p_vts, &buf->b_p_vts_ary);
+		else
+		    buf->b_p_vts_ary = 0;
+#endif
+	    }
 	    else
 	    {
 		buf->b_p_isk = vim_strsave(p_isk);
 		did_isk = TRUE;
 		buf->b_p_ts = p_ts;
+#ifdef FEAT_VARTABS
+		buf->b_p_vts = vim_strsave(p_vts);
+		if (p_vts && p_vts != empty_option && !buf->b_p_vts_ary)
+		    tabstop_set(p_vts, &buf->b_p_vts_ary);
+		else
+		    buf->b_p_vts_ary = 0;
+#endif
 		buf->b_help = FALSE;
 #ifdef FEAT_QUICKFIX
 		if (buf->b_p_bt[0] == 'h')
@@ -11269,6 +11413,12 @@
 		buf->b_p_wm_nopaste = buf->b_p_wm;
 		buf->b_p_sts_nopaste = buf->b_p_sts;
 		buf->b_p_ai_nopaste = buf->b_p_ai;
+#ifdef FEAT_VARTABS
+		if (buf->b_p_vsts_nopaste)
+		    vim_free(buf->b_p_vsts_nopaste);
+		buf->b_p_vsts_nopaste = buf->b_p_vsts && buf->b_p_vsts != empty_option
+				     ? vim_strsave(buf->b_p_vsts) : NULL;
+#endif
 	    }
 
 	    /* save global options */
@@ -11285,6 +11435,11 @@
 	    p_wm_nopaste = p_wm;
 	    p_sts_nopaste = p_sts;
 	    p_ai_nopaste = p_ai;
+#ifdef FEAT_VARTABS
+	    if (p_vsts_nopaste)
+		vim_free(p_vsts_nopaste);
+	    p_vsts_nopaste = p_vsts && p_vsts != empty_option ? vim_strsave(p_vsts) : NULL;
+#endif
 	}
 
 	/*
@@ -11298,6 +11453,14 @@
 	    buf->b_p_wm = 0;	    /* wrapmargin is 0 */
 	    buf->b_p_sts = 0;	    /* softtabstop is 0 */
 	    buf->b_p_ai = 0;	    /* no auto-indent */
+#ifdef FEAT_VARTABS
+	    if (buf->b_p_vsts)
+		free_string_option(buf->b_p_vsts);
+	    buf->b_p_vsts = empty_option;
+	    if (buf->b_p_vsts_ary)
+		vim_free(buf->b_p_vsts_ary);
+	    buf->b_p_vsts_ary = 0;
+#endif
 	}
 
 	/* set global options */
@@ -11318,6 +11481,11 @@
 	p_wm = 0;
 	p_sts = 0;
 	p_ai = 0;
+#ifdef FEAT_VARTABS
+	if (p_vsts)
+	    free_string_option(p_vsts);
+	p_vsts = empty_option;
+#endif
     }
 
     /*
@@ -11332,6 +11500,18 @@
 	    buf->b_p_wm = buf->b_p_wm_nopaste;
 	    buf->b_p_sts = buf->b_p_sts_nopaste;
 	    buf->b_p_ai = buf->b_p_ai_nopaste;
+#ifdef FEAT_VARTABS
+	    if (buf->b_p_vsts)
+		free_string_option(buf->b_p_vsts);
+	    buf->b_p_vsts = buf->b_p_vsts_nopaste
+			 ? vim_strsave(buf->b_p_vsts_nopaste) : empty_option;
+	    if (buf->b_p_vsts_ary)
+		vim_free(buf->b_p_vsts_ary);
+	    if (buf->b_p_vsts && buf->b_p_vsts != empty_option)
+		tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_ary);
+	    else
+		buf->b_p_vsts_ary = 0;
+#endif
 	}
 
 	/* restore global options */
@@ -11352,6 +11532,11 @@
 	p_wm = p_wm_nopaste;
 	p_sts = p_sts_nopaste;
 	p_ai = p_ai_nopaste;
+#ifdef FEAT_VARTABS
+	if (p_vsts)
+	    free_string_option(p_vsts);
+	p_vsts = p_vsts_nopaste ? vim_strsave(p_vsts_nopaste) : empty_option;
+#endif
     }
 
     old_p_paste = p_paste;
@@ -11693,6 +11878,301 @@
     return check_opt_strings(p, p_ff_values, FALSE);
 }
 
+#ifdef FEAT_VARTABS
+
+/*
+ * Set the integer values corresponding to the string setting of 'vartabstop'.
+ */
+    int
+tabstop_set(var, array)
+    char_u	*var;
+    int		**array;
+{
+    int valcount = 1;
+    int t;
+    char_u *cp;
+
+    if ((!var[0] || (var[0] == '0' && !var[1])))
+    {
+	*array = (int *)0;
+	return TRUE;
+    }
+
+    for (cp = var; *cp; ++cp)
+    {
+	if (cp == var || *(cp - 1) == ',')
+	    if (atoi((char *)cp) <= 0)
+		return FALSE;
+
+	if (VIM_ISDIGIT(*cp))
+	    continue;
+	if (*cp == ',' && cp > var && *(cp - 1) != ',')
+	{
+	    ++valcount;
+	    continue;
+	}
+	return FALSE;
+    }
+
+    *array = (int *) alloc((unsigned) ((valcount + 1) * sizeof(int)));
+    (*array)[0] = valcount;
+
+    t = 1;
+    for (cp = var; *cp;)
+    {
+	(*array)[t++] = atoi((char *)cp);
+	while (*cp && *cp != ',')
+	    ++cp;
+	if (*cp)
+	    ++cp;
+    }
+
+    return TRUE;
+}
+
+/*
+ * Calculate the number of screen spaces a tab will occupy.
+ * If vts is set then the tab widths are taken from that array,
+ * otherwise the value of ts is used.
+ */
+    int
+tabstop_padding(col, ts, vts)
+    colnr_T	col;
+    int		ts;
+    int		*vts;
+{
+    int		tabcount;
+    colnr_T	tabcol = 0;
+    int		t;
+    int		padding = 0;
+
+    if (ts == 0)
+	ts = 8;
+    if (vts == 0 || vts[0] == 0)
+	return ts - (col % ts);
+
+    tabcount = vts[0];
+
+    for (t = 1; t <= tabcount; ++t)
+    {
+	tabcol += vts[t];
+	if (tabcol > col)
+	{
+	    padding = (int)(tabcol - col);
+	    break;
+	}
+    }
+    if (t > tabcount)
+	padding = vts[tabcount] - (int)((col - tabcol) % vts[tabcount]);
+
+    return padding;
+}
+
+/*
+ * Find the size of the tab that covers a particular column.
+ */
+    int
+tabstop_at(col, ts, vts)
+    colnr_T	col;
+    int		ts;
+    int		*vts;
+{
+    int		tabcount;
+    colnr_T	tabcol = 0;
+    int		t;
+    int		tab_size = 0;
+
+    if (vts == 0 || vts[0] == 0)
+	return ts;
+
+    tabcount = vts[0];
+    for (t = 1; t <= tabcount; ++t)
+    {
+	tabcol += vts[t];
+	if (tabcol > col)
+	{
+	    tab_size = vts[t];
+	    break;
+	}
+    }
+    if (t > tabcount)
+	tab_size = vts[tabcount];
+
+    return tab_size;
+}
+
+/*
+ * Find the column on which a tab starts.
+ */
+    colnr_T
+tabstop_start(col, ts, vts)
+    colnr_T	col;
+    int		ts;
+    int		*vts;
+{
+    int		tabcount;
+    colnr_T	tabcol = 0;
+    int		t;
+
+    if (vts == 0 || vts[0] == 0)
+	return (col / ts) * ts;
+
+    tabcount = vts[0];
+    for (t = 1; t <= tabcount; ++t)
+    {
+	tabcol += vts[t];
+	if (tabcol > col)
+	    return tabcol - vts[t];
+    }
+
+    int excess = tabcol % vts[tabcount];
+    return excess + ((col - excess) / vts[tabcount]) * vts[tabcount];
+}
+
+/*
+ * Find the number of tabs and spaces necessary to get from one column
+ * to another.
+ */
+    void
+tabstop_fromto(start_col, end_col, ts, vts, ntabs, nspcs)
+    colnr_T	start_col;
+    colnr_T	end_col;
+    int		ts;
+    int		*vts;
+    int		*ntabs;
+    int		*nspcs;
+{
+    int		spaces = end_col - start_col;
+    colnr_T	tabcol = 0;
+    int		padding = 0;
+    int		tabcount;
+    int		t;
+
+    if (vts == 0 || vts[0] == 0)
+    {
+	int tabs = 0;
+	int initspc = ts - (start_col % ts);
+	if (spaces >= initspc)
+	{
+	    spaces -= initspc;
+	    tabs++;
+	}
+	tabs += spaces / ts;
+	spaces -= (spaces / ts) * ts;
+
+	*ntabs = tabs;
+	*nspcs = spaces;
+	return;
+    }
+
+    /* Find the padding needed to reach the next tabstop. */
+    tabcount = vts[0];
+    for (t = 1; t <= tabcount; ++t)
+    {
+	tabcol += vts[t];
+	if (tabcol > start_col)
+	{
+	    padding = (int)(tabcol - start_col);
+	    break;
+	}
+    }
+    if (t > tabcount)
+	padding = vts[tabcount] - (int)((start_col - tabcol) % vts[tabcount]);
+
+    /* If the space needed is less than the padding no tabs can be used. */
+    if (spaces < padding)
+    {
+	*ntabs = 0;
+	*nspcs = spaces;
+	return;
+    }
+
+    *ntabs = 1;
+    spaces -= padding;
+
+    /* At least one tab has been used. See if any more will fit. */
+    while (spaces != 0 && ++t <= tabcount)
+    {
+	padding = vts[t];
+	if (spaces < padding)
+	{
+	    *nspcs = spaces;
+	    return;
+	}
+	++*ntabs;
+	spaces -= padding;
+    }
+
+    *ntabs += spaces / vts[tabcount];
+    *nspcs =  spaces % vts[tabcount];
+}
+
+/*
+ * See if two tabstop arrays contain the same values.
+ */
+    int
+tabstop_eq(ts1, ts2)
+    int		*ts1;
+    int		*ts2;
+{
+    int		t;
+
+    if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0))
+	return FALSE;
+    if (ts1 == ts2)
+	return TRUE;
+    if (ts1[0] != ts2[0])
+	return FALSE;
+
+    for (t = 1; t <= ts1[0]; ++t)
+	if (ts1[t] != ts2[t])
+	    return FALSE;
+
+    return TRUE;
+}
+
+/*
+ * Copy a tabstop array, allocating space for the new array.
+ */
+    int *
+tabstop_copy(oldts)
+    int		*oldts;
+{
+    int		*newts;
+    int		t;
+
+    if (oldts == 0)
+	return 0;
+
+    newts = (int *) alloc((unsigned) ((oldts[0] + 1) * sizeof(int)));
+    for (t = 0; t <= oldts[0]; ++t)
+	newts[t] = oldts[t];
+
+    return newts;
+}
+
+/*
+ * Return a count of the number of tabstops.
+ */
+    int
+tabstop_count(ts)
+    int		*ts;
+{
+    return ts ? ts[0] : 0;
+}
+
+/*
+ * Return the first tabstop, or 8 if there are no tabstops defined.
+ */
+    int
+tabstop_first(ts)
+    int		*ts;
+{
+    return ts ? ts[1] : 8;
+}
+
+#endif
+
 /*
  * Return the effective shiftwidth value for current buffer, using the
  * 'tabstop' value when 'shiftwidth' is zero.
diff --git a/src/option.h b/src/option.h
--- a/src/option.h
+++ b/src/option.h
@@ -1030,6 +1030,10 @@
     , BV_TX
     , BV_UDF
     , BV_WM
+#ifdef FEAT_VARTABS
+    , BV_VSTS
+    , BV_VTS
+#endif
     , BV_COUNT	    /* must be the last one */
 };
 
diff --git a/src/proto/misc1.pro b/src/proto/misc1.pro
--- a/src/proto/misc1.pro
+++ b/src/proto/misc1.pro
@@ -3,6 +3,9 @@
 int get_indent_lnum __ARGS((linenr_T lnum));
 int get_indent_buf __ARGS((buf_T *buf, linenr_T lnum));
 int get_indent_str __ARGS((char_u *ptr, int ts));
+#ifdef FEAT_VARTABS
+int get_indent_str_vtab __ARGS((char_u *ptr, int ts, int *vts));
+#endif
 int set_indent __ARGS((int size, int flags));
 int get_number_indent __ARGS((linenr_T lnum));
 int open_line __ARGS((int dir, int flags, int second_line_indent));
diff --git a/src/proto/option.pro b/src/proto/option.pro
--- a/src/proto/option.pro
+++ b/src/proto/option.pro
@@ -59,6 +59,17 @@
 void save_file_ff __ARGS((buf_T *buf));
 int file_ff_differs __ARGS((buf_T *buf, int ignore_empty));
 int check_ff_value __ARGS((char_u *p));
+#ifdef FEAT_VARTABS
+int tabstop_set __ARGS((char_u *var, int **array));
+int tabstop_padding __ARGS((colnr_T col, int ts, int *vts));
+colnr_T tabstop_start __ARGS((colnr_T col, int ts, int *vts));
+int tabstop_at __ARGS((colnr_T col, int ts, int *vts));
+void tabstop_fromto __ARGS((colnr_T start_col, colnr_T end_col, int ts, int *vts, int *ntabs, int *nspcs));
+int tabstop_eq __ARGS((int *ts1, int *ts2));
+int *tabstop_copy __ARGS((int *oldts));
+int tabstop_count __ARGS((int *ts));
+int tabstop_first __ARGS((int *ts));
+#endif
 long get_sw_value __ARGS((void));
 long get_sts_value __ARGS((void));
 void find_mps_values __ARGS((int *initc, int *findc, int *backwards, int switchit));
diff --git a/src/screen.c b/src/screen.c
--- a/src/screen.c
+++ b/src/screen.c
@@ -4290,8 +4290,13 @@
 		if (c == TAB && (!wp->w_p_list || lcs_tab1))
 		{
 		    /* tab amount depends on current column */
+#ifdef FEAT_VARTABS
+		    n_extra = tabstop_padding(vcol, wp->w_buffer->b_p_ts,
+						    wp->w_buffer->b_p_vts_ary) - 1;
+#else
 		    n_extra = (int)wp->w_buffer->b_p_ts
 					- vcol % (int)wp->w_buffer->b_p_ts - 1;
+#endif
 #ifdef FEAT_CONCEAL
 		    /* Tab alignment should be identical regardless of
 		     * 'conceallevel' value. So tab compensates of all
diff --git a/src/structs.h b/src/structs.h
--- a/src/structs.h
+++ b/src/structs.h
@@ -1573,6 +1573,13 @@
     long	b_p_wm;		/* 'wrapmargin' */
     long	b_p_wm_nobin;	/* b_p_wm saved for binary mode */
     long	b_p_wm_nopaste;	/* b_p_wm saved for paste mode */
+#ifdef FEAT_VARTABS
+    char_u	*b_p_vsts;	/* 'varsofttabstop' */
+    int		*b_p_vsts_ary;	/* 'varsofttabstop' in internal format */
+    char_u	*b_p_vsts_nopaste; /* b_p_vsts saved for paste mode */
+    char_u	*b_p_vts;	/* 'vartabstop' */
+    int		*b_p_vts_ary;	/* 'vartabstop' in internal format */
+#endif
 #ifdef FEAT_KEYMAP
     char_u	*b_p_keymap;	/* 'keymap' */
 #endif
diff --git a/src/testdir/Make_amiga.mak b/src/testdir/Make_amiga.mak
--- a/src/testdir/Make_amiga.mak
+++ b/src/testdir/Make_amiga.mak
@@ -33,7 +33,7 @@
 		test76.out test77.out test78.out test79.out test80.out \
 		test81.out test82.out test83.out test84.out test88.out \
 		test89.out test90.out test91.out test92.out test93.out \
-		test94.out test95.out
+		test94.out test95.out test100.out
 
 .SUFFIXES: .in .out
 
@@ -145,3 +145,4 @@
 test93.out: test93.in
 test94.out: test94.in
 test95.out: test95.in
+test100.out: test100.in
diff --git a/src/testdir/Make_dos.mak b/src/testdir/Make_dos.mak
--- a/src/testdir/Make_dos.mak
+++ b/src/testdir/Make_dos.mak
@@ -32,7 +32,7 @@
 		test79.out test80.out test81.out test82.out test83.out \
 		test84.out test85.out test86.out test87.out test88.out \
 		test89.out test90.out test91.out test92.out test93.out \
-		test94.out test95.out
+		test94.out test95.out test100.out
 
 SCRIPTS32 =	test50.out test70.out
 
diff --git a/src/testdir/Make_ming.mak b/src/testdir/Make_ming.mak
--- a/src/testdir/Make_ming.mak
+++ b/src/testdir/Make_ming.mak
@@ -52,7 +52,7 @@
 		test79.out test80.out test81.out test82.out test83.out \
 		test84.out test85.out test86.out test87.out test88.out \
 		test89.out test90.out test91.out test92.out test93.out \
-		test94.out test95.out
+		test94.out test95.out test100.out
 
 SCRIPTS32 =	test50.out test70.out
 
diff --git a/src/testdir/Make_os2.mak b/src/testdir/Make_os2.mak
--- a/src/testdir/Make_os2.mak
+++ b/src/testdir/Make_os2.mak
@@ -33,7 +33,7 @@
 		test76.out test77.out test78.out test79.out test80.out \
 		test81.out test82.out test83.out test84.out test88.out \
 		test89.out test90.out test91.out test92.out test93.out \
-		test94.out test95.out
+		test94.out test95.out test100.out
 
 .SUFFIXES: .in .out
 
diff --git a/src/testdir/Make_vms.mms b/src/testdir/Make_vms.mms
--- a/src/testdir/Make_vms.mms
+++ b/src/testdir/Make_vms.mms
@@ -78,7 +78,7 @@
 	 test77.out test78.out test79.out test80.out test81.out \
 	 test82.out test83.out test84.out test88.out test89.out \
 	 test90.out test91.out test92.out test93.out test94.out \
-	 test95.out
+	 test95.out test100.out
 
 # Known problems:
 # Test 30: a problem around mac format - unknown reason
diff --git a/src/testdir/Makefile b/src/testdir/Makefile
--- a/src/testdir/Makefile
+++ b/src/testdir/Makefile
@@ -29,7 +29,7 @@
 		test79.out test80.out test81.out test82.out test83.out \
 		test84.out test85.out test86.out test87.out test88.out \
 		test89.out test90.out test91.out test92.out test93.out \
-		test94.out test95.out
+		test94.out test95.out test100.out
 
 SCRIPTS_GUI = test16.out
 
diff --git a/src/testdir/test100.in b/src/testdir/test100.in
new file mode 100644
--- /dev/null
+++ b/src/testdir/test100.in
@@ -0,0 +1,86 @@
+Test for variable tabstops
+
+STARTTEST
+:so small.vim
+:if !has("vartabs") | e! test.ok | w! test.out | qa! | endif
+:%d
+:" [1,2] Test normal operation of tabstops and softtabstops.
+:set ts=4
+aa	a	a	a	a:retab 8
+:.w! >>test.out
+:%d
+:set ts=8 sts=6
+ab	b	b	b	b:.w! >>test.out
+:%d
+:" [3,4] Test variable tabstops.
+:set sts=0 vts=4,8,4,8
+ac	c	c	c	c	c:retab 8
+:.w! >>test.out
+:%d
+:set et vts=4,8,4,8
+ad	d	d	d	d	d:.w! >>test.out
+:%d
+:" [5] Changing ts should have no effect if vts is in use.
+:set ts=6
+ae	e	e	e	e	e:.w! >>test.out
+:%d
+:" [6] Clearing vts should revert to using ts.
+:set vts=
+af	f	f	f	f	f:.w! >>test.out
+:%d
+:" [7] Test variable softtabstops.
+:set noet ts=8 vsts=12,2,6
+ag	g	g	g	g	g:.w! >>test.out
+:%d
+:" [8] Variable tabstops and softtabstops combined.
+:set vsts=6,12,8 vts=4,6,8
+ah	h	h	h	h:.w! >>test.out
+:%d
+:" [9] Retab with a single value, not using vts.
+:set ts=8 sts=0 vts= vsts=
+ai	i	i	i	i:retab 4
+o='9: ts='.&ts.' vts='.&vts.' sts='.&sts.' vsts='.&vsts
:.-1,.w! >>test.out
+:%d
+:" [10] Retab with a single value, using vts.
+:set ts=8 sts=0 vts=6 vsts=
+aj	j	j	j	j:retab 4
+o='10: ts='.&ts.' vts='.&vts.' sts='.&sts.' vsts='.&vsts
:.-1,.w! >>test.out
+:%d
+:" [11] Retab with multiple values, not using vts.
+:set ts=6 sts=0 vts= vsts=
+ak	k	k	k	k	k:retab 4,8
+o='11: ts='.&ts.' vts='.&vts.' sts='.&sts.' vsts='.&vsts
:.-1,.w! >>test.out
+:%d
+:" [12] Retab with multiple values, using vts.
+:set ts=8 sts=0 vts=6 vsts=
+al	l	l	l	l	l:retab 4,8
+o='12: ts='.&ts.' vts='.&vts.' sts='.&sts.' vsts='.&vsts
:.-1,.w! >>test.out
+:%d
+:" [13] Check that global and local values are set.
+:set ts=4 vts=6 sts=8 vsts=10
+am	m	m	m:.w! >>test.out
+o='13a: ts='.&ts.' vts='.&vts.' sts='.&sts.' vsts='.&vsts
:.w! >>test.out
+:new
+an	n	n	n:.w! >>test.out
+o='13b: ts='.&ts.' vts='.&vts.' sts='.&sts.' vsts='.&vsts
:.w! >>test.out
+:q!
+:%d
+:" [14] Check that local values only are set.
+:setlocal ts=5 vts=7 sts=9 vsts=11
+ao	o	o	o:.w! >>test.out
+o='14a: ts='.&ts.' vts='.&vts.' sts='.&sts.' vsts='.&vsts
:.w! >>test.out
+:new
+ap	p	p	p:.w! >>test.out
+o='14b: ts='.&ts.' vts='.&vts.' sts='.&sts.' vsts='.&vsts
:.w! >>test.out
+:q!
+:%d
+:" [15] Check that global values only are set.
+:setglobal ts=6 vts=8 sts=10 vsts=12
+aq	q	q	q:.w! >>test.out
+o='15a: ts='.&ts.' vts='.&vts.' sts='.&sts.' vsts='.&vsts
:.w! >>test.out
+:new
+ar	r	r	r:.w! >>test.out
+o='15b: ts='.&ts.' vts='.&vts.' sts='.&sts.' vsts='.&vsts
:.w! >>test.out
+:qa!
+ENDTEST
+dummy text
diff --git a/src/testdir/test100.ok b/src/testdir/test100.ok
new file mode 100644
--- /dev/null
+++ b/src/testdir/test100.ok
@@ -0,0 +1,28 @@
+a   a	a   a	a
+b     b	    b	  b	b
+c   c	    c	c	c	c
+d   d       d   d       d       d
+e   e       e   e       e       e
+f     f     f     f     f     f
+g	    g g	    g	  g	g
+h	  h		h	h	h
+i		i		i		i		i
+9: ts=4 vts= sts=0 vsts=
+j	  j		j	  j		j
+10: ts=8 vts=4 sts=0 vsts=
+k	  k	k     k	    k	  k
+11: ts=6 vts=4,8 sts=0 vsts=
+l	  l	l     l	    l	  l
+12: ts=8 vts=4,8 sts=0 vsts=
+m	    m		  m		m
+13a: ts=4 vts=6 sts=8 vsts=10
+n	    n		  n		n
+13b: ts=4 vts=6 sts=8 vsts=10
+o	    o		 o	     o
+14a: ts=5 vts=7 sts=9 vsts=11
+p	    p		  p		p
+14b: ts=4 vts=6 sts=8 vsts=10
+q	    q		 q	     q
+15a: ts=5 vts=7 sts=9 vsts=11
+r	    r		r	    r
+15b: ts=6 vts=8 sts=10 vsts=12
diff --git a/src/testdir/test100.out b/src/testdir/test100.out
new file mode 100644
--- /dev/null
+++ b/src/testdir/test100.out
@@ -0,0 +1,28 @@
+a   a	a   a	a
+b     b	    b	  b	b
+c   c	    c	c	c	c
+d   d       d   d       d       d
+e   e       e   e       e       e
+f     f     f     f     f     f
+g	    g g	    g	  g	g
+h	  h		h	h	h
+i		i		i		i		i
+9: ts=4 vts= sts=0 vsts=
+j	  j		j	  j		j
+10: ts=8 vts=4 sts=0 vsts=
+k	  k	k     k	    k	  k
+11: ts=6 vts=4,8 sts=0 vsts=
+l	  l	l     l	    l	  l
+12: ts=8 vts=4,8 sts=0 vsts=
+m	    m		  m		m
+13a: ts=4 vts=6 sts=8 vsts=10
+n	    n		  n		n
+13b: ts=4 vts=6 sts=8 vsts=10
+o	    o		 o	     o
+14a: ts=5 vts=7 sts=9 vsts=11
+p	    p		  p		p
+14b: ts=4 vts=6 sts=8 vsts=10
+q	    q		 q	     q
+15a: ts=5 vts=7 sts=9 vsts=11
+r	    r		r	    r
+15b: ts=6 vts=8 sts=10 vsts=12
diff --git a/src/version.c b/src/version.c
--- a/src/version.c
+++ b/src/version.c
@@ -628,6 +628,11 @@
 #else
 	"-user_commands",
 #endif
+#ifdef FEAT_VARTABS
+	"+vartabs",
+#else
+	"-vartabs",
+#endif
 #ifdef FEAT_VERTSPLIT
 	"+vertsplit",
 #else
diff --git a/src/workshop.c b/src/workshop.c
--- a/src/workshop.c
+++ b/src/workshop.c
@@ -50,7 +50,11 @@
 static void	 warp_to_pc(int);
 #ifdef FEAT_BEVAL
 void		 workshop_beval_cb(BalloonEval *, int);
+# ifdef FEAT_VARTABS
+static int	 computeIndex(int, char_u *, int, int *);
+# else
 static int	 computeIndex(int, char_u *, int);
+# endif
 #endif
 static char	*fixAccelText(char *);
 static void	 addMenu(char *, char *, char *);
@@ -1537,7 +1541,11 @@
 	     * a column number. Compute the index from col. Also set
 	     * line to 0 because thats what dbx expects.
 	     */
+#ifdef FEAT_VARTABS
+	    idx = computeIndex(col, text, beval->ts, beval->vts);
+#else
 	    idx = computeIndex(col, text, beval->ts);
+#endif
 	    if (idx > 0)
 	    {
 		lnum = 0;
@@ -1572,7 +1580,11 @@
 computeIndex(
 	int		 wantedCol,
 	char_u		*line,
-	int		 ts)
+	int		 ts
+#ifdef FEAT_VARTABS
+	int		*vts
+#else
+	)
 {
     int		 col = 0;
     int		 idx = 0;
@@ -1580,7 +1592,11 @@
     while (line[idx])
     {
 	if (line[idx] == '\t')
+#ifdef FEAT_VARTABS
+	    col += tabstop_padding(col, ts, vts);
+#else
 	    col += ts - (col % ts);
+#endif
 	else
 	    col++;
 	idx++;

Raspunde prin e-mail lui