1

In Vim, one searches for newlines as \n, but newlines in the replacement-string must be entered as \r. I understand that there are reasons for this behavior, but for my purposes I just find it irritating. I want to be able to use \n to mean (essentially) the same thing in both a search pattern and in a replacement string: specifically, in a search pattern it should match newlines, and in a replacement string it should insert newlines.

Is there a way to get this behavior? I don't think simple remappings will work, since the substitute command needs to be parsed in order to determine whether \n is part of the search pattern or the replacement string.

4
  • 1
    Yes, it's a frustration for virtually everybody, but it's unlikely to be changed. Vim is extremely big on backwards compatibility, and this chance wouldn't be.
    – Heptite
    Apr 17, 2014 at 18:11
  • @Heptite I'm not asking for the Vim codebase to be changed; I'm wondering if it's possible to get this behavior via some form of customization, such as a plugin, possibly making use of github.com/coot/CRDispatcher. Apr 17, 2014 at 18:44
  • @Kyle do not edit other people’s answers. Edit your own post instead if you want to elaborate.
    – kinokijuf
    Apr 17, 2014 at 22:15
  • @kinkijuf I wasn't elaborating on my question; I was taking the answerer's suggestion and contributing (as per his own suggestion) my version of the regex used to determine whether or not to perform the replacement. This addition definitely doesn't belong in my question, and since the main part of the solution was the trick of using c_CTRL-\_e with getcmdline() =~# , not the details of the regex used, I don't think it deserves its own answer. Apr 17, 2014 at 22:29

3 Answers 3

3

You have to hook into the command-line; either when executing it via <CR>, or immediately when you enter a \n. With :help c_CTRL-\_e, you can evaluate a function and modify the command-line. Here's a demo that you can build upon and improve (the check for a substitution is simplistic right now):

cnoremap n <C-\>eTranslateBackslashN()<CR>
function! TranslateBackslashN()
    if getcmdtype() ==# ':' && getcmdline() =~# 's/.*/.*\\$' " Improve this!
        return getcmdline() . 'r'
    endif
    return getcmdline() . 'n'
endfunction
3
  • ...fancy. <C-\>e is a pretty cool feature I didn't know about. Apr 17, 2014 at 21:03
  • I've added the regex I'm using in my .vimrc, with an explanation. Apr 17, 2014 at 22:11
  • Never mind, my edit was rejected. I still think you deserve the core credit for figuring out this solution, but I'm going to post my own answer just so that the full implementation can get visibility. Apr 17, 2014 at 23:15
1

As Ingo Karkat wrote, a solution can be found using c_CTRL-\_e. Here's the version I'm using, which is identical to the original solution except for the regex pattern. (Since this is really just Ingo Karkat's answer with a modest improvement suggested by Ingo Karkat, I don't think it actually deserves its own answer, but I guess I'm outvoted: http://chat.stackexchange.com/transcript/message/15021397#15021397)

function! TranslateBackslashN()
    if getcmdtype() ==# ':' && getcmdline() =~#
      \ '[%>]s\([um]\w*\)\?\(.\).\{-}\(\\\)\@<!\2.*\\$'
        return getcmdline() . 'r'
    endif
    return getcmdline() . 'n'
endfunction
cnoremap n <C-\>eTranslateBackslashN()<CR>

Explanation of the regex pattern:

  • [%>]s ensures that the replacement only occurs when s comes immediately after a range
  • \([um]\w*\)\? matches variations of the s command, such as smagic.
  • \(.\) will be the first non-word character, since all word characters will already be eaten by \w in the optional group. Note that this is group 2. This is the separator character, which is usually /. Note that _, which is a word character, is also a valid separator, so the pattern won't work correctly if _ is used that way. I don't ever use _ as a separator, though, so I'm not going to worry about it. Similarly, there are some characters that are not word characters but which shouldn't be used as separators, but I'm not worried about that here because in that case the command should fail anyway. I wanted to use \W here, but for some reason that didn't work when I tested it.
  • .\{-} should match the pattern (\{-} is like Perl's *?).
  • \(\\\)\@<!\2 matches the next instance of the separator character not preceded by a back-slash. The Perl equivalent would be (?<!\\)\2.
  • .* matches whatever is in the replacement string so far.
  • \\$ matches only if the command so far ends with a backslash.
2
  • Take a look at :h range. You aren't catching all ranges (I'm not sure if there is a good way to do it.)
    – FDinoff
    Apr 18, 2014 at 2:01
  • @FDinoff I'm matching the only two range expressions I use at the moment. It doesn't look like it would be that hard to match the others, though, so I'll update as I need updates. Apr 18, 2014 at 4:23
0

Alternate answer using a plugin (this is the solution I have decided to use, although it only works with Vim 7.4+):

Using Enchanted Vim, put let g:VeryMagicSubstituteNormalise = 1 in your .vimrc.

(This plugin uses the CRDispatcher plugin, which allows text-substitutions in commands after the Enter key has been pressed but before the command is actually executed. So no remapping of \n is required, and the substitution is invisible to the user, making \n appear to have the same "meaning" in both contexts.)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .