diff src/ops.c @ 6868:9798a98a1583 v7.4.754

patch 7.4.754 Problem: Using CTRL-A in Visual mode does not work well. (Gary Johnson) Solution: Make it increment all numbers in the Visual area. (Christian Brabandt)
author Bram Moolenaar <bram@vim.org>
date Thu, 25 Jun 2015 13:57:36 +0200
parents ac13f3533571
children f9876721bedc
line wrap: on
line diff
--- a/src/ops.c
+++ b/src/ops.c
@@ -5375,9 +5375,10 @@ reverse_line(s)
  * return FAIL for failure, OK otherwise
  */
     int
-do_addsub(command, Prenum1)
+do_addsub(command, Prenum1, g_cmd)
     int		command;
     linenr_T	Prenum1;
+    int		g_cmd;		    /* was g<c-a>/g<c-x> */
 {
     int		col;
     char_u	*buf1;
@@ -5385,6 +5386,7 @@ do_addsub(command, Prenum1)
     int		hex;		/* 'X' or 'x': hex; '0': octal */
     static int	hexupper = FALSE;	/* 0xABC */
     unsigned long n;
+    long	offset = 0;		/* line offset for Ctrl_V mode */
     long_u	oldn;
     char_u	*ptr;
     int		c;
@@ -5394,247 +5396,302 @@ do_addsub(command, Prenum1)
     int		dooct;
     int		doalp;
     int		firstdigit;
-    int		negative;
     int		subtract;
+    int		negative = FALSE;
+    int		visual = VIsual_active;
+    int		i;
+    int		lnum = curwin->w_cursor.lnum;
+    int		lnume = curwin->w_cursor.lnum;
 
     dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL);	/* "heX" */
     dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL);	/* "Octal" */
     doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL);	/* "alPha" */
 
-    ptr = ml_get_curline();
-    RLADDSUBFIX(ptr);
-
     /*
      * First check if we are on a hexadecimal number, after the "0x".
      */
     col = curwin->w_cursor.col;
-    if (dohex)
-	while (col > 0 && vim_isxdigit(ptr[col]))
-	    --col;
-    if (       dohex
-	    && col > 0
-	    && (ptr[col] == 'X'
-		|| ptr[col] == 'x')
-	    && ptr[col - 1] == '0'
-	    && vim_isxdigit(ptr[col + 1]))
-    {
-	/*
-	 * Found hexadecimal number, move to its start.
-	 */
-	--col;
+    if (VIsual_active)
+    {
+	if (lt(curwin->w_cursor, VIsual))
+	{
+	    pos_T t;
+	    t = curwin->w_cursor;
+	    curwin->w_cursor = VIsual;
+	    VIsual = t;
+	}
+	if (VIsual_mode == 'V')
+	    VIsual.col = 0;
+
+	ptr = ml_get(VIsual.lnum);
+	RLADDSUBFIX(ptr);
+
+	/* 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;
+
+	col = VIsual.col;
+	lnum = VIsual.lnum;
+	lnume = curwin->w_cursor.lnum;
+	if (ptr[col] == '-')
+	{
+	    negative = TRUE;
+	    col++;
+	}
     }
     else
     {
-	/*
-	 * Search forward and then backward to find the start of number.
-	 */
-	col = curwin->w_cursor.col;
-
-	while (ptr[col] != NUL
-		&& !vim_isdigit(ptr[col])
-		&& !(doalp && ASCII_ISALPHA(ptr[col])))
-	    ++col;
-
-	while (col > 0
-		&& vim_isdigit(ptr[col - 1])
-		&& !(doalp && ASCII_ISALPHA(ptr[col])))
+	ptr = ml_get_curline();
+	RLADDSUBFIX(ptr);
+
+	if (dohex)
+	    while (col > 0 && vim_isxdigit(ptr[col]))
+		--col;
+	if (       dohex
+		&& col > 0
+		&& (ptr[col] == 'X'
+		    || ptr[col] == 'x')
+		&& ptr[col - 1] == '0'
+		&& vim_isxdigit(ptr[col + 1]))
+	{
+	    /* Found hexadecimal number, move to its start. */
 	    --col;
-    }
-
-    /*
-     * If a number was found, and saving for undo works, replace the number.
-     */
-    firstdigit = ptr[col];
-    RLADDSUBFIX(ptr);
-    if ((!VIM_ISDIGIT(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit)))
-	    || u_save_cursor() != OK)
-    {
-	beep_flush();
-	return FAIL;
-    }
-
-    /* get ptr again, because u_save() may have changed it */
-    ptr = ml_get_curline();
-    RLADDSUBFIX(ptr);
-
-    if (doalp && ASCII_ISALPHA(firstdigit))
-    {
-	/* decrement or increment alphabetic character */
-	if (command == Ctrl_X)
+	}
+	else
 	{
-	    if (CharOrd(firstdigit) < Prenum1)
+	    /*
+	     * Search forward and then backward to find the start of number.
+	     */
+	    col = curwin->w_cursor.col;
+
+	    while (ptr[col] != NUL
+		    && !vim_isdigit(ptr[col])
+		    && !(doalp && ASCII_ISALPHA(ptr[col])))
+		++col;
+
+	    while (col > 0
+		    && vim_isdigit(ptr[col - 1])
+		    && !(doalp && ASCII_ISALPHA(ptr[col])))
+		--col;
+	}
+    }
+
+    for (i = lnum; i <= lnume; i++)
+    {
+	curwin->w_cursor.lnum = i;
+	ptr = ml_get_curline();
+	RLADDSUBFIX(ptr);
+	if ((int)STRLEN(ptr) <= col)
+	    col = 0;
+	/*
+	 * If a number was found, and saving for undo works, replace the number.
+	 */
+	firstdigit = ptr[col];
+	RLADDSUBFIX(ptr);
+	if ((!VIM_ISDIGIT(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit)))
+		|| u_save_cursor() != OK)
+	{
+	    if (lnum < lnume)
+		/* Try again on next line */
+		continue;
+	    beep_flush();
+	    return FAIL;
+	}
+
+	ptr = ml_get_curline();
+	RLADDSUBFIX(ptr);
+
+	if (doalp && ASCII_ISALPHA(firstdigit))
+	{
+	    /* decrement or increment alphabetic character */
+	    if (command == Ctrl_X)
 	    {
-		if (isupper(firstdigit))
-		    firstdigit = 'A';
+		if (CharOrd(firstdigit) < Prenum1)
+		{
+		    if (isupper(firstdigit))
+			firstdigit = 'A';
+		    else
+			firstdigit = 'a';
+		}
 		else
-		    firstdigit = 'a';
+#ifdef EBCDIC
+		    firstdigit = EBCDIC_CHAR_ADD(firstdigit, -Prenum1);
+#else
+		    firstdigit -= Prenum1;
+#endif
 	    }
 	    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;
-#endif
+		    firstdigit += Prenum1;
+#endif
+	    }
+	    curwin->w_cursor.col = col;
+	    (void)del_char(FALSE);
+	    ins_char(firstdigit);
 	}
 	else
 	{
-	    if (26 - CharOrd(firstdigit) - 1 < Prenum1)
+	    if (col > 0 && ptr[col - 1] == '-' && !visual)
+	    {
+		/* negative number */
+		--col;
+		negative = TRUE;
+	    }
+
+	    /* get the number value (unsigned) */
+	    vim_str2nr(ptr + col, &hex, &length, dooct, dohex, NULL, &n);
+
+	    /* ignore leading '-' for hex and octal numbers */
+	    if (hex && negative)
 	    {
-		if (isupper(firstdigit))
-		    firstdigit = 'Z';
-		else
-		    firstdigit = 'z';
+		++col;
+		--length;
+		negative = FALSE;
 	    }
+
+	    /* add or subtract */
+	    subtract = FALSE;
+	    if (command == Ctrl_X)
+		subtract ^= TRUE;
+	    if (negative)
+		subtract ^= TRUE;
+
+	    oldn = n;
+	    if (subtract)
+		n -= (unsigned long)Prenum1;
 	    else
-#ifdef EBCDIC
-		firstdigit = EBCDIC_CHAR_ADD(firstdigit, Prenum1);
-#else
-		firstdigit += Prenum1;
-#endif
-	}
-	curwin->w_cursor.col = col;
-	(void)del_char(FALSE);
-	ins_char(firstdigit);
-    }
-    else
-    {
-	negative = FALSE;
-	if (col > 0 && ptr[col - 1] == '-')	    /* negative number */
-	{
-	    --col;
-	    negative = TRUE;
-	}
-
-	/* get the number value (unsigned) */
-	vim_str2nr(ptr + col, &hex, &length, dooct, dohex, NULL, &n);
-
-	/* ignore leading '-' for hex and octal numbers */
-	if (hex && negative)
-	{
-	    ++col;
-	    --length;
-	    negative = FALSE;
-	}
-
-	/* add or subtract */
-	subtract = FALSE;
-	if (command == Ctrl_X)
-	    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 (!hex)
-	{
-	    if (subtract)
+		n += (unsigned long)Prenum1;
+
+	    /* handle wraparound for decimal numbers */
+	    if (!hex)
+	    {
+		if (subtract)
+		{
+		    if (n > oldn)
+		    {
+			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;
+	    }
+
+	    /*
+	     * Delete the old number.
+	     */
+	    curwin->w_cursor.col = col;
+	    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 (n > oldn)
+		if (c < 0x100 && isalpha(c))
 		{
-		    n = 1 + (n ^ (unsigned long)-1);
-		    negative ^= TRUE;
+		    if (isupper(c))
+			hexupper = TRUE;
+		    else
+			hexupper = FALSE;
 		}
+		/* del_char() will mark line needing displaying */
+		(void)del_char(FALSE);
+		c = gchar_cursor();
 	    }
-	    else /* add */
+
+	    /*
+	     * 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;
+	    /* do not add leading '-' for visual mode */
+	    if (negative && !visual)
+	    {
+		*ptr++ = '-';
+	    }
+	    if (hex)
+	    {
+		*ptr++ = '0';
+		--length;
+	    }
+	    if (hex == 'x' || hex == 'X')
 	    {
-		if (n < oldn)
-		{
-		    n = (n ^ (unsigned long)-1);
-		    negative ^= TRUE;
-		}
+		*ptr++ = hex;
+		--length;
 	    }
-	    if (n == 0)
-		negative = FALSE;
+
+	    /*
+	     * Put the number characters in buf2[].
+	     */
+	    if (hex == 0)
+		sprintf((char *)buf2, "%lu", n + offset);
+	    else if (hex == '0')
+		sprintf((char *)buf2, "%lo", n + offset);
+	    else if (hex && hexupper)
+		sprintf((char *)buf2, "%lX", n + offset);
+	    else
+		sprintf((char *)buf2, "%lx", n + offset);
+	    length -= (int)STRLEN(buf2);
+
+	    if (g_cmd)
+	    {
+		if (subtract)
+		    offset -= (unsigned long)Prenum1;
+		else
+		    offset += (unsigned long)Prenum1;
+	    }
+
+	    /*
+	     * 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 && hex == 0))
+		while (length-- > 0)
+		    *ptr++ = '0';
+	    *ptr = NUL;
+	    STRCAT(buf1, buf2);
+	    ins_str(buf1);		/* insert the new number */
+	    vim_free(buf1);
 	}
-
-	/*
-	 * Delete the old number.
-	 */
-	curwin->w_cursor.col = col;
-	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))
-	    {
-		if (isupper(c))
-		    hexupper = TRUE;
-		else
-		    hexupper = FALSE;
-	    }
-	    /* del_char() will mark line needing displaying */
-	    (void)del_char(FALSE);
-	    c = gchar_cursor();
-	}
-
-	/*
-	 * 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)
-	{
-	    *ptr++ = '-';
-	}
-	if (hex)
-	{
-	    *ptr++ = '0';
-	    --length;
-	}
-	if (hex == 'x' || hex == 'X')
-	{
-	    *ptr++ = hex;
-	    --length;
-	}
-
-	/*
-	 * Put the number characters in buf2[].
-	 */
-	if (hex == 0)
-	    sprintf((char *)buf2, "%lu", n);
-	else if (hex == '0')
-	    sprintf((char *)buf2, "%lo", n);
-	else if (hex && hexupper)
-	    sprintf((char *)buf2, "%lX", n);
-	else
-	    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 && hex == 0))
-	    while (length-- > 0)
-		*ptr++ = '0';
-	*ptr = NUL;
-	STRCAT(buf1, buf2);
-	ins_str(buf1);		/* insert the new number */
-	vim_free(buf1);
-    }
-    --curwin->w_cursor.col;
-    curwin->w_set_curswant = TRUE;
+	--curwin->w_cursor.col;
+	curwin->w_set_curswant = TRUE;
 #ifdef FEAT_RIGHTLEFT
-    ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
-    RLADDSUBFIX(ptr);
-#endif
+	ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
+	RLADDSUBFIX(ptr);
+#endif
+    }
     return OK;
 }