188

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'
Community
  • 1
  • 1
davetapley
  • 17,000
  • 12
  • 60
  • 86

8 Answers8

159

Use abolish.vim:

:%S/badjob/goodjob/g
Mark Lodato
  • 50,015
  • 5
  • 41
  • 32
  • 55
    We do not deserve Tim Pope. – Andrew Keeton May 23 '19 at 19:47
  • 3
    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. – Foobar Jul 16 '19 at 23:47
  • @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 Apr 26 '20 at 22:53
  • @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. – Luke Griffiths Aug 12 '21 at 02:45
  • With this answer, how would abolish know to capitalise the 'J' in `GoodJob`? I would imagine at the very least one needs `%S/BadJob/GoodJob/g` – run_the_race Nov 14 '22 at 11:23
  • for me when i run this, it just opens a new copy of the current file as if i have typed `:sp` and it opens it on top only a few lines high. wierd.. – blamb Feb 27 '23 at 19:31
28

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...

LB40
  • 12,041
  • 17
  • 72
  • 107
12

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 :)

fc.
  • 161
  • 5
  • Yes it does since 2007: :%SubstituteCase/\cbadjob/GoodJob/g ^^ – Luc Hermitte Apr 23 '09 at 21:21
  • Meaning since gVim 7.2? I tried it in gVim 7.1 (12-May-2007) and it din't work :( – fc. Apr 24 '09 at 17:43
  • 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. – Luc Hermitte Apr 29 '09 at 11:56
  • 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 `lambada` → `lamgooda`. Fixes and explanations for these bugs and a few other things in my [answer](http://stackoverflow.com/questions/782511/case-preserving-substitute-in-vim/30684545#30684545). (Also LOLOWLs!) – Aaron Thoma Jun 06 '15 at 15:49
8

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.


*`is#`*?? Let me explain… (If interested.)

# (also in ==# and others) enforces case sensitivity. Otherwise, with :set ignorecase (which I use, because that is required for the useful :set smartcase), vim will consider 'a' == 'A'!!

Crazy as it is, we really should account for it: Because it is user-settings-dependent, == should NEVAR be used! (Except where that would actually be what you want.) I will even follow the recommendation to use ==# when comparing integers: http://learnvimscriptthehardway.stevelosh.com/chapters/22.html#code-defensively

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.
Aaron Thoma
  • 3,820
  • 1
  • 37
  • 34
5

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
rampion
  • 87,131
  • 49
  • 199
  • 315
4

What about

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

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

Gabriele
  • 420
  • 4
  • 15
2

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

Ingo Karkat
  • 167,457
  • 16
  • 250
  • 324
  • 1
    Is there a trick to shorten those hard to remember and tedious to type commands like `:%s/file\A\?size/\=SmartCase("LastModifiedTime")/ig`? – Michael Härtl Mar 10 '14 at 13:15
  • 1
    @MichaelHärtl: You can use the `:SmartCase` command. I've extended that [in my own fork](https://github.com/inkarkat/SmartCase). Note that this requires [ingo-library](http://www.vim.org/scripts/script.php?script_id=4433) as a dependency. – Ingo Karkat Mar 11 '14 at 13:51
0

Of course, you can do this:

First declare some variables containing cases and replacements:

let a = ['badjob', 'BadJob', 'badJob', 'BADJOB']

let b = ['goodjob', 'GoodJob', 'goodJob', 'GOODJOB']

Second, apply a for loop as follow:

for i in range(0,3) | exec '%s/' . a[i] . '/' . b[i] . '/g' | endfor

Note that range function starts at 0.

Best regards.

Alan Gómez
  • 211
  • 1
  • 6