ChatGPT解决这个技术问题 Extra ChatGPT

Case preserving substitute in Vim

Can this can be done in Vim?

What I mean is: searching for 'BadJob' and replacing with 'GoodJob' would do the following replacements 'badjob' -> 'goodjob' 'BadJob' -> 'GoodJob' 'badJob' -> 'goodJob' 'BADJOB' -> 'GOODJOB'

This should be a vim feature. It makes so much sense.

M
Mark Lodato

Use abolish.vim:

:%S/badjob/goodjob/g

We do not deserve Tim Pope.
This plugin doesn't seem to work for me. If I a word like BadJob and I want to replace it with GoodJob, I can't use %S/badjob/goodjob/g. It fails to detect a match.
@Roymunson What you need to do is this: %S/BadJob/GoodJob/g, then the Subvert command will switch to mixed-case mode and will do all the substitions as given by OP.
@shivams Does the presence of mixed case anywhere in the arguments to %S activate mixed-case mode, or does the input argument BadJob need to literally match the thing you're trying to replace? The former seems like strange ux, and the latter seems to defeat the purpose.
L
LB40

I don't know if this is the kind of solution you're looking for... but i've used this: keepcase.vim

There's no support otherwise in vim...


f
fc.

sure u can

:s/\cbad/\= strpart(submatch(0), 0 ,1) == toupper(strpart(submatch(0), 0, 1)) ? "GOOD" : "good"/

ps. i'm guessing keepcase.vim encapsulates some similar logic :)


Yes it does since 2007: :%SubstituteCase/\cbadjob/GoodJob/g ^^
Meaning since gVim 7.2? I tried it in gVim 7.1 (12-May-2007) and it din't work :(
No, I've added the :SubstituteCase command to the plugin in 2007. That's all. The plugin is available on vim.org, and it is not shipped with vim as usual with with most plugins.
1. This fails when the user has :set ignorecase. 2. Bad will be substituted by GOOD instead of Good. 3. The "job" part of the question is ignored, so this will also replace lambadalamgooda. Fixes and explanations for these bugs and a few other things in my answer. (Also LOLOWLs!)
A
Aaron Thoma

For most (non-complex) cases, i recommend @rampion’s answer over mine.
If you got a minute, my post might be still be worthwhile, though. Level up your awareness for scripting gotchas.

You can just paste and adapt this: (Of course, if you do this from time to time, you will want a plugin instead of this monstrosity. But for some who are in a hurry and only need it once, this is a quick hack for your pasting pleasure:)

:%s/\cbad\zejob/= ( submatch(0)[0] is# toupper(submatch(0)[0]) ? 'G' : 'g' ) . ( submatch(0)[1] is# toupper(submatch(0)[1]) ? 'OOD' : 'ood' )

Apart from the search pattern, you have to edit the four 'strings' in the replacement code: Edit the parts in bold:

:%s/\cbad\zejob/= ( submatch(0)[0] is# toupper(submatch(0)[0]) ? 'G' : 'g' ) . ( submatch(0)[1] is# toupper(submatch(0)[1]) ? 'OOD' : 'ood' )

Don't use this 'orange' version for pasting, since its linebreak characters will also break the command.

/\ze is vim regex syntactic sugar for marking a positive lookahead: The pattern after \ze is checked for, but not substituted.

https://i.stack.imgur.com/99eI5.jpg

is# instead of ==# is another way of coding defensively: It improves type safety: http://google.github.io/styleguide/vimscriptguide.xml?showone=Type_checking#Type_checking
It should be used when comparing against a string literal.

'single-quoted' instead of "double quoted" strings are another good practice: http://google.github.io/styleguide/vimscriptguide.xml?showone=Strings#Strings

HT @fc. - this answer builds on their [answer](https://stackoverflow.com/questions/782511/case-preserving-substitute-in-vim/782617#782617), fixing a few shortcomings.


r
rampion

If you're only matching an exact (case-independent) string with a few possible capitalizations, another possibility is:

:s/abc/\={'abc':'xyz','Abc':'Xyz'}[submatch(0)]/i

I
Ingo Karkat

An alternative to the keepcase plugin is SmartCase - replacing words while keeping original case. (Don't let yourself be discourage by the bad ratings.)


Is there a trick to shorten those hard to remember and tedious to type commands like :%s/file\A\?size/\=SmartCase("LastModifiedTime")/ig?
@MichaelHärtl: You can use the :SmartCase command. I've extended that in my own fork. Note that this requires ingo-library as a dependency.
G
Gabriele

What about

:%s/\Cbadjob/goodjob/
:%s/\CBadJob/GoodJob/
:%s/\CbadJob/goodJob/  
:%s/\CBADJOB/GOODJOB/

See: https://stackoverflow.com/a/2287449/5599687