Say that I have a java class with these fields(included only few fields, but the idea is that there could many more fields).

public class Entity {

    private String firstName;

    private String lastName;

    private String costPrice;

    private String sellingPriceForSomeItem;

Now I need to convert the above code to below code:

public class Entity {

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")    
    private String lastName;

    @Column(name = "cost_price")
    private String costPrice;

    @Column(name = "selling_price_for_some_item")
    private String sellingPriceForSomeItem;

Can we use macros to do this? Now in the above class, you see only 4 fields, but say that I have 100 such fields and I want to use some vi/vim way to speed up this process? How would one go about solving this? I am fine if we are unable to get the last field to work(i.e., fields with more than one camelCase word in it like sellingPriceForSomeItem).


It can be done with a substitution with a sub-replace-expression:

:%s/\ze\(\s*\)private \w\+ \(\w\+\)/\=submatch(1).'@Column(name="'.substitute(submatch(2), '\u', '_\l&', 'g').'");'."\r"

For more help see:

:h :s
:h sub-replace-expression
:h \zs
:h submatch()
:h substitute()
:h /\u
:h sub-replace-special
:h s/\l

Yet another solution

g/^\s*private/copy. | normal! -2dwdw"_C@Column(name = "^R=substitute(@-, '\(\l\)\(\u\)', '\1_\l\2', 'g')^M")

An explanation:

g/^\s*private/...           for all lines starting with "<spaces>private"
    copy. | normal! ...     make a copy of the current line then enter normal mode command
    -2dwdw"_C               up one line on first non-blank char; delete 2 words;
                            then delete one word putting it into register "minus";
                            then delete remaining part (i.e. semicolon)
                            and enter insert mode
    @Column(name = "^R=...^M") The new text with a computed part (note: ^R and ^M
                               must be entered as ^V^R and ^V^M respectively)
    substitute(@-, ...)     value of register "minus" from camelCase to snake_case

If you'd accept a solution that requires a Vim plug-in (specifically for the snake case coercion), then I could recommend one that involves recording a macro to process each line.

In particular, you would have to install plug-in vim-abolish, which includes a cr operation to do case coersion, in particular crs (or equivalently cr_) for snake_case.

So you could record a macro to process a specific line with:

  • qq: Record macro @q.
  • YP: Yank current line and put it above the current line. This duplicates the current line and leaves you at the top line.
  • $: Move to the end of the line, cursor will be on top of the ;.
  • s ") Esc: Replace the ; with a ") which is what you want at the end of the line.
  • B: Move to the start of the word, this should be the field name.
  • crs: This is the vim-abolish command to convert case to snake_case.
  • c^ @Column(name = " Esc: Changes from the beginning of the line (^) until the beginning of the field name, and insert the @Column contents you wanted to insert.
  • q: Stop recording the macro.

At this point, you can move the cursor to the next line (if you're using a search such as /private or /String then you can use n, actually nn to skip the current match; otherwise use a 3j for three lines down, or a similar motion), then replay the macro using @q. The second time you repeat it, you can simply use @@, which repeats the last macro that was executed (@q in this case.)

Or you can use a :g command as well:

:g/private/norm @q

This will execute the @q macro on every line that matches the /private search.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.