I do a lot of programming in Ruby, and a common edit suggested by Rubocop (a linter) is to reformat an array of symbols

[:foo, :bar, :baz]

as a percent string:

%i[foo bar baz]

I'm looking for a quick way to remove all colons and commas between the brackets without affecting text on the same line outside the brackets.

Is there any way to specify a search query that will ignore matches outside of a given set of delimiters? I tried tossing the delimiters into the search query, but any way I can think of to structure the regular expression winds up consuming the other matches.


I'm late but here is my current solution based on @mike's work:

nnoremap cri mmva[<bar>:s/\v:(\w+),?/\1/g<bar>s/\v(\[([^\]])*\])/%i\1/g<bar>:nohlsearch<bar>:normal `m<Return>

Short breakdown:

  • nnoremap cri maps cri in normal mode
  • mm marks the current position
  • va[ select the array around the cursor position
  • <bar> pipes another command
  • :s/\v:(\w+),?/\1/g<bar>s/\v(\[([^\]])*\])/%i\1/g the substution in two steps
  • :nohlsearch highlight off
  • normal 'm back to the marked position

And if you try to do the same thing for arrays of strings:

nnoremap crw mmva[<bar>:s/\v(["'])(\w+)\1,?/\2/g<bar>s/\v(\[([^\]])*\])/%w\1/g<bar>:nohlsearch<bar>:normal `m<Return>

This can be solved by using two substitutions together. The second one will only match, if the first one matched.

If you get errors on the second one, you can use /ge so that errors are ignored.

%s/\v[:(\w+),?]/\1/g | %s/\v(\[.*\])/%i\1/g

Short breakdown:

\v - very magic mode

[:(\w+),?]/\1/g - the substitution

,? - match the , zero or once

| - concatenate another command; executed, if the first was sucessful

\1 - references the first match group

  • Very clever to use match groups here. Note: I believe your first regexp is more correct with without the square brackets, as 1) ? inside square brackets matches a literal question mark, and 2) (\w+) doesn't make a lot of sense inside square brackets (‘The [] construct matches a single character’; see :h 27.6). Also, the idiomatic vim flag for an optional single character is =, though ? certainly also works; see :h 27.4. – Ryan Lue Dec 12 '17 at 2:58
  • Just realized this solves my problem but doesn't strictly answer the question of how to limit the search expression to between the delimiters, so I'm withdrawing the "Accepted" mark. Sorry! – Ryan Lue Dec 12 '17 at 3:00

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.