Skip to content

Fix cell detection broken by DokuWiki Mort's lexer anchor change - #23

Open
gh189 wants to merge 1 commit into
dr4Ke:masterfrom
gh189:fix/mort-lexer-anchor
Open

Fix cell detection broken by DokuWiki Mort's lexer anchor change#23
gh189 wants to merge 1 commit into
dr4Ke:masterfrom
gh189:fix/mort-lexer-anchor

Conversation

@gh189

@gh189 gh189 commented Jul 19, 2026

Copy link
Copy Markdown

Problem

Since DokuWiki release 2026-07-14 "Mort", cellbg stops setting cell background colors — @color: markers in table cells are rendered as literal text instead of being converted to a bgcolor attribute.

Root cause

connectTo() registers the trigger pattern:

'^@#?[0-9a-zA-Z]*:(?=[^\n]*\|[[:space:]]*\n)'

The leading ^ was relying on an old lexer implementation detail: DokuWiki's ParallelRegex::split() used to match patterns against only the remaining unparsed substring, so ^ effectively matched "wherever the parser cursor currently is" — which happened to be right after the | that opens a table cell, even though that's mid-line, not a real line start.

Mort's parser/lexer rewrite (needed to correctly support lookbehind assertions for the new GFM emphasis/closer engine) changed ParallelRegex::split() to match against the full document with a real byte offset via PREG_OFFSET_CAPTURE. Under normal PCRE multiline semantics, ^ now only matches right after a literal \n (or at the very start of the document). Since @color: inside a table cell always follows a literal |/^ delimiter on the same source line, ^ no longer matches there, the special pattern is never triggered, and handle()/render() are never called for it.

I reproduced this against the actual dokuwiki-2026-07-14 release tarball and confirmed:

  • @red:test| as the very first line of a document → still matches (real line start).
  • The same marker inside a table cell (| @lightgreen:**Span** | ... |) → never matches anymore.

Fix

Replace the ^ anchor with a bounded-width lookbehind for the actual preceding table delimiter (| or ^, optionally followed by spaces/tabs). This matches at the same effective position under both the old cursor-relative lexer and Mort's full-document/offset-based lexer, so it works across DokuWiki versions:

'(?<=[|^][\t ]{0,20})@#?[0-9a-zA-Z]*:(?=[^\n]*\|[[:space:]]*\n)'

Testing

Verified against the real dokuwiki-2026-07-14 "Mort" release (via bin/render.php) using all the example tables from the plugin's documentation page (solid color name, hex color, default @: color, and colors on non-first cells) — all now render with the correct bgcolor attribute again. Also confirmed no regression for the plain-first-line-of-document case.

DokuWiki's Mort release (2026-07-14) changed how the parser lexer
matches special patterns: it now matches against the full document
with a real byte offset instead of a cursor-relative substring, so a
bare '^' anchor only matches a true line start (right after a literal
"\n"), per normal PCRE multiline semantics.

cellbg's trigger pattern used '^@...' relying on the old cursor-relative
behaviour, where '^' effectively meant "wherever the lexer cursor
currently is" -- which let it match right after the '|' delimiter that
opens a table cell, even though that position is mid-line, not a real
line start.

With Mort's stricter/correct anchor semantics, '@color:' inside a table
cell is never preceded by a literal newline, so the special pattern
never matches, handle()/render() are never invoked, and the marker is
emitted as literal text instead of turning into a bgcolor attribute.

Fix: replace the '^' anchor with a bounded-width lookbehind for the
preceding table delimiter ('|' or '^', optionally followed by spaces or
tabs), which matches at the same effective position under both the old
and the new lexer.

Verified against the dokuwiki-2026-07-14 (Mort) release: all example
cells from the plugin's documentation (solid color, hex color, default
color, colors on non-first cells) now render with the correct bgcolor
attribute again.
@michieltimmers

michieltimmers commented Jul 28, 2026

Copy link
Copy Markdown

The fix doesn't work for me. I now get "A PCRE internal error occured. This might be caused by a faulty plugin" when I try to load a page. And the markup of the page has been converted to text only. Don't see any errors in the nginx log.

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 24.04.4 LTS
Release:	24.04
Codename:	noble
$ php -v
PHP 8.3.6 (cli) (built: Jul 16 2026 18:30:41) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.6, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.6, Copyright (c), by Zend Technologies

This seems to be a bit of an old plugin.. don't think it will be fixed properly? are there any alternatives to change the colors in a table?

@michieltimmers

Copy link
Copy Markdown

It might be an issue with the PCRE version on ubuntu 24.04

$ php -r 'echo PCRE_VERSION, PHP_EOL;'
10.42 2022-12-11

This should probably be fine in ubuntu 26.04. But I wait to upgrade to that version until 26.04.1 is released (scheduled August 27, 2026)

@ProjectsKoryHasWorkedOn

Copy link
Copy Markdown

@michieltimmers

I also had same PCRE error

This is working for me

After editing page and saving it again

Replacement syntax.php

<?php
/**
 * cellbg Plugin: Allows user-defined colored cells in tables
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     dr4Ke <dr4ke@dr4ke.net>
 * @link       http://git.dr4ke.net/?p=dr4Ke/forks/dokuwiki-cells-bg.git
 *
 * Derived from the highlight plugin from : http://www.dokuwiki.org/plugin:highlight
 * and : http://www.staddle.net/wiki/plugins/highlight
 */

/**
 * All DokuWiki plugins to extend the parser/rendering mechanism
 * need to inherit from this class
 */
class syntax_plugin_cellbg extends \dokuwiki\Extension\SyntaxPlugin {

    // What kind of syntax are we?
    function getType(){ return 'substition'; }

    // What kind of syntax do we allow (optional)
    function getAllowedTypes() {
        return array('formatting', 'substition', 'disabled');
    }

    // What about paragraphs? (optional)
    function getPType(){ return 'normal'; }

    // Where to sort in?
    function getSort(){ return 200; }

    // Connect pattern to lexer
    function connectTo($mode) {
        $this->Lexer->addSpecialPattern('(?<=[\t |^])@#?[0-9a-zA-Z]+:', $mode, 'plugin_cellbg');
    }

    // Handle the match
    function handle($match, $state, $pos, \Doku_Handler $handler){
        switch ($state) {
            case DOKU_LEXER_ENTER :
                break;
            case DOKU_LEXER_MATCHED :
                break;
            case DOKU_LEXER_UNMATCHED :
                break;
            case DOKU_LEXER_EXIT :
                break;
            case DOKU_LEXER_SPECIAL :
                preg_match("/@([^:]*)/", $match, $color); // get the color
                if ($this->_isValid($color[1])) return array($state, $color[1], $match);
                break;
        }
        return array($state, "yellow", $match);
    }

    // Create output
    function render($mode, \Doku_Renderer $renderer, $data) {
        if ($mode == 'xhtml') {
            list($state, $color, $text) = $data;
            switch ($state) {
                case DOKU_LEXER_SPECIAL :
                    // Escape the color value to prevent XSS
                    $color = hsc($color);

                    // Match the last opened <td or <th tag. Newer DokuWiki versions
                    // (Greebo+) may insert additional markup (wrappers, classes, etc.)
                    // between the opening tag and our position, so we use a broader
                    // match that looks back further in the document.
                    if (preg_match('/(<t[dh]\b[^>]*)>((?:(?!<t[dh]\b).)*?)$/s', $renderer->doc, $m)) {
                        $renderer->doc = preg_replace(
                            '/(<t[dh]\b[^>]*)>((?:(?!<t[dh]\b).)*?)$/s',
                            '$1 style="background-color:' . $color . ';">$2',
                            $renderer->doc
                        );
                    } else {
                        $renderer->doc .= $text;
                    }
                    break;
            }
            return true;
        }
        return false;
    }

    // validate color value $c
    // this is cut price validation - only to ensure the basic format is
    // correct and there is nothing harmful
    // three basic formats  "colorname", "#fff[fff]", "rgb(255[%],255[%],255[%])"
    function _isValid($c) {

        $c = trim($c);

        $pattern = "/
            (^[a-zA-Z]+$)|                                #colorname - not verified
            (^\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$)|       #colorvalue
            (^rgb\(([0-9]{1,3}%?,){2}[0-9]{1,3}%?\)$)    #rgb triplet
            /x";

        return (preg_match($pattern, $c));

    }
}

@splitbrain splitbrain mentioned this pull request Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants