1

I need to change the content within a [count] number of braces for each line in my buffer.

1 {Lorem} ipsum dolor {amet} blah blah {change this text} more blah blah
2 Hello. The {sun} rises in {the} east. blah {blah}. {also change this text}. final blah

In line 1, I need to substitute the content within the 3rd set of braces to random forest. For line 2, I need to change the text within 4th set of braces to alchemy.

I know of a naive method for doing this, i.e. for each line, first move the cursor to the desired braces and perform a ci}

For the reasons of efficiency (as I am editing/proof-reading a large text-file with such markup), I wish to do these per-line substitutions without moving the cursor from column 1 for each linewise substitution.

How can I achieve this? Does ex mode help?

2

The answer from @RubenWesterberg is a correct sequence of Normal mode commands to do what you want. But I read your request to not move the cursor a little more literally. Also, if you need to make substitutions like this frequently you probably want something a bit less manual.

You can use standard substitution in a mapping like this:

:nnoremap <expr> <leader>{ ':<c-u>s/\({.\{-}\)\{' . v:count1 . '}\zs[^}]*/'

This is used in Normal mode by entering N\{ where N is the numerical 1-based "index" of the bracket pair within which you want to replace text. (I'm assuming you are using default <leader> key \). This will take you to the command-line with a pre-populated :substitute command. The cursor will be placed so you merely enter the replacement text followed by Enter and the work is done.

Keep in mind that the value of N is counted from the current cursor position. It will still work if the cursor is not in the first column but if any brackets appear before the cursor they will not be counted.

And N can be omitted in which case a default of index 1 will be used.


Update: For fun, if we want to take the no-cursor-movement requirement to the extreme this version will add Normal mode command 0 after the substitution so you always end up back in the first column:

:nnoremap <expr> <leader>{ ':<c-u>s/\({.\{-}\)\{' . v:count1 . '}\zs[^}]*// \| norm! 0<c-left><c-left><c-left><left><left>'

Update 2: Didn't have time to do this earlier so here's a breakdown of the pattern in the substitution. If we have N=3 then substitution renders as the following (with the cursor at the very end):

s/\({.\{-}\)\{3}\zs[^}]*/
  • {.\{-} - Match a left bracket, {, followed by zero or more characters until a later segment of the pattern is matched or the pattern end is reached. .\{-} is like .* except that the latter matches as many characters as possible while the former matches as few as possible.
  • \(...\)\{3} - Match the previous pattern exactly three times.
  • \zs - Indicates that the substitution will replace characters starting at this position rather than replacing the whole pattern match. In this case, it is the position of the first character inside the target bracket pair.
  • [^}]* - Match zero or more characters that are not } or, in other words, match everything inside the target bracket pair.
1

Execute the following in normal mode on the first line:

2f{ci{REPLACEMENT_TEXT<ESC>0

This will find the target brace, change the content to REPLACEMENT_TEXT and then return the cursor back to the beginning of the line.

For the second line:

4f{ci{REPLACEMENT_TEXT<ESC>0

Note that the exact number of braces to search for depend on if the first character on the line is a brace.

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.