5

Consider I work on a file like this:

'90008000','90008005'

How can I insert the following text after '90008000', in Vim:

'90008001','90008002','90008003','90008004',

I have read this post, but I do not know how to insert these numbers in the same line.

1
  • 2
    You could use a strategy: split lines first, use that solution, than join all lines.
    – VanLaser
    Jan 22, 2016 at 10:21

3 Answers 3

6

You could use a macro. Starting with the cursor on the first character of the line, type qqyf,P<C-a>F'q3@q:

qq      "start the macro recording to register `q`
yf,     "yank the part you are looking to duplicate,
P       "paste it *before* the cursor.
<C-a>   "increment the next number by one
F'      "move backwards until '
q       "stop recording the macro,
3@q     "execute previously recorded macro 3 times.

In practice, it is often easier to do this kind of things linewise and join the lines afterward. :join! can be used to avoid spaces while joining the lines.

5

Personally, I'd probably just do this manually using regular insert/motion operations, y, p, and <c-a>, but you could do this by recording and replaying a macro with the following keystrokes:

0qqt,ya';a,<c-r>"<esc>h<c-a>q3@q

Broken down:

  1. 0: Move to the start of the line,
  2. qq: Start recording a macro,
  3. t,: Jump "till" the first comma,
  4. ya': Yank the number and the surrounding quotes,
  5. ;: Jump "till" the comma again,
  6. a,<c-r>"<esc>: Insert a new comma, and the text you yanked previously,
  7. h<c-a>: Increment the number,
  8. q: Stop recording the macro,
  9. 3@q: Replay the macro 3 times to add the next three numbers.

There are certainly other ways to achieve this with a macro: this is just the first that sprung to mind.

Macros are an incredibly powerful tool once you've got your head around how to order operations in a way such that repeating them will produce the desired results.

When you first start using them it might take you a couple of tries or more to figure out a working macro (u is your friend!), but after a while you can pretty much get it right on the first go every time, without having to think too much about how to do it. Remember that everything you type is recorded, so minor editing mistakes in recording can easily be corrected just as you would do so normally.

Relevant help topics:

:help complex-repeat
:help CTRL-A
:help registers
:help i_CTRL-R
0
3

If you have a lot of similar lines, here is a mapping that could help you:

nnoremap <silent> ,x :let [a, b] = map(split(getline('.'), ','), 'eval(v:val)') <bar> execute "s/^.*$/'" . join(range(a,b), "','") . "'"<CR>

Hit ,x on a line following the form 'a','b' (with a < b), and it should be converted into:
'a','a+1','a+2',...,'b'.


The mapping executes 2 Ex commands separated by a pipe symbol (<bar>).

The 1st command stores the 2 numbers on the line in the variables a and b.

:let [a, b] = map(split(getline('.'), ','), 'eval(v:val)')

It can be broken down like this:

getline('.')    returns the current line

split(g, ',')   splits the string g where a comma is found
                example: split('1,2,3', ',') returns [1, 2, 3]

                here g stands for: getline('.')

map(s, 'eval(v:val)')
                s being a list, map() takes each of its item and transforms it with
                the operation: eval(v:val)
                where v:val is an item of s
                example: map(['1', '2', '3'], 'eval(v:val)') returns [1, 2, 3]

                eval() evaluates a string; useful to get a number inside quotes
                example: eval('123') returns 123

                here s stands for: split(getline('.'), ',')

let [a, b] = map(s, 'eval(v:val)')
                stores in the variables a and b the 2 items inside the list returned
                by the map() function

The 2nd command substitutes the current line with a new one, containing the missing numbers between a and b.

execute "s/^.*$/'" . join(range(a,b), "','") . "'"

It can be broken down like this:

range(a,b)      returns the list of numbers between a and b
                example: range(1,3) returns [1, 2, 3]

join(r, "','")  returns a string which concatenates all the items from the list r
                separated by the 3 characters ','
                example: join([1, 2, 3], "','") returns "1','2','3"

                here r stands for: range(a,b)

execute "s/^.*$/'" . j . "'"

                executes the Ex command resulting from the concatenation of 3 strings:
                "s/^.*$/'"
                j 
                "'"

                The resulting Ex command is a substitution command (:s/^.*$/'j')
                which substitutes the whole line (^.*$) with the string "'j'"

                here j stands for: join(range(a,b), "','")
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.