2

The input line contains pairs of curly brace and in each pair of curly braces there might also be curly braces in the contents. Multiple layers of curly braces may also happen. Here's some example input:

{ A1 {B1 C1} {D1} } { {A2 B2} C2 D2}  { A3 { {B3} F3} }

The target is to separate them into lines for each of the outmost layer of curly braced items. For example, the above line should be divided into 3 lines like this.

{ A1 {B1 C1} {D1} }
{ {A2 B2} C2 D2} 
{ A3 { {B3} F3} }

How do I achieve this in Vim? The input line has many items and I need a quick way to get this done.

5

Recursive macro time!

  • qaqqa: start a macro in the register a after clearing it
  • 0%aEnterEsc: find a whole block and add a newline after it
  • @a: recursive invocation
  • q@a: finish recording and run

If you try this, it may not work the first time. Rather, it may run forever and leave blank lines in your file :)

The reason is that, with matchit loaded, % does not correctly "error" when there are no pairs to jump between. This is needed to correctly stop a recursive macro (and many other classes of operations, see :help map-error).

Two options:

  • :unmap % if you loaded matchit before trying this macro; or
  • insert a correct-but-technically-useless-except-for-its-erroring-behavior operation: qaqqa0vaB<esc>a<CR><esc>@aq@a<esc>. Here we changed % for vaB (equivalent to va{). This worked for me, but leaves you in visual mode when the macro ends, hence the extra escape.

A simpler version of the last bullet is to use f{ before % to make sure we have the chance to jump with %, but this requires whitespace at the start of each line. The safest way to do that is as follows:

  • I <esc>: insert a space at the beginning
  • qaqqa0f{%a<CR> <esc>@aq@a: our recursive macro, which also inserts spaces at the beginnings to make sure f{ will work correctly. I would probably consider trying =ap afterwards to re-align things.

Lastly, when making these kinds of macros, sometimes it's easier to do

:let @a = '0f{%a^M ^[@a'
@a

though you lose any feedback of the first go-through. Note that ^M is Ctrl-vEnter and similarly for ^[ with Esc; we need to insert the characters literally in the string.

Alternately, with double-quotes:

:let @a = "0f{%a\<CR> \<esc>@a"
3
  • There's no kill condition. Does the job, but also spewed out over 900 blank lines
    – Zoe
    Aug 8 at 17:08
  • Add an explicit f{ to make it abort, like this: qa0f{%a<enter><esc>@aq Aug 9 at 7:42
  • well, it looks like from the example you only need to add one leading whitespace. for the initial content. Aug 9 at 12:36
2

A lot of people will likely think of using a regular expression to parse and split up those "bracket blocks" but that's problematic. Identifying recursively nested parens/brackets/braces is really not possible with the typical regex engine. However, some engines support recursion and this is exactly what's needed for a pattern-based solution.

Since Vim doesn't support such recursion your best bet is to look outside Vim. If you have access to the standard Gnu command line tools (coreutils) then you can use their implementation of grep which supports Perl-compatible regular expressions (PCRE) by way of its -P flag. I'll proceed under the assumption that this is available to you.

The pattern we want looks like this:

{((?>[^{}]+)|(?R))*}

Since this is a Vim site I'm not going to break this down but I'll provide a link that explains it once I find one.

Anyways, we can leverage this from within Vim by putting the cursor on the target line and running this:

:.!grep -oP '{((?>[^{}]+)|(?R))*}'

In the case of the example you've provided the result will be:

{ A1 {B1 C1} {D1} }
{ {A2 B2} C2 D2}
{ A3 { {B3} F3} }

The Link

Here's the link I promised. This is related to the PHP language but its regex engine is also PCRE so the pattern is exactly the same (except they are targeting parentheses rather than curly braces). https://www.php.net/manual/en/regexp.reference.recursive.php

6
  • If you don't have access to Gnu grep then perhaps you have Perl. That can work, too, with the right invocation. There are a few other options, too, but Gnu grep and Perl are the most widely available ones.
    – B Layer
    Aug 8 at 7:21
  • Thanks for providing the answer. It's not in Vim so I accepted the other answer. Still, your help is appreciated.
    – Ken.H
    Aug 8 at 19:24
  • @Ken.H No problem. Cheers.
    – B Layer
    Aug 8 at 21:45
  • @Ken.H BTW, it is in Vim. Either you missed that or you meant it's not pure Vim (which is true).
    – B Layer
    Aug 9 at 6:14
  • PS I’m lazy, so rather than :.! I would type !!, which also happens to be significantly easier for my fingers
    – D. Ben Knoble
    Aug 9 at 23:54

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.