| 1c00c021 | 09-Jul-2026 |
Andreas Gohr <gohr@cosmocode.de> |
fix(parser): validate inline formatting closers with a single memoized scan
The inline formatting modes only open a span when a valid closer exists ahead. That check was a lookahead built on CONTENT
fix(parser): validate inline formatting closers with a single memoized scan
The inline formatting modes only open a span when a valid closer exists ahead. That check was a lookahead built on CONTENT_UNTIL_PARA, tested character by character up to the next paragraph break and re-evaluated from scratch for every opener candidate — openers times paragraph length. With pcre.jit=0 a crafted 32KB page took 16s and an ordinary 34KB page with long paragraphs 37s; with the JIT on (the PHP default) the per-character lookahead exhausted the JIT stack, the match silently failed, and the formatting — or everything after it — rendered as plain text.
The check also decided the wrong thing. It scanned raw text, so a closer lookalike inside content the lexer consumes atomically — a nowiki or %% span, a backtick code span, a link, a URL — counted as a real closer even though the mode's exit pattern can never fire there. And it ignored the enclosing span: an inner delimiter whose only closer lay past the closer of the mode it sits in was entered anyway, so a stray delimiter paired with one in a following sibling span and dragged the boundary along — the `*` in ''glob/*.conf'' joined the `*` of the next ''...'' span and corrupted the paragraph; the same held for //, ** and __ inside monospace, and an emphasis opened inside ((...)) ran past the footnote's )) and the enclosing bold's **.
Each formatting mode now declares its closer through Lexer::addCloserPattern(), mirroring addExitPattern(), and the lexer answers "does a valid closer exist ahead" with one anchored possessive scan per range instead of a lookahead per opener:
- The scan runs left to right from the opener, hopping over opaque spans derived from already-registered patterns — a plain or special match is consumed in one step, an entry into a verbatim mode (nowiki, the backtick code spans) extends to that mode's first exit — so a closer lookalike inside consumed content is never mistaken for a closer. Each hop finds the earliest of boundary, closer, or opaque span in a single leftmost search, keeping the check linear. - An opener is rejected when the nearest enclosing mode that has a closer of its own would close before the opener's own closer, so a delimiter that can never close within its span stays literal. That ancestor is found by walking the mode stack past modes that declare no closer (plugins, footnotes); the nearest guarded ancestor suffices, as it was itself validated against its own when it opened. - Both verdicts are memoized and reset per parse() run: a proven closer validates every earlier candidate, and a proven closer-free range rejects every later candidate before the next boundary. With the lexer consuming each opened span, the whole parse is linear in document size.
Closer patterns match the closing delimiter itself with flanking context in lookarounds — the convention exit patterns already follow — so closer positions compare exactly across modes and a closer directly after an inner opener is seen. AbstractFormatting derives the closer from the exit pattern and registers it with the paragraph break as the boundary, preserving the rule that formatting never spans paragraphs; a mode with other needs can pass a different boundary or none.
Footnote declares its )) as a closer rather than guarding its (( entry with a (?=.*)) lookahead, so the footnote becomes a boundary the scan sees and formatting inside it no longer pairs across the )); its closer takes no paragraph boundary, as footnotes are block-level. GfmEmphasis gains a closer pattern so single * emphasis is validated the same way, while its entry lookahead still enforces CommonMark nearest-delimiter pairing. GfmEmphasis and GfmStrong span bodies cannot contain their delimiter, so their in-pattern lookaheads stay linear on their own; the GFM backtick span bodies get deterministic alternatives with possessive quantifiers, removing their per-character backtracking.
CONTENT_UNTIL_PARA is removed: any entry pattern built with it recreates the quadratic scan. ParallelRegex gains escapePattern() so embedded closer fragments follow the lexer's bare-parenthesis convention, reports PREG_JIT_STACKLIMIT_ERROR so a future JIT exhaustion surfaces instead of silently truncating, and no longer rewrites its registered patterns in place while compiling the compound regex.
The adversarial 32KB page drops to 0.1s, the 37-second benign page to 0.1s, and a 128KB variant stays under 0.6s.
show more ...
|
| 3a5ba395 | 08-Jul-2026 |
Andreas Gohr <gohr@cosmocode.de> |
fix(parser): treat quotes next to bold/underline markup as smart quotes
With typography=2 a single-quoted phrase wrapped in strong or underline markup (**'x'** / __'x'__) rendered apostrophes instea
fix(parser): treat quotes next to bold/underline markup as smart quotes
With typography=2 a single-quoted phrase wrapped in strong or underline markup (**'x'** / __'x'__) rendered apostrophes instead of opening and closing quotes: the lexer matches against the full subject at a byte offset, so the singlequoteopening lookbehind sees the consumed * or _ delimiter and fails, letting apostrophe win. Add * and _ to the quote boundary set, as / (emphasis) already was, so the surrounding markup is recognised as a word boundary.
show more ...
|
| 9a38b8db | 05-Jul-2026 |
Andreas Gohr <andi@splitbrain.org> |
fix(parser): keep a mode's directly-assigned allowedModes when it also declares categories
setModeRegistry() replaced $this->allowedModes with the category-derived list whenever a mode declared cate
fix(parser): keep a mode's directly-assigned allowedModes when it also declares categories
setModeRegistry() replaced $this->allowedModes with the category-derived list whenever a mode declared categories, discarding any mode names the subclass had assigned to the property directly. The old SyntaxPlugin::accepts() merged the category modes into the existing list instead, so a mode using the sibling-component pattern - a non-empty getAllowedTypes() plus $this->allowedModes[] = 'plugin_foo_bar' in its constructor - kept both and its sibling syntax nested as intended.
Merge the category-derived modes into the directly-assigned list rather than replacing it, then deduplicate. The no-categories path is unchanged: the directly-assigned list is used as-is.
show more ...
|
| 945681fc | 05-Jul-2026 |
Andreas Gohr <andi@splitbrain.org> |
fix(parser): keep an indented list from being swallowed by a table
The parser rework dropped the list-awareness lookahead from preformatted's entry patterns ('\n (?![\*\-])' became '\n '). In base
fix(parser): keep an indented list from being swallowed by a table
The parser rework dropped the list-awareness lookahead from preformatted's entry patterns ('\n (?![\*\-])' became '\n '). In base mode this is masked by sort order - listblock sorts before preformatted and wins the tie - but table mode connects preformatted as a protected sub-mode without connecting any list mode. Inside a table an indented list line therefore entered preformatted instead of ending the table, and the table rewriter discarded the resulting call: the list vanished from the output and the table's section-edit range grew to cover it, so saving via the inline table editor overwrote the list text.
Restore the lookahead. For DokuWiki syntax it is the original '(?![\*\-])' (Listblock treats a bare * or - as a marker). For Markdown-preferred syntax the guard follows GfmListblock, rejecting -, *, + and ordered markers only when followed by whitespace or end of line, so mixed dw+md / md+dw wikis are fixed too while an indented code block that merely starts with such a character (e.g. a *** line) still parses as code.
show more ...
|
| e248eb6f | 05-Jul-2026 |
Andreas Gohr <andi@splitbrain.org> |
fix(parser): don't drop document content after a blank indented line
Preformatted's exit pattern (?=\n[^ \t\n]) is a lookahead-only exit: it consumes no byte so the boundary \n stays in the stream f
fix(parser): don't drop document content after a blank indented line
Preformatted's exit pattern (?=\n[^ \t\n]) is a lookahead-only exit: it consumes no byte so the boundary \n stays in the stream for a following block mode (an <hr> or header after an indented code block) to anchor on.
When that exit fired with nothing else consumed - a "blank" line that is empty after its indent, followed by a column-0 line - the match was zero-width at the current offset and the lexer's no-advance guard aborted the whole parse, silently discarding the rest of the document. Reachable on ordinary trailing whitespace ("abc\n \nmore") and inside footnotes.
Exempt zero-width MODE_EXIT matches from the guard: popping the mode stack is real progress and leaves the boundary byte for the parent mode to consume on the next iteration. The stack strictly shrinks on each such exit, so this cannot loop; the infinite-loop protection is unchanged for every other zero-width match. Preformatted's pattern is left as-is, so <hr>/header after an indented code block still works, including after a blank indented line.
Add regressions for the blank-line, blank-line-after-code, and boundary-preservation (hr after a blank indented line) cases.
show more ...
|
| 4b31eadf | 04-Jun-2026 |
Andreas Gohr <gohr@cosmocode.de> |
fix (parsing): avoid newline loss on GFM section editing
The GFM header parsing returned a byte position pointing at the newline before the actual header resulting in the observed newline eatings as
fix (parsing): avoid newline loss on GFM section editing
The GFM header parsing returned a byte position pointing at the newline before the actual header resulting in the observed newline eatings as reported in https://github.com/dokuwiki/dokuwiki/pull/4636#issuecomment-4491970909
Additionally this fixes an oddity of DW header parsing which accidentally allowed text on the line before the opening = chars. Whitespace is still allowed.
show more ...
|
| 47a02a10 | 04-Jun-2026 |
Andreas Gohr <gohr@cosmocode.de> |
Parsing: make parse syntax a per-parse value, drop ModeInterface
The active parse's syntax flavour is a per-parse question, not process- global state: within a single request a plugin can render bun
Parsing: make parse syntax a per-parse value, drop ModeInterface
The active parse's syntax flavour is a per-parse question, not process- global state: within a single request a plugin can render bundled DokuWiki-syntax text inside an otherwise-Markdown page. Yet ModeRegistry was a singleton that read $conf['syntax'] and the $PARSER_MODES global, and every mode reached it through ModeRegistry::getInstance() — so the flavour lived in shared mutable state that two parses in one request would fight over.
Make the registry a short-lived value instead:
- ModeRegistry is constructed once per parse with an explicit $syntax and injected into Parser, Handler and every mode. getSyntax() / isDwPreferred() / isMdPreferred() consult $this->syntax; the DOKU_UNITTEST-gated mode-list cache hack is gone (each registry is fresh, nothing to invalidate). - p_get_instructions() is now the single place in the pipeline where $conf['syntax'] is read; from there the flavour travels as a parameter. No code under inc/Parsing/ reads $conf['syntax'] directly anymore — the five syntax-reading modes (Preformatted, GfmHr, GfmEscape, Externallink, GfmQuote) route through $this->registry.
Keep the two concepts apart, as documented in the ModeRegistry and AbstractMode docblocks: the user's configured *preference* stays in $conf['syntax'] for UI code (toolbar, settings), while the active parse's syntax is a parameter carried by the registry.
$PARSER_MODES is demoted to a deprecated, read-only mirror, published during loadPluginModes() — third-party syntax plugins (columnlist, alphalist2, phpwikify, skipentity) and the bundled info plugin read the global directly, often from their constructors, so the taxonomy must stay visible there. No core code reads the mirror.
Fold ModeInterface into AbstractMode while here: getSort()/handle() are abstract, the connect callbacks carry defaults, and the public $Lexer "FIXME should be done by setter" becomes setLexer()/getLexer() injected by Parser::addMode() alongside the registry. Nested-content resolution moves to the allowedCategories()/filterAllowedModes() hooks, resolved once when the registry is attached.
Tests build their own parser/registry through ParserTestBase::setSyntax() instead of mutating $conf and calling the removed ModeRegistry::reset().
show more ...
|
| 4f32c45b | 26-May-2026 |
Andreas Gohr <gohr@cosmocode.de> |
GfmLink: allow soft line break inside link text
The label character class explicitly forbade `\n`, so a CommonMark soft line break inside link text (e.g. `[link with<EOL>more](url)`) fell through to
GfmLink: allow soft line break inside link text
The label character class explicitly forbade `\n`, so a CommonMark soft line break inside link text (e.g. `[link with<EOL>more](url)`) fell through to literal text instead of producing a link. Loosen the class to accept a bare `\n` as long as it is not followed by a blank line — soft breaks are spec-allowed inside link text, blank lines are not, and refusing them also keeps `\n#`-anchored block modes (header, hr, ...) from being swallowed by a runaway link match.
The `\n` survives into the label string and renders as a literal line ending in HTML, which browsers display as a single space. This soft break behavior has been checked against https://spec.commonmark.org/dingus/
Note that this behavior differs from github where the line break is rendered as a hard break <br>.
show more ...
|
| 65dd2042 | 26-May-2026 |
Andreas Gohr <gohr@cosmocode.de> |
GfmEscape: defer \\<EOL> to DW Linebreak in mixed-syntax modes
Both GfmEscape (sort 5) and DW Linebreak (sort 140) can claim the two backslashes of `\\` followed by space/tab/newline. The lexer's ti
GfmEscape: defer \\<EOL> to DW Linebreak in mixed-syntax modes
Both GfmEscape (sort 5) and DW Linebreak (sort 140) can claim the two backslashes of `\\` followed by space/tab/newline. The lexer's tie-breaker picked GfmEscape, so DW's forced linebreak silently lost its delimiter under dw+md and md+dw. Add a negative lookahead that declines `\\[ \t\n]` whenever DW syntax is loaded — pure md keeps GFM-spec behavior. Mid-line `\\` (UNC paths etc.) still escapes.
show more ...
|
| d331a839 | 12-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
GFM modes: follow CATEGORY_SUBSTITION → CATEGORY_SUBSTITUTION rename
Constant was renamed on master (the typo'd 'substition' value is kept, but the constant name spells it correctly). Update GfmTabl
GFM modes: follow CATEGORY_SUBSTITION → CATEGORY_SUBSTITUTION rename
Constant was renamed on master (the typo'd 'substition' value is kept, but the constant name spells it correctly). Update GfmTable's use of the constant, plus stale docblock/comment references in GfmEscape, GfmHtmlEntity, GfmLinebreak, and GfmLinebreakTest.
show more ...
|
| 465aec67 | 12-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
Parsing tests: split LinksTest into per-ParserMode files
Replaces the monolithic LinksTest with one test class per ParserMode: CamelcaselinkTest, EmaillinkTest, ExternallinkTest, FilelinkTest, Inter
Parsing tests: split LinksTest into per-ParserMode files
Replaces the monolithic LinksTest with one test class per ParserMode: CamelcaselinkTest, EmaillinkTest, ExternallinkTest, FilelinkTest, InternallinkTest, WindowssharelinkTest. Media-parser dispatch tests move into the existing MediaTest alongside the audio/video coverage.
ExternallinkTest also adds unit coverage for the GFM autolink extension trim step: balanced parens, trailing entity refs (valid named, numeric, and unknown round-trip), non-trailing entities staying inside the URL, and mixed paren/entity peeling.
show more ...
|
| 0f694376 | 05-May-2026 |
Andreas Gohr <gohr@cosmocode.de> |
GfmLink: accept escaped brackets inside link labels
The label slot used `[^\[\]\n]+`, which rejected `\[` / `\]` and left labels with escaped brackets unmatched. Promote it to `(?:\\.|[^\[\]\n])+` —
GfmLink: accept escaped brackets inside link labels
The label slot used `[^\[\]\n]+`, which rejected `\[` / `\]` and left labels with escaped brackets unmatched. Promote it to `(?:\\.|[^\[\]\n])+` — the same backslash-escape trick the URL slot already uses — so spec example 523 (`[link \[bar](/uri)`) matches and unescapes cleanly. The image-as-label sub-pattern gets the same upgrade.
handle() needs no change: the new class still rejects bare `]`, so the first literal `](` in the match is still the separator; Escape::unescapeBackslashes() was already collapsing `\[` to `[` before the label reached the link handler.
Adds two GfmLinkTest cases for the `\[` / `\]` forms.
show more ...
|
| 113171bb | 05-May-2026 |
Andreas Gohr <gohr@cosmocode.de> |
Preformatted: strip leading/trailing blank-line padding from body
The lexer's `\n ` continuation pattern eats the indent off blank-but-indented source lines, leaving a `\n` in the rewriter buffer
Preformatted: strip leading/trailing blank-line padding from body
The lexer's `\n ` continuation pattern eats the indent off blank-but-indented source lines, leaving a `\n` in the rewriter buffer for each one. Trim those padding newlines before emitting so the preformatted body starts and ends on a non-blank line, as GFM example #87 requires. Whitespace-only blocks are still skipped entirely.
Adds two PreformattedTest cases pinning the new behavior.
show more ...
|
| dccbd514 | 05-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
GfmQuote: accept ^> line starts so quotes can follow tables and lists
GfmTable, DW Table, and DW Listblock all consume the boundary \n on their way out. A pure-lookahead exit pattern at that boundar
GfmQuote: accept ^> line starts so quotes can follow tables and lists
GfmTable, DW Table, and DW Listblock all consume the boundary \n on their way out. A pure-lookahead exit pattern at that boundary would trip the lexer's no-advance safety check, because tables and lists exit right after consuming a marker token and have no leading unmatched content for the lookahead to attach to (unlike Preformatted, whose body leaves code lines as UNMATCHED right before the boundary).
Fix this on the consumer side: change the first-line anchor from \n> to (?:^|\n)>. With the lexer's m flag, ^ matches at offset 0 and at any position immediately following a \n in the subject, including the position right after a \n that a preceding mode just consumed. Subsequent quote lines keep the \n> anchor.
Adds three handoff tests in GfmQuoteTest covering GfmTable, DW Table, and DW Listblock. Resolves GFM spec example 201.
show more ...
|
| f9d3b7bd | 05-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
Externallink: add per-scheme angle-bracket autolinks for MD syntax
Adds CommonMark §6.5 <URL> autolinks to Externallink, gated to md/md+dw/dw+md syntax via ModeRegistry::isMdPreferred(). Per-scheme
Externallink: add per-scheme angle-bracket autolinks for MD syntax
Adds CommonMark §6.5 <URL> autolinks to Externallink, gated to md/md+dw/dw+md syntax via ModeRegistry::isMdPreferred(). Per-scheme patterns share the existing conf/scheme.conf allow-list so unknown schemes fall through to literal cdata instead of being silently dropped by the renderer. Internal whitespace inside the brackets disqualifies the autolink and the whole envelope is emitted as cdata to keep the bare-URL detector off the URL.
LinksTest gains 5 cases covering success, internal-whitespace and leading-whitespace disqualification, unregistered scheme fallthrough, and the dw-only no-op path. SpecCompatRenderer URL encoder is updated to match cmark-gfm's HREF_SAFE table (square brackets and a few other characters move from safe to encoded). skip.php loses the obsolete #356 entry and gains #605/#606/#607/#609 explaining the unregistered- scheme cases that the per-scheme regex naturally rejects.
show more ...
|
| c2248fda | 05-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
Block: collapse [ \t]*\n[ \t]* to \n inside paragraphs
Strip [ \t]+ on either side of the soft-break joiner emitted for a single eol, and ltrim the first cdata of each paragraph. Without this, DokuW
Block: collapse [ \t]*\n[ \t]* to \n inside paragraphs
Strip [ \t]+ on either side of the soft-break joiner emitted for a single eol, and ltrim the first cdata of each paragraph. Without this, DokuWiki preserved leading/trailing whitespace on continuation lines verbatim, which is invisible in HTML but may visible in plain-text and other renderers. It is also a requirement in the Markdown spec.
Re-baseline the parser-mode tests that pinned the old preserve behavior (cdata adjacent to <code>/<file>/<rss>/header/footnote).
show more ...
|
| f57da51c | 05-May-2026 |
Andreas Gohr <gohr@cosmocode.de> |
Preformatted: leave boundary \n in stream when next line has content
Adds a zero-width lookahead exit (?=\n[^ \t\n]) ahead of the existing consuming \n exit. When an indented code block is followed
Preformatted: leave boundary \n in stream when next line has content
Adds a zero-width lookahead exit (?=\n[^ \t\n]) ahead of the existing consuming \n exit. When an indented code block is followed by a non- blank line, the boundary newline now stays available for downstream block-level matchers (GfmHr, GfmHeader, etc.) instead of being eaten on the way out of preformatted mode.
Concretely fixes a thematic-break-after-indented-code case (GFM spec case 85's trailing ----): without this change, GfmHr's \n anchor failed because preformatted had already consumed the newline, and the bare ---- fell through to Entity which converted --- to an em-dash.
The consuming branch is kept as a fall-through for the blank-line and end-of-input cases, where a pure lookahead would trip the lexer's no-advance safety check.
Six PreformattedTest expectations updated: trailing cdata after a preformatted block now carries the leading \n (rendered output is unchanged — paragraph whitespace is trimmed).
show more ...
|
| ab6ac090 | 04-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
add tests pinning blank-line tolerance in GFM listblock |
| eb15e634 | 04-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
extract Helpers\HtmlEntity, wire into GfmCode and GfmLink URL slot
Numeric and named HTML entity decoding moves out of GfmHtmlEntity into a pure helper, so capture-by-regex modes can apply the same
extract Helpers\HtmlEntity, wire into GfmCode and GfmLink URL slot
Numeric and named HTML entity decoding moves out of GfmHtmlEntity into a pure helper, so capture-by-regex modes can apply the same decode post-extraction (the inline lexer never reaches their bodies). Mirrors the Helpers\Escape pattern.
Wired up in two slots:
- GfmCode info string: föö now decodes to föö in the language class. Clears spec example #330.
- GfmLink URL: GfmLink::extractUrl() decodes entities. URL pattern extends from `[^)\n]+` to `(?:\\.|[^)\n])+` so an escaped \) no longer terminates the URL early; the existing post-classify Escape::unescapeBackslashes call strips the backslashes after Link::classify has done its work. Clears #504, #506, #508.
Skip #328 with a self-contained title-slot reason: the URL side now decodes correctly, but the title attribute is still discarded (DokuWiki link instructions have no title slot).
show more ...
|
| d2085866 | 04-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
extend GfmNumericEntity to HTML5 named entities, rename to GfmHtmlEntity
Numeric refs are still decoded explicitly: PHP's html_entity_decode returns the input unchanged for U+0000, surrogates, U+10F
extend GfmNumericEntity to HTML5 named entities, rename to GfmHtmlEntity
Numeric refs are still decoded explicitly: PHP's html_entity_decode returns the input unchanged for U+0000, surrogates, U+10FFFF, and BMP noncharacters where CommonMark requires U+FFFD or the literal codepoint. Named refs delegate to html_entity_decode with ENT_HTML5, which carries the full HTML5 named-entity table (including multi- codepoint decodes like ≧̸ -> U+2267 + U+0338).
Unknown names stay literal: the original &xxx; passes through as cdata and the renderer's &-escaping turns it into &xxx;.
show more ...
|
| 150dc5f2 | 04-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
add GfmNumericEntity for CommonMark numeric character references
Decodes &#nnn; (decimal, 1-7 digits) and &#xhhh; / &#Xhhh; (hex, 1-6 digits) to the corresponding Unicode codepoint, emitted as plain
add GfmNumericEntity for CommonMark numeric character references
Decodes &#nnn; (decimal, 1-7 digits) and &#xhhh; / &#Xhhh; (hex, 1-6 digits) to the corresponding Unicode codepoint, emitted as plain cdata. Codepoint 0, codepoints above U+10FFFF, and the surrogate range U+D800..U+DFFF map to U+FFFD per the spec.
Distinct from the typography Entity mode, which is renderer-side configurable via entities.conf. Numeric refs are not configurable so decoding happens at parse time and the renderer needs no changes.
Lexer leftmost-match consumes the run before any structural pattern, so *foo* renders as literal *foo* and * foo does not start a list - matching the spec rule that numeric refs cannot stand in for structural markers.
show more ...
|
| 13a62f81 | 04-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
rename syntax flavors 'dokuwiki' / 'markdown' to 'dw' / 'md'
Symmetry with the existing 'dw+md' / 'md+dw' setting values. |
| c4bcbc2e | 04-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
add GfmLinebreak for GFM hard line breaks
Two-or-more trailing spaces, or a single backslash, immediately before a non-final newline render as a `<br/>`. Both delimiter forms share a single SUBSTITI
add GfmLinebreak for GFM hard line breaks
Two-or-more trailing spaces, or a single backslash, immediately before a non-final newline render as a `<br/>`. Both delimiter forms share a single SUBSTITION mode at sort 140, loaded under any MD-active syntax (markdown, dw+md, md+dw); pure dokuwiki is unaffected.
Reuses the existing `linebreak` handler call and renderer; no new instructions or renderer changes. SpecCompatRenderer overrides linebreak() to emit the spec's `<br />` shape. Examples 662, 663 (line break inside a raw HTML tag) are skipped — raw HTML is not passed through by default.
show more ...
|
| 3e6baeff | 30-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
replace DW Hr with unified GfmHr
Single mode covers both DokuWiki (4+ dashes) and GFM (3+ of -/*/_) horizontal rules; pattern self-narrows on $conf['syntax']. Always loaded across all four syntax se
replace DW Hr with unified GfmHr
Single mode covers both DokuWiki (4+ dashes) and GFM (3+ of -/*/_) horizontal rules; pattern self-narrows on $conf['syntax']. Always loaded across all four syntax settings, mirroring the GfmQuote replacement pattern. Same `hr` handler call so renderers and the call API are unchanged.
Drops DW's old [ \t]* leading-whitespace tolerance — inert in practice past 0-1 spaces (Preformatted at sort 20 intercepts everything ≥ 2 spaces or any tab).
Spec examples 13, 20, 26-28, 224 turn green; 17, 21-24, 29, 30, 31 go to skip.php as deliberate non-implementations (whitespace tolerance and list-precedence cases).
show more ...
|
| 309a0852 | 30-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
replace DW Quote with unified GfmQuote
GfmQuote covers blockquote parsing for both DokuWiki and GFM dialects in a single mode. Same quote_open/quote_close handler instructions; a DW-preferred post-p
replace DW Quote with unified GfmQuote
GfmQuote covers blockquote parsing for both DokuWiki and GFM dialects in a single mode. Same quote_open/quote_close handler instructions; a DW-preferred post-pass flattens sub-parsed paragraph wrapping into linebreak calls so existing pages keep their <br/>-between-lines rendering. MD-preferred keeps the <p>-wrapped spec shape.
Block content (lists, fenced code, tables) inside `>` quotes now renders, since the body is sub-parsed. Headers stay excluded (BASEONLY) — TOC and section-edit anchors don't compose with <blockquote>, same rationale as GfmListblock.
Convert ModeRegistry's sub-parser cache into an acquire/release pool to support same-key re-entrancy: a list inside a quote re-enters gfm_quote during the list-item sub-parse, and the inner call needs its own parser instance even though the exclusion key matches. GfmListblock is updated to use the new acquire/release primitives.
show more ...
|