1

Scenario

I would like to perform the following,

  1. Visually select some text that spans multiple lines and occurs multiple times in the file: V
  2. Yank to buffer: y
  3. Perform substitute with text from buffer: (:%s,<Ctrl-r> 0,new text,g)

Example

Given the following example:

metadata:
  name: redis
spec:
  version: 5.0
...
metadata:
  name: redis

I want to change all occurrences of metadata.

Output: :%s,metadata:^M name: redis,some other text,g

Error: E486: Pattern not found: metadata:^M name: redis

2 Answers 2

0

You can use vim-visual-multi. Use multi line mode with \\M. Select all occurrences with \\A and do c to change.

Other people will tell you the vim way.

You can also use %s/pattern\n nextline/something/ (or ^v<enter> instead of \n - would surely work ).

3
  • Ctrl-V + Enter doesn't work instead of \n: you end up with the same substitution that's described in the question.
    – Rich
    Oct 3, 2019 at 8:46
  • So it is not clear why the original substitution didn't work. Unix vs windows file format?
    – eyal karni
    Oct 3, 2019 at 23:03
  • 1
    No. When you yank a newline, it's stored in the yank register as a NL (regardless of the file format--I think this is how it's also stored in memory). But you cannot enter NL characters into the command-line: if you press <Enter> it runs the command; if you type Ctrl-V Ctrl-J it enters a NUL instead (See :help NL-used-for-Nul), and if you insert the contents of a register with <C-R>, it converts the NL characters into CR characters. So when you search, instead of looking for multiple lines, it's looking for single lines that contain CR characters.
    – Rich
    Oct 4, 2019 at 8:52
3

The problem is that the characters used to represent newlines in a buffer are not the same as those needed to match a newline in a regular expression. As eyal kami suggests, you can fix this by manually replacing the Ctrl-M ^M characters in your substitution with \n, or, perhaps cleaner, you could write a function to do both steps 2 and 3 for you:

function! MultiLineSubstitute() abort
  normal!gvy
  let expression = substitute(@0, "\<C-J>", '\\n', 'g')
  execute '%s,'.expression.','.input("Replacement: ")
endfunction

vnoremap <F1> :<C-U>call MultiLineSubstitute()<CR>

Note however that new lines are far from the only character that has a different meaning in a regular expression. You might want to look into the very nomagic atom (:help /\V) and the escape() function to work around this.

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.