How to improve RegExp in „Columns in Kirbytext“ blog article?


actually I’m not using columns but my code is based on the suggestion in the blog article Columns in Kirbytext. The difference is I only need a start/end point but no mid delimiters (+++). So my first attempt was this (I use extra instead of columns):

kirbytext::$pre[] = function($kirbytext, $text) {
    $text = preg_replace_callback('!\(extra(…|\.{3})\)(.*)\((…|\.{3})extra\)!is', function($matches) use($kirbytext) {

        $content = $matches[2];

        return '<div class="extratext-wrapper"><div class="extratext-label"></div><div class="extratext-text">' . kirbytext($content) . '</div></div>';

    }, $text);
    return $text;

This worked fine except when using more than one pair of (extra…)(…extra):

Normal text

(extra…)Extra Text(…extra)      //  <---
                                //     | is parsed
Normal Text                     //     | as one
                                //     | block
(extra…)Extra Text(…extra)      //  <---

Normal Text

In that case anything between the first (extra…) and the last (…extra) is $content, i.e. there is only one extra text box on the page.

To improve this I separated the search for begin and end in two steps, made the filter post instead of pre and added some more code to remove <br> tags and move <p> tags to the right position:

kirbytext::$post[] = function($kirbytext, $text) {
    $text = preg_replace_callback('!(<p>)?\s*\(extra(…|\.{3})\)\s*(<br>|<br />)?!i', function($matches) use($kirbytext) {

        return '<div class="extratext-wrapper"><div class="extratext-label"></div><div class="extratext-text"><p>';

    }, $text);
    $text = preg_replace_callback('!(<br>|<br />)?\s*\((…|\.{3})extra\)\s*(<br>|<br />)?\s*(</p>)?\s*(<br>|<br />)?!i', function($matches) use($kirbytext) {

        return '</p></div></div>';

    }, $text);
    return $text;

Although it works it feels like a bad hack/workaround for an easy problem. Could anyone provide a more elegant solution?

Thanks in advance!

Not sure, but maybe it would already help to make the whitespace in the regex non-greedy (.*?)?


Yes. It seems to work. Thanks!

I don’t understand why, though – regex magic :dizzy:

Adding the ? to your wildcard group (.*) that is supposed to capture everything between (extra…) and (…extra) makes it non-greedy. So it doesn’t look for the biggest match possible (with two (extra…)(…extra) groups everything between the opening one of the first and the closing one of the second), but instead already matches with the smallest match possible, when the first closing (…extra) is parsed.

Ahhh :slight_smile: thanks again!