0

Suppose I have a file with the following text:

aaa
bbb
ccc
bbb
ccc
eee

I need to add a missing line ddd after the second occurrence of ccc.

For that reason, use of the built-in search() function, as suggested here, won't work, because that will find the first occurrence.

I have the beginnings of a vimscript function:

function! addLine()
  normal /bbb
  normal /bbb
  " MISSING LINE
  wq!
endfunction

Is it possible to do this just using normal mode etc? If so, how?

Alex Harvey
  • 14,494
  • 5
  • 61
  • 97
  • Successive calls to `search()` should find the next occurrences. `call search("bbb") | call search("bbb")` or `for i in range(10) | call search("bbb") | endfor`, etc. – muru Apr 01 '19 at 03:38
  • If I do `search("ccc") | search("bbb")` I seem to end up at the first `bbb` not the second? – Alex Harvey Apr 01 '19 at 03:45
  • Which line did you start from? Starting from the first line, `:call search("ccc") | call search("bbb")` gets me to line 4 (the second `bbb`). – muru Apr 01 '19 at 03:46
  • Hmmm yes seems to behave differently if called from command mode inside the file as opposed to inside a function. – Alex Harvey Apr 01 '19 at 03:48
  • Ah, I see. Try `search("ccc", "e")` instead - it should move the cursor to the end of the match, but it seems that behaviour varies depending on it was called, so specifying that behaviour explicitly using the `e` flag should fix that. – muru Apr 01 '19 at 03:54
  • Actually [this](https://gist.github.com/alexharv074/c56e8ff4b190d27da3b9c81a95a02d10) works. If you want to convert that to an answer? That said I would still welcome a solution based on normal mode. I'm sure it's possible. – Alex Harvey Apr 01 '19 at 05:01

2 Answers2

2

With :normal, any presses of Enter (Carriage Return, <CR>) or Escape needed by the normal mode command need to passed to it. So /... command would look like:

exec "normal /bbb\<cr>"

You need the Enter, without it the / command gets cancelled.

So your function would look like:

exec "normal /ccc\<cr>noddd\<esc>"

(Pressing n to repeat the search, then o to start input in the next line, then Escape to exit insert mode.)

Or split into multiple :normals:

exec "normal /ccc\<cr>"
exec "normal /ccc\<cr>"
exec "normal oddd\<esc>"

You have to be careful that the text being inserted here doesn't have strings that might be special.

muru
  • 4,723
  • 1
  • 34
  • 78
0

Aside from Muru's excellent and helpful answer, I wanted to provide the actual code:

function! AddLine()
  normal /bbb\<cr>n/ccc\<cr>oddd\<esc>
  wq!
endfunction

Note that:

  • \<cr> appears as ^M in the file and is entered as CTRL-v then CTRL-m.
  • \<esc> appears as ^[ in the file and is entered as CTRL-v then ESC.

To understand the script normal /bbb\<cr>n/ccc\<cr>oddd\<esc>:

  • /bbb\<cr> - find the first line bbb
  • n - find the next match
  • /ccc\<cr> - find the next line ccc
  • o - enter insert mode and enter the text:
  • ddd
  • \<esc\> - exit insert mode.

This can be rewritten using the Vim functions as:

function! AddLine()
  call search("bbb")
  call search("bbb")
  let l:foundline = search("ccc")
  call append(l:foundline, "ddd")
  wq!
endfunction

Both of those functions correctly edit the file as required, although of course I do agree that the use of Vim functions is cleaner. If you can remember all the functions!

See also:

Alex Harvey
  • 14,494
  • 5
  • 61
  • 97