*** ../vim71-no-vts/src/buffer.c	2008-01-16 21:06:25.000000000 +0000
--- src/buffer.c	2008-01-17 20:51:42.000000000 +0000
***************
*** 1751,1756 ****
--- 1751,1769 ----
      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);
*** ../vim71-no-vts/src/charset.c	2008-01-16 21:06:12.000000000 +0000
--- src/charset.c	2008-01-17 14:33:01.000000000 +0000
***************
*** 796,801 ****
--- 796,810 ----
   * 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)) \
      { \
***************
*** 805,810 ****
--- 814,820 ----
      } \
      else \
  	return ptr2cells(p);
+ #endif
  
  #if defined(FEAT_VREPLACE) || defined(FEAT_EX_EXTRA) || defined(FEAT_GUI) \
  	|| defined(FEAT_VIRTUALEDIT) || defined(PROTO)
***************
*** 1040,1045 ****
--- 1050,1058 ----
      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
***************
*** 1147,1153 ****
--- 1160,1174 ----
  	{
  	    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)
***************
*** 1177,1184 ****
--- 1198,1210 ----
  
      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
***************
*** 1239,1244 ****
--- 1265,1273 ----
      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;
  
***************
*** 1274,1280 ****
--- 1303,1313 ----
  	    }
  	    /* 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
*** ../vim71-no-vts/src/edit.c	2008-01-16 21:10:25.000000000 +0000
--- src/edit.c	2008-01-17 13:30:05.000000000 +0000
***************
*** 651,657 ****
  	    mincol = curwin->w_wcol;
  	    validate_cursor_col();
  
! 	    if ((int)curwin->w_wcol < (int)mincol - curbuf->b_p_ts
  		    && curwin->w_wrow == W_WINROW(curwin)
  						 + curwin->w_height - 1 - p_so
  		    && (curwin->w_cursor.lnum != curwin->w_topline
--- 651,663 ----
  	    mincol = curwin->w_wcol;
  	    validate_cursor_col();
  
! 	    if (
! #ifdef FEAT_VARTABS
! 		(int)curwin->w_wcol < (int)mincol
! 		    - tabstop_at(get_nolist_virtcol(), curbuf->b_p_ts, curbuf->b_p_vts_ary)
! #else
! 		(int)curwin->w_wcol < (int)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
***************
*** 8459,8465 ****
  	 */
  	if (	   mode == BACKSPACE_CHAR
  		&& ((p_sta && in_indent)
! 		    || (curbuf->b_p_sts != 0
  			&& curwin->w_cursor.col > 0
  			&& (*(ml_get_cursor() - 1) == TAB
  			    || (*(ml_get_cursor() - 1) == ' '
--- 8465,8477 ----
  	 */
  	if (	   mode == BACKSPACE_CHAR
  		&& ((p_sta && in_indent)
! 		    || (
! #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) == ' '
***************
*** 8472,8481 ****
--- 8484,8495 ----
  	    colnr_T	start_vcol;
  
  	    *inserted_space_p = FALSE;
+ #ifndef FEAT_VARTABS
  	    if (p_sta && in_indent)
  		ts = curbuf->b_p_sw;
  	    else
  		ts = curbuf->b_p_sts;
+ #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. */
***************
*** 8484,8490 ****
--- 8498,8512 ----
  	    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
***************
*** 9135,9142 ****
       * When nothing special, insert TAB like a normal character
       */
      if (!curbuf->b_p_et
  	    && !(p_sta && ind && curbuf->b_p_ts != curbuf->b_p_sw)
! 	    && curbuf->b_p_sts == 0)
  	return TRUE;
  
      if (stop_arrow() == FAIL)
--- 9157,9177 ----
       * 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) != curbuf->b_p_sw)
+ 	            || (tabstop_count(curbuf->b_p_vts_ary) == 0
+ 		        && curbuf->b_p_ts != curbuf->b_p_sw)))
+ 	    && tabstop_count(curbuf->b_p_vsts_ary) == 0
+ 	    && curbuf->b_p_sts == 0
+ #else
  	    && !(p_sta && ind && curbuf->b_p_ts != curbuf->b_p_sw)
! 	    && curbuf->b_p_sts == 0
! #endif
! 	    )
  	return TRUE;
  
      if (stop_arrow() == FAIL)
***************
*** 9150,9155 ****
--- 9185,9204 ----
  #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)curbuf->b_p_sw;
      else if (curbuf->b_p_sts > 0) /* use 'softtabstop' when set */
***************
*** 9157,9162 ****
--- 9206,9212 ----
      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
***************
*** 9181,9187 ****
--- 9231,9243 ----
      /*
       * 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
+                             || curbuf->b_p_sts > 0
+ 			    || (p_sta && ind)))
+ #else
      if (!curbuf->b_p_et && (curbuf->b_p_sts || (p_sta && ind)))
+ #endif
      {
  	char_u		*ptr;
  #ifdef FEAT_VREPLACE
*** ../vim71-no-vts/src/eval.c	2008-01-16 21:10:24.000000000 +0000
--- src/eval.c	2008-01-17 12:15:33.000000000 +0000
***************
*** 11101,11106 ****
--- 11101,11109 ----
  #ifdef FEAT_VIMINFO
  	"viminfo",
  #endif
+ #ifdef FEAT_VARTABS
+ 	"vartabs",
+ #endif
  #ifdef FEAT_VERTSPLIT
  	"vertsplit",
  #endif
*** ../vim71-no-vts/src/ex_cmds.c	2008-01-16 21:10:25.000000000 +0000
--- src/ex_cmds.c	2008-01-17 21:41:09.000000000 +0000
***************
*** 572,583 ****
      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 */
      int		new_ts;
      int		save_list;
      linenr_T	first_line = 0;		/* first changed line */
      linenr_T	last_line = 0;		/* last changed line */
--- 572,588 ----
      long	vcol;
      long	start_col = 0;		/* For start of white-space string */
      long	start_vcol = 0;		/* For start of white-space string */
      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 */
***************
*** 585,590 ****
--- 590,616 ----
      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)
      {
***************
*** 593,598 ****
--- 619,625 ----
      }
      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);
***************
*** 625,630 ****
--- 652,663 ----
  		    num_tabs = 0;
  		    if (!curbuf->b_p_et)
  		    {
+ #ifdef FEAT_VARTABS
+ 			int t, s;
+ 			tabstop_fromto(start_vcol, vcol, 0, new_ts, &t, &s);
+ 			num_tabs = t;
+ 			num_spaces = s;
+ #else
  			temp = new_ts - (start_vcol % new_ts);
  			if (num_spaces >= temp)
  			{
***************
*** 633,638 ****
--- 666,672 ----
  			}
  			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))
***************
*** 690,703 ****
--- 724,777 ----
      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();
*** ../vim71-no-vts/src/feature.h	2008-01-16 21:06:06.000000000 +0000
--- src/feature.h	2008-01-17 12:15:33.000000000 +0000
***************
*** 804,809 ****
--- 804,816 ----
  #endif
  
  /*
+  * +vartabs		'vartabstop' and 'varsofttabstop' options.
+  */
+ #ifdef FEAT_BIG
+ # define FEAT_VARTABS
+ #endif
+ 
+ /*
   * Preferences:
   * ============
   */
*** ../vim71-no-vts/src/gui_beval.c	2007-05-07 20:33:41.000000000 +0100
--- src/gui_beval.c	2008-01-17 13:50:26.000000000 +0000
***************
*** 221,226 ****
--- 221,229 ----
  	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,
***************
*** 264,269 ****
--- 267,276 ----
  # else
      XtDestroyWidget(beval->balloonShell);
  # endif
+ # ifdef FEAT_VARTABS
+     if (beval->vts)
+ 	vim_free(beval->vts);
+ # endif
      vim_free(beval);
  }
  #endif
***************
*** 401,406 ****
--- 408,418 ----
  		*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;
  	    }
*** ../vim71-no-vts/src/gui_beval.h	2004-06-07 15:32:29.000000000 +0100
--- src/gui_beval.h	2008-01-17 13:51:03.000000000 +0000
***************
*** 58,63 ****
--- 58,66 ----
      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));
*** ../vim71-no-vts/src/gui_w32.c	2008-01-16 21:10:19.000000000 +0000
--- src/gui_w32.c	2008-01-17 13:51:17.000000000 +0000
***************
*** 4711,4716 ****
--- 4711,4719 ----
  	beval->msg = mesg;
  	beval->msgCB = mesgCB;
  	beval->clientData = clientData;
+ #ifdef FEAT_VARTABS
+ 	beval->vts = 0;
+ #endif
  
  	InitCommonControls();
  	cur_beval = beval;
***************
*** 4771,4776 ****
--- 4774,4783 ----
  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 */
*** ../vim71-no-vts/src/hardcopy.c	2008-01-16 21:10:24.000000000 +0000
--- src/hardcopy.c	2008-01-17 13:51:48.000000000 +0000
***************
*** 901,907 ****
--- 901,912 ----
  	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)
  	    {
*** ../vim71-no-vts/src/message.c	2008-01-16 21:06:30.000000000 +0000
--- src/message.c	2008-01-17 13:52:07.000000000 +0000
***************
*** 1608,1614 ****
--- 1608,1618 ----
  	    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 = ' ';
*** ../vim71-no-vts/src/misc1.c	2008-01-16 21:06:35.000000000 +0000
--- src/misc1.c	2008-01-17 14:59:10.000000000 +0000
***************
*** 28,34 ****
--- 28,38 ----
      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
  }
  
  /*
***************
*** 38,44 ****
--- 42,52 ----
  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)
***************
*** 51,57 ****
--- 59,69 ----
      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
  
***************
*** 78,83 ****
--- 90,121 ----
      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.
***************
*** 102,107 ****
--- 140,148 ----
      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
***************
*** 134,141 ****
--- 175,187 ----
  	    {
  		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;
***************
*** 152,174 ****
--- 198,247 ----
  		++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)
***************
*** 179,184 ****
--- 252,258 ----
  	    ++ind_len;
  	    /* ind_done += (int)curbuf->b_p_ts; */
  	}
+ #endif
      }
      /* count spaces required for indent */
      while (todo > 0)
***************
*** 253,260 ****
--- 327,339 ----
  	    {
  		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;
***************
*** 270,290 ****
--- 349,389 ----
  	    }
  
  	    /* 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)
      {
***************
*** 331,336 ****
--- 430,438 ----
      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. */
***************
*** 339,344 ****
--- 441,449 ----
  	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 */
***************
*** 346,363 ****
--- 451,478 ----
  	{
  	    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)
***************
*** 366,381 ****
--- 481,516 ----
  	}
  
  	/* 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)
  	{
  	    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)
  	{
  	    todo -= (int)curbuf->b_p_ts;
***************
*** 383,388 ****
--- 518,524 ----
  	    if (p != NULL)
  		*p++ = TAB;
  	}
+ #endif
  
  	/* Count/add spaces required for indent */
  	while (todo > 0)
***************
*** 652,658 ****
--- 788,799 ----
  	/*
  	 * 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)
  	    newindent = old_indent;	/* for ^^D command in insert mode */
  
***************
*** 1173,1179 ****
--- 1314,1325 ----
  					|| 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)
*** ../vim71-no-vts/src/ops.c	2008-01-16 21:06:35.000000000 +0000
--- src/ops.c	2008-01-17 15:07:20.000000000 +0000
***************
*** 385,390 ****
--- 385,393 ----
      char_u		*newp, *oldp, *midp, *ptr;
      int			oldcol = curwin->w_cursor.col;
      int			p_sw = (int)curbuf->b_p_sw;
+ #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			internal = 0;
***************
*** 434,445 ****
--- 437,455 ----
  	}
  	/* 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;
***************
*** 455,518 ****
      }
      else /* left */
      {
! 	vcol = oap->start_vcol;
! 	/* walk vcol past ws to be removed */
! 	for (midp = oldp + bd.textcol;
! 	      vcol < (oap->start_vcol + total) && vim_iswhite(*midp); )
! 	{
! 	    incr = lbr_chartabsize_adv(&midp, (colnr_T)vcol);
! 	    vcol += incr;
! 	}
! 	/* internal is the block-internal ws replacing a split TAB */
! 	if (vcol > (oap->start_vcol + total))
  	{
! 	    /* we have to split the TAB *(midp-1) */
! 	    internal = vcol - (oap->start_vcol + total);
! 	}
! 	/* if 'expandtab' is not set, use TABs */
  
! 	split = bd.startspaces + internal;
! 	if (split > 0)
! 	{
  	    if (!curbuf->b_p_et)
  	    {
  		for (ptr = oldp, col = 0; ptr < oldp+bd.textcol; )
  		    col += lbr_chartabsize_adv(&ptr, (colnr_T)col);
  
! 		/* col+1 now equals the start col of the first char of the
! 		 * block (may be < oap.start_vcol if we're splitting a TAB) */
! 		i = ((col % p_ts) + split) / p_ts; /* number of tabs */
  	    }
- 	    if (i)
- 		j = ((col % p_ts) + split) % p_ts; /* number of spp */
- 	    else
- 		j = split;
- 	}
  
! 	newp = alloc_check(bd.textcol + i + j + (unsigned)STRLEN(midp) + 1);
! 	if (newp == NULL)
! 	    return;
! 	vim_memset(newp, NUL, (size_t)(bd.textcol + i + j + STRLEN(midp) + 1));
  
! 	/* copy first part we want to keep */
! 	mch_memmove(newp, oldp, (size_t)bd.textcol);
! 	/* Now copy any TABS and spp to ensure correct alignment! */
! 	while (vim_iswhite(*midp))
! 	{
! 	    if (*midp == TAB)
! 		i++;
! 	    else /*space */
! 		j++;
! 	    midp++;
  	}
! 	/* We might have an extra TAB worth of spp now! */
! 	if (j / p_ts && !curbuf->b_p_et)
  	{
! 	    i++;
! 	    j -= p_ts;
  	}
! 	copy_chars(newp + bd.textcol, (size_t)i, TAB);
! 	copy_spaces(newp + bd.textcol + i, (size_t)j);
  
  	/* the end */
  	mch_memmove(newp + STRLEN(newp), midp, (size_t)STRLEN(midp) + 1);
--- 465,610 ----
      }
      else /* left */
      {
! #ifdef FEAT_VARTABS
! 	if (tabstop_count(p_vts) > 1)
  	{
! 	    /* With multiple tabstops a completely different method is needed.
! 	     * Tabs count for different widths in different parts of the line,
! 	     * so instead of trying to preserve tabs copied from the old line
! 	     * the total width of all whitespace is calculated, the shift is
! 	     * removed, and then width is converted back to tabs and spaces.
! 	     */
! 	    int  totalspace = 0;
! 	    int  tcol;		/* used for tracking tab columns */
  
! 	    vcol = oap->start_vcol;
! 	    /* walk vcol past ws to be removed */
! 	    for (midp = oldp + bd.textcol;
! 		  vcol < (oap->start_vcol + total) && vim_iswhite(*midp); )
! 	    {
! 		incr = lbr_chartabsize_adv(&midp, (colnr_T)vcol);
! 		vcol += incr;
! 	    }
! 	    /* internal is the block-internal ws replacing a split TAB */
! 	    if (vcol > (oap->start_vcol + total))
! 	    {
! 		/* we have to split the TAB *(midp-1) */
! 		internal = vcol - (oap->start_vcol + total);
! 	    }
! 
! 	    totalspace = bd.startspaces + internal;
! 	    /* Add the remaining ws that will be retained in the shift. */
! 	    tcol = vcol;
! 	    while (vim_iswhite(*midp))
! 	    {
! 		if (*midp == TAB)
! 		{
! 		    int pad = tabstop_padding(tcol, p_ts, p_vts);
! 		    totalspace += pad;
! 		    tcol += pad;
! 		}
! 		else /*space */
! 		{
! 		    totalspace++;
! 		    tcol++;
! 		}
! 		midp++;
! 	    }
! 
! 	    /* Convert the ws width into tabs and spaces. */
  	    if (!curbuf->b_p_et)
  	    {
  		for (ptr = oldp, col = 0; ptr < oldp+bd.textcol; )
  		    col += lbr_chartabsize_adv(&ptr, (colnr_T)col);
  
! 		tabstop_fromto(col, col + totalspace, p_ts, p_vts, &i, &j);
  	    }
  
! 	    newp = alloc_check(bd.textcol + i + j + (unsigned)STRLEN(midp) + 1);
! 	    if (newp == NULL)
! 		return;
! 	    vim_memset(newp, NUL, (size_t)(bd.textcol + i + j + STRLEN(midp) + 1));
  
! 	    /* copy first part we want to keep */
! 	    mch_memmove(newp, oldp, (size_t)bd.textcol);
! 
! 	    copy_chars(newp + bd.textcol, (size_t)i, TAB);
! 	    copy_spaces(newp + bd.textcol + i, (size_t)j);
  	}
! 	else
! 	/* If there's only a single value for 'vartabstop' then this is copied
! 	 * into p_ts and we fall through into the original code for normal tabs.
! 	 * This ensures that the behaviour for a single tabstop value is exactly
! 	 * the same as with earlier versions of Vim.
! 	 *   Note that when FEAT_VARTABS is undefined the code below is indented
! 	 * one position too far; that is, it's contained within an "else" block
! 	 * that exists only when FEAT_VARTABS is defined.
! 	 */
  	{
! 	    if (tabstop_count(p_vts) == 1)
! 		p_ts = tabstop_first(p_vts);
! #endif
! 	    vcol = oap->start_vcol;
! 	    /* walk vcol past ws to be removed */
! 	    for (midp = oldp + bd.textcol;
! 		  vcol < (oap->start_vcol + total) && vim_iswhite(*midp); )
! 	    {
! 		incr = lbr_chartabsize_adv(&midp, (colnr_T)vcol);
! 		vcol += incr;
! 	    }
! 	    /* internal is the block-internal ws replacing a split TAB */
! 	    if (vcol > (oap->start_vcol + total))
! 	    {
! 		/* we have to split the TAB *(midp-1) */
! 		internal = vcol - (oap->start_vcol + total);
! 	    }
! 	    /* if 'expandtab' is not set, use TABs */
! 
! 	    split = bd.startspaces + internal;
! 	    if (split > 0)
! 	    {
! 		if (!curbuf->b_p_et)
! 		{
! 		    for (ptr = oldp, col = 0; ptr < oldp+bd.textcol; )
! 			col += lbr_chartabsize_adv(&ptr, (colnr_T)col);
! 
! 		    /* col+1 now equals the start col of the first char of the
! 		     * block (may be < oap.start_vcol if we're splitting a TAB) */
! 		    i = ((col % p_ts) + split) / p_ts; /* number of tabs */
! 		}
! 		if (i)
! 		    j = ((col % p_ts) + split) % p_ts; /* number of spp */
! 		else
! 		    j = split;
! 	    }
! 
! 	    newp = alloc_check(bd.textcol + i + j + (unsigned)STRLEN(midp) + 1);
! 	    if (newp == NULL)
! 		return;
! 	    vim_memset(newp, NUL, (size_t)(bd.textcol + i + j + STRLEN(midp) + 1));
! 
! 	    /* copy first part we want to keep */
! 	    mch_memmove(newp, oldp, (size_t)bd.textcol);
! 	    /* Now copy any TABS and spp to ensure correct alignment! */
! 	    while (vim_iswhite(*midp))
! 	    {
! 		if (*midp == TAB)
! 		    i++;
! 		else /*space */
! 		    j++;
! 		midp++;
! 	    }
! 	    /* We might have an extra TAB worth of spp now! */
! 	    if (j / p_ts && !curbuf->b_p_et)
! 	    {
! 		i++;
! 		j -= p_ts;
! 	    }
! 	    copy_chars(newp + bd.textcol, (size_t)i, TAB);
! 	    copy_spaces(newp + bd.textcol + i, (size_t)j);
! #ifdef FEAT_VARTABS
  	}
! #endif
  
  	/* the end */
  	mch_memmove(newp + STRLEN(newp), midp, (size_t)STRLEN(midp) + 1);
***************
*** 3371,3380 ****
--- 3463,3480 ----
  	{
  	    /* 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;
  	}
*** ../vim71-no-vts/src/option.c	2008-01-16 21:06:24.000000000 +0000
--- src/option.c	2008-01-17 21:28:42.000000000 +0000
***************
*** 177,182 ****
--- 177,186 ----
  #define PV_TW		OPT_BUF(BV_TW)
  #define PV_TX		OPT_BUF(BV_TX)
  #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.
***************
*** 357,362 ****
--- 361,370 ----
  static long	p_tw;
  static int	p_tx;
  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
***************
*** 372,377 ****
--- 380,388 ----
  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
  {
***************
*** 2556,2561 ****
--- 2567,2580 ----
      {"updatetime",  "ut",   P_NUM|P_VI_DEF,
  			    (char_u *)&p_ut, PV_NONE,
  			    {(char_u *)4000L, (char_u *)0L}},
+ #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 *)"", (char_u *)0L}},
+ #endif
      {"verbose",	    "vbs",  P_NUM|P_VI_DEF,
  			    (char_u *)&p_verbose, PV_NONE,
  			    {(char_u *)0L, (char_u *)0L}},
***************
*** 5050,5055 ****
--- 5069,5078 ----
      /* 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
  }
  
  /*
***************
*** 5161,5166 ****
--- 5184,5193 ----
      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
  }
  
  /*
***************
*** 6584,6589 ****
--- 6611,6698 ----
      }
  #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
      {
***************
*** 7598,7604 ****
--- 7707,7720 ----
      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
      }
  
      /*
***************
*** 9163,9168 ****
--- 9279,9288 ----
  #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! */
***************
*** 9432,9437 ****
--- 9552,9566 ----
  #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
***************
*** 9538,9549 ****
--- 9667,9693 ----
  	     * 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')
***************
*** 10272,10277 ****
--- 10416,10427 ----
  		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 */
***************
*** 10288,10293 ****
--- 10438,10448 ----
  	    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
  	}
  
  	/*
***************
*** 10301,10306 ****
--- 10456,10469 ----
  	    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 */
***************
*** 10321,10326 ****
--- 10484,10494 ----
  	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
      }
  
      /*
***************
*** 10335,10340 ****
--- 10503,10520 ----
  	    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 */
***************
*** 10355,10360 ****
--- 10535,10545 ----
  	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;
***************
*** 10679,10681 ****
--- 10864,11161 ----
  {
      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(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(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
*** ../vim71-no-vts/src/option.h	2007-05-07 20:23:03.000000000 +0100
--- src/option.h	2008-01-17 12:15:34.000000000 +0000
***************
*** 993,998 ****
--- 993,1002 ----
      , BV_TW
      , BV_TX
      , BV_WM
+ #ifdef FEAT_VARTABS
+     , BV_VSTS
+     , BV_VTS
+ #endif
      , BV_COUNT	    /* must be the last one */
  };
  
*** ../vim71-no-vts/src/screen.c	2008-01-16 21:10:24.000000000 +0000
--- src/screen.c	2008-01-17 14:26:45.000000000 +0000
***************
*** 4054,4061 ****
--- 4054,4066 ----
  		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_MBYTE
  		    mb_utf8 = FALSE;	/* don't draw as UTF-8 */
  #endif
*** ../vim71-no-vts/src/structs.h	2008-01-16 21:06:24.000000000 +0000
--- src/structs.h	2008-01-17 12:15:34.000000000 +0000
***************
*** 1433,1438 ****
--- 1433,1445 ----
      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
*** ../vim71-no-vts/src/version.c	2008-01-16 21:10:26.000000000 +0000
--- src/version.c	2008-01-17 12:15:34.000000000 +0000
***************
*** 566,571 ****
--- 566,576 ----
  #else
  	"-user_commands",
  #endif
+ #ifdef FEAT_VARTABS
+ 	"+vartabs",
+ #else
+ 	"-vartabs",
+ #endif
  #ifdef FEAT_VERTSPLIT
  	"+vertsplit",
  #else
*** ../vim71-no-vts/src/workshop.c	2007-05-07 20:47:26.000000000 +0100
--- src/workshop.c	2008-01-17 19:56:17.000000000 +0000
***************
*** 62,68 ****
--- 62,72 ----
  static char	*fixAccelText(char *);
  static void	 addMenu(char *, char *, char *);
  static char	*lookupVerb(char *, int);
+ #ifdef FEAT_VARTABS
+ static int	 computeIndex(int, char_u *, int, int *);
+ #else
  static int	 computeIndex(int, char_u *, int);
+ #endif
  static void	 coloncmd(char *, Boolean);
  
  extern Widget	 vimShell;
***************
*** 1597,1603 ****
--- 1601,1611 ----
  	     * 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;
***************
*** 1634,1640 ****
  computeIndex(
  	int		 wantedCol,
  	char_u		*line,
! 	int		 ts)
  {
      int		 col = 0;
      int		 idx = 0;
--- 1642,1652 ----
  computeIndex(
  	int		 wantedCol,
  	char_u		*line,
! 	int		 ts
! #ifdef FEAT_VARTABS
! 	int		*vts
! #else
! 	)
  {
      int		 col = 0;
      int		 idx = 0;
***************
*** 1642,1648 ****
--- 1654,1664 ----
      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++;
*** ../vim71-no-vts/src/proto/misc1.pro	2008-01-16 21:06:22.000000000 +0000
--- src/proto/misc1.pro	2008-01-17 14:58:30.000000000 +0000
***************
*** 3,8 ****
--- 3,11 ----
  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 old_indent));
*** ../vim71-no-vts/src/proto/option.pro	2007-05-12 11:39:41.000000000 +0100
--- src/proto/option.pro	2008-01-17 21:16:29.000000000 +0000
***************
*** 53,56 ****
--- 53,67 ----
  void save_file_ff __ARGS((buf_T *buf));
  int file_ff_differs __ARGS((buf_T *buf));
  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
  /* vim: set ft=c : */
