Hi Bram,

2016-1-10(Sun) 22:14:01 UTC+9 Bram Moolenaar:
> Hirohito Higashi wrote:
> 
> > 2016-1-10(Sun) 4:15:14 UTC+9 Bram Moolenaar:
> > > Hirohito Higashi wrote:
> > > 
> > > > 2016-1-8(Fri) 2:27:17 UTC+9 Bram Moolenaar:
> > > > > Hirohito Higashi wrote:
> > > > > 
> > > > > > > > To: Bram (As a Vimboss)
> > > > > > > > To: Christian Brabandt (As a visual <C-A>/<C-X> first patch 
> > > > > > > > author)
> > > > > > > > To: Jason Schulz (As a support for bin 'nrformats' patch author)
> > > > > > > > 
> > > > > > > > Hi,
> > > > > > > > 
> > > > > > > > I refactored visual <C-A>/<C-X> to support vcol et al.
> > > > > > > > This mean is <TAB> code free!
> > > > > > > > 
> > > > > > > > Contents of patch.
> > > > > > > > - visual <C-A>/<C-X> support vcol. (<TAB> code free)
> > > > > > > > - 'test_increment' convert from old style test to new style 
> > > > > > > > test. and added some test items. 
> > > > > > > > - Processing was allowed to separate.
> > > > > > > >   (line loop process and add/subtract process)
> > > > > > > >   (We have to use the existing function block_prep() to process 
> > > > > > > > the block-wise)
> > > > > > > > - We removed the halfway right-to-left processing.
> > > > > > > >   (Remove RLADDSUBFIX() macro)
> > > > > > > >   (This is causing the actual problem)
> > > > > > > >    $ vim -Nu NONE -c "set rightleft"
> > > > > > > >    i123 45<Esc>
> > > > > > > >    <C-A>           " Unexpected swap the numbers of strings 
> > > > > > > > occurred.
> > > > > > > > 
> > > > > > > > Christian Brabandt and Jason Schulz and List>
> > > > > > > > I was wondering if you could review this patch.
> > > > > > > > 
> > > > > > > > Jason Schulz>
> > > > > > > > Sorry to such just your patch was included.
> > > > > > > > I have just completed the doing has been working since last 
> > > > > > > > fall :-)
> > > > > > > 
> > > > > > > That's a big change.  Can you give an example of what didn't work 
> > > > > > > before
> > > > > > > and works now?
> > > > > > 
> > > > > > Please see Test_visual_increment_27() ~ Test_visual_increment_34().
> > > > > > Below is ather exsample.
> > > > > > 
> > > > > > Case 1 (Visual blockwise <C-A> with TAB and SPACE mixed)
> > > > > >   - Manipulate
> > > > > >     $ vim -Nu NONE
> > > > > >     :call setline(1, ["1234    56", "\<TAB>78"])
> > > > > >     :exec "norm! ggw\<C-V>jl\<C-A>"
> > > > > >   - Expect result
> > > > > >     "1234    57"
> > > > > >     "\<TAB>79"
> > > > > >   - Unpatched result
> > > > > >     "1235    56"
> > > > > >     "\<TAB>79"
> > > > > 
> > > > > 
> > > > > I see, thanks for fixing that.
> > > > > 
> > > > > > > To make reviewing easier, it would be good to first make a patch 
> > > > > > > to
> > > > > > > change the test from old to new style.  Then we know the test 
> > > > > > > works with
> > > > > > > the old code.
> > > > > > 
> > > > > > Okay. I attached simple patch that only convert to new style test of
> > > > > > test_increment.
> > > > > 
> > > > > Thanks.  The original test had a nice explanation of what it was 
> > > > > doing.
> > > > > Although the new style test do have the assert_equal() calls that make
> > > > > it easier to see what is going on, the commands themselves are still a
> > > > > bit of a puzzle.  Since the explanations were already written, we can
> > > > > keep them.  Using the comment above the test function should work 
> > > > > well.
> > > > 
> > > > Indeed. I did it.  Please check and include attached patch.
> > > 
> > > Thanks!
> > 
> > Thanks for including this.
> >   Patch 7.4.1072
> >   https://groups.google.com/d/topic/vim_dev/_K0eQkIB5aY/discussion
> > 
> > > 
> > > > BTW, The following changes I thought happy for test_increment.vim.
> > > > How do you like it?
> > > 
> > > Yes, that's better than the arbitrary order we have now.
> > > Unfortunately it break test_quickfix, it makes an assumption about test
> > > function ordering.  That needs to be fixed.
> > 
> > Thanks for including this too.
> >   Patch 7.4.1071
> >   https://groups.google.com/d/topic/vim_dev/p6IAS6bPFDU/discussion
> > 
> > 
> > Well, the next simple patch is about this.
> > > - We removed the halfway right-to-left processing.
> > >   (Remove RLADDSUBFIX() macro)
> > >   (This is causing the actual problem)
> > >    $ vim -Nu NONE -c "set rightleft"
> > >    i123 45<Esc>
> > >    <C-A>           " Unexpected swap the numbers of strings occurred.
> > 
> > Investigation result:
> > Reverse line process of 'rightleft' is performed by the display part.   
> > therefore it doesn't need in do_addsub().
> > 
> > I've attached a patch containing the test.
> > Please check it.
> 
> Thanks.  Now I could check that the test fails before including the
> change in ops.c.

Thanks for include this quickly.
  Patch 7.4.1076
  https://groups.google.com/d/topic/vim_dev/TOHFHDxek34/discussion

The last patch fixing this.
- visual <C-A>/<C-X> support vcol. (<TAB> code free)
- Processing was allowed to separate.
  (line loop process and add/subtract process)
  (We have to use the existing function block_prep() to process the block-wise)

Sorry, more than this is difficult to separate the patch...
Please include this.
--
Best regards,
Hirohito Higashi (a.k.a h_east)

-- 
-- 
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/src/normal.c b/src/normal.c
index 640c060..afaf43d 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -40,7 +40,6 @@ static void	find_start_of_word __ARGS((pos_T *));
 static void	find_end_of_word __ARGS((pos_T *));
 static int	get_mouse_class __ARGS((char_u *p));
 #endif
-static void	prep_redo_visual __ARGS((cmdarg_T *cap));
 static void	prep_redo_cmd __ARGS((cmdarg_T *cap));
 static void	prep_redo __ARGS((int regname, long, int, int, int, int, int));
 static int	checkclearop __ARGS((oparg_T *oap));
@@ -1392,6 +1391,7 @@ do_pending_operator(cap, old_col, gui_yank)
     static linenr_T redo_VIsual_line_count; /* number of lines */
     static colnr_T  redo_VIsual_vcol;	    /* number of cols or end column */
     static long	    redo_VIsual_count;	    /* count for Visual operator */
+    static int	    redo_VIsual_arg;	    /* extra argument */
 #ifdef FEAT_VIRTUALEDIT
     int		    include_line_break = FALSE;
 #endif
@@ -1699,6 +1699,7 @@ do_pending_operator(cap, old_col, gui_yank)
 		    redo_VIsual_vcol = resel_VIsual_vcol;
 		    redo_VIsual_line_count = resel_VIsual_line_count;
 		    redo_VIsual_count = cap->count0;
+		    redo_VIsual_arg = cap->arg;
 		}
 	    }
 
@@ -2108,6 +2109,24 @@ do_pending_operator(cap, old_col, gui_yank)
 			       oap->op_type == OP_FOLDDELREC, oap->is_VIsual);
 	    break;
 #endif
+	case OP_ADD:
+	case OP_SUBTRACT:
+	    if (empty_region_error)
+	    {
+		vim_beep(BO_OPER);
+		CancelRedo();
+	    }
+	    else
+	    {
+		VIsual_active = TRUE;
+#ifdef FEAT_LINEBREAK
+		curwin->w_p_lbr = lbr_saved;
+#endif
+		op_addsub(oap, cap->count1, redo_VIsual_arg);
+		VIsual_active = FALSE;
+	    }
+	    check_cursor_col();
+	    break;
 	default:
 	    clearopbeep(oap);
 	}
@@ -3603,43 +3622,6 @@ find_ident_at_pos(wp, lnum, startcol, string, find_type)
 }
 
 /*
- * Add commands to reselect Visual mode into the redo buffer.
- */
-    static void
-prep_redo_visual(cap)
-    cmdarg_T *cap;
-{
-    ResetRedobuff();
-    AppendCharToRedobuff(VIsual_mode);
-    if (VIsual_mode == 'V' && curbuf->b_visual.vi_end.lnum
-					    != curbuf->b_visual.vi_start.lnum)
-    {
-	AppendNumberToRedobuff(curbuf->b_visual.vi_end.lnum
-					    - curbuf->b_visual.vi_start.lnum);
-	AppendCharToRedobuff('j');
-    }
-    else if (VIsual_mode == 'v' || VIsual_mode == Ctrl_V)
-    {
-	/* block visual mode or char visual mmode*/
-	if (curbuf->b_visual.vi_end.lnum != curbuf->b_visual.vi_start.lnum)
-	{
-	    AppendNumberToRedobuff(curbuf->b_visual.vi_end.lnum -
-		    curbuf->b_visual.vi_start.lnum);
-	    AppendCharToRedobuff('j');
-	}
-	if (curbuf->b_visual.vi_curswant == MAXCOL)
-	    AppendCharToRedobuff('$');
-	else if (curbuf->b_visual.vi_end.col > curbuf->b_visual.vi_start.col)
-	{
-	    AppendNumberToRedobuff(curbuf->b_visual.vi_end.col
-					 - curbuf->b_visual.vi_start.col - 1);
-	    AppendCharToRedobuff(' ');
-	}
-    }
-    AppendNumberToRedobuff(cap->count1);
-}
-
-/*
  * Prepare for redo of a normal command.
  */
     static void
@@ -4243,30 +4225,16 @@ nv_help(cap)
 nv_addsub(cap)
     cmdarg_T	*cap;
 {
-    int visual = VIsual_active;
-
-    if (cap->oap->op_type == OP_NOP
-	    && do_addsub((int)cap->cmdchar, cap->count1, cap->arg) == OK)
+    if (!VIsual_active && cap->oap->op_type == OP_NOP)
     {
-	if (visual)
-	{
-	    prep_redo_visual(cap);
-	    if (cap->arg)
-		AppendCharToRedobuff('g');
-	    AppendCharToRedobuff(cap->cmdchar);
-	}
-	else
-	    prep_redo_cmd(cap);
+	cap->oap->op_type = cap->cmdchar == Ctrl_A ?  OP_ADD : OP_SUBTRACT;
+	op_addsub(cap->oap, cap->count1, cap->arg);
+	cap->oap->op_type = OP_NOP;
     }
+    else if (VIsual_active)
+	nv_operator(cap);
     else
-	clearopbeep(cap->oap);
-    if (visual)
-    {
-	VIsual_active = FALSE;
-	redo_VIsual_busy = FALSE;
-	may_clear_cmdline();
-	redraw_later(INVERTED);
-    }
+	clearop(cap->oap);
 }
 
 /*
@@ -7924,6 +7892,7 @@ nv_g_cmd(cap)
 	{
 	    cap->arg = TRUE;
 	    cap->cmdchar = cap->nchar;
+	    cap->nchar = NUL;
 	    nv_addsub(cap);
 	}
 	else
diff --git a/src/ops.c b/src/ops.c
index 12978a8..729148e 100644
--- a/src/ops.c
+++ b/src/ops.c
@@ -112,6 +112,7 @@ static void	dis_msg __ARGS((char_u *p, int skip_esc));
 static char_u	*skip_comment __ARGS((char_u *line, int process, int include_space, int *is_comment));
 #endif
 static void	block_prep __ARGS((oparg_T *oap, struct block_def *, linenr_T, int));
+static int	do_addsub __ARGS((int op_type, pos_T *pos, int length, linenr_T Prenum1));
 #if defined(FEAT_CLIPBOARD) || defined(FEAT_EVAL)
 static void	str_to_reg __ARGS((struct yankreg *y_ptr, int yank_type, char_u *str, long len, long blocklen, int str_list));
 #endif
@@ -158,6 +159,8 @@ static char opchars[][3] =
     {'z', 'D', TRUE},	/* OP_FOLDDELREC */
     {'g', 'w', TRUE},	/* OP_FORMAT2 */
     {'g', '@', FALSE},	/* OP_FUNCTION */
+    {Ctrl_A, NUL, FALSE},	/* OP_ADD */
+    {Ctrl_X, NUL, FALSE},	/* OP_SUBTRACT */
 };
 
 /*
@@ -175,6 +178,10 @@ get_op_type(char1, char2)
 	return OP_REPLACE;
     if (char1 == '~')		/* when tilde is an operator */
 	return OP_TILDE;
+    if (char1 == 'g' && char2 == Ctrl_A)	/* add */
+	return OP_ADD;
+    if (char1 == 'g' && char2 == Ctrl_X)	/* subtract */
+	return OP_SUBTRACT;
     for (i = 0; ; ++i)
 	if (opchars[i][0] == char1 && opchars[i][1] == char2)
 	    break;
@@ -5340,16 +5347,126 @@ block_prep(oap, bdp, lnum, is_del)
 }
 
 /*
- * add or subtract 'Prenum1' from a number in a line
- * 'command' is CTRL-A for add, CTRL-X for subtract
+ * Handle the add/subtract operator.
+ */
+    void
+op_addsub(oap, Prenum1, g_cmd)
+    oparg_T	*oap;
+    linenr_T	Prenum1;	    /* Amount of add/subtract */
+    int		g_cmd;		    /* was g<c-a>/g<c-x> */
+{
+    pos_T		pos;
+    struct block_def	bd;
+    int			change_cnt = 0;
+    linenr_T		amount = Prenum1;
+
+    if (!VIsual_active)
+    {
+	pos = curwin->w_cursor;
+	if (u_save_cursor() == FAIL)
+	    return;
+	change_cnt = do_addsub(oap->op_type, &pos, 0, amount);
+	if (change_cnt)
+	    changed_lines(pos.lnum, 0, pos.lnum + 1, 0L);
+    }
+    else
+    {
+	int one_change;
+	int length;
+
+	if (u_save((linenr_T)(oap->start.lnum - 1),
+					(linenr_T)(oap->end.lnum + 1)) == FAIL)
+	    return;
+
+	pos = oap->start;
+	for (; pos.lnum <= oap->end.lnum; ++pos.lnum)
+	{
+	    if (oap->block_mode)		    /* Visual block mode */
+	    {
+		block_prep(oap, &bd, pos.lnum, FALSE);
+		pos.col = bd.textcol;
+		length = bd.textlen;
+	    }
+	    else
+	    {
+		if (oap->motion_type == MLINE)
+		{
+		    curwin->w_cursor.col = 0;
+		    pos.col = 0;
+		    length = (colnr_T)STRLEN(ml_get(pos.lnum));
+		}
+		else if (oap->motion_type == MCHAR)
+		{
+		    if (!oap->inclusive)
+			dec(&(oap->end));
+		    length = (colnr_T)STRLEN(ml_get(pos.lnum));
+		    pos.col = 0;
+		    if (pos.lnum == oap->start.lnum)
+		    {
+			pos.col += oap->start.col;
+			length -= oap->start.col;
+		    }
+		    if (pos.lnum == oap->end.lnum)
+		    {
+			length = (int)STRLEN(ml_get(oap->end.lnum));
+			if (oap->end.col >= length)
+			    oap->end.col = length - 1;
+			length = oap->end.col - pos.col + 1;
+		    }
+		}
+	    }
+	    one_change = do_addsub(oap->op_type, &pos, length, amount);
+	    if (one_change)
+		++change_cnt;
+
+#ifdef FEAT_NETBEANS_INTG
+	    if (netbeans_active() && one_change)
+	    {
+		char_u *ptr = ml_get_buf(curbuf, pos.lnum, FALSE);
+
+		netbeans_removed(curbuf, pos.lnum, pos.col, (long)length);
+		netbeans_inserted(curbuf, pos.lnum, pos.col,
+						&ptr[pos.col], length);
+	    }
+#endif
+	    if (g_cmd && one_change)
+		amount += Prenum1;
+	}
+	if (change_cnt)
+	    changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L);
+
+	if (!change_cnt && oap->is_VIsual)
+	    /* No change: need to remove the Visual selection */
+	    redraw_curbuf_later(INVERTED);
+
+	/*
+	* Set '[ and '] marks.
+	*/
+	curbuf->b_op_start = oap->start;
+	curbuf->b_op_end = oap->end;
+
+	if (change_cnt > p_report)
+	{
+	    if (change_cnt == 1)
+		MSG(_("1 line changed"));
+	    else
+		smsg((char_u *)_("%ld lines changed"), change_cnt);
+	}
+    }
+}
+
+/*
+ * Add or subtract 'Prenum1' from a number in a line
+ * op_type is OP_ADD or OP_SUBTRACT
  *
- * return FAIL for failure, OK otherwise
+ * Returns TRUE if some character was changed.
  */
-    int
-do_addsub(command, Prenum1, g_cmd)
-    int		command;
+    static int
+do_addsub(op_type, pos, length, Prenum1)
+    int		op_type;
+    pos_T	*pos;
+    int		length;
     linenr_T	Prenum1;
-    int		g_cmd;		    /* was g<c-a>/g<c-x> */
 {
     int		col;
     char_u	*buf1;
@@ -5357,11 +5474,9 @@ do_addsub(command, Prenum1, g_cmd)
     int		pre;		/* 'X'/'x': hex; '0': octal; 'B'/'b': bin */
     static int	hexupper = FALSE;	/* 0xABC */
     unsigned long n;
-    unsigned long offset = 0;		/* line offset for Ctrl_V mode */
     long_u	oldn;
     char_u	*ptr;
     int		c;
-    int		length = 0;		/* character length of the number */
     int		todel;
     int		dohex;
     int		dooct;
@@ -5372,66 +5487,27 @@ do_addsub(command, Prenum1, g_cmd)
     int		negative = FALSE;
     int		was_positive = TRUE;
     int		visual = VIsual_active;
-    int		i;
-    int		lnum = curwin->w_cursor.lnum;
-    int		lnume = curwin->w_cursor.lnum;
-    int		startcol = 0;
     int		did_change = FALSE;
     pos_T	t = curwin->w_cursor;
     int		maxlen = 0;
-    int		pos = 0;
-    int		bit = 0;
-    int		bits = sizeof(unsigned long) * 8;
 
     dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL);	/* "heX" */
     dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL);	/* "Octal" */
     dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL);	/* "Bin" */
     doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL);	/* "alPha" */
 
+    curwin->w_cursor = *pos;
+    ptr = ml_get(pos->lnum);
+    col = pos->col;
+
+    if (*ptr == NUL)
+	goto theend;
+
     /*
      * First check if we are on a hexadecimal number, after the "0x".
      */
-    col = curwin->w_cursor.col;
-    if (VIsual_active)
-    {
-	if (lt(curwin->w_cursor, VIsual))
-	{
-	    curwin->w_cursor = VIsual;
-	    VIsual = t;
-	}
-
-	ptr = ml_get(VIsual.lnum);
-	if (VIsual_mode == 'V')
-	{
-	    VIsual.col = 0;
-	    curwin->w_cursor.col = (colnr_T)STRLEN(ptr);
-	}
-	else if (VIsual_mode == Ctrl_V && VIsual.col > curwin->w_cursor.col)
-	{
-	    t = VIsual;
-	    VIsual.col = curwin->w_cursor.col;
-	    curwin->w_cursor.col = t.col;
-	}
-
-	/* store visual area for 'gv' */
-	curbuf->b_visual.vi_start = VIsual;
-	curbuf->b_visual.vi_end = curwin->w_cursor;
-	curbuf->b_visual.vi_mode = VIsual_mode;
-	curbuf->b_visual.vi_curswant = curwin->w_curswant;
-
-	if (VIsual_mode != 'v')
-	    startcol = VIsual.col < curwin->w_cursor.col ? VIsual.col
-						       : curwin->w_cursor.col;
-	else
-	    startcol = VIsual.col;
-	col = startcol;
-	lnum = VIsual.lnum;
-	lnume = curwin->w_cursor.lnum;
-    }
-    else
+    if (!VIsual_active)
     {
-	ptr = ml_get_curline();
-
 	if (dobin)
 	    while (col > 0 && vim_isbdigit(ptr[col]))
 		--col;
@@ -5451,7 +5527,7 @@ do_addsub(command, Prenum1, g_cmd)
 
 	    /* In case of binary/hexadecimal pattern overlap match, rescan */
 
-	    col = curwin->w_cursor.col;
+	    col = pos->col;
 
 	    while (col > 0 && vim_isdigit(ptr[col]))
 		col--;
@@ -5478,7 +5554,7 @@ do_addsub(command, Prenum1, g_cmd)
 	    /*
 	     * Search forward and then backward to find the start of number.
 	     */
-	    col = curwin->w_cursor.col;
+	    col = pos->col;
 
 	    while (ptr[col] != NUL
 		    && !vim_isdigit(ptr[col])
@@ -5492,303 +5568,248 @@ do_addsub(command, Prenum1, g_cmd)
 	}
     }
 
-    for (i = lnum; i <= lnume; i++)
+    if (visual)
     {
-	colnr_T stop = 0;
-
-	t = curwin->w_cursor;
-	curwin->w_cursor.lnum = i;
-	ptr = ml_get_curline();
-	if ((int)STRLEN(ptr) <= col)
-	    /* try again on next line */
-	    continue;
-	if (visual)
+	while (ptr[col] != NUL && length > 0
+		&& !vim_isdigit(ptr[col])
+		&& !(doalp && ASCII_ISALPHA(ptr[col])))
 	{
-	    if (VIsual_mode == 'v'
-		    && i == lnume)
-		stop = curwin->w_cursor.col;
-	    else if (VIsual_mode == Ctrl_V
-		    && curbuf->b_visual.vi_curswant != MAXCOL)
-		stop = curwin->w_cursor.col;
+	    ++col;
+	    --length;
+	}
 
-	    while (ptr[col] != NUL
-		    && !vim_isdigit(ptr[col])
-		    && !(doalp && ASCII_ISALPHA(ptr[col])))
-	    {
-		if (col > 0  && col == stop)
-		    break;
-		++col;
-	    }
+	if (length == 0)
+	    goto theend;
 
-	    if (col > startcol && ptr[col - 1] == '-')
-	    {
-		negative = TRUE;
-		was_positive = FALSE;
-	    }
-	}
-	/*
-	 * If a number was found, and saving for undo works, replace the number.
-	 */
-	firstdigit = ptr[col];
-	if ((!VIM_ISDIGIT(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit)))
-		|| u_save_cursor() != OK)
+	if (col > pos->col && ptr[col - 1] == '-')
 	{
-	    if (lnum < lnume)
-	    {
-		if (visual && VIsual_mode != Ctrl_V)
-		    col = 0;
-		else
-		    col = startcol;
-		/* Try again on next line */
-		continue;
-	    }
-	    beep_flush();
-	    return FAIL;
+	    negative = TRUE;
+	    was_positive = FALSE;
 	}
+    }
+
+    /*
+     * If a number was found, and saving for undo works, replace the number.
+     */
+    firstdigit = ptr[col];
+    if (!VIM_ISDIGIT(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit)))
+    {
+	beep_flush();
+	goto theend;
+    }
 
-	if (doalp && ASCII_ISALPHA(firstdigit))
+    if (doalp && ASCII_ISALPHA(firstdigit))
+    {
+	/* decrement or increment alphabetic character */
+	if (op_type == OP_SUBTRACT)
 	{
-	    /* decrement or increment alphabetic character */
-	    if (command == Ctrl_X)
+	    if (CharOrd(firstdigit) < Prenum1)
 	    {
-		if (CharOrd(firstdigit) < Prenum1)
-		{
-		    if (isupper(firstdigit))
-			firstdigit = 'A';
-		    else
-			firstdigit = 'a';
-		}
+		if (isupper(firstdigit))
+		    firstdigit = 'A';
 		else
-#ifdef EBCDIC
-		    firstdigit = EBCDIC_CHAR_ADD(firstdigit, -Prenum1);
-#else
-		    firstdigit -= Prenum1;
-#endif
+		    firstdigit = 'a';
 	    }
 	    else
-	    {
-		if (26 - CharOrd(firstdigit) - 1 < Prenum1)
-		{
-		    if (isupper(firstdigit))
-			firstdigit = 'Z';
-		    else
-			firstdigit = 'z';
-		}
-		else
 #ifdef EBCDIC
-		    firstdigit = EBCDIC_CHAR_ADD(firstdigit, Prenum1);
+		firstdigit = EBCDIC_CHAR_ADD(firstdigit, -Prenum1);
 #else
-		    firstdigit += Prenum1;
+		firstdigit -= Prenum1;
 #endif
-	    }
-	    curwin->w_cursor.col = col;
-	    did_change = TRUE;
-	    (void)del_char(FALSE);
-	    ins_char(firstdigit);
-	    curwin->w_cursor.col = col;
 	}
 	else
 	{
-	    if (col > 0 && ptr[col - 1] == '-' && !visual)
-	    {
-		/* negative number */
-		--col;
-		negative = TRUE;
-	    }
-	    /* get the number value (unsigned) */
-	    if (visual && VIsual_mode != 'V')
-	    {
-		if (VIsual_mode == 'v')
-		{
-		    if (i == lnum)
-			maxlen = (lnum == lnume
-					    ? curwin->w_cursor.col - col + 1
-					    : (int)STRLEN(ptr) - col);
-		    else
-			maxlen = (i == lnume ? curwin->w_cursor.col - col  + 1
-					     : (int)STRLEN(ptr) - col);
-		}
-		else if (VIsual_mode == Ctrl_V)
-		    maxlen = (curbuf->b_visual.vi_curswant == MAXCOL
-					?  (int)STRLEN(ptr) - col
-					: curwin->w_cursor.col - col + 1);
-	    }
-
-	    vim_str2nr(ptr + col, &pre, &length,
-		    0 + (dobin ? STR2NR_BIN : 0)
-		      + (dooct ? STR2NR_OCT : 0)
-		      + (dohex ? STR2NR_HEX : 0),
-		    NULL, &n, maxlen);
-
-	    /* ignore leading '-' for hex and octal and bin numbers */
-	    if (pre && negative)
+	    if (26 - CharOrd(firstdigit) - 1 < Prenum1)
 	    {
-		++col;
-		--length;
-		negative = FALSE;
+		if (isupper(firstdigit))
+		    firstdigit = 'Z';
+		else
+		    firstdigit = 'z';
 	    }
+	    else
+#ifdef EBCDIC
+		firstdigit = EBCDIC_CHAR_ADD(firstdigit, Prenum1);
+#else
+		firstdigit += Prenum1;
+#endif
+	}
+	curwin->w_cursor.col = col;
+	did_change = TRUE;
+	(void)del_char(FALSE);
+	ins_char(firstdigit);
+	curwin->w_cursor.col = col;
+    }
+    else
+    {
+	if (col > 0 && ptr[col - 1] == '-' && !visual)
+	{
+	    /* negative number */
+	    --col;
+	    negative = TRUE;
+	}
+	/* get the number value (unsigned) */
+	if (visual && VIsual_mode != 'V')
+	    maxlen = (curbuf->b_visual.vi_curswant == MAXCOL
+		    ? (int)STRLEN(ptr) - col
+		    : length);
 
-	    /* add or subtract */
-	    subtract = FALSE;
-	    if (command == Ctrl_X)
-		subtract ^= TRUE;
-	    if (negative)
-		subtract ^= TRUE;
+	vim_str2nr(ptr + col, &pre, &length,
+		0 + (dobin ? STR2NR_BIN : 0)
+		    + (dooct ? STR2NR_OCT : 0)
+		    + (dohex ? STR2NR_HEX : 0),
+		NULL, &n, maxlen);
 
-	    oldn = n;
+	/* ignore leading '-' for hex and octal and bin numbers */
+	if (pre && negative)
+	{
+	    ++col;
+	    --length;
+	    negative = FALSE;
+	}
+	/* add or subtract */
+	subtract = FALSE;
+	if (op_type == OP_SUBTRACT)
+	    subtract ^= TRUE;
+	if (negative)
+	    subtract ^= TRUE;
+
+	oldn = n;
+	if (subtract)
+	    n -= (unsigned long)Prenum1;
+	else
+	    n += (unsigned long)Prenum1;
+	/* handle wraparound for decimal numbers */
+	if (!pre)
+	{
 	    if (subtract)
-		n -= (unsigned long)Prenum1;
-	    else
-		n += (unsigned long)Prenum1;
-
-	    /* handle wraparound for decimal numbers */
-	    if (!pre)
 	    {
-		if (subtract)
+		if (n > oldn)
 		{
-		    if (n > oldn)
-		    {
-			n = 1 + (n ^ (unsigned long)-1);
-			negative ^= TRUE;
-		    }
+		    n = 1 + (n ^ (unsigned long)-1);
+		    negative ^= TRUE;
 		}
-		else
-		{
-		    /* add */
-		    if (n < oldn)
-		    {
-			n = (n ^ (unsigned long)-1);
-			negative ^= TRUE;
-		    }
-		}
-		if (n == 0)
-		    negative = FALSE;
-	    }
-
-	    if (visual && !was_positive && !negative && col > 0)
-	    {
-		/* need to remove the '-' */
-		col--;
-		length++;
 	    }
-
-
-	    /*
-	     * Delete the old number.
-	     */
-	    curwin->w_cursor.col = col;
-	    did_change = TRUE;
-	    todel = length;
-	    c = gchar_cursor();
-
-	    /*
-	     * Don't include the '-' in the length, only the length of the
-	     * part after it is kept the same.
-	     */
-	    if (c == '-')
-		--length;
-	    while (todel-- > 0)
+	    else
 	    {
-		if (c < 0x100 && isalpha(c))
+		/* add */
+		if (n < oldn)
 		{
-		    if (isupper(c))
-			hexupper = TRUE;
-		    else
-			hexupper = FALSE;
+		    n = (n ^ (unsigned long)-1);
+		    negative ^= TRUE;
 		}
-		/* del_char() will mark line needing displaying */
-		(void)del_char(FALSE);
-		c = gchar_cursor();
 	    }
+	    if (n == 0)
+		negative = FALSE;
+	}
 
-	    /*
-	     * Prepare the leading characters in buf1[].
-	     * When there are many leading zeros it could be very long.
-	     * Allocate a bit too much.
-	     */
-	    buf1 = alloc((unsigned)length + NUMBUFLEN);
-	    if (buf1 == NULL)
-		return FAIL;
-	    ptr = buf1;
-	    if (negative && (!visual || (visual && was_positive)))
-	    {
-		*ptr++ = '-';
-	    }
-	    if (pre)
-	    {
-		*ptr++ = '0';
-		--length;
-	    }
-	    if (pre == 'b' || pre == 'B' || 
-		pre == 'x' || pre == 'X')
-	    {
-		*ptr++ = pre;
-		--length;
-	    }
+	if (visual && !was_positive && !negative && col > 0)
+	{
+	    /* need to remove the '-' */
+	    col--;
+	    length++;
+	}
 
-	    /*
-	     * Put the number characters in buf2[].
-	     */
-	    if (pre == 'b' || pre == 'B')
+	/*
+	 * Delete the old number.
+	 */
+	curwin->w_cursor.col = col;
+	did_change = TRUE;
+	todel = length;
+	c = gchar_cursor();
+	/*
+	 * Don't include the '-' in the length, only the length of the
+	 * part after it is kept the same.
+	 */
+	if (c == '-')
+	    --length;
+	while (todel-- > 0)
+	{
+	    if (c < 0x100 && isalpha(c))
 	    {
-		/* leading zeros */
-		for (bit = bits; bit > 0; bit--)
-		    if ((n >> (bit - 1)) & 0x1) break;
-
-		for (pos = 0; bit > 0; bit--)
-		    buf2[pos++] = ((n >> (bit - 1)) & 0x1) ? '1' : '0';
-
-		buf2[pos] = '\0';
+		if (isupper(c))
+		    hexupper = TRUE;
+		else
+		    hexupper = FALSE;
 	    }
-	    else if (pre == 0)
-		sprintf((char *)buf2, "%lu", n);
-	    else if (pre == '0')
-		sprintf((char *)buf2, "%lo", n);
-	    else if (pre && hexupper)
-		sprintf((char *)buf2, "%lX", n);
-	    else
-		sprintf((char *)buf2, "%lx", n);
-	    length -= (int)STRLEN(buf2);
+	    /* del_char() will mark line needing displaying */
+	    (void)del_char(FALSE);
+	    c = gchar_cursor();
+	}
 
-	    /*
-	     * Adjust number of zeros to the new number of digits, so the
-	     * total length of the number remains the same.
-	     * Don't do this when
-	     * the result may look like an octal number.
-	     */
-	    if (firstdigit == '0' && !(dooct && pre == 0))
-		while (length-- > 0)
-		    *ptr++ = '0';
-	    *ptr = NUL;
-	    STRCAT(buf1, buf2);
-	    ins_str(buf1);		/* insert the new number */
-	    vim_free(buf1);
-	    if (lnum < lnume)
-		curwin->w_cursor.col = t.col;
-	    else if (did_change && curwin->w_cursor.col)
-		--curwin->w_cursor.col;
+	/*
+	 * Prepare the leading characters in buf1[].
+	 * When there are many leading zeros it could be very long.
+	 * Allocate a bit too much.
+	 */
+	buf1 = alloc((unsigned)length + NUMBUFLEN);
+	if (buf1 == NULL)
+	    goto theend;
+	ptr = buf1;
+	if (negative && (!visual || (visual && was_positive)))
+	{
+	    *ptr++ = '-';
+	}
+	if (pre)
+	{
+	    *ptr++ = '0';
+	    --length;
+	}
+	if (pre == 'b' || pre == 'B' ||
+	    pre == 'x' || pre == 'X')
+	{
+	    *ptr++ = pre;
+	    --length;
 	}
 
-	if (g_cmd)
+	/*
+	 * Put the number characters in buf2[].
+	 */
+	if (pre == 'b' || pre == 'B')
 	{
-	    offset = (unsigned long)Prenum1;
-	    g_cmd = 0;
+	    int i;
+	    int bit = 0;
+	    int bits = sizeof(unsigned long) * 8;
+
+	    /* leading zeros */
+	    for (bit = bits; bit > 0; bit--)
+		if ((n >> (bit - 1)) & 0x1) break;
+
+	    for (i = 0; bit > 0; bit--)
+		buf2[i++] = ((n >> (bit - 1)) & 0x1) ? '1' : '0';
+
+	    buf2[i] = '\0';
 	}
-	/* reset */
-	subtract = FALSE;
-	negative = FALSE;
-	was_positive = TRUE;
-	if (visual && VIsual_mode == Ctrl_V)
-	    col = startcol;
+	else if (pre == 0)
+	    sprintf((char *)buf2, "%lu", n);
+	else if (pre == '0')
+	    sprintf((char *)buf2, "%lo", n);
+	else if (pre && hexupper)
+	    sprintf((char *)buf2, "%lX", n);
 	else
-	    col = 0;
-	Prenum1 += offset;
-	curwin->w_set_curswant = TRUE;
+	    sprintf((char *)buf2, "%lx", n);
+	length -= (int)STRLEN(buf2);
+
+	/*
+	 * Adjust number of zeros to the new number of digits, so the
+	 * total length of the number remains the same.
+	 * Don't do this when
+	 * the result may look like an octal number.
+	 */
+	if (firstdigit == '0' && !(dooct && pre == 0))
+	    while (length-- > 0)
+		*ptr++ = '0';
+	*ptr = NUL;
+	STRCAT(buf1, buf2);
+	ins_str(buf1);		/* insert the new number */
+	vim_free(buf1);
+	if (did_change && curwin->w_cursor.col)
+	    --curwin->w_cursor.col;
     }
+
+theend:
     if (visual)
-	/* cursor at the top of the selection */
-	curwin->w_cursor = VIsual;
-    return OK;
+	curwin->w_cursor = t;
+    return did_change;
 }
 
 #ifdef FEAT_VIMINFO
diff --git a/src/proto/ops.pro b/src/proto/ops.pro
index aaffa02..27e0f11 100644
--- a/src/proto/ops.pro
+++ b/src/proto/ops.pro
@@ -43,7 +43,7 @@ void op_formatexpr __ARGS((oparg_T *oap));
 int fex_format __ARGS((linenr_T lnum, long count, int c));
 void format_lines __ARGS((linenr_T line_count, int avoid_fex));
 int paragraph_start __ARGS((linenr_T lnum));
-int do_addsub __ARGS((int command, linenr_T Prenum1, int g_cmd));
+void op_addsub __ARGS((oparg_T *oap, linenr_T Prenum1, int g_cmd));
 int read_viminfo_register __ARGS((vir_T *virp, int force));
 void write_viminfo_registers __ARGS((FILE *fp));
 void x11_export_final_selection __ARGS((void));
diff --git a/src/testdir/test_increment.vim b/src/testdir/test_increment.vim
index 277f71d..5f1b037 100644
--- a/src/testdir/test_increment.vim
+++ b/src/testdir/test_increment.vim
@@ -133,7 +133,7 @@ func Test_visual_increment_04()
   exec "norm! vf-\<C-A>"
   call assert_equal(["foobar-10"], getline(1, '$'))
   " NOTE: I think this is correct behavior...
-  "call assert_equal([0, 1, 1, 0], getpos('.'))
+  call assert_equal([0, 1, 1, 0], getpos('.'))
 endfunc
 
 " 5) g<Ctrl-A> on letter
@@ -575,4 +575,109 @@ func Test_visual_increment_27()
   endif
 endfunc
 
+
+" Tab code and linewise-visual inc/dec
+func Test_visual_increment_28()
+  call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+  exec "norm! Vj\<C-A>"
+  call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$'))
+  call assert_equal([0, 1, 1, 0], getpos('.'))
+
+  call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+  exec "norm! ggVj\<C-X>"
+  call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$'))
+  call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" Tab code and linewise-visual inc/dec with 'nrformats'+=alpha
+func Test_visual_increment_29()
+  set nrformats+=alpha
+  call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+  exec "norm! Vj\<C-A>"
+  call assert_equal(["y\<TAB>10", "\<TAB>0"], getline(1, '$'))
+  call assert_equal([0, 1, 1, 0], getpos('.'))
+
+  call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+  exec "norm! ggVj\<C-X>"
+  call assert_equal(["w\<TAB>10", "\<TAB>-2"], getline(1, '$'))
+  call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" Tab code and character-visual inc/dec
+func Test_visual_increment_30()
+  call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+  exec "norm! f1vjf1\<C-A>"
+  call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$'))
+  call assert_equal([0, 1, 3, 0], getpos('.'))
+
+  call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+  exec "norm! ggf1vjf1\<C-X>"
+  call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$'))
+  call assert_equal([0, 1, 3, 0], getpos('.'))
+endfunc
+
+" Tab code and blockwise-visual inc/dec
+func Test_visual_increment_31()
+  call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+  exec "norm! f1\<C-V>jl\<C-A>"
+  call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$'))
+  call assert_equal([0, 1, 3, 0], getpos('.'))
+
+  call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+  exec "norm! ggf1\<C-V>jl\<C-X>"
+  call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$'))
+  call assert_equal([0, 1, 3, 0], getpos('.'))
+endfunc
+
+" Tab code and blockwise-visual decrement with 'linebreak' and 'showbreak'
+func Test_visual_increment_32()
+  28vnew dummy_31
+  set linebreak showbreak=+
+  call setline(1, ["x\<TAB>\<TAB>\<TAB>10", "\<TAB>\<TAB>\<TAB>\<TAB>-1"])
+  exec "norm! ggf0\<C-V>jg_\<C-X>"
+  call assert_equal(["x\<TAB>\<TAB>\<TAB>1-1", "\<TAB>\<TAB>\<TAB>\<TAB>-2"], getline(1, '$'))
+  call assert_equal([0, 1, 6, 0], getpos('.'))
+  bwipe!
+endfunc
+
+" Tab code and blockwise-visual increment with $
+func Test_visual_increment_33()
+  call setline(1, ["\<TAB>123", "456"])
+  exec "norm! gg0\<C-V>j$\<C-A>"
+  call assert_equal(["\<TAB>124", "457"], getline(1, '$'))
+  call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" Tab code and blockwise-visual increment and redo
+func Test_visual_increment_34()
+  call setline(1, ["\<TAB>123", "     456789"])
+  exec "norm! gg0\<C-V>j\<C-A>"
+  call assert_equal(["\<TAB>123", "     457789"], getline(1, '$'))
+  call assert_equal([0, 1, 1, 0], getpos('.'))
+
+  exec "norm! .."
+  call assert_equal(["\<TAB>123", "     459789"], getline(1, '$'))
+  call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" Tab code, spaces and character-visual increment and redo
+func Test_visual_increment_35()
+  call setline(1, ["\<TAB>123", "        123", "\<TAB>123", "\<TAB>123"])
+  exec "norm! ggvjf3\<C-A>..."
+  call assert_equal(["\<TAB>127", "        127", "\<TAB>123", "\<TAB>123"], getline(1, '$'))
+  call assert_equal([0, 1, 2, 0], getpos('.'))
+endfunc
+
+" Tab code, spaces and blockwise-visual increment and redo
+func Test_visual_increment_36()
+  call setline(1, ["           123", "\<TAB>456789"])
+  exec "norm! G0\<C-V>kl\<C-A>"
+  call assert_equal(["           123", "\<TAB>556789"], getline(1, '$'))
+  call assert_equal([0, 1, 1, 0], getpos('.'))
+
+  exec "norm! ..."
+  call assert_equal(["           123", "\<TAB>856789"], getline(1, '$'))
+  call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
 " vim: tabstop=2 shiftwidth=2 expandtab
diff --git a/src/vim.h b/src/vim.h
index fd0b0b0..d70db95 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1457,6 +1457,10 @@ typedef UINT32_TYPEDEF UINT32_T;
 #define OP_FOLDDELREC	25	/* "zD" delete folds recursively */
 #define OP_FORMAT2	26	/* "gw" format operator, keeps cursor pos */
 #define OP_FUNCTION	27	/* "g@" call 'operatorfunc' */
+#define OP_ADD		28	/* "<C-A>" Add to the number or alphabetic
+				   character */
+#define OP_SUBTRACT	29	/* "<C-C>" Subtract to the number or alphabetic
+				   character */
 
 /*
  * Motion types, used for operators and for yank/delete registers.

Raspunde prin e-mail lui