On 16-Mar-2014 James McCoy <[email protected]> wrote:
> On Sat, Mar 15, 2014 at 03:37:27PM +0100, Lech Lorens wrote:
> > I believe what you are trying to achieve is what I did in the patch 
> > I posted 4 years ago here:
> > https://groups.google.com/forum/#!msg/vim_dev/JDLJgDk-ZrQ/nROMNPdwfhwJ
> > 
> > I added an option called 'tabindent'.  The patch was ignored but I've 
> > been using it happily ever since.
> 
> I'm not sure how I missed that initially, but this patch is awesome!
> 
> Whenever someone brings up using tabs for indentation, my usual response
> is “If only there was an editor that properly handled tabs for
> indentation & spaces for alignment, tabs could be worth using.”  >From my
> initial testing, this patch does exactly that and would make Vim the one
> editor I know of that can handle this. :)

There are a few problems with this approach:
- if you use tabs and spaces to have consistent indentation you can 
  either force your collaborators to use Vim (can even Emacs handle tabs 
  and spaces properly?) or fix the indentation yourself.  I like neither 
  of these options,
- not everything can be indented properly.  E.g. what do you do with 
  lambda expressions in C++ or with anonymous classes in Java?

> > You might try applying the patches attached to the message I posted 
> > a link to.  If the patch doesn't apply cleanly (well, I know it doesn't) 
> > and fixing it is difficult, I can send you a version that applies to 
> > current Vim.  Unfortunately, I'm quite busy for the next 2 weeks so it 
> > will take a bit of time.
> 
> I couldn't wait until you got back, so here are updated patches.
> 
> Cheers,
> -- 
> James
> GPG Key: 4096R/331BA3DB 2011-12-05 James McCoy <[email protected]>

OK, so I managed to find some time and looked at what I have in my Vim 
sources.  It appears that since 4 years ago I have managed to find and 
fix a few problems with the patch (I can e.g. remember Vim hanging when 
indenting the first line of a file).  Here it goes again with the fixes.  
Thanks for your input!

Cheers,
Lech

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

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index 9b9445a..f57e22a 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -483,8 +483,8 @@ mode: hit "v", move to the end of the block, and type "gq".  See also |gq|.
 4. 'expandtab', 'smarttab' and 'softtabstop' options	*ins-expandtab*
 
 If the 'expandtab' option is on, spaces will be used to fill the amount of
-whitespace of the tab.  If you want to enter a real <Tab>, type CTRL-V first
-(use CTRL-Q when CTRL-V is mapped |i_CTRL-Q|).
+whitespace of the tab (also see 'tabindent').  If you want to enter a real
+<Tab>, type CTRL-V first (use CTRL-Q when CTRL-V is mapped |i_CTRL-Q|).
 The 'expandtab' option is off by default.  Note that in Replace mode, a single
 character is replaced with several spaces.  The result of this is that the
 number of characters in the line increases.  Backspacing will delete one
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 2b846b8..28de0b9 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1404,7 +1404,8 @@ A jump table for the options with a short description can be found at |Q_op|.
 			feature}
 	Enables automatic C program indenting.  See 'cinkeys' to set the keys
 	that trigger reindenting in insert mode and 'cinoptions' to set your
-	preferred indent style.
+	preferred indent style. Also, refer to 'tabindent' to see how its
+	value affects indenting.
 	If 'indentexpr' is not empty, it overrules 'cindent'.
 	If 'lisp' is not on and both 'indentexpr' and 'equalprg' are empty,
 	the "=" operator indents using this algorithm rather than calling an
@@ -2734,7 +2735,8 @@ A jump table for the options with a short description can be found at |Q_op|.
 	In Insert mode: Use the appropriate number of spaces to insert a
 	<Tab>.  Spaces are used in indents with the '>' and '<' commands and
 	when 'autoindent' is on.  To insert a real tab when 'expandtab' is
-	on, use CTRL-V<Tab>.  See also |:retab| and |ins-expandtab|.
+	on, use CTRL-V<Tab>.  See also |:retab|, |ins-expandtab| and
+	'tabindent'.
 	NOTE: This option is reset when 'compatible' is set.
 
 					*'exrc'* *'ex'* *'noexrc'* *'noex'*
@@ -4197,7 +4199,7 @@ A jump table for the options with a short description can be found at |Q_op|.
 	match, excluding the characters that were already typed.
 	NOTE: This option is reset when 'compatible' is set.
 
-						*'indentexpr'* *'inde'*
+						*'indentexpr'* *'inde'* *E856*
 'indentexpr' 'inde'	string	(default "")
 			local to buffer
 			{not in Vi}
@@ -4213,9 +4215,14 @@ A jump table for the options with a short description can be found at |Q_op|.
 	The expression is evaluated with |v:lnum| set to the line number for
 	which the indent is to be computed.  The cursor is also in this line
 	when the expression is evaluated (but it may be moved around).
-	The expression must return the number of spaces worth of indent.  It
-	can return "-1" to keep the current indent (this means 'autoindent' is
-	used for the indent).
+	The expression must either return:
+	- the number of spaces worth of indent. It can return "-1" to keep the
+	  current indent (this means 'autoindent' is used for the indent),
+	- or a list of two numbers. In this case the first number will be
+	  treated as indent level and the second one will be the indent
+	  amount. This will trigger indent behaviour described under
+	  'tabindent'. See $VIMRUNTIME/indent/vim.vim for an example of indent
+	  script returning a list of two values.
 	Functions useful for computing the indent are |indent()|, |cindent()|
 	and |lispindent()|.
 	The evaluation of the expression must not have side effects!  It must
@@ -6965,6 +6972,22 @@ A jump table for the options with a short description can be found at |Q_op|.
 	'S' flag in 'cpoptions'.
 	Only normal file name characters can be used, "/\*?[|<>" are illegal.
 
+						*'tabindent'* *'ti'*
+'tabindent' 'ti'	boolean	(default off)
+			local to buffer
+			{not in Vi}
+	When on, makes Vim use tabs for indenting starting lines and pad with
+	spaces continuation lines while 'cindent' is set. This allows for
+	changing the value of 'tabstop' while viewing code indented this way
+	without affecting alignment.
+	Note that when 'tabindent' is set, you will probably want to set
+	'tabstop' and 'shiftwidth' to the same value, as otherwise it might
+	happen that the number of tabs in indentation does not reflect the
+	indentation level.
+	The value of 'expandtab' is ignored during indenting when 'tabindent'
+	is on.
+	NOTE: This option is reset when 'compatible' is set.
+
 						*'tabline'* *'tal'*
 'tabline' 'tal'		string	(default empty)
 			global
@@ -7026,6 +7049,8 @@ A jump table for the options with a short description can be found at |Q_op|.
 	   though.  Otherwise aligned comments will be wrong when 'tabstop' is
 	   changed.
 
+	Also see 'tabindent'.
+
 			*'tagbsearch'* *'tbs'* *'notagbsearch'* *'notbs'*
 'tagbsearch' 'tbs'	boolean	(default on)
 			global
diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt
index 90d3597..5f42b7e 100644
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -889,6 +889,7 @@ Short explanation of each option:		*option-list*
 'switchbuf'	  'swb'     sets behavior when switching to another buffer
 'synmaxcol'	  'smc'     maximum column to find syntax items
 'syntax'	  'syn'     syntax to be loaded for current buffer
+'tabindent'	  'ti'	    indent C code so that changing 'ts' keeps alignment
 'tabstop'	  'ts'	    number of spaces that <Tab> in file uses
 'tabline'	  'tal'     custom format for the console tab pages line
 'tabpagemax'	  'tpm'     maximum number of tab pages for |-p| and "tab all"
diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim
index 8c21573..cdd0b65 100644
--- a/runtime/indent/vim.vim
+++ b/runtime/indent/vim.vim
@@ -21,6 +21,20 @@ endif
 let s:keepcpo= &cpo
 set cpo&vim
 
+function s:ReturnValue(level, amount)
+  if &tabindent
+    return [a:level, a:amount]
+  endif
+  return a:amount
+endfunction
+
+function s:GetLevel(lnum)
+  let line = getline(a:lnum)
+  let line = substitute(line, '^\(\t*\).*$', '\1', '')
+  let line = substitute(line, '\t', 'x', 'g')
+  return strlen(line)
+endfunction
+
 function GetVimIndent()
   let ignorecase_save = &ignorecase
   try
@@ -32,6 +46,7 @@ function GetVimIndent()
 endfunc
 
 function GetVimIndentIntern()
+  let level = 0
   " Find a non-blank line above the current line.
   let lnum = prevnonblank(v:lnum - 1)
 
@@ -45,29 +60,33 @@ function GetVimIndentIntern()
 
   " At the start of the file use zero indent.
   if lnum == 0
-    return 0
+    return s:ReturnValue(level, 0)
   endif
 
   " Add a 'shiftwidth' after :if, :while, :try, :catch, :finally, :function
   " and :else.  Add it three times for a line that starts with '\' after
   " a line that doesn't (or g:vim_indent_cont if it exists).
   let ind = indent(lnum)
+  let level = s:GetLevel(lnum)
   if getline(v:lnum) =~ '^\s*\\' && v:lnum > 1 && getline(lnum) !~ '^\s*\\'
     if exists("g:vim_indent_cont")
       let ind = ind + g:vim_indent_cont
     else
       let ind = ind + &sw * 3
     endif
-  elseif getline(lnum) =~ '^\s*aug\%[roup]' && getline(lnum) !~ '^\s*aug\%[roup]\s*!\=\s\+END'
+  elseif getline(lnum) =~ '^\s*aug\%[roup]' && getline(lnum) !~ '^\s*aug\%[roup]\s*!\=\s\+\%(END\|end\)'
     let ind = ind + &sw
+    let level = level + 1
   else
     let line = getline(lnum)
     let i = match(line, '\(^\||\)\s*\(if\|wh\%[ile]\|for\|try\|cat\%[ch]\|fina\%[lly]\|fu\%[nction]\|el\%[seif]\)\>')
     if i >= 0
       let ind += &sw
+      let level += 1
       if strpart(line, i, 1) == '|' && has('syntax_items')
             \ && synIDattr(synID(lnum, i, 1), "name") =~ '\(Comment\|String\)$'
         let ind -= &sw
+	let level -= 1
       endif
     endif
   endif
@@ -80,17 +99,19 @@ function GetVimIndentIntern()
   if i > 0 && line !~ '^\s*au\%[tocmd]'
     if !has('syntax_items') || synIDattr(synID(lnum, i + 2, 1), "name") !~ '\(Comment\|String\)$'
       let ind = ind - &sw
+      let level = level - 1
     endif
   endif
 
 
   " Subtract a 'shiftwidth' on a :endif, :endwhile, :catch, :finally, :endtry,
   " :endfun, :else and :augroup END.
-  if getline(v:lnum) =~ '^\s*\(ene\@!\|cat\|fina\|el\|aug\%[roup]\s*!\=\s\+END\)'
+  if getline(v:lnum) =~ '^\s*\(ene\@!\|cat\|fina\|el\|aug\%[roup]\s*!\=\s\+\%(END\|end\)\)'
     let ind = ind - &sw
+    let level = level - 1
   endif
 
-  return ind
+  return s:ReturnValue(level, ind)
 endfunction
 
 let &cpo = s:keepcpo
diff --git a/src/edit.c b/src/edit.c
index 57bd776..308e675 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -1829,6 +1829,234 @@ undisplay_dollar()
 }
 
 /*
+ * A slightly simplified implementation of change_indent() for the case when
+ * 'tabindent' is set. Will try to set the indentation of a line to
+ * "level" tabs + ("amount" - "level" * 'ts') spaces.
+ */
+    static void
+change_indent_with_level(level, amount)
+    int		level;
+    int		amount;
+{
+    int		vcol;
+    int		last_vcol;
+    int		insstart_less;		/* reduction for Insstart.col */
+    int		new_cursor_col;
+    int		i;
+    char_u	*ptr;
+    int		save_p_list;
+    int		start_col;
+    colnr_T	vc;
+#ifdef FEAT_VREPLACE
+    colnr_T	orig_col = 0;		/* init for GCC */
+    char_u	*new_line, *orig_line = NULL;	/* init for GCC */
+
+    /* VREPLACE mode needs to know what the line was like before changing */
+    if (State & VREPLACE_FLAG)
+    {
+	orig_line = vim_strsave(ml_get_curline());  /* Deal with NULL below */
+	orig_col = curwin->w_cursor.col;
+    }
+#endif
+
+    /* for the following tricks we don't want list mode */
+    save_p_list = curwin->w_p_list;
+    curwin->w_p_list = FALSE;
+    vc = getvcol_nolist(&curwin->w_cursor);
+    vcol = vc;
+
+    /*
+     * For Replace mode we need to fix the replace stack later, which is only
+     * possible when the cursor is in the indent.  Remember the number of
+     * characters before the cursor if it's possible.
+     */
+    start_col = curwin->w_cursor.col;
+
+    /* determine offset from first non-blank */
+    new_cursor_col = curwin->w_cursor.col;
+    beginline(BL_WHITE);
+    new_cursor_col -= curwin->w_cursor.col;
+
+    insstart_less = curwin->w_cursor.col; /* first non-white character in line */
+
+    /*
+     * If the cursor is in the indent, compute how many screen columns the
+     * cursor is to the left of the first non-blank.
+     */
+    if (new_cursor_col < 0)
+	vcol = get_indent() - vcol; /* distance from the original cursor position
+				     * to the first non-blank character in line */
+
+    if (new_cursor_col > 0)	    /* can't fix replace stack */
+	start_col = -1;
+
+    /*
+     * Set the new indent.  The cursor will be put on the first non-blank.
+     * NOTE: it seems that it will be on the last blank in the indent.
+     */
+    (void)set_indent_with_level(level, amount, 0);
+    insstart_less -= curwin->w_cursor.col;
+
+    /*
+     * Try to put cursor on same character.
+     * If the cursor is at or after the first non-blank in the line,
+     * compute the cursor column relative to the column of the first
+     * non-blank character.
+     * If we are not in insert mode, leave the cursor on the first non-blank.
+     * If the cursor is before the first non-blank, position it relative
+     * to the first non-blank, counted in screen columns.
+     */
+    if (new_cursor_col >= 0)
+    {
+	/*
+	 * When changing the indent while the cursor is touching it, reset
+	 * Insstart_col to 0.
+	 */
+	if (new_cursor_col == 0)
+	    insstart_less = MAXCOL;
+	new_cursor_col += curwin->w_cursor.col;
+    }
+    else if (!(State & INSERT))
+	new_cursor_col = curwin->w_cursor.col;
+    else
+    {
+	/*
+	 * Compute the screen column where the cursor should be.
+	 */
+	vcol = get_indent() - vcol;
+	curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol);
+
+	/*
+	 * Advance the cursor until we reach the right screen column.
+	 */
+	vcol = last_vcol = 0;
+	new_cursor_col = -1;
+	ptr = ml_get_curline();
+	while (vcol <= (int)curwin->w_virtcol)
+	{
+	    last_vcol = vcol;
+#ifdef FEAT_MBYTE
+	    if (has_mbyte && new_cursor_col >= 0)
+		new_cursor_col += (*mb_ptr2len)(ptr + new_cursor_col);
+	    else
+#endif
+		++new_cursor_col;
+	    vcol += lbr_chartabsize(ptr + new_cursor_col, (colnr_T)vcol);
+	}
+	vcol = last_vcol;
+
+	/*
+	 * May need to insert spaces to be able to position the cursor on
+	 * the right screen column.
+	 */
+	if (vcol != (int)curwin->w_virtcol)
+	{
+	    curwin->w_cursor.col = (colnr_T)new_cursor_col;
+	    i = (int)curwin->w_virtcol - vcol;
+	    ptr = alloc((unsigned)(i + 1));
+	    if (ptr != NULL)
+	    {
+		new_cursor_col += i;
+		ptr[i] = NUL;
+		while (--i >= 0)
+		    ptr[i] = ' ';
+		ins_str(ptr);
+		vim_free(ptr);
+	    }
+	}
+
+	/*
+	 * When changing the indent while the cursor is in it, reset
+	 * Insstart_col to 0.
+	 */
+	insstart_less = MAXCOL;
+    }
+
+    curwin->w_p_list = save_p_list;
+
+    if (new_cursor_col <= 0)
+	curwin->w_cursor.col = 0;
+    else
+	curwin->w_cursor.col = (colnr_T)new_cursor_col;
+    curwin->w_set_curswant = TRUE;
+    changed_cline_bef_curs();
+
+    /*
+     * May have to adjust the start of the insert.
+     */
+    if (State & INSERT)
+    {
+	if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0)
+	{
+	    if ((int)Insstart.col <= insstart_less)
+		Insstart.col = 0;
+	    else
+		Insstart.col -= insstart_less;
+	}
+	if ((int)ai_col <= insstart_less)
+	    ai_col = 0;
+	else
+	    ai_col -= insstart_less;
+    }
+
+    /*
+     * For REPLACE mode, may have to fix the replace stack, if it's possible.
+     * If the number of characters before the cursor decreased, need to pop a
+     * few characters from the replace stack.
+     * If the number of characters before the cursor increased, need to push a
+     * few NULs onto the replace stack.
+     */
+    if (REPLACE_NORMAL(State) && start_col >= 0)
+    {
+	while (start_col > (int)curwin->w_cursor.col)
+	{
+	    replace_join(0);	    /* remove a NUL from the replace stack */
+	    --start_col;
+	}
+	while (start_col < (int)curwin->w_cursor.col)
+	{
+	    replace_push(NUL);
+	    ++start_col;
+	}
+    }
+
+#ifdef FEAT_VREPLACE
+    /*
+     * For VREPLACE mode, we also have to fix the replace stack.  In this case
+     * it is always possible because we backspace over the whole line and then
+     * put it back again the way we wanted it.
+     */
+    if (State & VREPLACE_FLAG)
+    {
+	/* If orig_line didn't allocate, just return.  At least we did the job,
+	 * even if you can't backspace. */
+	if (orig_line == NULL)
+	    return;
+
+	/* Save new line */
+	new_line = vim_strsave(ml_get_curline());
+	if (new_line == NULL)
+	    return;
+
+	/* We only put back the new line up to the cursor */
+	new_line[curwin->w_cursor.col] = NUL;
+
+	/* Put back original line */
+	ml_replace(curwin->w_cursor.lnum, orig_line, FALSE);
+	curwin->w_cursor.col = orig_col;
+
+	/* Backspace from cursor to start of line */
+	backspace_until_column(0);
+
+	/* Insert new stuff into line again */
+	ins_bytes(new_line);
+
+	vim_free(new_line);
+    }
+#endif
+}
+
+/*
  * Insert an indent (for <Tab> or CTRL-T) or delete an indent (for CTRL-D).
  * Keep the cursor on the same character.
  * type == INDENT_INC	increase indent (for CTRL-T or <Tab>)
@@ -7722,9 +7950,14 @@ cindent_on()
 
     void
 fixthisline(get_the_indent)
-    int (*get_the_indent) __ARGS((void));
+    int (*get_the_indent) __ARGS((int *plevel, int *pamount));
 {
-    change_indent(INDENT_SET, get_the_indent(), FALSE, 0, TRUE);
+    int level, amount;
+    if (INDENT_LVL_AMOUNT != get_the_indent(&level, &amount))
+	change_indent(INDENT_SET, amount, FALSE, 0, TRUE);
+    else
+	change_indent_with_level(level, amount);
+
     if (linewhite(curwin->w_cursor.lnum))
 	did_ai = TRUE;	    /* delete the indent if the line stays empty */
 }
diff --git a/src/eval.c b/src/eval.c
index 59cfd12..a55e9da 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -9407,8 +9407,32 @@ f_cindent(argvars, rettv)
     lnum = get_tv_lnum(argvars);
     if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
     {
+	int level, amount;
 	curwin->w_cursor.lnum = lnum;
-	rettv->vval.v_number = get_c_indent();
+	get_c_indent(&level, &amount);
+	if (curbuf->b_p_ti)
+	{
+	    list_T   *l;
+	    typval_T li_tv;
+
+	    l = list_alloc();
+
+	    li_tv.v_type = VAR_NUMBER;
+	    li_tv.vval.v_number = level;
+	    list_append_tv(l, &li_tv);
+
+	    li_tv.vval.v_number = amount;
+	    list_append_tv(l, &li_tv);
+
+	    rettv->v_type = VAR_LIST;
+	    rettv->v_lock = 0;
+	    rettv->vval.v_list = l;
+
+	    l->lv_refcount = 1;
+	}
+	else
+	    rettv->vval.v_number = amount;
+
 	curwin->w_cursor = pos;
     }
     else
@@ -13713,8 +13737,13 @@ f_lispindent(argvars, rettv)
     lnum = get_tv_lnum(argvars);
     if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
     {
+	int level, amount;
 	curwin->w_cursor.lnum = lnum;
-	rettv->vval.v_number = get_lisp_indent();
+	get_lisp_indent(&level, &amount);
+	/* FIXME:2010-03-21:llorens: depending on the value returned by
+	 * get_lisp_indent() rettv->v_type should either be VAR_NUMBER or
+	 * VAR_LIST. */
+	rettv->vval.v_number = amount;
 	curwin->w_cursor = pos;
     }
     else
diff --git a/src/misc1.c b/src/misc1.c
index 9434961..d3bd899 100644
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -83,6 +83,203 @@ get_indent_str(ptr, ts)
 }
 
 /*
+ * Set the indent of the current line to to "level" tabs + ("size" - "level" *
+ * 'ts') spaces unless 'preserveindent' indicates that some of the current
+ * indenting of the line should be retained.
+ * Leaves the cursor on the first non-blank in the line.
+ * Caller must take care of undo.
+ * "flags":
+ *	SIN_UNDO:	save line for undo before changing it.
+ * Returns TRUE if the line was changed.
+ */
+    int
+set_indent_with_level(level, size, flags)
+    int		level;
+    int		size;
+    int		flags;
+{
+    char_u	*p;
+    char_u	*newline;
+    char_u	*oldline;
+    char_u	*s;
+    int		todo;
+    int		ind_len;	    /* measured in characters */
+    int		line_len;
+    int		doit = FALSE;
+    int		ind_done = 0;	    /* measured in spaces */
+    int		tab_pad;
+    int		retval = FALSE;
+    int		orig_char_len = -1; /* number of initial whitespace chars when
+				       'et' and 'pi' are both set */
+    int		tabs_left = level;
+
+    /* NOTE that
+     *     (level * 'ts') > size
+     * is possible if the middle part of a 3-part comment has a negative offset
+     * relative to the opening part.
+     */
+
+    /*
+     * First check if there is anything to do and compute the number of
+     * characters needed for the indent.
+     */
+    todo = size;
+    ind_len = 0;
+    p = oldline = ml_get_curline();
+
+    /* Calculate the buffer size for the new indent, and check to see if it
+     * isn't already set */
+
+    /* If 'preserveindent' is set count the number of characters at the
+     * beginning of the line to be copied */
+    /* If 'preserveindent' is set then reuse as much as possible of
+     * the existing indent structure for the new indent */
+    if (curbuf->b_p_pi)
+    {
+	ind_done = 0;
+
+	/* count as many characters as we can use */
+	while (todo > 0 && vim_iswhite(*p))
+	{
+	    if (*p == TAB)
+	    {
+		tab_pad = (int)curbuf->b_p_ts
+		    - (ind_done % (int)curbuf->b_p_ts);
+		/* stop if this tab will overshoot the target */
+		if (todo < tab_pad)
+		    break;
+		todo -= tab_pad;
+		++ind_len;
+		ind_done += tab_pad;
+	    }
+	    else
+	    {
+		--todo;
+		++ind_len;
+		++ind_done;
+	    }
+	    ++p;
+	}
+
+	tabs_left -= ind_done / (int)curbuf->b_p_ts;
+	if (tabs_left < 0)
+	    tabs_left = 0;
+
+	orig_char_len = ind_len;
+
+	/* Fill to next tabstop with a tab, if possible. */
+	if (tabs_left)
+	{
+	    tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
+	    if (todo >= tab_pad)
+	    {
+		doit = TRUE;
+		todo -= tab_pad;
+		++ind_len;
+		--tabs_left;
+	    }
+	}
+    }
+
+    /* count tabs required for indent */
+    while (tabs_left && todo >= (int)curbuf->b_p_ts)
+    {
+	if (*p != TAB)
+	    doit = TRUE;
+	else
+	    ++p;
+	todo -= (int)curbuf->b_p_ts;
+	++ind_len;
+	--tabs_left;
+    }
+
+    /* count spaces required for indent */
+    while (todo > 0)
+    {
+	if (*p != ' ')
+	    doit = TRUE;
+	else
+	    ++p;
+	--todo;
+	++ind_len;
+    }
+
+    /* Return if the indent is OK already. */
+    if (!doit && !vim_iswhite(*p))
+	return FALSE;
+
+    /* Allocate memory for the new line. */
+    p = skipwhite(p);
+    line_len = (int)STRLEN(p) + 1;
+
+    newline = alloc(ind_len + line_len);
+    if (newline == NULL)
+	return FALSE;
+
+    /* If 'preserveindent' is set, keep the original characters. */
+    if (orig_char_len > 0)
+    {
+	todo = size - ind_done;
+	ind_len = orig_char_len + todo;    /* Set total length of indent in
+					    * characters, which may have been
+					    * undercounted until now  */
+	p = oldline;
+	s = newline;
+	while (orig_char_len > 0)
+	{
+	    *s++ = *p++;
+	    orig_char_len--;
+	}
+
+	/* Skip over any additional white space (useful when newindent is less
+	 * than old) */
+	while (vim_iswhite(*p))
+	    ++p;
+	tabs_left = level - ind_done / (int)curbuf->b_p_ts;
+	if (tabs_left < 0)
+	    tabs_left = 0;
+    }
+    else
+    {
+	todo = size;
+	s = newline;
+	tabs_left = level;
+	if (tabs_left > size / (int)curbuf->b_p_ts)
+	    tabs_left = size / (int)curbuf->b_p_ts;
+    }
+
+    copy_chars(s, tabs_left, TAB);
+    s += tabs_left;
+    todo = size - tabs_left * (int)curbuf->b_p_ts;
+    if (todo > 0)
+    {
+	copy_spaces(s, todo);
+	s += todo;
+    }
+
+    mch_memmove(s, p, (size_t)line_len);
+
+    /* Replace the line (unless undo fails). */
+    if (!(flags & SIN_UNDO) || u_savesub(curwin->w_cursor.lnum) == OK)
+    {
+	ml_replace(curwin->w_cursor.lnum, newline, FALSE);
+	changed_bytes(curwin->w_cursor.lnum, 0);
+	/* Correct saved cursor position if it's after the indent. */
+	if (saved_cursor.lnum == curwin->w_cursor.lnum
+	    && saved_cursor.col >= (colnr_T)(p - oldline))
+	{
+	    saved_cursor.col += ind_len - (colnr_T)(p - oldline);
+	}
+	retval = TRUE;
+    }
+    else
+	vim_free(newline);
+
+    curwin->w_cursor.col = ind_len;
+    return retval;
+}
+
+/*
  * Set the indent of the current line.
  * Leaves the cursor on the first non-blank in the line.
  * Caller must take care of undo.
@@ -125,11 +322,12 @@ set_indent(size, flags)
     /* if 'expandtab' isn't set: use TABs; if both 'expandtab' and
      * 'preserveindent' are set count the number of characters at the
      * beginning of the line to be copied */
-    if (!curbuf->b_p_et || (!(flags & SIN_INSERT) && curbuf->b_p_pi))
+    if ((!curbuf->b_p_et && !curbuf->b_p_ti)
+	|| (!(flags & SIN_INSERT) && (curbuf->b_p_pi || curbuf->b_p_ti)))
     {
 	/* If 'preserveindent' is set then reuse as much as possible of
 	 * the existing indent structure for the new indent */
-	if (!(flags & SIN_INSERT) && curbuf->b_p_pi)
+	if (!(flags & SIN_INSERT) && (curbuf->b_p_pi || curbuf->b_p_ti))
 	{
 	    ind_done = 0;
 
@@ -158,7 +356,7 @@ set_indent(size, flags)
 
 	    /* Set initial number of whitespace chars to copy if we are
 	     * preserving indent but expandtab is set */
-	    if (curbuf->b_p_et)
+	    if (curbuf->b_p_et || curbuf->b_p_ti)
 		orig_char_len = ind_len;
 
 	    /* Fill to next tabstop with a tab, if possible */
@@ -244,7 +442,7 @@ set_indent(size, flags)
 
     /* Put the characters in the new line. */
     /* if 'expandtab' isn't set: use TABs */
-    if (!curbuf->b_p_et)
+    if (!curbuf->b_p_et && !curbuf->b_p_ti)
     {
 	/* If 'preserveindent' is set then reuse as much as possible of
 	 * the existing indent structure for the new indent */
@@ -6630,6 +6828,19 @@ find_last_paren(l, start, end)
     return retval;
 }
 
+    static int
+get_initial_tabs_count(lnum)
+    linenr_T	lnum;	/* line number */
+{
+    char_u	*ptr = ml_get_buf(curbuf, lnum, FALSE);
+    int		result = 0;
+
+    while (*ptr++ == TAB)
+	++result;
+
+    return result;
+}
+
 /*
  * Parse 'cinoptions' and set the values in "curbuf".
  * Must be called when 'cinoptions', 'shiftwidth' and/or 'tabstop' changes.
@@ -6851,12 +7062,16 @@ parse_cino(buf)
 }
 
     int
-get_c_indent()
+get_c_indent(plevel, pamount)
+    int		*plevel;
+    int		*pamount;
 {
     pos_T	cur_curpos;
     int		amount;
+    int		level = 0;
     int		scope_amount;
     int		cur_amount = MAXCOL;
+    int		cur_level = 0;
     colnr_T	col;
     char_u	*theline;
     char_u	*linecopy;
@@ -6903,14 +7118,20 @@ get_c_indent()
 
     /* if we are at line 1 0 is fine, right? */
     if (cur_curpos.lnum == 1)
-	return 0;
+    {
+	*pamount = 0;
+	return INDENT_AMOUNT;
+    }
 
     /* Get a copy of the current contents of the line.
      * This is required, because only the most recent line obtained with
      * ml_get is valid! */
     linecopy = vim_strsave(ml_get(cur_curpos.lnum));
     if (linecopy == NULL)
-	return 0;
+    {
+	*pamount = 0;
+	return INDENT_ERROR;
+    }
 
     /*
      * In insert mode and the cursor is on a ')' truncate the line at the
@@ -6959,6 +7180,7 @@ get_c_indent()
 	/* find how indented the line beginning the comment is */
 	getvcol(curwin, trypos, &col, NULL, NULL);
 	amount = col;
+	level = get_initial_tabs_count(trypos->lnum);
     }
 
     /*
@@ -6984,6 +7206,7 @@ get_c_indent()
 	amount = col;
 	*lead_start = NUL;
 	*lead_middle = NUL;
+	level = get_initial_tabs_count(trypos->lnum);
 
 	p = curbuf->b_p_com;
 	while (*p != NUL)
@@ -7035,11 +7258,15 @@ get_c_indent()
 			 * line, use the indent of that line.  XXX */
 			look = skipwhite(ml_get(curwin->w_cursor.lnum - 1));
 			if (STRNCMP(look, lead_start, lead_start_len) == 0)
+			{
 			    amount = get_indent_lnum(curwin->w_cursor.lnum - 1);
+			    level = get_initial_tabs_count(curwin->w_cursor.lnum - 1);
+			}
 			else if (STRNCMP(look, lead_middle,
 							lead_middle_len) == 0)
 			{
 			    amount = get_indent_lnum(curwin->w_cursor.lnum - 1);
+			    level = get_initial_tabs_count(curwin->w_cursor.lnum - 1);
 			    break;
 			}
 			/* If the start comment string doesn't match with the
@@ -7063,6 +7290,7 @@ get_c_indent()
 		{
 		    amount = get_indent_lnum(curwin->w_cursor.lnum - 1);
 								     /* XXX */
+		    level = get_initial_tabs_count(curwin->w_cursor.lnum - 1);
 		    if (off != 0)
 			amount += off;
 		    else if (align == COM_RIGHT)
@@ -7138,6 +7366,7 @@ get_c_indent()
 
       if (trypos != NULL)
       {
+	level = get_initial_tabs_count(trypos->lnum);
 	/*
 	 * If the matching paren is more than one line away, use the indent of
 	 * a previous non-empty line that matches the same paren.
@@ -7174,6 +7403,7 @@ get_c_indent()
 			&& trypos->col == our_paren_pos.col)
 		{
 			amount = get_indent_lnum(lnum);	/* XXX */
+			level = get_initial_tabs_count(lnum);
 
 			if (theline[0] == ')')
 			{
@@ -7357,10 +7587,10 @@ get_c_indent()
 			amount += curbuf->b_ind_unclosed2;
 		    else
 		    {
-			if (is_if_for_while)
-			    amount += curbuf->b_ind_if_for_while;
-			else
-			    amount += curbuf->b_ind_unclosed;
+			int increment = is_if_for_while ? curbuf->b_ind_if_for_while
+			                                : curbuf->b_ind_unclosed;
+			level += increment / curbuf->b_p_sw;
+			amount += increment;
 		    }
 		}
 		/*
@@ -7386,11 +7616,15 @@ get_c_indent()
        */
       else
       {
+	int scope_level = 0;
 	trypos = tryposBrace;
 
 	ourscope = trypos->lnum;
 	start = ml_get(ourscope);
 
+	level = get_initial_tabs_count(trypos->lnum) + 1;
+	scope_level = level;
+
 	/*
 	 * Now figure out how indented the line is in general.
 	 * If the brace was at the start of the line, we use that;
@@ -7433,9 +7667,15 @@ get_c_indent()
 	     */
 	    if (curbuf->b_ind_js || (curbuf->b_ind_keep_case_label
 			   && cin_iscase(skipwhite(ml_get_curline()), FALSE)))
+	    {
 		amount = get_indent();
+		level = get_initial_tabs_count(curwin->w_cursor.lnum) + 1;
+	    }
 	    else
+	    {
 		amount = skip_label(lnum, &l);
+		level = get_initial_tabs_count(curwin->w_cursor.lnum) + 1;
+	    }
 
 	    start_brace = BRACE_AT_END;
 	}
@@ -7452,6 +7692,7 @@ get_c_indent()
 	     * other than the open brace.  indulge them, if so.
 	     */
 	    amount += curbuf->b_ind_close_extra;
+	    level += (curbuf->b_ind_close_extra / curbuf->b_p_sw) - 1;
 	}
 	else
 	{
@@ -7472,6 +7713,7 @@ get_c_indent()
 		if (find_match(lookfor, ourscope) == OK)
 		{
 		    amount = get_indent();	/* XXX */
+		    level = get_initial_tabs_count(curwin->w_cursor.lnum);
 		    goto theend;
 		}
 	    }
@@ -7525,11 +7767,15 @@ get_c_indent()
 	    {
 		lookfor = LOOKFOR_CASE;	/* find a previous switch() label */
 		amount += curbuf->b_ind_case;
+		if (curbuf->b_ind_case >= curbuf->b_p_sw)
+		    level += 1;
 	    }
 	    else if (cin_isscopedecl(theline))	/* private:, ... */
 	    {
 		lookfor = LOOKFOR_SCOPEDECL;	/* class decl is this block */
 		amount += curbuf->b_ind_scopedecl;
+		if (curbuf->b_ind_scopedecl >= curbuf->b_p_sw)
+		    level += 1;
 	    }
 	    else
 	    {
@@ -7542,6 +7788,7 @@ get_c_indent()
 		amount += curbuf->b_ind_level;
 	    }
 	    scope_amount = amount;
+	    scope_level = level;
 	    whilelevel = 0;
 
 	    /*
@@ -7688,10 +7935,12 @@ get_c_indent()
 					  && lookfor != LOOKFOR_CPP_BASECLASS)
 			{
 			    amount = scope_amount;
+			    level = scope_level;
 			    if (theline[0] == '{')
 			    {
 				amount += curbuf->b_ind_open_extra;
 				added_to_amount = curbuf->b_ind_open_extra;
+				level += 1;
 			    }
 			}
 
@@ -7730,6 +7979,7 @@ get_c_indent()
 			    {
 				amount += curbuf->b_ind_cpp_namespace
 							    - added_to_amount;
+				level += 1;
 				break;
 			    }
 
@@ -7841,10 +8091,17 @@ get_c_indent()
 			if (l != NULL && cin_is_cinword(l))
 			{
 			    if (theline[0] == '{')
+			    {
 				amount += curbuf->b_ind_open_extra;
+				if (curbuf->b_ind_open_extra >= curbuf->b_p_sw)
+				    level += 1;
+			    }
 			    else
+			    {
+				level += 1;
 				amount += curbuf->b_ind_level
 						     + curbuf->b_ind_no_brace;
+			    }
 			}
 			break;
 		    }
@@ -7860,6 +8117,11 @@ get_c_indent()
 		    scope_amount = get_indent() + (iscase    /* XXX */
 					? curbuf->b_ind_case_code
 					: curbuf->b_ind_scopedecl_code);
+		    scope_level = get_initial_tabs_count(curwin->w_cursor.lnum);
+		    if ((iscase
+			 ? curbuf->b_ind_case_code
+			 : curbuf->b_ind_scopedecl_code) >= curbuf->b_p_sw)
+			scope_level += 1;
 		    lookfor = curbuf->b_ind_case_break
 					      ? LOOKFOR_NOBREAK : LOOKFOR_ANY;
 		    continue;
@@ -8026,6 +8288,7 @@ get_c_indent()
 			cur_amount = skip_label(curwin->w_cursor.lnum, &l);
 		    else
 			cur_amount = get_indent();
+		    cur_level = get_initial_tabs_count(curwin->w_cursor.lnum);
 		    /*
 		     * If this is just above the line we are indenting, and it
 		     * starts with a '{', line it up with this line.
@@ -8037,6 +8300,7 @@ get_c_indent()
 							 && theline[0] == '{')
 		    {
 			amount = cur_amount;
+			level = cur_level;
 			/*
 			 * Only add b_ind_open_extra when the current line
 			 * doesn't start with a '{', which must have a match
@@ -8045,7 +8309,10 @@ get_c_indent()
 			 * ->	{ 3, 4 }
 			 */
 			if (*skipwhite(l) != '{')
+			{
 			    amount += curbuf->b_ind_open_extra;
+			    level  += curbuf->b_ind_open_extra / curbuf->b_p_sw;
+			}
 
 			if (curbuf->b_ind_cpp_baseclass)
 			{
@@ -8076,7 +8343,11 @@ get_c_indent()
 			    if (cont_amount > 0)
 				amount = cont_amount;
 			    else
+			    {
 				amount += ind_continuation;
+				level = get_initial_tabs_count(curwin->w_cursor.lnum)
+				    + ind_continuation / curbuf->b_p_sw;
+			    }
 			    break;
 			}
 
@@ -8094,12 +8365,14 @@ get_c_indent()
 			 * ->	here;
 			 */
 			amount = cur_amount;
+			level = cur_level;
 			if (theline[0] == '{')
 			    amount += curbuf->b_ind_open_extra;
 			if (lookfor != LOOKFOR_TERM)
 			{
 			    amount += curbuf->b_ind_level
 						     + curbuf->b_ind_no_brace;
+			    level = get_initial_tabs_count(curwin->w_cursor.lnum) + 1;
 			    break;
 			}
 
@@ -8182,7 +8455,10 @@ get_c_indent()
 			    /* Ignore unterminated lines in between, but
 			     * reduce indent. */
 			    if (amount > cur_amount)
+			    {
 				amount = cur_amount;
+				level = cur_level;
+			    }
 			}
 			else
 			{
@@ -8193,6 +8469,7 @@ get_c_indent()
 			     * ->	    here;
 			     */
 			    amount = cur_amount;
+			    level = cur_level;
 
 			    /*
 			     * If previous line ends in ',', check whether we
@@ -8383,13 +8660,22 @@ term_again:
 			 * ignoring any jump label.
 			 */
 			amount = skip_label(curwin->w_cursor.lnum, &l);
+			level = get_initial_tabs_count(curwin->w_cursor.lnum);
 
 			if (theline[0] == '{')
+			{
 			    amount += curbuf->b_ind_open_extra;
-			/* See remark above: "Only add b_ind_open_extra.." */
+			    if (curbuf->b_ind_open_extra >= curbuf->b_p_sw)
+				level += 1;
+			}
+			/* See remark above: "Only add ind_open_extra.." */
 			l = skipwhite(l);
 			if (*l == '{')
+			{
 			    amount -= curbuf->b_ind_open_extra;
+			    if (curbuf->b_ind_open_extra >= curbuf->b_p_sw)
+				level -= 1;
+			}
 			lookfor = iscase ? LOOKFOR_ANY : LOOKFOR_TERM;
 
 			/*
@@ -8483,6 +8769,7 @@ term_again:
 		&& !cin_isterminated(theline, FALSE, TRUE))
 	{
 	    amount = curbuf->b_ind_func_type;
+	    level = 1;
 	}
 	else
 	{
@@ -8701,8 +8988,13 @@ theend:
     vim_free(linecopy);
 
     if (amount < 0)
-	return 0;
-    return amount;
+	amount = 0;
+    if (level < 0)
+	level = 0;
+    *pamount = amount;
+    *plevel = level;
+
+    return (curbuf->b_p_ti? INDENT_LVL_AMOUNT: INDENT_AMOUNT);
 }
 
     static int
@@ -8824,8 +9116,11 @@ find_match(lookfor, ourscope)
  * Get indent level from 'indentexpr'.
  */
     int
-get_expr_indent()
+get_expr_indent(plevel, pamount)
+    int		*plevel;
+    int		*pamount;
 {
+    int		level;
     int		indent;
     pos_T	save_pos;
     colnr_T	save_curswant;
@@ -8833,6 +9128,9 @@ get_expr_indent()
     int		save_State;
     int		use_sandbox = was_set_insecurely((char_u *)"indentexpr",
 								   OPT_LOCAL);
+    typval_T	*rettv;
+    char_u	*p;
+    int		result = INDENT_ERROR;
 
     /* Save and restore cursor position and curswant, in case it was changed
      * via :normal commands */
@@ -8843,7 +9141,66 @@ get_expr_indent()
     if (use_sandbox)
 	++sandbox;
     ++textlock;
-    indent = eval_to_number(curbuf->b_p_inde);
+
+    p = skipwhite(curbuf->b_p_inde);
+
+    ++emsg_off; /* we must do it like eval_to_number() does it: silently. */
+    rettv = eval_expr(p, NULL);
+    --emsg_off;
+
+    if (rettv == NULL)
+	indent = -1;
+    else
+    {
+	int failed = FALSE;
+	if (rettv->v_type == VAR_LIST)
+	{
+	    listitem_T *li;
+	    if (rettv->vval.v_list->lv_len == 2)
+	    {
+		int idx = 0;
+		for (li = rettv->vval.v_list->lv_first; li != NULL; li = li->li_next)
+		{
+		    switch (idx)
+		    {
+			case 0:
+			    level = get_tv_number_chk(&li->li_tv, &failed);
+			    break;
+			case 1:
+			    indent = get_tv_number_chk(&li->li_tv, &failed);
+			    break;
+			default:
+                            failed = TRUE; /* this should in fact never happen */
+			    break;
+		    }
+		    ++idx;
+		    if (failed)
+			break;
+		}
+		if (!failed)
+		    result = INDENT_LVL_AMOUNT;
+	    }
+            else
+                failed = TRUE;
+	}
+	else
+	{
+	    indent = get_tv_number_chk(rettv, &failed);
+	    result = INDENT_AMOUNT;
+	}
+
+	if (failed)
+	{
+	    EMSG(_("E856: The value returned by 'indentexpr' should be a number or a list of two numbers"));
+	    level = -1;
+	    indent = -1;
+	    result = INDENT_AMOUNT;
+	}
+
+	clear_tv(rettv);
+	vim_free(rettv);
+    }
+
     if (use_sandbox)
 	--sandbox;
     --textlock;
@@ -8862,8 +9219,12 @@ get_expr_indent()
     /* If there is an error, just keep the current indent. */
     if (indent < 0)
 	indent = get_indent();
+    if (level < 0)
+	level = 0;
 
-    return indent;
+    *pamount = indent;
+    *plevel = level;
+    return result;
 }
 # endif
 
@@ -8908,7 +9269,9 @@ lisp_match(p)
  * I tried to fix the first two issues.
  */
     int
-get_lisp_indent()
+get_lisp_indent(plevel, pamount)
+    int		*plevel;
+    int		*pamount;
 {
     pos_T	*pos, realpos, paren;
     int		amount;
@@ -9086,7 +9449,10 @@ get_lisp_indent()
 
     curwin->w_cursor = realpos;
 
-    return amount;
+    /* FIXME:llorens:2010-03-21: get_lisp_indent() should return
+     * INDENT_LVL_AMOUNT if 'tabindent' is set. */
+    *pamount = amount;
+    return INDENT_AMOUNT;
 }
 #endif /* FEAT_LISP */
 
diff --git a/src/ops.c b/src/ops.c
index 4517e42..6cf0325 100644
--- a/src/ops.c
+++ b/src/ops.c
@@ -671,7 +671,7 @@ block_insert(oap, s, b_insert, bdp)
     void
 op_reindent(oap, how)
     oparg_T	*oap;
-    int		(*how) __ARGS((void));
+    int		(*how) __ARGS((int *plevel, int *pamount));
 {
     long	i;
     char_u	*l;
@@ -706,13 +706,16 @@ op_reindent(oap, how)
 						    || how != get_lisp_indent)
 #endif
 	{
+	    int result = INDENT_ERROR;
+	    int level;
 	    l = skipwhite(ml_get_curline());
 	    if (*l == NUL)		    /* empty or blank line */
 		count = 0;
 	    else
-		count = how();		    /* get the indent for this line */
+		result = how(&level, &count);	    /* get the indent for this line */
 
-	    if (set_indent(count, SIN_UNDO))
+	    if ((INDENT_LVL_AMOUNT == result && set_indent_with_level(level, count, SIN_UNDO))
+		|| (INDENT_LVL_AMOUNT != result && set_indent(count, SIN_UNDO)))
 	    {
 		/* did change the indent, call changed_lines() later */
 		if (first_changed == 0)
diff --git a/src/option.c b/src/option.c
index b1c888d..e275724 100644
--- a/src/option.c
+++ b/src/option.c
@@ -172,6 +172,7 @@
 #define PV_SW		OPT_BUF(BV_SW)
 #define PV_SWF		OPT_BUF(BV_SWF)
 #define PV_TAGS		OPT_BOTH(OPT_BUF(BV_TAGS))
+#define PV_TI		OPT_BUF(BV_TI)
 #define PV_TS		OPT_BUF(BV_TS)
 #define PV_TW		OPT_BUF(BV_TW)
 #define PV_TX		OPT_BUF(BV_TX)
@@ -362,6 +363,7 @@ static char_u	*p_spc;
 static char_u	*p_spf;
 static char_u	*p_spl;
 #endif
+static int	p_ti;
 static long	p_ts;
 static long	p_tw;
 static int	p_tx;
@@ -2504,6 +2506,9 @@ static struct vimoption
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCRIPTID_INIT},
+    {"tabindent",   "ti",   P_BOOL|P_VI_DEF|P_VIM,
+			    (char_u *)&p_ti, PV_TI,
+			    {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT},
     {"tabline",	    "tal",  P_STRING|P_VI_DEF|P_RALL,
 #ifdef FEAT_STL_OPT
 			    (char_u *)&p_tal, PV_NONE,
@@ -10170,6 +10175,7 @@ get_varp(p)
 	case PV_SPL:	return (char_u *)&(curwin->w_s->b_p_spl);
 #endif
 	case PV_SW:	return (char_u *)&(curbuf->b_p_sw);
+	case PV_TI:	return (char_u *)&(curbuf->b_p_ti);
 	case PV_TS:	return (char_u *)&(curbuf->b_p_ts);
 	case PV_TW:	return (char_u *)&(curbuf->b_p_tw);
 	case PV_TX:	return (char_u *)&(curbuf->b_p_tx);
@@ -10508,6 +10514,7 @@ buf_copy_options(buf, flags)
 	    buf->b_p_ft = empty_option;
 #endif
 	    buf->b_p_pi = p_pi;
+	    buf->b_p_ti = p_ti;
 #if defined(FEAT_SMARTINDENT) || defined(FEAT_CINDENT)
 	    buf->b_p_cinw = vim_strsave(p_cinw);
 #endif
diff --git a/src/option.h b/src/option.h
index 5144c33..12354d1 100644
--- a/src/option.h
+++ b/src/option.h
@@ -1028,6 +1028,7 @@ enum
     , BV_SW
     , BV_SWF
     , BV_TAGS
+    , BV_TI
     , BV_TS
     , BV_TW
     , BV_TX
diff --git a/src/proto/edit.pro b/src/proto/edit.pro
index 1eed378..57c7088 100644
--- a/src/proto/edit.pro
+++ b/src/proto/edit.pro
@@ -33,7 +33,7 @@ char_u *get_last_insert __ARGS((void));
 char_u *get_last_insert_save __ARGS((void));
 void replace_push __ARGS((int c));
 int replace_push_mb __ARGS((char_u *p));
-void fixthisline __ARGS((int (*get_the_indent)(void)));
+void fixthisline __ARGS((int (*get_the_indent)(int *plevel, int *pamount))); /* FIXME: I don't know how I should write this declaration. */
 void fix_indent __ARGS((void));
 int in_cinkeys __ARGS((int keytyped, int when, int line_is_empty));
 int hkmap __ARGS((int c));
diff --git a/src/proto/misc1.pro b/src/proto/misc1.pro
index 394880a..9d916c3 100644
--- a/src/proto/misc1.pro
+++ b/src/proto/misc1.pro
@@ -3,6 +3,7 @@ int get_indent __ARGS((void));
 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));
+int set_indent_with_level __ARGS((int level, int size, int flags));
 int set_indent __ARGS((int size, int flags));
 int get_number_indent __ARGS((linenr_T lnum));
 int open_line __ARGS((int dir, int flags, int second_line_indent));
@@ -85,9 +86,9 @@ int cin_islabel __ARGS((void));
 int cin_iscase __ARGS((char_u *s, int strict));
 int cin_isscopedecl __ARGS((char_u *s));
 void parse_cino __ARGS((buf_T *buf));
-int get_c_indent __ARGS((void));
-int get_expr_indent __ARGS((void));
-int get_lisp_indent __ARGS((void));
+int get_c_indent __ARGS((int *plevel, int *pamount));
+int get_expr_indent __ARGS((int *plevel, int *pamount));
+int get_lisp_indent __ARGS((int *plevel, int *pamount));
 void prepare_to_exit __ARGS((void));
 void preserve_exit __ARGS((void));
 int vim_fexists __ARGS((char_u *fname));
diff --git a/src/proto/ops.pro b/src/proto/ops.pro
index a8c144e..033e732 100644
--- a/src/proto/ops.pro
+++ b/src/proto/ops.pro
@@ -5,7 +5,7 @@ int get_op_char __ARGS((int optype));
 int get_extra_op_char __ARGS((int optype));
 void op_shift __ARGS((oparg_T *oap, int curs_top, int amount));
 void shift_line __ARGS((int left, int round, int amount, int call_changed_bytes));
-void op_reindent __ARGS((oparg_T *oap, int (*how)(void)));
+void op_reindent __ARGS((oparg_T *oap, int (*how)(int *plevel, int *pamount)));
 int get_expr_register __ARGS((void));
 void set_expr_line __ARGS((char_u *new_line));
 char_u *get_expr_line __ARGS((void));
diff --git a/src/structs.h b/src/structs.h
index 3f0a948..fdfab70 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1611,6 +1611,7 @@ struct file_buffer
     long	b_p_smc;	/* 'synmaxcol' */
     char_u	*b_p_syn;	/* 'syntax' */
 #endif
+    int		b_p_ti;		/* 'tabindent' */
     long	b_p_ts;		/* 'tabstop' */
     int		b_p_tx;		/* 'textmode' */
     long	b_p_tw;		/* 'textwidth' */
diff --git a/src/vim.h b/src/vim.h
index ab73531..62be414 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -977,6 +977,14 @@ extern char *(*dyn_libintl_textdomain)(const char *domainname);
 #define INDENT_INC	2	/* increase indent */
 #define INDENT_DEC	3	/* decrease indent */
 
+/* Values returned by get_c_indent(), get_lisp_indent() and get_expr_indent() */
+#define INDENT_ERROR		-1 /* something wrong happened while
+				    * calculating indentation */
+#define INDENT_AMOUNT		 0 /* the level indentation returned by the
+				    * function should be ignored */
+#define INDENT_LVL_AMOUNT	 1 /* the function returned both the indent
+				    * level and the amount of indentation */
+
 /* Values for flags argument for findmatchlimit() */
 #define FM_BACKWARD	0x01	/* search backwards */
 #define FM_FORWARD	0x02	/* search forwards */
diff --git a/src/testdir/Makefile b/src/testdir/Makefile
index c9e8922..f11a800 100644
--- a/src/testdir/Makefile
+++ b/src/testdir/Makefile
@@ -31,7 +31,7 @@ SCRIPTS = test1.out test2.out test3.out test4.out test5.out test6.out \
 		test89.out test90.out test91.out test92.out test93.out \
 		test94.out test95.out test96.out test97.out test98.out \
 		test99.out test100.out test101.out test102.out test103.out \
-		test104.out test105.out test106.out
+		test104.out test105.out test106.out test-tabindent.out
 
 SCRIPTS_GUI = test16.out
 
diff --git a/src/testdir/test-tabindent.in b/src/testdir/test-tabindent.in
new file mode 100644
index 0000000..9b704b3
--- /dev/null
+++ b/src/testdir/test-tabindent.in
@@ -0,0 +1,188 @@
+/*
+This file tests the 'tabindent' option, which makes Vim
+indent text using tabs and pad it with spaces. This allows
+changing 'tabstop' without making the indentation incorrect.
+*/
+/*
+ * This tests for correct shifting of already indented lines.
+ * If this test fails, Vim probably violates the rules of 'tabindent'
+ * indenting when it changes indenting of lines.
+STARTTEST
+:so small.vim
+:set nocompatible cin ts=4 sw=4
+:set tabindent
+:set nopreserveindent noet shiftround
+/start of AUTO
+4jV<jV2<
+3jV>jV2>
+:set nopreserveindent noet noshiftround
+4jV<jV2<
+3jV>jV2>
+:set preserveindent et shiftround
+4jV<jV2<
+3jV>jV2>
+:set preserveindent et noshiftround
+4jV<jV2<
+3jV>jV2>
+ENDTEST */
+/* start of AUTOmatic test for changing indent */
+void func()
+{
+	for (int i = 0;
+	     i < 10;
+	     ++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+			       " %d\n",
+			       i);
+	}
+
+
+	for (int i = 0;
+	     i < 10;
+	     ++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+			       " %d\n",
+			       i);
+	}
+
+
+	for (int i = 0;
+	     i < 10;
+	     ++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+			       " %d\n",
+			       i);
+	}
+
+
+	for (int i = 0;
+	     i < 10;
+	     ++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+			       " %d\n",
+			       i);
+	}
+}
+
+/* end of AUTOmatic test for changing indent */
+
+/*
+ * This tests for correct indenting of C code.
+ * TODO: test indenting of switch statements and case labels.
+STARTTEST
+:set cinoptions=(0
+:set nopreserveindent et
+/start of AUTO
+=/end of AUTO
+n:set noet
+/start of AUTO
+=/end of AUTO
+ENDTEST */
+
+/* start of AUTOmatic test for indenting C code */
+	void
+func()
+{
+	for (int i = 0;
+		 i < 10;
+		 ++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+				   " %d\n",
+				   i);
+	}
+	for (int i = 0;
+		 i < 10;
+		 ++i)
+		if (!(i & 1)) printf("i is even: %d\n",
+							 i);
+		else
+			printf("i is odd: %d\n",
+				   i);
+}
+
+void
+func()
+{
+for (int i = 0;
+i < 10;
+++i)
+{
+if (i & 1)
+printf("i is odd:"
+" %d\n",
+i);
+}
+for (int i = 0;
+i < 10;
+++i)
+if (!(i & 1)) printf("i is even: %d\n",
+i);
+else
+printf("i is odd: %d\n",
+i);
+}
+/* end of AUTOmatic test for indenting C code */
+
+/* start of AUTOmatic test for indenting C code */
+	void
+func()
+{
+	for (int i = 0;
+		 i < 10;
+		 ++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+				   " %d\n",
+				   i);
+	}
+	for (int i = 0;
+		 i < 10;
+		 ++i)
+		if (!(i & 1)) printf("i is even: %d\n",
+							 i);
+		else
+			printf("i is odd: %d\n",
+				   i);
+}
+
+void
+func()
+{
+/* NOTE: if this code gets indented with spaces, this is a problem with 'tabindent' handling */
+for (int i = 0;
+i < 10;
+++i)
+{
+if (i & 1)
+printf("i is odd:"
+" %d\n",
+i);
+}
+for (int i = 0;
+i < 10;
+++i)
+if (!(i & 1)) printf("i is even: %d\n",
+i);
+else
+printf("i is odd: %d\n",
+i);
+/* NOTE: if this code gets indented with spaces, this is a problem with 'tabindent' handling */
+}
+/* end of AUTOmatic test for indenting C code */
+/*
+STARTTEST
+:%wq! test.out
+ENDTEST
+vim: ft=c ts=4 sw=4
+*/
diff --git a/src/testdir/test-tabindent.ok b/src/testdir/test-tabindent.ok
new file mode 100644
index 0000000..43d7237
--- /dev/null
+++ b/src/testdir/test-tabindent.ok
@@ -0,0 +1,188 @@
+/*
+This file tests the 'tabindent' option, which makes Vim
+indent text using tabs and pad it with spaces. This allows
+changing 'tabstop' without making the indentation incorrect.
+*/
+/*
+ * This tests for correct shifting of already indented lines.
+ * If this test fails, Vim probably violates the rules of 'tabindent'
+ * indenting when it changes indenting of lines.
+STARTTEST
+:so small.vim
+:set nocompatible cin ts=4 sw=4
+:set tabindent
+:set nopreserveindent noet shiftround
+/start of AUTO
+4jV<jV2<
+3jV>jV2>
+:set nopreserveindent noet noshiftround
+4jV<jV2<
+3jV>jV2>
+:set preserveindent et shiftround
+4jV<jV2<
+3jV>jV2>
+:set preserveindent et noshiftround
+4jV<jV2<
+3jV>jV2>
+ENDTEST */
+/* start of AUTOmatic test for changing indent */
+void func()
+{
+	for (int i = 0;
+	    i < 10;
+	++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+			        " %d\n",
+			            i);
+	}
+
+
+	for (int i = 0;
+	 i < 10;
+ ++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+			           " %d\n",
+			               i);
+	}
+
+
+	for (int i = 0;
+	    i < 10;
+	++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+			        " %d\n",
+			            i);
+	}
+
+
+	for (int i = 0;
+	 i < 10;
+ ++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+			           " %d\n",
+			               i);
+	}
+}
+
+/* end of AUTOmatic test for changing indent */
+
+/*
+ * This tests for correct indenting of C code.
+ * TODO: test indenting of switch statements and case labels.
+STARTTEST
+:set cinoptions=(0
+:set nopreserveindent et
+/start of AUTO
+=/end of AUTO
+n:set noet
+/start of AUTO
+=/end of AUTO
+ENDTEST */
+
+/* start of AUTOmatic test for indenting C code */
+	void
+func()
+{
+	for (int i = 0;
+	     i < 10;
+	     ++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+			       " %d\n",
+			       i);
+	}
+	for (int i = 0;
+	     i < 10;
+	     ++i)
+		if (!(i & 1)) printf("i is even: %d\n",
+		                     i);
+		else
+			printf("i is odd: %d\n",
+			       i);
+}
+
+	void
+func()
+{
+	for (int i = 0;
+	     i < 10;
+	     ++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+			       " %d\n",
+			       i);
+	}
+	for (int i = 0;
+	     i < 10;
+	     ++i)
+		if (!(i & 1)) printf("i is even: %d\n",
+		                     i);
+		else
+			printf("i is odd: %d\n",
+			       i);
+}
+/* end of AUTOmatic test for indenting C code */
+
+/* start of AUTOmatic test for indenting C code */
+	void
+func()
+{
+	for (int i = 0;
+	     i < 10;
+	     ++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+			       " %d\n",
+			       i);
+	}
+	for (int i = 0;
+	     i < 10;
+	     ++i)
+		if (!(i & 1)) printf("i is even: %d\n",
+		                     i);
+		else
+			printf("i is odd: %d\n",
+			       i);
+}
+
+	void
+func()
+{
+	/* NOTE: if this code gets indented with spaces, this is a problem with 'tabindent' handling */
+	for (int i = 0;
+	     i < 10;
+	     ++i)
+	{
+		if (i & 1)
+			printf("i is odd:"
+			       " %d\n",
+			       i);
+	}
+	for (int i = 0;
+	     i < 10;
+	     ++i)
+		if (!(i & 1)) printf("i is even: %d\n",
+		                     i);
+		else
+			printf("i is odd: %d\n",
+			       i);
+	/* NOTE: if this code gets indented with spaces, this is a problem with 'tabindent' handling */
+}
+/* end of AUTOmatic test for indenting C code */
+/*
+STARTTEST
+:%wq! test.out
+ENDTEST
+vim: ft=c ts=4 sw=4
+*/

Raspunde prin e-mail lui