Elastic tabstops by "Yarin" on vim_dev:

http://groups.google.com/group/vim_dev/browse_thread/thread/6b1db9723c62d157

Tweaked so that it applies cleanly.

Tweaked to include updated code from base source in screen.c.

Tweaked to compile in normal.c.

diff -r 974ed4c3395a runtime/doc/options.txt
--- a/runtime/doc/options.txt	Sat Feb 18 11:49:28 2012 -0600
+++ b/runtime/doc/options.txt	Sat Feb 18 12:18:00 2012 -0600
@@ -1423,7 +1423,6 @@
 	program.  See |cinoptions-values| for the values of this option, and
 	|C-indenting| for info on C indenting in general.
 
-
 						*'cinwords'* *'cinw'*
 'cinwords' 'cinw'	string	(default "if,else,while,do,for,switch")
 			local to buffer
@@ -2526,6 +2525,29 @@
 	also 'gdefault' option.
 	Switching this option on is discouraged!
 
+		    *'elastictabstops'* *'el'* *'noelastictabstops'* *'noel'*
+'elastictabstops' 'el'	boolean	(default off)
+			global or local to buffer |global-local|
+			{not in Vi}
+	Makes tabstops elastic. Tabs will automatically group when next
+	to each other, and share the same, elastic, tabstop.
+
+				    	*'elastictabstopsindentation'* *'eli'*
+'elastictabstopsindentation' 'eli'	number	(default 0)
+				global or local to buffer |global-local|
+					{not in Vi}
+	When elastic tabstops are on, and this value is positive, leading
+	identation tabs will be at least this width. Otherwise, the default
+        tabstop width is used.
+
+				    	*'elastictabstopsseparation'* *'els'*
+'elastictabstopsseparation' 'els'	number	(default 0)
+				global or local to buffer |global-local|
+					{not in Vi}
+	When elastic tabstops are on, and this value is positive, content
+	separating tabs will be at least this width. Otherwise, the default
+        tabstop width is used.
+
 					*'encoding'* *'enc'* *E543*
 'encoding' 'enc'	string (default: "latin1" or value from $LANG)
 			global
diff -r 974ed4c3395a runtime/doc/tags
--- a/runtime/doc/tags	Sat Feb 18 11:49:28 2012 -0600
+++ b/runtime/doc/tags	Sat Feb 18 12:18:00 2012 -0600
@@ -199,6 +199,12 @@
 'efm'	options.txt	/*'efm'*
 'ei'	options.txt	/*'ei'*
 'ek'	options.txt	/*'ek'*
+'el'	options.txt	/*'el'*
+'elastictabstops'	options.txt	/*'elastictabstops'*
+'elastictabstopsindentation'	options.txt	/*'elastictabstopsindentation'*
+'elastictabstopsseparation'	options.txt	/*'elastictabstopsseparation'*
+'eli'	options.txt	/*'eli'*
+'els'	options.txt	/*'els'*
 'enc'	options.txt	/*'enc'*
 'encoding'	options.txt	/*'encoding'*
 'endofline'	options.txt	/*'endofline'*
@@ -512,6 +518,8 @@
 'noed'	options.txt	/*'noed'*
 'noedcompatible'	options.txt	/*'noedcompatible'*
 'noek'	options.txt	/*'noek'*
+'noel'	options.txt	/*'noel'*
+'noelastictabstops'	options.txt	/*'noelastictabstops'*
 'noendofline'	options.txt	/*'noendofline'*
 'noeol'	options.txt	/*'noeol'*
 'noequalalways'	options.txt	/*'noequalalways'*
diff -r 974ed4c3395a runtime/syntax/vim.vim
--- a/runtime/syntax/vim.vim	Sat Feb 18 11:49:28 2012 -0600
+++ b/runtime/syntax/vim.vim	Sat Feb 18 12:18:00 2012 -0600
@@ -34,12 +34,12 @@
 syn keyword vimOption contained	al antialias autochdir background balloondelay bexpr bk bs casemap cf cink cmdheight colorcolumn completefunc copyindent cryptmethod cscopeverbose csverb debug dict dir eb enc errorbells expandtab fdl fenc fileformat fml foldignore foldopen fs gfn grepprg guiheadroom helplang history hls imactivatekey iminsert inde insertmode iskeyword keymodel laststatus lisp lpl magic maxfuncdepth menuitems mm modifiable mousem mps number opendevice paste pex pmbcs printencoding prompt rdt revins ro runtimepath scroll sections sh shellslash shortmess showtabline slm sn spellcapcheck splitright ssop stmp swapfile syn tag tb termbidi tgst titlelen toolbar tsl ttybuiltin tw undodir ur verbose viminfo warn wfh wig wildmode winfixwidth wiw wrap writedelay
 syn keyword vimOption contained	aleph ar autoindent backspace ballooneval bg bkc bsdir cb cfu cinkeys cmdwinheight columns completeopt cot cscopepathcomp cspc cuc deco dictionary directory ed encoding errorfile exrc fdls fencs fileformats fmr foldlevel foldtext fsync gfs gtl guioptions hf hk hlsearch imak ims indentexpr is isp keywordprg lazyredraw lispwords ls makeef maxmapdepth mfd mmd modified mousemodel msm numberwidth operatorfunc pastetoggle pexpr pmbfn printexpr pt readonly ri rs sb scrollbind secure shcf shelltemp shortname shq sm so spellfile spr st sts swapsync synmaxcol tagbsearch tbi termencoding thesaurus titleold toolbariconsize tsr ttyfast tx undofile ut verbosefile virtualedit wb wfw wildchar wildoptions winheight wm wrapmargin ws
 syn keyword vimOption contained	allowrevins arab autoread backup balloonexpr bh bl bsk cc ch cino cmp com concealcursor cp cscopeprg csprg cul def diff display edcompatible endofline errorformat fcl fdm fex filetype fo foldlevelstart formatexpr ft gfw gtt guipty hh hkmap ic imc imsearch indentkeys isf isprint km lbr list lsp makeprg maxmem mh mmp more mouses mzq nuw opfunc patchexpr pfn popt printfont pumheight redrawtime rightleft rtp sbo scrolljump sel shell shelltype showbreak si smartcase softtabstop spelllang sps sta su swb syntax taglength tbidi terse tildeop titlestring top ttimeout ttym uc undolevels vb vfile visualbell wc wh wildcharm wim winminheight wmh wrapscan ww
-syn keyword vimOption contained	altkeymap arabic autowrite backupcopy bdir bin bomb bt ccv charconvert cinoptions cms comments conceallevel cpo cscopequickfix csqf cursorbind define diffexpr dy ef eol esckeys fcs fdn ff fillchars foldclose foldmarker formatlistpat gcr ghr guicursor guitablabel hi hkmapp icon imcmdline inc indk isfname joinspaces kmp lcs listchars lw mat maxmempattern mis mmt mouse mouseshape mzquantum odev osfiletype patchmode ph preserveindent printheader pvh relativenumber rightleftcmd ru sbr scrolloff selection shellcmdflag shellxquote showcmd sidescroll smartindent sol spellsuggest sr stal sua swf ta tagrelative tbis textauto
+syn keyword vimOption contained	altkeymap arabic autowrite backupcopy bdir bin bomb bt ccv charconvert cinoptions cms comments conceallevel cpo cscopequickfix csqf cursorbind define diffexpr dy ef eol esckeys fcs fdn ff fillchars foldclose foldmarker formatlistpat gcr ghr guicursor guitablabel hi hkmapp icon imcmdline inc indk isfname joinspaces kmp lcs listchars lw mat maxmempattern mis mmt mouse mouseshape mzquantum odev osfiletype patchmode ph preserveindent printheader pvh relativenumber rightleftcmd ru sbr scrolloff selection shellcmdflag shellxquote showcmd sidescroll smartindent sol spellsuggest sr stal sua swf ta tagrelative tbis textauto elastictabstops el elastictabstopsindentation eli elastictabstopsseparation els
 
 " vimOptions: These are the turn-off setting variants {{{2
 syn keyword vimOption contained	noacd noallowrevins noantialias noarabic noarshape noautoread noaw noballooneval nobinary nobk nobuflisted nocin noconfirm nocopyindent nocscopetag nocsverb nocursorbind nodeco nodiff noeb noek noequalalways noesckeys noex noexrc nofk nofoldenable nogdefault nohid nohk nohkmapp nohls noic noignorecase noimc noimd noincsearch noinfercase nois nojs nolbr nolisp noloadplugins nolz nomacatsui nomh nomod nomodifiable nomore nomousefocus nonu noodev nopaste nopreserveindent noprompt noreadonly noremap norevins norightleft nornu nors noruler nosc noscrollbind nosecure noshellslash noshiftround noshowcmd noshowmatch nosi nosmartcase nosmarttab nosn nospell nosplitright nosr nosta nostmp noswf notagbsearch notagstack notbidi notermbidi notextauto notf notildeop notitle notop nottimeout nottyfast novb nowa nowb nowfh nowildignorecase* * nowinfixheight nowiv nowrap nowrite nowritebackup
 syn keyword vimOption contained	noai noaltkeymap noar noarabicshape noautochdir noautowrite noawa nobeval nobiosk nobl nocf nocindent noconsk nocp nocscopeverbose nocuc nocursorcolumn nodelcombine nodigraph noed noendofline noerrorbells noet noexpandtab nofen nofkmap nogd noguipty nohidden nohkmap nohkp nohlsearch noicon noim noimcmdline noimdisable noinf noinsertmode nojoinspaces nolazyredraw nolinebreak nolist nolpl noma nomagic noml nomodeline nomodified nomousef nomousehide nonumber noopendevice nopi nopreviewwindow nopvw norelativenumber norestorescreen nori norl noro noru nosb noscb noscs nosft noshelltemp noshortname noshowfulltag noshowmode nosm nosmartindent nosmd nosol nosplitbelow nospr nossl nostartofline noswapfile nota notagrelative notbi notbs noterse notextmode notgst notimeout noto notr nottybuiltin notx novisualbell nowarn noweirdinvert nowfw nowildmenu nowinfixwidth nowmnu nowrapscan nowriteany nows
-syn keyword vimOption contained	noakm noanti noarab noari noautoindent noautowriteall nobackup nobin nobioskey nobomb noci nocompatible noconskey nocrb nocst nocul nocursorline nodg noea noedcompatible noeol
+syn keyword vimOption contained	noakm noanti noarab noari noautoindent noautowriteall nobackup nobin nobioskey nobomb noci nocompatible noconskey nocrb nocst nocul nocursorline nodg noea noedcompatible noeol  noelastictabstops noel
 
 " vimOptions: These are the invertible variants {{{2
 syn keyword vimOption contained	invacd invallowrevins invantialias invarabic invarshape invautoread invaw invballooneval invbinary invbk invbuflisted invcin invconfirm invcopyindent invcscopetag invcsverb invcursorbind invdeco invdiff inveb invek invequalalways invesckeys invex invexrc invfk invfoldenable invgdefault invhid invhk invhkmapp invhls invic invignorecase invimc invimd invincsearch invinfercase invis invjs invlbr invlisp invloadplugins invlz invmacatsui invmh invmod invmodifiable invmore invmousefocus invnu invodev invpaste invpreserveindent invprompt invreadonly invremap invrevins invrightleft invrnu invrs invruler invsc invscrollbind invsecure invshellslash invshiftround invshowcmd invshowmatch invsi invsmartcase invsmarttab invsn invspell invsplitright invsr invsta invstmp invswf invtagbsearch invtagstack invtbidi invtermbidi invtextauto invtf invtildeop invtitle invtop invttimeout invttyfast invvb invwa invwb invwfh invwildignorecase* * invwinfixheight invwiv invwrap invwrite invwritebackup
diff -r 974ed4c3395a src/buffer.c
--- a/src/buffer.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/buffer.c	Sat Feb 18 12:18:00 2012 -0600
@@ -65,6 +65,10 @@
 static char *msg_qflist = N_("[Quickfix List]");
 #endif
 
+#ifndef NO_ELASTIC_TABSTOPS
+void reset_etabs(buf_T*);
+#endif
+
 /*
  * Open current buffer, that is: open the memfile and read the file into
  * memory.
@@ -565,6 +569,11 @@
     if (buf == curbuf && !is_curbuf)
 	return;
 #endif
+
+   #ifndef NO_ELASTIC_TABSTOPS
+   reset_etabs(buf);
+   #endif
+
 #ifdef FEAT_DIFF
     diff_buf_delete(buf);	    /* Can't use 'diff' for unloaded buffer. */
 #endif
diff -r 974ed4c3395a src/charset.c
--- a/src/charset.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/charset.c	Sat Feb 18 12:18:00 2012 -0600
@@ -10,15 +10,16 @@
 #include "vim.h"
 
 #ifdef FEAT_LINEBREAK
-static int win_chartabsize __ARGS((win_T *wp, char_u *p, colnr_T col));
+static int win_chartabsize __ARGS((win_T *wp, char_u *p, linenr_T line, colnr_T col));
 #endif
 
 #ifdef FEAT_MBYTE
 # if defined(HAVE_WCHAR_H)
 #  include <wchar.h>	    /* for towupper() and towlower() */
 # endif
-static int win_nolbr_chartabsize __ARGS((win_T *wp, char_u *s, colnr_T col, int *headp));
+static int win_nolbr_chartabsize __ARGS((win_T *wp, char_u *s, linenr_T line, colnr_T col, int *headp));
 #endif
+int get_etab_char_width(buf_T*, linenr_T line, colnr_T);
 
 static unsigned nr2hex __ARGS((unsigned c));
 
@@ -810,35 +811,33 @@
  * Also see getvcol() below.
  */
 
-#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
+#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, line, col) \
     if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) \
-    { \
-	int ts; \
-	ts = (buf)->b_p_ts; \
-	return (int)(ts - (col % ts)); \
-    } \
+	return get_etab_char_width(buf, line, col); \
     else \
 	return ptr2cells(p);
 
 #if defined(FEAT_VREPLACE) || defined(FEAT_EX_EXTRA) || defined(FEAT_GUI) \
 	|| defined(FEAT_VIRTUALEDIT) || defined(PROTO)
     int
-chartabsize(p, col)
+chartabsize(p, line, col)
     char_u	*p;
-    colnr_T	col;
+    linenr_T line;
+    colnr_T     col;
 {
-    RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, p, col)
+    RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, p, line, col)
 }
 #endif
 
 #ifdef FEAT_LINEBREAK
     static int
-win_chartabsize(wp, p, col)
+win_chartabsize(wp, p, line, col)
     win_T	*wp;
     char_u	*p;
-    colnr_T	col;
+    linenr_T line;
+    colnr_T     col;
 {
-    RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, p, col)
+    RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, p, line, col)
 }
 #endif
 
@@ -846,25 +845,24 @@
  * Return the number of characters the string 's' will take on the screen,
  * taking into account the size of a tab.
  */
-    int
-linetabsize(s)
-    char_u	*s;
+int linetabsize(linenr_T line, char_u* s)
 {
-    return linetabsize_col(0, s);
+    return linetabsize_col(line, 0, s);
 }
 
 /*
  * Like linetabsize(), but starting at column "startcol".
  */
     int
-linetabsize_col(startcol, s)
-    int		startcol;
+linetabsize_col(line, startcol, s)
+    linenr_T line;
+    colnr_T     startcol;
     char_u	*s;
 {
     colnr_T	col = startcol;
 
     while (*s != NUL)
-	col += lbr_chartabsize_adv(&s, col);
+	col += lbr_chartabsize_adv(&s, line, col);
     return (int)col;
 }
 
@@ -872,16 +870,18 @@
  * Like linetabsize(), but for a given window instead of the current one.
  */
     int
-win_linetabsize(wp, p, len)
+win_linetabsize(wp, p, len, line, startcol)
     win_T	*wp;
     char_u	*p;
     colnr_T	len;
+    linenr_T line;
+    colnr_T     startcol;
 {
-    colnr_T	col = 0;
+    colnr_T	col = startcol;
     char_u	*s;
 
     for (s = p; *s != NUL && (len == MAXCOL || s < p + len); mb_ptr_adv(s))
-	col += win_lbr_chartabsize(wp, s, col, NULL);
+	col += win_lbr_chartabsize(wp, s, line, col, NULL);
     return (int)col;
 }
 
@@ -1010,9 +1010,10 @@
  * like chartabsize(), but also check for line breaks on the screen
  */
     int
-lbr_chartabsize(s, col)
+lbr_chartabsize(s, line, col)
     unsigned char	*s;
-    colnr_T		col;
+    linenr_T line;
+    colnr_T             col;
 {
 #ifdef FEAT_LINEBREAK
     if (!curwin->w_p_lbr && *p_sbr == NUL)
@@ -1020,12 +1021,12 @@
 #endif
 #ifdef FEAT_MBYTE
 	if (curwin->w_p_wrap)
-	    return win_nolbr_chartabsize(curwin, s, col, NULL);
+	    return win_nolbr_chartabsize(curwin, s, line, col, NULL);
 #endif
-	RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, s, col)
+	RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, s, line, col)
 #ifdef FEAT_LINEBREAK
     }
-    return win_lbr_chartabsize(curwin, s, col, NULL);
+    return win_lbr_chartabsize(curwin, s, line, col, NULL);
 #endif
 }
 
@@ -1033,13 +1034,14 @@
  * Call lbr_chartabsize() and advance the pointer.
  */
     int
-lbr_chartabsize_adv(s, col)
+lbr_chartabsize_adv(s, line, col)
     char_u	**s;
-    colnr_T	col;
+    linenr_T line;
+    colnr_T     col;
 {
     int		retval;
 
-    retval = lbr_chartabsize(*s, col);
+    retval = lbr_chartabsize(*s, line, col);
     mb_ptr_adv(*s);
     return retval;
 }
@@ -1052,10 +1054,11 @@
  * value, init to 0 before calling.
  */
     int
-win_lbr_chartabsize(wp, s, col, headp)
+win_lbr_chartabsize(wp, s, line, col, headp)
     win_T	*wp;
     char_u	*s;
-    colnr_T	col;
+    linenr_T line;
+    colnr_T     col;
     int		*headp UNUSED;
 {
 #ifdef FEAT_LINEBREAK
@@ -1082,16 +1085,16 @@
     {
 #ifdef FEAT_MBYTE
 	if (wp->w_p_wrap)
-	    return win_nolbr_chartabsize(wp, s, col, headp);
+	    return win_nolbr_chartabsize(wp, s, line, col, headp);
 #endif
-	RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, s, col)
+	RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, s, line, col)
     }
 
 #ifdef FEAT_LINEBREAK
     /*
      * First get normal size, without 'linebreak'
      */
-    size = win_chartabsize(wp, s, col);
+    size = win_chartabsize(wp, s, line, col);
     c = *s;
 
     /*
@@ -1133,7 +1136,7 @@
 			    && (col2 == col || !vim_isbreak(*ps))))))
 		break;
 
-	    col2 += win_chartabsize(wp, s, col2);
+	    col2 += win_chartabsize(wp, s, line, col2);
 	    if (col2 >= colmax)		/* doesn't fit */
 	    {
 		size = colmax - col;
@@ -1191,19 +1194,17 @@
  * doesn't fit at the end of the screen line.
  */
     static int
-win_nolbr_chartabsize(wp, s, col, headp)
+win_nolbr_chartabsize(wp, s, line, col, headp)
     win_T	*wp;
     char_u	*s;
-    colnr_T	col;
+    linenr_T line;
+    colnr_T     col;
     int		*headp;
 {
     int		n;
 
     if (*s == TAB && (!wp->w_p_list || lcs_tab1))
-    {
-	n = wp->w_buffer->b_p_ts;
-	return (int)(n - (col % n));
-    }
+	return get_etab_char_width(wp->w_buffer, line, col);
     n = ptr2cells(s);
     /* Add one cell for a double-width character in the last column of the
      * window, displayed with a ">". */
@@ -1265,7 +1266,6 @@
     char_u	*posptr;	/* points to char at pos->col */
     int		incr;
     int		head;
-    int		ts = wp->w_buffer->b_p_ts;
     int		c;
 
     vcol = 0;
@@ -1303,7 +1303,7 @@
 	    }
 	    /* A tab gets expanded, depending on the current column */
 	    if (c == TAB)
-		incr = ts - (vcol % ts);
+		incr = get_etab_char_width(wp->w_buffer, pos->lnum, vcol);
 	    else
 	    {
 #ifdef FEAT_MBYTE
@@ -1344,7 +1344,7 @@
 	{
 	    /* A tab gets expanded, depending on the current column */
 	    head = 0;
-	    incr = win_lbr_chartabsize(wp, ptr, vcol, &head);
+	    incr = win_lbr_chartabsize(wp, ptr, pos->lnum, vcol, &head);
 	    /* make sure we don't go past the end of the line */
 	    if (*ptr == NUL)
 	    {
@@ -2033,3 +2033,194 @@
 	buffer[i] = ebcdic2ascii_tab[buffer[i]];
 }
 #endif
+
+#ifndef NO_ELASTIC_TABSTOPS
+
+int get_smallest_printable_etab_width(buf_T* buf, int level)
+{
+   if(level <= 0 && buf->b_p_elastic_tabstops_indentation > 0)
+      return buf->b_p_elastic_tabstops_indentation;
+   if(level > 0 && buf->b_p_elastic_tabstops_separation > 0)
+      return buf->b_p_elastic_tabstops_separation;
+   if(buf->b_p_ts >= 1)
+      return buf->b_p_ts;
+   return 1;
+}
+
+int step_get_etab_start_pt(buf_T* buf, colnr_T col, linenr_T line,
+                           int layer, colnr_T* deepest, int* highestlevel)
+{
+   int i;
+
+   /* find a fitting etab entry */
+   i = buf->b_etab_count;
+   while (--i >= 0)
+      if(buf->b_etabs[i].line == line)
+         if(buf->b_etabs[i].layer == layer)
+         {
+            if(buf->b_etabs[i].col > *deepest) /* if this etab starts deeper */
+               *deepest = buf->b_etabs[i].col; /* it takes priority */
+            if(buf->b_etabs[i].level > *highestlevel)
+               *highestlevel = buf->b_etabs[i].level;
+            return 1; /* we're doing good */
+         }
+   return 0; /* we've reached this end of the rope */
+}
+
+int get_etab_char_print_width(buf_T* buf, linenr_T line, colnr_T col,
+                              int layer, int level, int* back_up)
+{
+   int i, twidth, hlevel;
+   etab_T new_etab, * ctab, * old_etabs;
+   linenr_T tline_top, tline_bot;
+   colnr_T deepest;
+
+   /* if elastic tabstops are turned off, just return like a standard tab */
+   if(!buf->b_p_elastic_tabstops)
+      return (buf->b_p_ts - (col % buf->b_p_ts));
+
+   /* FIXME: i have some places pass 0x7FFFFFFF as the line number      */
+   /* i don't know what else to do about the few places that don't have */
+   /* one to give. All we can do is wish the caller luck                */
+   if(line < 1 || line > buf->b_ml.ml_line_count)
+      return get_smallest_printable_etab_width(buf, level);
+
+   /* find a record of a etab at any location provided by the caller        */
+   i = buf->b_etab_count;
+   while (--i >= 0)
+      if(buf->b_etabs[i].line == line)
+         if(layer > 0 ? (buf->b_etabs[i].layer == layer) :
+            (col >= 0 ? (buf->b_etabs[i].col == col) : 1))
+         {
+            ctab = &buf->b_etabs[i]; /* yay - pick it */
+            goto done_changing; /* warp to the end to wrap things up */
+         }
+   if(i < 0) /* if no record could be found, see about making one */
+   {
+      /* if the caller doesn't already know where the etab is, and we can't */
+      /* find an entry for it, then there's no telling if the etab in       */ 
+      /* question even exists. So all we can do is wish the caller luck     */
+      if(col < 0)
+         return get_smallest_printable_etab_width(buf, level);
+
+      /* since the caller knows where the etab is, */
+      /* we can prepare a new record for it        */
+      new_etab.line = line;
+      new_etab.col = col;
+      new_etab.width = 0; /* tbd */
+      new_etab.layer = layer;
+      new_etab.level = level;
+
+      /* try to find an unused slot to put the new etab record into */
+      i = buf->b_etab_count;
+      while (--i >= 0)
+         if(buf->b_etabs[i].line == 0)
+         {
+            ctab = &buf->b_etabs[i];
+            memcpy(&buf->b_etabs[i], &new_etab, sizeof(etab_T));
+            break;
+         }
+      if(i < 0) /* if there's no free room for the new etab record, make room */
+      {
+         old_etabs = buf->b_etabs;
+         buf->b_etabs = (etab_T*)malloc(sizeof(etab_T) * (buf->b_etab_count + 1
+                                        )); /* assuming successful allocation */
+         if(buf->b_etab_count) /* if there are existing records, move them */
+         {
+            memcpy(buf->b_etabs, old_etabs, sizeof(etab_T) * buf->b_etab_count);
+            free((void*)old_etabs);
+         }
+         ctab = &buf->b_etabs[buf->b_etab_count];
+         memcpy(&buf->b_etabs[buf->b_etab_count++], &new_etab, sizeof(etab_T));
+      }
+   }
+
+   /* search the records to find out what other etabs belong in our cluster.  */
+   /* We do this by looking for etabs on neighboring lines, on the same layer */
+   /* And while we're at it, find the deepest column our cluster is touching  */
+   deepest = ctab->col;
+   hlevel = ctab->level;
+   tline_top = line;
+   while(step_get_etab_start_pt(buf, col, --tline_top, ctab->layer,
+                                &deepest, &hlevel));
+   tline_top++;
+   tline_bot = line;
+   while(step_get_etab_start_pt(buf, col, ++tline_bot, ctab->layer,
+                                &deepest, &hlevel));
+   tline_bot--;
+   deepest += get_smallest_printable_etab_width(buf, hlevel);
+
+   /* enumerate our cluster, and make sure it's properly aligned */
+   i = buf->b_etab_count;
+   while (--i >= 0)
+      if(buf->b_etabs[i].line >= tline_top && buf->b_etabs[i].line <= tline_bot)
+         if(buf->b_etabs[i].layer == ctab->layer)
+         {
+            twidth = deepest - buf->b_etabs[i].col; /* get the min deep width */
+            if(twidth != buf->b_etabs[i].width) /* correct only when needed */
+            {
+               buf->b_etabs[i].width = twidth;
+               /* if the caller is willing to back up it's rendering to make */
+               /* corrections, and we just corrected a previous line, tell   */
+               /* the caller that it's time to visit the last back up point  */
+               if(back_up) if(buf->b_etabs[i].line < line)
+                  if((*back_up) == 1) (*back_up) = -1;
+            }
+         }
+
+   done_changing:
+
+   /* if the caller is willing to back up it's rendering to make corrections */
+   /* but they haven't defined a back up point yet, set it to this line      */
+   if(back_up) if((*back_up) == 0) (*back_up) = 1;
+
+   return ctab->width; /* return the width of the caller's inquery */
+}
+
+void shift_etab_chars(buf_T* buf, linenr_T line, int line_shift)
+{
+   int i;
+
+   /* enumerate all the etabs on record, and adjust them according to the */
+   /* change of lines as indicated by the caller                          */
+   i = buf->b_etab_count;
+   while (--i >= 0)
+      /* if this etab is on the event's originating line(s), then we need to */
+      /* forget it, because anything could've happend to it, and it'll be    */
+      /* re-rendered and re-recorded on demand anyway                        */
+      if(buf->b_etabs[i].line == line)
+         buf->b_etabs[i].line = 0; /* empty the slot for re-use */
+      else if(line_shift && buf->b_etabs[i].line == line - 1)
+         buf->b_etabs[i].line = 0;
+      /* if this etab is after the event's originating line, then we need to */
+      /* change it's known line according to the event's change of lines     */
+      else if(buf->b_etabs[i].line > line)
+         buf->b_etabs[i].line += line_shift;
+         /* if this etab is suddenly on the event's originating line, then  */
+         /* we need to forget it, otherwise cluster merges may go unnoticed */
+}
+
+void reset_etabs(buf_T* buf)
+{
+   if(!buf->b_etab_count)
+      return;
+   free(buf->b_etabs);
+   buf->b_etabs = 0;
+   buf->b_etab_count = 0;
+}
+
+#else
+
+int get_etab_char_print_width(buf_T* buf, linenr_T line, colnr_T col, int layer
+                              UNUSED, int level UNUSED, int* back_up UNUSED)
+{
+   return (buf->b_p_ts - (col % buf->b_p_ts));
+}
+
+#endif /* NO_ELASTIC_TABSTOPS */
+
+int get_etab_char_width(buf_T* buf, linenr_T line, colnr_T col)
+{
+   return get_etab_char_print_width(buf, line, col, 0, 0, 0);
+}
+
diff -r 974ed4c3395a src/edit.c
--- a/src/edit.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/edit.c	Sat Feb 18 12:18:00 2012 -0600
@@ -403,7 +403,7 @@
 	if (startln)
 	    Insstart.col = 0;
     }
-    Insstart_textlen = (colnr_T)linetabsize(ml_get_curline());
+    Insstart_textlen = (colnr_T)linetabsize(curwin->w_cursor.lnum, ml_get_curline());
     Insstart_blank_vcol = MAXCOL;
     if (!did_ai)
 	ai_col = 0;
@@ -1902,7 +1902,7 @@
 	    else
 #endif
 		++new_cursor_col;
-	    vcol += lbr_chartabsize(ptr + new_cursor_col, (colnr_T)vcol);
+	    vcol += lbr_chartabsize(ptr + new_cursor_col, curwin->w_cursor.lnum, (colnr_T)vcol);
 	}
 	vcol = last_vcol;
 
@@ -6617,7 +6617,7 @@
 	    ins_need_undo = FALSE;
 	}
 	Insstart = curwin->w_cursor;	/* new insertion starts here */
-	Insstart_textlen = (colnr_T)linetabsize(ml_get_curline());
+	Insstart_textlen = (colnr_T)linetabsize(curwin->w_cursor.lnum, ml_get_curline());
 	ai_col = 0;
 #ifdef FEAT_VREPLACE
 	if (State & VREPLACE_FLAG)
@@ -7487,7 +7487,7 @@
 	    /* Get the number of screen cells used by the character we are
 	     * going to delete. */
 	    getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL);
-	    orig_vcols = chartabsize(ml_get_cursor(), start_vcol);
+	    orig_vcols = chartabsize(ml_get_cursor(), curwin->w_cursor.lnum, start_vcol);
 	}
 #endif
 #ifdef FEAT_MBYTE
@@ -7520,7 +7520,7 @@
 	    vcol = start_vcol;
 	    for (i = 0; i < ins_len; ++i)
 	    {
-		vcol += chartabsize(p + i, vcol);
+		vcol += chartabsize(p + i, curwin->w_cursor.lnum, vcol);
 #ifdef FEAT_MBYTE
 		i += (*mb_ptr2len)(p) - 1;
 #endif
@@ -9618,7 +9618,7 @@
 	 * 'linebreak' adding extra virtual columns. */
 	while (vim_iswhite(*ptr))
 	{
-	    i = lbr_chartabsize((char_u *)"\t", vcol);
+	    i = lbr_chartabsize((char_u *)"\t", curwin->w_cursor.lnum, vcol);
 	    if (vcol + i > want_vcol)
 		break;
 	    if (*ptr != TAB)
@@ -9644,7 +9644,7 @@
 	    /* Skip over the spaces we need. */
 	    while (vcol < want_vcol && *ptr == ' ')
 	    {
-		vcol += lbr_chartabsize(ptr, vcol);
+		vcol += lbr_chartabsize(ptr, curwin->w_cursor.lnum, vcol);
 		++ptr;
 		++repl_off;
 	    }
@@ -9900,7 +9900,7 @@
     while ((colnr_T)temp < curwin->w_virtcol && *ptr != NUL)
     {
 	prev_ptr = ptr;
-	temp += lbr_chartabsize_adv(&ptr, (colnr_T)temp);
+	temp += lbr_chartabsize_adv(&ptr, lnum, (colnr_T)temp);
     }
     if ((colnr_T)temp > curwin->w_virtcol)
 	ptr = prev_ptr;
diff -r 974ed4c3395a src/eval.c
--- a/src/eval.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/eval.c	Sat Feb 18 12:18:00 2012 -0600
@@ -9321,7 +9321,7 @@
 		char_u	*p = ml_get_cursor();
 
 		if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p,
-				 curwin->w_virtcol - curwin->w_cursor.coladd))
+				 curwin->w_cursor.lnum, curwin->w_virtcol - curwin->w_cursor.coladd))
 		{
 # ifdef FEAT_MBYTE
 		    int		l;
@@ -17153,7 +17153,7 @@
     if (argvars[1].v_type != VAR_UNKNOWN)
 	col = get_tv_number(&argvars[1]);
 
-    rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col);
+    rettv->vval.v_number = (varnumber_T)(linetabsize_col(0x7FFFFFFF, col, s) - col);
 }
 
 /*
diff -r 974ed4c3395a src/ex_cmds.c
--- a/src/ex_cmds.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/ex_cmds.c	Sat Feb 18 12:18:00 2012 -0600
@@ -262,7 +262,7 @@
 	;
     save = *last;
     *last = NUL;
-    len = linetabsize(line);		/* get line length */
+    len = linetabsize(curwin->w_cursor.lnum, line);		/* get line length */
     if (has_tab != NULL)		/* check for embedded TAB */
 	*has_tab = (vim_strrchr(first, TAB) != NULL);
     *last = save;
@@ -689,7 +689,7 @@
 	    }
 	    if (ptr[col] == NUL)
 		break;
-	    vcol += chartabsize(ptr + col, (colnr_T)vcol);
+	    vcol += chartabsize(ptr + col, lnum, (colnr_T)vcol);
 #ifdef FEAT_MBYTE
 	    if (has_mbyte)
 		col += (*mb_ptr2len)(ptr + col);
diff -r 974ed4c3395a src/feature.h
--- a/src/feature.h	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/feature.h	Sat Feb 18 12:18:00 2012 -0600
@@ -1316,3 +1316,8 @@
 #ifdef FEAT_NORMAL
 # define FEAT_PERSISTENT_UNDO
 #endif
+
+/* Elastic Tabstops */
+/* Uncomment the below line to exclude the elastic tabstops feature */
+/* #define NO_ELASTIC_TABSTOPS */
+
diff -r 974ed4c3395a src/getchar.c
--- a/src/getchar.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/getchar.c	Sat Feb 18 12:18:00 2012 -0600
@@ -2619,7 +2619,7 @@
 				    if (!vim_iswhite(ptr[col]))
 					curwin->w_wcol = vcol;
 				    vcol += lbr_chartabsize(ptr + col,
-							       (colnr_T)vcol);
+							       curwin->w_cursor.lnum, (colnr_T)vcol);
 #ifdef FEAT_MBYTE
 				    if (has_mbyte)
 					col += (*mb_ptr2len)(ptr + col);
diff -r 974ed4c3395a src/gui.c
--- a/src/gui.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/gui.c	Sat Feb 18 12:18:00 2012 -0600
@@ -4432,7 +4432,7 @@
     if (*p != NUL)
 	for (;;)
 	{
-	    w = chartabsize(p, col);
+	    w = chartabsize(p, lnum, col);
 	    mb_ptr_adv(p);
 	    if (*p == NUL)		/* don't count the last character */
 		break;
diff -r 974ed4c3395a src/gui_beval.c
--- a/src/gui_beval.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/gui_beval.c	Sat Feb 18 12:18:00 2012 -0600
@@ -335,7 +335,7 @@
 	{
 	    /* Not past end of the file. */
 	    lbuf = ml_get_buf(wp->w_buffer, lnum, FALSE);
-	    if (col <= win_linetabsize(wp, lbuf, (colnr_T)MAXCOL))
+	    if (col <= win_linetabsize(wp, lbuf, (colnr_T)MAXCOL, lnum, 0))
 	    {
 		/* Not past end of line. */
 		if (getword)
diff -r 974ed4c3395a src/memline.c
--- a/src/memline.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/memline.c	Sat Feb 18 12:18:00 2012 -0600
@@ -261,6 +261,7 @@
 #ifdef FEAT_BYTEOFF
 static void ml_updatechunk __ARGS((buf_T *buf, long line, long len, int updtype));
 #endif
+void shift_etab_chars(buf_T*, linenr_T, int);
 
 /*
  * Open a new memline for "buf".
@@ -3042,6 +3043,11 @@
 							   (char_u *)"\n", 1);
     }
 #endif
+
+    #ifndef NO_ELASTIC_TABSTOPS
+    shift_etab_chars(buf, lnum, 1);
+    #endif
+
     return OK;
 }
 
@@ -3263,6 +3269,11 @@
 #ifdef FEAT_BYTEOFF
     ml_updatechunk(buf, lnum, line_size, ML_CHNK_DELLINE);
 #endif
+
+    #ifndef NO_ELASTIC_TABSTOPS
+    shift_etab_chars(buf, lnum, -1);
+    #endif
+
     return OK;
 }
 
@@ -3483,6 +3494,10 @@
 	vim_free(new_line);
 
 	entered = FALSE;
+
+        #ifndef NO_ELASTIC_TABSTOPS
+        shift_etab_chars(buf, lnum, 0);
+        #endif
     }
 
     buf->b_ml.ml_line_lnum = 0;
diff -r 974ed4c3395a src/misc1.c
--- a/src/misc1.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/misc1.c	Sat Feb 18 12:18:00 2012 -0600
@@ -1757,7 +1757,7 @@
     s = ml_get_buf(wp->w_buffer, lnum, FALSE);
     if (*s == NUL)		/* empty line */
 	return 1;
-    col = win_linetabsize(wp, s, (colnr_T)MAXCOL);
+    col = win_linetabsize(wp, s, (colnr_T)MAXCOL, lnum, 0);
 
     /*
      * If list mode is on, then the '$' at the end of the line may take up one
@@ -1813,7 +1813,7 @@
     col = 0;
     while (*s != NUL && --column >= 0)
     {
-	col += win_lbr_chartabsize(wp, s, (colnr_T)col, NULL);
+	col += win_lbr_chartabsize(wp, s, lnum, (colnr_T)col, NULL);
 	mb_ptr_adv(s);
     }
 
@@ -1825,7 +1825,7 @@
      * 'ts') -- webb.
      */
     if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1))
-	col += win_lbr_chartabsize(wp, s, (colnr_T)col, NULL) - 1;
+	col += win_lbr_chartabsize(wp, s, lnum, (colnr_T)col, NULL) - 1;
 
     /*
      * Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
@@ -2012,10 +2012,10 @@
 	    buf[0] = c;
 	    buf[1] = NUL;
 #endif
-	    new_vcol = vcol + chartabsize(buf, vcol);
+	    new_vcol = vcol + chartabsize(buf, lnum, vcol);
 	    while (oldp[col + oldlen] != NUL && vcol < new_vcol)
 	    {
-		vcol += chartabsize(oldp + col + oldlen, vcol);
+		vcol += chartabsize(oldp + col + oldlen, lnum, vcol);
 		/* Don't need to remove a TAB that takes us to the right
 		 * position. */
 		if (vcol > new_vcol && oldp[col + oldlen] == TAB)
@@ -8582,7 +8582,7 @@
 		amount = 0;
 		while (*that && col)
 		{
-		    amount += lbr_chartabsize_adv(&that, (colnr_T)amount);
+		    amount += lbr_chartabsize_adv(&that, pos->lnum, (colnr_T)amount);
 		    col--;
 		}
 
@@ -8605,7 +8605,7 @@
 
 		    while (vim_iswhite(*that))
 		    {
-			amount += lbr_chartabsize(that, (colnr_T)amount);
+			amount += lbr_chartabsize(that, pos->lnum, (colnr_T)amount);
 			++that;
 		    }
 
@@ -8644,14 +8644,14 @@
 				    --parencount;
 				if (*that == '\\' && *(that+1) != NUL)
 				    amount += lbr_chartabsize_adv(&that,
-							     (colnr_T)amount);
+							     pos->lnum, (colnr_T)amount);
 				amount += lbr_chartabsize_adv(&that,
-							     (colnr_T)amount);
+							     pos->lnum, (colnr_T)amount);
 			    }
 			}
 			while (vim_iswhite(*that))
 			{
-			    amount += lbr_chartabsize(that, (colnr_T)amount);
+			    amount += lbr_chartabsize(that, pos->lnum, (colnr_T)amount);
 			    that++;
 			}
 			if (!*that || *that == ';')
diff -r 974ed4c3395a src/misc2.c
--- a/src/misc2.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/misc2.c	Sat Feb 18 12:18:00 2012 -0600
@@ -166,7 +166,7 @@
 #ifdef FEAT_VIRTUALEDIT
 	    if ((addspaces || finetune) && !VIsual_active)
 	    {
-		curwin->w_curswant = linetabsize(line) + one_more;
+		curwin->w_curswant = linetabsize(pos->lnum, line) + one_more;
 		if (curwin->w_curswant > 0)
 		    --curwin->w_curswant;
 	    }
@@ -184,7 +184,7 @@
 # endif
 		&& wcol >= (colnr_T)width)
 	{
-	    csize = linetabsize(line);
+	    csize = linetabsize(pos->lnum, line);
 	    if (csize > 0)
 		csize--;
 
@@ -205,7 +205,7 @@
 	{
 	    /* Count a tab for what it's worth (if list mode not on) */
 #ifdef FEAT_LINEBREAK
-	    csize = win_lbr_chartabsize(curwin, ptr, col, &head);
+	    csize = win_lbr_chartabsize(curwin, ptr, pos->lnum, col, &head);
 	    mb_ptr_adv(ptr);
 #else
 	    csize = lbr_chartabsize_adv(&ptr, col);
diff -r 974ed4c3395a src/normal.c
--- a/src/normal.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/normal.c	Sat Feb 18 12:18:00 2012 -0600
@@ -4474,7 +4474,7 @@
     int		dir;
     long	dist;
 {
-    int		linelen = linetabsize(ml_get_curline());
+    int		linelen = linetabsize(curwin->w_cursor.lnum, ml_get_curline());
     int		retval = OK;
     int		atend = FALSE;
     int		n;
@@ -4547,7 +4547,7 @@
 		    (void)hasFolding(curwin->w_cursor.lnum,
 						&curwin->w_cursor.lnum, NULL);
 #endif
-		linelen = linetabsize(ml_get_curline());
+		linelen = linetabsize(curwin->w_cursor.lnum, ml_get_curline());
 		if (linelen > width1)
 		    curwin->w_curswant += (((linelen - width1 - 1) / width2)
 								+ 1) * width2;
@@ -4577,7 +4577,7 @@
 		}
 		curwin->w_cursor.lnum++;
 		curwin->w_curswant %= width2;
-		linelen = linetabsize(ml_get_curline());
+		linelen = linetabsize(curwin->w_cursor.lnum, ml_get_curline());
 	    }
 	}
       }
diff -r 974ed4c3395a src/ops.c
--- a/src/ops.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/ops.c	Sat Feb 18 12:18:00 2012 -0600
@@ -428,7 +428,7 @@
 	}
 	for ( ; vim_iswhite(*bd.textstart); )
 	{
-	    incr = lbr_chartabsize_adv(&bd.textstart, (colnr_T)(bd.start_vcol));
+	    incr = lbr_chartabsize_adv(&bd.textstart, 0x7FFFFFFF, (colnr_T)(bd.start_vcol));
 	    total += incr;
 	    bd.start_vcol += incr;
 	}
@@ -488,7 +488,7 @@
 
 	while (vim_iswhite(*non_white))
 	{
-	    incr = lbr_chartabsize_adv(&non_white, non_white_col);
+	    incr = lbr_chartabsize_adv(&non_white, 0x7FFFFFFF, non_white_col);
 	    non_white_col += incr;
 	}
 
@@ -513,7 +513,7 @@
 	    verbatim_copy_width -= bd.start_char_vcols;
 	while (verbatim_copy_width < destination_col)
 	{
-	    incr = lbr_chartabsize(verbatim_copy_end, verbatim_copy_width);
+	    incr = lbr_chartabsize(verbatim_copy_end, 0x7FFFFFFF, verbatim_copy_width);
 	    if (verbatim_copy_width + incr > destination_col)
 		break;
 	    verbatim_copy_width += incr;
@@ -3568,7 +3568,7 @@
 	    for (ptr = oldp; vcol < col && *ptr; )
 	    {
 		/* Count a tab for what it's worth (if list mode not on) */
-		incr = lbr_chartabsize_adv(&ptr, (colnr_T)vcol);
+		incr = lbr_chartabsize_adv(&ptr, curwin->w_cursor.lnum, (colnr_T)vcol);
 		vcol += incr;
 	    }
 	    bd.textcol = (colnr_T)(ptr - oldp);
@@ -3602,7 +3602,7 @@
 	    /* calculate number of spaces required to fill right side of block*/
 	    spaces = y_width + 1;
 	    for (j = 0; j < yanklen; j++)
-		spaces -= lbr_chartabsize(&y_array[i][j], 0);
+		spaces -= lbr_chartabsize(&y_array[i][j], 0x7FFFFFFF, 0);
 	    if (spaces < 0)
 		spaces = 0;
 
@@ -4964,7 +4964,7 @@
     while (bdp->start_vcol < oap->start_vcol && *pstart)
     {
 	/* Count a tab for what it's worth (if list mode not on) */
-	incr = lbr_chartabsize(pstart, (colnr_T)bdp->start_vcol);
+	incr = lbr_chartabsize(pstart, lnum, (colnr_T)bdp->start_vcol);
 	bdp->start_vcol += incr;
 #ifdef FEAT_VISUALEXTRA
 	if (vim_iswhite(*pstart))
@@ -5033,7 +5033,7 @@
 	    {
 		/* Count a tab for what it's worth (if list mode not on) */
 		prev_pend = pend;
-		incr = lbr_chartabsize_adv(&pend, (colnr_T)bdp->end_vcol);
+		incr = lbr_chartabsize_adv(&pend, lnum, (colnr_T)bdp->end_vcol);
 		bdp->end_vcol += incr;
 	    }
 	    if (bdp->end_vcol <= oap->end_vcol
@@ -6529,7 +6529,7 @@
 	    validate_virtcol();
 	    col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1,
 		    (int)curwin->w_virtcol + 1);
-	    col_print(buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(p));
+	    col_print(buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(curwin->w_cursor.lnum, p));
 
 	    if (char_count_cursor == byte_count_cursor
 		    && char_count == byte_count)
diff -r 974ed4c3395a src/option.c
--- a/src/option.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/option.c	Sat Feb 18 12:18:00 2012 -0600
@@ -178,6 +178,14 @@
 # define PV_UDF		OPT_BUF(BV_UDF)
 #endif
 #define PV_WM		OPT_BUF(BV_WM)
+#ifndef NO_ELASTIC_TABSTOPS
+# define PV_ELASTIC_TABSTOPS \
+                        OPT_BOTH(OPT_BUF(BV_ELASTIC_TABSTOPS))
+# define PV_ELASTIC_TABSTOPS_INDENTATION \
+                        OPT_BOTH(OPT_BUF(BV_ELASTIC_TABSTOPS_INDENTATION))
+# define PV_ELASTIC_TABSTOPS_SEPARATION \
+                        OPT_BOTH(OPT_BUF(BV_ELASTIC_TABSTOPS_SEPARATION))
+#endif
 
 /*
  * Definition of the PV_ values for window-local options.
@@ -370,6 +378,11 @@
 #ifdef FEAT_KEYMAP
 static char_u	*p_keymap;
 #endif
+#ifndef NO_ELASTIC_TABSTOPS
+static int	p_elastic_tabstops;
+static long	p_elastic_tabstops_indentation;
+static long	p_elastic_tabstops_separation;
+#endif
 
 /* Saved values for when 'bin' is set. */
 static int	p_et_nobin;
@@ -1026,6 +1039,23 @@
     {"edcompatible","ed",   P_BOOL|P_VI_DEF,
 			    (char_u *)&p_ed, PV_NONE,
 			    {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT},
+#ifndef NO_ELASTIC_TABSTOPS
+    {"elastictabstops", "el",
+                            P_BOOL | P_VIM | P_RBUF,
+			    (char_u *)&p_elastic_tabstops,
+                            PV_ELASTIC_TABSTOPS,
+			    {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT},
+    {"elastictabstopsindentation", "eli",
+                            P_NUM | P_VIM | P_RBUF,
+			    (char_u *)&p_elastic_tabstops_indentation,
+                            PV_ELASTIC_TABSTOPS_INDENTATION,
+			    {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT},
+    {"elastictabstopsseparation", "els",
+                            P_NUM | P_VIM | P_RBUF,
+			    (char_u *)&p_elastic_tabstops_separation,
+                            PV_ELASTIC_TABSTOPS_SEPARATION,
+			    {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT},
+#endif
     {"encoding",    "enc",  P_STRING|P_VI_DEF|P_RCLR|P_NO_ML,
 #ifdef FEAT_MBYTE
 			    (char_u *)&p_enc, PV_NONE,
@@ -9550,6 +9580,14 @@
 #if defined(FEAT_CRYPT)
 	    case PV_CM:	  return (char_u *)&(curbuf->b_p_cm);
 #endif
+#ifndef NO_ELASTIC_TABSTOPS
+	    case PV_ELASTIC_TABSTOPS:
+                 return (char_u *)&(curbuf->b_p_elastic_tabstops);
+	    case PV_ELASTIC_TABSTOPS_INDENTATION:
+                 return (char_u *)&(curbuf->b_p_elastic_tabstops_indentation);
+	    case PV_ELASTIC_TABSTOPS_SEPARATION:
+                 return (char_u *)&(curbuf->b_p_elastic_tabstops_separation);
+#endif
 #ifdef FEAT_STL_OPT
 	    case PV_STL:  return (char_u *)&(curwin->w_p_stl);
 #endif
@@ -9787,6 +9825,14 @@
 #ifdef FEAT_KEYMAP
 	case PV_KMAP:	return (char_u *)&(curbuf->b_p_keymap);
 #endif
+#ifndef NO_ELASTIC_TABSTOPS
+	case PV_ELASTIC_TABSTOPS:
+                  return (char_u *)&(curbuf->b_p_elastic_tabstops);
+	case PV_ELASTIC_TABSTOPS_INDENTATION:
+                  return (char_u *)&(curbuf->b_p_elastic_tabstops_indentation);
+	case PV_ELASTIC_TABSTOPS_SEPARATION:
+                  return (char_u *)&(curbuf->b_p_elastic_tabstops_separation);
+#endif
 	default:	EMSG(_("E356: get_varp ERROR"));
     }
     /* always return a valid pointer to avoid a crash! */
@@ -10176,6 +10222,11 @@
 #ifdef FEAT_PERSISTENT_UNDO
 	    buf->b_p_udf = p_udf;
 #endif
+        #ifndef NO_ELASTIC_TABSTOPS
+	buf->b_p_elastic_tabstops = p_elastic_tabstops;
+	buf->b_p_elastic_tabstops_indentation = p_elastic_tabstops_indentation;
+	buf->b_p_elastic_tabstops_separation = p_elastic_tabstops_separation;
+        #endif
 
 	    /*
 	     * Don't copy the options set by ex_help(), use the saved values,
diff -r 974ed4c3395a src/option.h
--- a/src/option.h	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/option.h	Sat Feb 18 12:18:00 2012 -0600
@@ -1024,6 +1024,11 @@
     , BV_TX
     , BV_UDF
     , BV_WM
+#ifndef NO_ELASTIC_TABSTOPS
+    , BV_ELASTIC_TABSTOPS
+    , BV_ELASTIC_TABSTOPS_INDENTATION
+    , BV_ELASTIC_TABSTOPS_SEPARATION
+#endif
     , BV_COUNT	    /* must be the last one */
 };
 
diff -r 974ed4c3395a src/proto/charset.pro
--- a/src/proto/charset.pro	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/proto/charset.pro	Sat Feb 18 12:18:00 2012 -0600
@@ -13,10 +13,10 @@
 int ptr2cells __ARGS((char_u *p));
 int vim_strsize __ARGS((char_u *s));
 int vim_strnsize __ARGS((char_u *s, int len));
-int chartabsize __ARGS((char_u *p, colnr_T col));
-int linetabsize __ARGS((char_u *s));
-int linetabsize_col __ARGS((int startcol, char_u *s));
-int win_linetabsize __ARGS((win_T *wp, char_u *p, colnr_T len));
+int chartabsize __ARGS((char_u *p, linenr_T line, colnr_T col));
+int linetabsize __ARGS((linenr_T line, char_u *s));
+int linetabsize_col __ARGS((linenr_T line, colnr_T startcol, char_u *s));
+int win_linetabsize __ARGS((win_T *wp, char_u *p, colnr_T len, linenr_T line, colnr_T startcol));
 int vim_isIDc __ARGS((int c));
 int vim_iswordc __ARGS((int c));
 int vim_iswordp __ARGS((char_u *p));
@@ -25,9 +25,9 @@
 int vim_isfilec_or_wc __ARGS((int c));
 int vim_isprintc __ARGS((int c));
 int vim_isprintc_strict __ARGS((int c));
-int lbr_chartabsize __ARGS((unsigned char *s, colnr_T col));
-int lbr_chartabsize_adv __ARGS((char_u **s, colnr_T col));
-int win_lbr_chartabsize __ARGS((win_T *wp, char_u *s, colnr_T col, int *headp));
+int lbr_chartabsize __ARGS((unsigned char *s, linenr_T line, colnr_T col));
+int lbr_chartabsize_adv __ARGS((char_u **s, linenr_T line, colnr_T col));
+int win_lbr_chartabsize __ARGS((win_T *wp, char_u *s, linenr_T line, colnr_T col, int *headp));
 int in_win_border __ARGS((win_T *wp, colnr_T vcol));
 void getvcol __ARGS((win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end));
 colnr_T getvcol_nolist __ARGS((pos_T *posp));
diff -r 974ed4c3395a src/regexp.c
--- a/src/regexp.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/regexp.c	Sat Feb 18 12:18:00 2012 -0600
@@ -4269,7 +4269,7 @@
 		    if (top.col == MAXCOL || bot.col == MAXCOL)
 			end = MAXCOL;
 		    cols = win_linetabsize(wp,
-				      regline, (colnr_T)(reginput - regline));
+				      regline, (colnr_T)(reginput - regline), lnum, 0);
 		    if (cols < start || cols > end - (*p_sel == 'e'))
 			status = RA_NOMATCH;
 		}
@@ -4293,7 +4293,7 @@
 	  case RE_VCOL:
 	    if (!re_num_cmp((long_u)win_linetabsize(
 			    reg_win == NULL ? curwin : reg_win,
-			    regline, (colnr_T)(reginput - regline)) + 1, scan))
+			    regline, (colnr_T)(reginput - regline), reglnum, 0) + 1, scan))
 		status = RA_NOMATCH;
 	    break;
 
diff -r 974ed4c3395a src/screen.c
--- a/src/screen.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/screen.c	Sat Feb 18 12:18:00 2012 -0600
@@ -37,7 +37,7 @@
  * ScreenLines[].
  *
  * update_screen() is the function that updates all windows and status lines.
- * It is called form the main loop when must_redraw is non-zero.  It may be
+ * It is called from the main loop when must_redraw is non-zero.  It may be
  * called from other places when an immediate screen update is needed.
  *
  * The part of the buffer that is displayed in a window is set with:
@@ -120,7 +120,7 @@
 static void fill_foldcolumn __ARGS((char_u *p, win_T *wp, int closed, linenr_T lnum));
 static void copy_text_attr __ARGS((int off, char_u *buf, int len, int attr));
 #endif
-static int win_line __ARGS((win_T *, linenr_T, int, int, int nochange));
+static int win_line __ARGS((win_T *, linenr_T, int, int, int nochange, int* back_up));
 static int char_needs_redraw __ARGS((int off_from, int off_to, int cols));
 #ifdef FEAT_RIGHTLEFT
 static void screen_line __ARGS((int row, int coloff, int endcol, int clear_width, int rlflag));
@@ -172,7 +172,8 @@
 #endif
 #ifdef FEAT_CMDL_INFO
 static void win_redr_ruler __ARGS((win_T *wp, int always));
-#endif
+#endif 
+int get_etab_char_print_width(buf_T*, linenr_T, colnr_T, int, int, int*);
 
 #if defined(FEAT_CLIPBOARD) || defined(FEAT_VERTSPLIT)
 /* Ugly global: overrule attribute used by screen_char() */
@@ -655,7 +656,7 @@
 		start_search_hl();
 		prepare_search_hl(wp, lnum);
 # endif
-		win_line(wp, lnum, row, row + wp->w_lines[j].wl_size, FALSE);
+		win_line(wp, lnum, row, row + wp->w_lines[j].wl_size, FALSE, NULL);
 # if defined(FEAT_SEARCH_EXTRA)
 		end_search_hl();
 # endif
@@ -915,6 +916,11 @@
 #if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA)
     int		save_got_int;
 #endif
+    int back_up;
+    #ifndef NO_ELASTIC_TABSTOPS
+    int bu_idx, bu_row;
+    linenr_T bu_lnum;
+    #endif
 
     type = wp->w_redr_type;
 
@@ -924,6 +930,10 @@
 	wp->w_redr_status = TRUE;
 #endif
 	wp->w_lines_valid = 0;
+
+#ifndef NO_ELASTIC_TABSTOPS
+        reset_etabs(wp->w_buffer);
+#endif
     }
 
     /* Window is zero-height: nothing to draw. */
@@ -1574,8 +1584,27 @@
     row = 0;
     srow = 0;
     lnum = wp->w_topline;	/* first line shown in window */
+    #ifndef NO_ELASTIC_TABSTOPS
+    back_up = bu_idx = bu_row = bu_lnum = 0;
+    #endif
     for (;;)
     {
+        #ifndef NO_ELASTIC_TABSTOPS
+        if(back_up == 0) /* no saved point yet */
+        {
+           bu_idx = idx;
+           bu_row = row;
+           bu_lnum = lnum;
+        }
+        else if(back_up == -1) /* rewind to the saved point */
+        {
+           idx = bu_idx;
+           row = bu_row;
+           lnum = bu_lnum;
+           back_up = 0;
+        }
+        #endif
+
 	/* stop updating when reached the end of the window (check for _past_
 	 * the end of the window is at the end of the loop) */
 	if (row == wp->w_height)
@@ -1851,7 +1880,7 @@
 		/*
 		 * Display one line.
 		 */
-		row = win_line(wp, lnum, srow, wp->w_height, mod_top == 0);
+		row = win_line(wp, lnum, srow, wp->w_height, mod_top == 0, &back_up);
 
 #ifdef FEAT_FOLDING
 		wp->w_lines[idx].wl_folded = FALSE;
@@ -2667,12 +2696,13 @@
  * Return the number of last row the line occupies.
  */
     static int
-win_line(wp, lnum, startrow, endrow, nochange)
+win_line(wp, lnum, startrow, endrow, nochange, back_up)
     win_T	*wp;
     linenr_T	lnum;
     int		startrow;
     int		endrow;
     int		nochange UNUSED;	/* not updating for changed text */
+    int*        back_up;
 {
     int		col;			/* visual column on screen */
     unsigned	off;			/* offset in ScreenLines/ScreenAttrs */
@@ -2834,6 +2864,10 @@
 #else
 # define VCOL_HLC (vcol)
 #endif
+#ifndef NO_ELASTIC_TABSTOPS
+    int etab_layer = 0;
+    int etab_level = 0;
+#endif
 
     if (startrow > endrow)		/* past the end already! */
 	return startrow;
@@ -3139,7 +3173,8 @@
 #endif
 	while (vcol < v && *ptr != NUL)
 	{
-	    c = win_lbr_chartabsize(wp, ptr, (colnr_T)vcol, NULL);
+//  printf(">>>> %d\n", lnum);
+	    c = win_lbr_chartabsize(wp, ptr, lnum, (colnr_T)vcol, NULL);
 	    vcol += c;
 #ifdef FEAT_MBYTE
 	    prev_ptr = ptr;
@@ -4210,11 +4245,12 @@
 		if (wp->w_p_lbr && vim_isbreak(c) && !vim_isbreak(*ptr)
 							     && !wp->w_p_list)
 		{
+//   printf(">>>> %d\n", lnum);
 		    n_extra = win_lbr_chartabsize(wp, ptr - (
 # ifdef FEAT_MBYTE
 				has_mbyte ? mb_l :
 # endif
-				1), (colnr_T)vcol, NULL) - 1;
+				1), lnum, (colnr_T)vcol, NULL) - 1;
 		    c_extra = ' ';
 		    if (vim_iswhite(c))
 			c = ' ';
@@ -4257,8 +4293,8 @@
 		if (c == TAB && (!wp->w_p_list || lcs_tab1))
 		{
 		    /* tab amount depends on current column */
-		    n_extra = (int)wp->w_buffer->b_p_ts
-				   - VCOL_HLC % (int)wp->w_buffer->b_p_ts - 1;
+		    n_extra = get_etab_char_print_width(wp->w_buffer, lnum,
+		         VCOL_HLC, ++etab_layer, etab_level--, back_up) - 1;
 #ifdef FEAT_MBYTE
 		    mb_utf8 = FALSE;	/* don't draw as UTF-8 */
 #endif
@@ -4500,6 +4536,10 @@
 		is_concealing = FALSE;
 	    }
 #endif /* FEAT_CONCEAL */
+
+#ifndef NO_ELASTIC_TABS
+            etab_level++;
+#endif
 	}
 
 #ifdef FEAT_CONCEAL
diff -r 974ed4c3395a src/structs.h
--- a/src/structs.h	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/structs.h	Sat Feb 18 12:18:00 2012 -0600
@@ -1279,6 +1279,11 @@
  * A buffer is new if the associated file has never been loaded yet.
  */
 
+#ifndef NO_ELASTIC_TABSTOPS
+typedef struct etab_S etab_T;
+struct etab_S { linenr_T line; colnr_T col; int width, layer, level; };
+#endif
+
 struct file_buffer
 {
     memline_T	b_ml;		/* associated memline (also contains line
@@ -1672,9 +1677,15 @@
     int		b_was_netbeans_file;/* TRUE if b_netbeans_file was once set */
 #endif
 
+    #ifndef NO_ELASTIC_TABSTOPS
+    int b_p_elastic_tabstops;
+    long b_p_elastic_tabstops_indentation;
+    long b_p_elastic_tabstops_separation;
+    int b_etab_count;
+    etab_T* b_etabs;
+    #endif
 };
 
-
 #ifdef FEAT_DIFF
 /*
  * Stuff for diff mode.
diff -r 974ed4c3395a src/ui.c
--- a/src/ui.c	Sat Feb 18 11:49:28 2012 -0600
+++ b/src/ui.c	Sat Feb 18 12:18:00 2012 -0600
@@ -3109,7 +3109,7 @@
     start = ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
     while (count <= vcol && *ptr != NUL)
     {
-	count += win_lbr_chartabsize(wp, ptr, count, NULL);
+	count += win_lbr_chartabsize(wp, ptr, lnum, count, NULL);
 	mb_ptr_adv(ptr);
     }
     return (int)(ptr - start);
