From time to time I'll need to search and replace something with a newline, and I'll do :s/<pattern>/\n/ and I'll get a bunch of ^@s. Then I'll remember: "oh, right, vim uses \r for newline in replacements". Instead of this happening every single time, I would like an option to change that behavior.
As far as I can tell, there's no such option at the moment, and there doesn't seem to be a way to intercept the :s command and just change the replacement string.
I don't want to change the default behavior. I'm sure you have your reasons for why it behaves this way in the first place. I just want something I can set in my .vimrc so I can permanently forget about this thing.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.![]()
if you did that, you'd break all the plugins that rely on the current behavior
Do you mean because they use \r? I don't mind having both \r and \n mean the same thing in the replacement string.
Do plugins actually use \n in the replacement string with its current behavior? I don't mind not using those plugins.
People who have problems with it could choose either to not use the option or not use the plugins. Or plugins could get updated to detect this option. We already have many options that change behavior (e.g., magic) and plugins just deal with them by checking or by being careful with their regexes (e.g., \v, \m, \V, \M).
—
You are receiving this because you commented.
Plugins are probably using the substitue() function instead of the command :substitute. It doesn't need to apply to substitute().
If that's still not good enough. If there's a way to determine whether a command was entered by hand vs from a plugin, have the option only apply when the command was manually entered. This would cause an issue with plugin writers if they expect a plugin's behavior to be the same as when entered by hand, but presumably plugin writers should know how to read the docs.
The use of \r instead of \n in the replacement string is enough of an annoyance to new users that nearly every new user, upon hearing about an option to change the behavior, would set that option.
Seems like a pretty good argument for having such an option.
—
You are receiving this because you commented.
cabbrev s ((getcmdtype() == ':' && getcmdpos() <= 2) ? 'MyS' : 's')
Isn't cabbrev command line mode only? Why do you need to check getcmdtype()?
There could be an optional range in front of the command. getcmdpos() <= 2 would exclude that.
What's wrong with cabbrev s MyS?
In general I just find it super jarring when whatever I'm typing gets changed automatically from under my cursor. That's why I prefers not use imaps and cabbrevs. Since this is a single character command, it could feel different. I'll try it for a while to see if I end up hating it. I'd still prefer an option though.
—
You are receiving this because you commented.
The cabbrev method breaks incsearch for :substitute. There doesn't seem to be a way to add incsearch functionality to custom commands.
—
You are receiving this because you commented.
Oh wait, I can probably use call matchadd('IncSearch', {pattern}) in a CmdlineChanged autocmd.
—
You are receiving this because you commented.
Alright so now I have this monstrosity that should work exactly the same as :s. The incsearch highlighting doesn't work with ranges or custom delimiters yet, but they should be possible. When I'm done with this I should probably release it as a plugin.
function! s:Substitute(line1, line2, ...) abort let l:a000 = copy(a:000) if a:0 > 0 && a:1[0] =~ '[[:punct:]]' let l:args = split(a:1, a:1[0], 1) if len(l:args) >= 3 let l:args[2] = substitute(l:args[2], '\\n', '\\r', 'g') endif let l:a000[0] = join(l:args, a:1[0]) endif execute a:line1 . ',' . a:line2 . 'substitute' join(l:a000) endfunction command! -bar -range -nargs=* S call s:Substitute(<line1>, <line2>, <f-args>) cabbrev <expr> s getcmdtype() == ':' ? 'S' : 's' function! s:SubstituteHighlight(leave) abort if expand('<afile>') != ':' return endif if !exists('s:sub_ids') let s:sub_ids = {} else for [l:wid, l:mid] in items(s:sub_ids) if win_id2win(l:wid) != 0 call matchdelete(l:mid, l:wid) endif unlet s:sub_ids[l:wid] endfor endif if a:leave unlet s:sub_ids return endif let l:cmd = getcmdline() if l:cmd[0] != 'S' || l:cmd[1] != '/' return endif let l:pattern = split(l:cmd, '/', 1)[1] if empty(l:pattern) redraw return endif let l:wnr = winnr() windo let s:sub_ids[win_getid()] = matchadd('IncSearch', l:pattern, 0, -1, {'window': winnr()}) execute l:wnr . 'wincmd' 'w' redraw endfunction autocmd CmdlineChanged * call s:SubstituteHighlight(0) autocmd CmdlineLeave * call s:SubstituteHighlight(1)
—
You are receiving this because you commented.
Option settings like gdefault and magic are known to break plugins, because not many are aware of those. I am therefore not in favor of adding another option that changes how \r and \n is used in searches. You might be able to use c_CTRL-_e to evaluate your command line and replace \r by \n (or the other way around).
—
You are receiving this because you commented.
Since there is an alternative, and it will help only few users, adding an option isn't going to happen. Either just learn to use \r or use a custom command. Discussing the command doesn't need to take place here, therefore closing.
—
You are receiving this because you commented.