3

Consider the following constant definition:

define("_TBL_USERS","users");

Now, I want to transform it into:

define("_TBL_USER_ITEMS","user_items");

I tried the command:

s/\(user\)s\?/\1_items/gi

And the result is:

define("_TBL_USER_items","user_items");

Is there a way to use a substitute command that will modify the case depending on the matched pattern, so the first items is substituted in uppercase?

2 Answers 2

4

There is plugin for this - Abolish by Tim Pope. No configuration required.

For your example do one of these commands (does not matter which):

:Subvert;USERS;USER_ITEMS;g

:Subvert;users;user_items;g
2

It's possible to do this with one substitution command but it's a bit hairy and you might want to just use two substitutions. For instance here's a two substitution version that has a bit of a shortcut in it...

:s/\(user\)s\?/\1_items/gI | s/\(USER\)S\?/\U~/g

The ~ in the replacement part of the second command copies the replacement of the last executed substitution (in our case from the LHS of the |). By prepending that with \U we force \1_items to uppercase.

If you want to know the single substitution version...better take a seat. ;)

:s/\(user\)s\?/\=submatch(1) . (submatch(1) ==# 'user' ? '_items' : '_ITEMS')/gi

This is using what's known as a sub-replace expression. I've answered a couple question recently using this technique: How to run a substitute command on only a certain part of the line and How to create regex group with dependency with earlier group via math operations

The key is the \= that begins the substitution. It means "everything after this point is to be evaluated as a VimL expression and the result of that evaluation becomes the replacement string". submatch(1) is equivalent to \1 and we're appending to that a string that is conditional on the value of submatch(1). Specifically, the part between the parens says, If the submatch is case sensitively equal to 'user' then append to the submatch "_items" otherwise append "_ITEMS". "

The two-substitution version is easier to understand and less typing so that's what I would use. If you really want to avoid typing a pattern more than once then, hmmm, you could use this, I suppose...

:let c='s/\(USER\)S\?/\1_ITEMS/I' | exe c | exe tolower(c).'I'

Alternatively, run the first substitution then run a second command...

:s/\(USER\)S\?/\1_ITEMS/I
:exe tolower(@:).'I'

Both of these do two substitutions but at least you only have to type one of them. ;)

(Just in case there's confusion, I don't actually recommend using either of the last two...they're a bit too hacky. But, hey, if you want to do so anyways then go for it. :)

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.