KeyboardMacros are a useful tool for daily editing.
Sometimes, when generating lots of data using CopyAndPaste (KillingAndYanking),
you might have the need to increment numbers.

If you are careful, you can use NumbersInRegisters.  But some might find
the following defun interesting enough -- *more advanced* versions found below!

    (defun increment-number-at-point ()
      (interactive)
      (skip-chars-backward "0-9")
      (or (looking-at "[0-9]+")
          (error "No number at point"))
      (replace-match (number-to-string (1+ (string-to-number (match-string 0))))))

Suggested keybinding:

    (global-set-key (kbd "C-c +") 'increment-number-at-point)

----

Another version that allows C-u prefixes to set the increment (negative numbers also work), maintains field sizes, i.e. 0034 -> 0035 instead of 35, and will roll negative increments around: 0000 -> 9999:

<pre>
(defun my-increment-number-at-point (&optional increment)
  "Increment the number at point by INCREMENT."
  (interactive "*p")
  (let ((pos (point)))
    (save-match-data
      (skip-chars-backward "0-9")
      (if (looking-at "[0-9]+")
          (let ((field-width (- (match-end 0) (match-beginning 0)))
                (newval (+ (string-to-number (match-string 0) 10) increment)))
            (when (< newval 0)
              (setq newval (+ (expt 10 field-width) newval)))
            (replace-match (format (concat "%0" (int-to-string field-width) "d")
                                   newval)))
        (user-error "No number at point")))
    (goto-char pos)))
</pre>

And to decrement numbers (addition to the solution above):

<pre>
(defun my-decrement-number-at-point (&optional decrement)
  "Decrement the number at point by DECREMENT."
  (interactive "*p")
  (my-increment-number-at-point (- decrement)))
</pre>

And you always need hex:

<pre>
(defun my-increment-number-hexadecimal (&optional arg)
  "Increment the number forward from point by 'arg'."
  (interactive "p*")
  (save-excursion
    (save-match-data
      (let (inc-by field-width answer hex-format)
        (setq inc-by (if arg arg 1))
        (skip-chars-backward "0123456789abcdefABCDEF")
        (when (re-search-forward "[0-9a-fA-F]+" nil t)
          (setq field-width (- (match-end 0) (match-beginning 0)))
          (setq answer (+ (string-to-number (match-string 0) 16) inc-by))
          (when (< answer 0)
            (setq answer (+ (expt 16 field-width) answer)))
          (if (equal (match-string 0) (upcase (match-string 0)))
              (setq hex-format "X")
            (setq hex-format "x"))
          (replace-match (format (concat "%0" (int-to-string field-width)
                                         hex-format)
                                 answer)))))))
</pre>

And sometimes even binary:

<pre>
(defun my-format-bin (val width)
  "Convert a number to a binary string."
  (let (result)
    (while (> width 0)
      (if (equal (mod val 2) 1)
          (setq result (concat "1" result))
        (setq result (concat "0" result)))
      (setq val (/ val 2))
      (setq width (1- width)))
    result))

(defun my-increment-number-binary (&optional arg)
  "Increment the number forward from point by 'arg'."
  (interactive "p*")
  (save-excursion
    (save-match-data
      (let (inc-by field-width answer)
        (setq inc-by (if arg arg 1))
        (skip-chars-backward "01")
        (when (re-search-forward "[0-1]+" nil t)
          (setq field-width (- (match-end 0) (match-beginning 0)))
          (setq answer (+ (string-to-number (match-string 0) 2) inc-by))
          (when (< answer 0)
            (setq answer (+ (expt 2 field-width) answer)))
          (replace-match (my-format-bin answer field-width)))))))
</pre>

----
Another variation using vim-like keybindings:
<pre>
(defun my-change-number-at-point (change increment)
  (let ((number (number-at-point))
        (point (point)))
    (when number
      (progn
        (forward-word)
        (search-backward (number-to-string number))
        (replace-match (number-to-string (funcall change number increment)))
        (goto-char point)))))

(defun my-increment-number-at-point (&optional increment)
  "Increment number at point like vim's C-a"
  (interactive "p")
  (my-change-number-at-point '+ (or increment 1)))

(defun my-decrement-number-at-point (&optional increment)
  "Decrement number at point like vim's C-x"
  (interactive "p")
  (my-change-number-at-point '- (or increment 1)))

(global-set-key (kbd "C-c a") 'my-increment-number-at-point)
(global-set-key (kbd "C-c x") 'my-decrement-number-at-point)
</pre>

----

This yank-increment function acts like the regular yank function but increments the first integer
found in the yanked string before inserting it.  It also updates the string in the kill ring
so that successive yank-increments have increasing values.

It is good for adding printf debugging like this:
<pre>
  fprintf(stderr, "MyClass::myFunction() 1\n");
  functionThatMayHang();
  fprintf(stderr, "MyClass::myFunction() 2\n");
  otherSuspiciousFunction();
  fprintf(stderr, "MyClass::myFunction() 3\n");
</pre>

<pre>
(defun dlh-increment-string (string)
  (setq start (string-match "\\([0-9]+\\)" string))
  (setq end (match-end 0))
  (setq number (string-to-number (substring string start end)))
  (setq new-num-string (number-to-string (+ 1 number)))
  (concat
   (substring string 0 start)
   new-num-string
   (substring string end)))

(defun dlh-yank-increment ()
  "Yank text, incrementing the first integer found in it."
  (interactive "*")
  (setq new-text (dlh-increment-string (current-kill 0)))
  (insert-for-yank new-text)
  (kill-new new-text t))

(global-set-key (kbd "C-c C-y") 'dlh-yank-increment)
</pre>

----

A package that supports Vim-like incrementing and decrementing is [https://github.com/janpath/evil-numbers evil-numbers], it supports binary, octal, hex and decimal numbers.

----

There is also another simple package that provides 2 commands to
increase/decrease the number at point, on the current line or all numbers within a region (with support for rectangular regions):
[https://codeberg.org/ideasman42/emacs-shift-number shift-number].

----

See [[number-mark.el]], ReplaceRegexp, ReplaceCount, RenumberList and MacroMath for other ways to deal with automatic incrementation.

----

Calc's [[https://www.gnu.org/savannah-checkouts/gnu/emacs/manual/html_node/calc/Embedded-Mode-Overview.html embedded mode]] can also be used for arbitrary in-place operations on buffers.

----

Here is another version that uses the built-in 'thing-at-point' functions. It preserves leading zeroes, and the upper or lower case for hex numbers.

{{{
(defun increment-integer-at-point (&optional inc)
  "Increment the number at point by INC.
Negative INC decrement by the given amount.  Preserves leading zeros.
Hexadecimal numbers default to upper case.  Supports the number formats
recognised by `number-at-point'."
  (interactive "*p")
  (if-let* ((num (thing-at-point 'number))
            (i (integerp num))
            (bnds (bounds-of-thing-at-point 'number))
            (beg (car bnds))
            (end (cdr bnds))
            (numstr (buffer-substring-no-properties beg end))
            (fmtstr (let ((case-fold-search nil))
                      (pcase numstr
                        ((rx bos (regex thing-at-point-decimal-regexp) eos)
                         (concat "%" (when (string-match-p
                                            (rx bos (opt (or ?+ ?-)) 0)
                                            numstr)
                                       (format "0%d" (length numstr)))
                                 "d"))
                        ((rx bos (regex thing-at-point-hexadecimal-regexp) eos)
                         (concat (substring numstr 0 1) "x%"
                                 (when (string= "0" (substring numstr 2 3))
                                   (format "0%d" (- (length numstr) 2)))
                                 (if (string-match-p (rx (any "abcdef")) numstr)
                                     "x" "X")))
                        (_ (message "Unknown number format: \"%s\"" numstr)
                           nil)))))
      (save-excursion
        (delete-region beg end)
        (insert-before-markers-and-inherit (format fmtstr (+ num inc))))
    (message "No integer number at point")))
}}}

----
CategoryEditing
