herophant
herophant

Reputation: 670

Selecting multiple text spots with visual mode?

I'm doing some markdown editing in vim on a file. I'm trying to convert it to markdown with code highlighting, etc.

- Arithmetic operators:`+,−,*, /`
- Constants: `e`, `pi`
- Functions: (abs x), (max x y... ), (ceiling x) (expt x y), (exp x),
    (cos x), ...

I want to select only the things that are in parantheses (including the parantheses) in the following using visual mode (so they would be disjoint by the commas):

(abs x), (max x y... ), (ceiling x) (expt x y), (exp x), (cos x), ...

And then do S` to surround the each piece of text with backticks. How can I do this without selcting each one, then doing S` repeatedly?

Upvotes: 0

Views: 134

Answers (2)

Amadan
Amadan

Reputation: 198304

How can I do this without selcting each one, then doing S` repeatedly?

This is actually what works best in Vim. With a bit of help from macros:

Interactive version:

/(.\{-})<CR>
qqysa)`nnq
@q
@@
@@

... till you do them all and wrap around to where you started.

Non-interactive "just do it" version:

:set nows<CR>
gg
/(.\{-})<CR>
qqqqqysa)`nn@qq@q

You'll probably want to go back to :set ns afterwards.

Of course, if you know that there are no nested parentheses, then the simplest answer is using :s, like the other answerer suggested.

EDIT with the explanation of the macro:

qqqqq...@qq@q is a loop. Here's how it works:

  • qq followed by q clears the q register. This will be important later.
  • qq starts the macro recording.
  • ysa) surrounds around the parentheses with `.
  • nn goes to the next match. We have to do it twice, because surround jumps to before the paren, and n will match the same parentheses again.
  • @q invokes the q macro. It is empty, so this does nothing... now. However, read further...
  • q stops the macro recording, and stores it to the q register.
  • Now that q is not empty any more, we can execute it with @q. However, during the execution q will still not be empty, so when we get to the point in the macro that did nothing during the recording, the macro will relaunch, giving us a primitive, but functioning, recursion loop.
  • The loop stops when something in it breaks: for example, not being able to go to the next match. Usually, you'd just change all the matches so no more matches remain; however, the edit this macro does does not make the match fail, so we have to rely on :set nows to make sure we don't continue infinitely adding backticks to all parentheses.

After a bit of thought, you can actually rewrite the pattern so that :set nows (and additional n) is not needed:

/`\@<!(.\{-})<CR>
qqqqqysa)`n@qq@q

This matches a pair of parentheses not preceded by a backtick, so that after all matches have been dealt with there is no match for n, naturally breaking the loop.

If anyone thinks this is complex... note that most editors plain can't do it (since this takes into account proper parenthesis nesting, whereas I haven't yet seen an editor with search-replace robust enough to be able to pull it off).

Upvotes: 2

D. Ben Knoble
D. Ben Knoble

Reputation: 4673

Using a global command (assuming `S`` comes from surround.vim):

:global/(/normal f(ysab`

(This affects the whole file, and may only do one change at a time. Repeat with @:)

With a macro:

qqf(ysab`q

Repeat with @q and then @@

Or with substitute:

:substitute/([^)]\+)/`&`/g

Upvotes: 0

Related Questions