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. :)

> 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]>
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..d53dbac 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -9407,8 +9407,13 @@ 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);
+	/* FIXME:2010-03-21:llorens: depending on the value returned by
+	 * get_c_indent() rettv->v_type should either be VAR_NUMBER or
+	 * VAR_LIST. */
+	rettv->vval.v_number = amount;
 	curwin->w_cursor = pos;
     }
     else
@@ -13713,8 +13718,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..e5a9e6c 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,10 +7062,13 @@ 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;
     colnr_T	col;
@@ -6910,7 +7124,10 @@ get_c_indent()
      * 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 +7176,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);
     }
 
     /*
@@ -6982,6 +7200,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);
 	*lead_start = NUL;
 	*lead_middle = NUL;
 
@@ -7035,11 +7254,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 +7286,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 +7362,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.
@@ -7386,11 +7611,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 +7662,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);
+	    }
 
 	    start_brace = BRACE_AT_END;
 	}
@@ -7452,6 +7687,7 @@ get_c_indent()
 	     * other than the open brace.  indulge them, if so.
 	     */
 	    amount += curbuf->b_ind_close_extra;
+	    level -= 1;
 	}
 	else
 	{
@@ -7472,6 +7708,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 +7762,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 +7783,7 @@ get_c_indent()
 		amount += curbuf->b_ind_level;
 	    }
 	    scope_amount = amount;
+	    scope_level = level;
 	    whilelevel = 0;
 
 	    /*
@@ -7688,6 +7930,7 @@ get_c_indent()
 					  && lookfor != LOOKFOR_CPP_BASECLASS)
 			{
 			    amount = scope_amount;
+			    level = scope_level;
 			    if (theline[0] == '{')
 			    {
 				amount += curbuf->b_ind_open_extra;
@@ -7841,10 +8084,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;
+						    + curbuf->b_ind_no_brace;
+			    }
 			}
 			break;
 		    }
@@ -7860,6 +8110,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;
@@ -8076,7 +8331,10 @@ get_c_indent()
 			    if (cont_amount > 0)
 				amount = cont_amount;
 			    else
+			    {
 				amount += ind_continuation;
+				level = get_initial_tabs_count(curwin->w_cursor.lnum) + 1;
+			    }
 			    break;
 			}
 
@@ -8100,6 +8358,7 @@ get_c_indent()
 			{
 			    amount += curbuf->b_ind_level
 						     + curbuf->b_ind_no_brace;
+			    level = get_initial_tabs_count(curwin->w_cursor.lnum) + 1;
 			    break;
 			}
 
@@ -8383,13 +8642,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 +8751,7 @@ term_again:
 		&& !cin_isterminated(theline, FALSE, TRUE))
 	{
 	    amount = curbuf->b_ind_func_type;
+	    level = 1;
 	}
 	else
 	{
@@ -8701,8 +8970,11 @@ theend:
     vim_free(linecopy);
 
     if (amount < 0)
-	return 0;
-    return amount;
+	amount = 0;
+    *pamount = amount;
+    *plevel = level;
+
+    return (curbuf->b_p_ti? INDENT_LVL_AMOUNT: INDENT_AMOUNT);
 }
 
     static int
@@ -8824,8 +9096,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 +9108,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 +9121,59 @@ get_expr_indent()
     if (use_sandbox)
 	++sandbox;
     ++textlock;
-    indent = eval_to_number(curbuf->b_p_inde);
+
+    p = skipwhite(curbuf->b_p_inde);
+    if ((rettv = eval_expr(p, NULL)) == NULL)
+	indent = -1;
+    else
+    {
+	int failed = TRUE;
+	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:
+			    break;
+		    }
+		    ++idx;
+		    if (failed)
+			break;
+		}
+		if (!failed)
+		    result = INDENT_LVL_AMOUNT;
+	    }
+	}
+	else
+	{
+	    indent = get_tv_number_chk(rettv, &failed);
+	    result = INDENT_AMOUNT;
+	}
+
+	if (failed)
+	{
+	    EMSG(_("E817: 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 +9192,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 +9242,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 +9422,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 */
commit 9eee211f84f76b60ef08f424f6b85b64448ff967
Author: James McCoy <[email protected]>
Date:   Sat Mar 15 23:27:43 2014 -0400

    tabindent tests

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
+*/
diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim
index 0a6dbc1..679b745 100644
--- a/runtime/indent/vim.vim
+++ b/runtime/indent/vim.vim
@@ -21,7 +21,22 @@ if exists("*GetVimIndent")
 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 level = 0
   let ignorecase_save = &ignorecase
   try
     let &ignorecase = 0
@@ -31,13 +46,14 @@ function GetVimIndent()
 
   " 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
@@ -46,14 +62,17 @@ function GetVimIndent()
     endif
   elseif getline(lnum) =~ '^\s*aug\%[roup]' && getline(lnum) !~ '^\s*aug\%[roup]\s*!\=\s\+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 = level + 1
       if strpart(line, i, 1) == '|' && has('syntax_items')
             \ && synIDattr(synID(lnum, i, 1), "name") =~ '\(Comment\|String\)$'
         let ind -= &sw
+        let level = level - 1
       endif
     endif
   endif
@@ -58,6 +76,7 @@ function GetVimIndent()
   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
 
@@ -68,7 +87,7 @@ function GetVimIndent()
     let ind = ind - &sw
   endif
 
-  return ind
+  return s:ReturnValue(level, ind)
 endfunction
 
 " vim:sw=2

Attachment: signature.asc
Description: Digital signature

Raspunde prin e-mail lui