Download raw body.
vi(1) bug autoindent with expandtab
Hello,
while using vi(1) I found a bug that I managed to fix.
The bug happens also in nvi(1) from ports.
Right now autoindent behaves differently when expandtab is also set.
Steps to replicate the bug:
* open vi with just autoindent
* go into an empty line, go into insert mode and type <tab>
* esc, move around and go back to the line
* notice that tab stays there
Repeating the same process but with also expandtab set, will leave the
line empty, without the 2/4/8 spaces (the expanded tab).
With expandtab, <tab> behaves in the same way as <control-T>. Repeating
the process above with <control-T> with or without expandtab set will
result in the line being cleared.
The bug happens because <tab> with expandtab set calls the same function
(txt_dent) that is called for <control-T>, probably because the
expandtab feature was added afterwards, and this difference was not
noticed. I have fixed the function so that when called for <tab> it
does not reset the autoindent characters.
Bye,
jerry
diff --git vi/v_txt.c vi/v_txt.c
index 5f245a0d457..202b4852ed2 100644
--- vi/v_txt.c
+++ vi/v_txt.c
@@ -29,10 +29,12 @@
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
+enum dent_type {D_INDENT, D_OUTDENT, D_TAB};
+
static int txt_abbrev(SCR *, TEXT *, CHAR_T *, int, int *, int *);
static void txt_ai_resolve(SCR *, TEXT *, int *);
static TEXT *txt_backup(SCR *, TEXTH *, TEXT *, u_int32_t *);
-static int txt_dent(SCR *, TEXT *, int, int);
+static int txt_dent(SCR *, TEXT *, int, enum dent_type);
static int txt_emark(SCR *, TEXT *, size_t);
static void txt_err(SCR *, TEXTH *);
static int txt_fc(SCR *, TEXT *, int *);
@@ -968,7 +970,7 @@ leftmargin: tp->lb[tp->cno - 1] = ' ';
if (tp->ai == 0 || tp->cno > tp->ai + tp->offset)
goto ins_ch;
- (void)txt_dent(sp, tp, O_SHIFTWIDTH, 0);
+ (void)txt_dent(sp, tp, O_SHIFTWIDTH, D_OUTDENT);
break;
default:
abort();
@@ -1184,7 +1186,7 @@ leftmargin: tp->lb[tp->cno - 1] = ' ';
case K_CNTRLT: /* Add autoindent characters. */
if (!LF_ISSET(TXT_CNTRLT))
goto ins_ch;
- if (txt_dent(sp, tp, O_SHIFTWIDTH, 1))
+ if (txt_dent(sp, tp, O_SHIFTWIDTH, D_INDENT))
goto err;
goto ebuf_chk;
case K_RIGHTBRACE:
@@ -1216,7 +1218,7 @@ leftmargin: tp->lb[tp->cno - 1] = ' ';
case K_TAB:
if (sp->showmode != SM_COMMAND && quote != Q_VTHIS &&
O_ISSET(sp, O_EXPANDTAB)) {
- if (txt_dent(sp, tp, O_TABSTOP, 1))
+ if (txt_dent(sp, tp, O_TABSTOP, D_TAB))
goto err;
goto ebuf_chk;
}
@@ -1854,13 +1856,13 @@ txt_backup(SCR *sp, TEXTH *tiqh, TEXT *tp, u_int32_t *flagsp)
* there are screens with different character representations.
*
* txt_dent --
- * Handle ^T indents, ^D outdents.
+ * Handle ^T indents, ^D outdents and <tab> when expandtab is set.
*
* If anything changes here, check the ex version to see if it needs similar
* changes.
*/
static int
-txt_dent(SCR *sp, TEXT *tp, int swopt, int isindent)
+txt_dent(SCR *sp, TEXT *tp, int swopt, enum dent_type dent_type)
{
CHAR_T ch;
u_long sw, ts;
@@ -1891,7 +1893,7 @@ txt_dent(SCR *sp, TEXT *tp, int swopt, int isindent)
COL_OFF(current, ts) : KEY_LEN(sp, tp->lb[cno]);
target = current;
- if (isindent)
+ if (dent_type == D_INDENT || dent_type == D_TAB)
target += COL_OFF(target, sw);
else {
--target;
@@ -1904,9 +1906,12 @@ txt_dent(SCR *sp, TEXT *tp, int swopt, int isindent)
* and the indent flag because there's no single test. (^T can only
* be detected by the cursor position, and while we know that the test
* is always true for ^D, the cursor can be in more than one place, as
- * "0^D" and "^D" are different.)
+ * "0^D" and "^D" are different). Since <tab> without expandtab is a
+ * normal character and does not reset the ai count, we do not reset it
+ * also when expandtab is set.
*/
- ai_reset = !isindent || tp->cno == tp->ai + tp->offset;
+ ai_reset = dent_type == D_OUTDENT ||
+ (dent_type == D_INDENT && tp->cno == tp->ai + tp->offset);
/*
* Back up over any previous <blank> characters, changing them into
@@ -1937,10 +1942,9 @@ txt_dent(SCR *sp, TEXT *tp, int swopt, int isindent)
else {
cno = current;
tabs = 0;
- if (!O_ISSET(sp, O_EXPANDTAB)) {
+ if (!O_ISSET(sp, O_EXPANDTAB))
for (; cno + COL_OFF(cno, ts) <= target; ++tabs)
cno += COL_OFF(cno, ts);
- }
spaces = target - cno;
}
vi(1) bug autoindent with expandtab