4

I have been using https://github.com/vim-scripts/increment.vim--Avadhanula for vim for years.

How could i leverage functionality similarly in emacs?

The idea be something like this:

given a list such as the following:

the_array[0] 
the_array[0] 
the_array[0] 

I want to be able to select all the zeros, issue a command, and have the text replaced with this:

the_array[0] 
the_array[1] 
the_array[2] 

I am using emacs evil-mode if it matters, hoping to do the equivelent of a block select for the region of numbers i want to increment.

Thanks,

joefromct
  • 1,506
  • 13
  • 33

2 Answers2

6

Say you start with this text in your buffer:

the_array[0]
the_array[0]
the_array[0]

Move the cursor to the first 0 and use C-v 2 j d to delete all the zeros. C-v } F 0 d will work for an arbitrary number of lines as long as the last the_array[0] line is at the end of a paragraph, but note that it requires (setq evil-cross-lines t) in your config.

Regardless of how you delete the 0's, you should now have this in your buffer:

the_array[]
the_array[]
the_array[]

Select all the ending ]'s in the same way you selected the 0's. Now press C-u C-x r N 0 <Enter> <Backspace> <Enter>. C-x r N runs rectangle-number-lines, which prompts for a starting number and format string when invoked with a prefix argument (C-u). We specify that it should start and 0 and insert only the numbers (<Backspace> removes a trailing space in this case).

Your buffer should now contain this:

the_array[1]
the_array[2]
the_array[3]
Gordon Gustafson
  • 40,133
  • 25
  • 115
  • 157
  • wow, a lot of keystrokes to remember. i am accepting this answer because i think it's as close the functionality of the plugin i have been using in vim (vim-increment). – joefromct Apr 06 '15 at 14:56
  • @joefromct Unfortunately it is a lot of keystrokes, but assuming you know how to select columns the only new thing you'll have to memorize is the `C-x r N` with a prefix argument (and the fact that you need to delete the `0`s beforehand). Favoriting the question to remind me to port the plugin to evil if I ever find the time. – Gordon Gustafson Apr 06 '15 at 16:29
  • 1
    The most important is to remember the command name: `rectangle-number-lines`. We can call it via `M-x`. We can type `C-u` (prefix argument) then `M-x `. – Ehvince Jan 30 '20 at 18:50
1

One approach is to set a region on the numbers you want to increment, then narrow to that region (typically bound to C-x n n), and then use replace-regexp with some elisp to generate the replacement text. Something like the following will work, where the text shown in keyboard font is what you enter, the text shown in code font is what emacs prompts you with, and you're expected to hit enter after each line:

M-x replace-regexp
Replace regexp: [0-9]+
Replace regexp [0-9]+ with: \,(1- (line-number-at-pos (point)))

The \,( ... ) construct used for the replacement is elisp that replace-regexp executes to generate the replacement text. In this case, the elisp gets the line number of each match to be replaced, subtracts 1 from it (since our narrowed region starts with line 1), and returns the resulting value as the replacement text. If you want the array indices to start with something other than 0, modify the math accordingly.

Steve Vinoski
  • 19,847
  • 3
  • 31
  • 46
  • Instead of narrowing and getting the line number, do you know if it is possible to get the index of the iteration ? (the "i" in "for i in lines") – Ehvince Apr 03 '15 at 09:50
  • 1
    Without the narrow, if you have to be extra careful with the regexp, which matches all digits, so you don't accidentally change other parts of the text. You can use `query-replace-regexp` instead as a safeguard. Regarding the index, instead of `\,(1- (line-number-at-pos (point)))` you could use `\,(if (boundp 'my-iter-val) (setq my-iter-val (1+ my-iter-val)) (setq my-iter-val 0))`, which creates a variable named `my-iter-val` to use as an index, but the downside is that the variable remains afterwards and won't work properly if you use it again unless you unbind it or reinitialize it. – Steve Vinoski Apr 03 '15 at 12:07
  • Good point. So it would be useful to have the index instead of creating the global variable. – Ehvince Apr 03 '15 at 12:14
  • And of course, you can do it all in emacs-lisp if you like. Try `M-x eval-expression` (normally bound to `M-:`) and evaluate the following: `(let ((i 0)) (while (re-search-forward "[0-9]+" (point-max) t) (replace-match (number-to-string i)) (setq i (1+ i))))`, which creates a local variable `i` and increments it with each replacement. This way the variable is not global, so it doesn't break the next time you use it. – Steve Vinoski Apr 03 '15 at 12:19