| bcefb8ae | 20-Apr-2026 |
Andreas Gohr <gohr@cosmocode.de> |
add GFM emphasis and underscore-delimited strong modes
Three new inline formatting modes for GitHub Flavored Markdown:
GfmEmphasis `*text*` → <em> GfmEmphasisUnderscore `_text_`
add GFM emphasis and underscore-delimited strong modes
Three new inline formatting modes for GitHub Flavored Markdown:
GfmEmphasis `*text*` → <em> GfmEmphasisUnderscore `_text_` → <em> (MD-preferred only) GfmStrongUnderscore `__text__` → <strong> (MD-preferred only)
All three emit the same handler instructions as DokuWiki's Emphasis / Strong, so existing renderers need no changes.
Design notes:
* Lexer mode names use snake_case (gfm_emphasis, gfm_emphasis_underscore, gfm_strong_underscore) to keep PascalCase readable at the class level. The asterisk variant emits `emphasis_open`/`emphasis_close` via the getInstructionName() hook, so DW's Emphasis (`//...//`) and GfmEmphasis (`*...*`) can coexist in mixed modes without a lexer state collision while still producing the same <em> output.
* Underscore variants gate on Markdown-preferred syntax (`markdown`, `md+dw`) because `__` otherwise means DW underline. GfmStrongUnderscore sorts at 70 (matching Strong) — below Underline at 90 — so when loaded it wins the lexer race for `__` runs. Underline is already gated out of MD-preferred modes in the previous commit.
* Entry patterns enforce the simplified CommonMark flanking rules already shared across DW inline modes (non-whitespace adjacency, no paragraph-boundary crossing) plus the word-boundary check for underscore variants using NO_WORD_BEFORE / NO_WORD_AFTER. The positive non-word-char enumeration makes them multibyte-safe without requiring the `u` flag: `für_etwas` and `пристаням_стремятся_` correctly stay literal.
Per-mode unit tests cover basic matching, single-char bodies, leading/trailing-whitespace rejection, empty-delimiter rejection, paragraph-boundary rejection, multibyte intraword protection, and sort values. ModeRegistryTest's gating data provider picks up the three new rules.
show more ...
|
| 35f91432 | 20-Apr-2026 |
Andreas Gohr <gohr@cosmocode.de> |
gate Underline on DokuWiki-preferred syntax; tidy registry plumbing
Three related changes to ModeRegistry, prep work for the Markdown modes that follow.
1. Underline (`__text__`) is moved out of lo
gate Underline on DokuWiki-preferred syntax; tidy registry plumbing
Three related changes to ModeRegistry, prep work for the Markdown modes that follow.
1. Underline (`__text__`) is moved out of loadAlwaysModes() and into loadDokuWikiModes(), gated on a new `\$dwPreferred` check that evaluates true for 'dokuwiki' and 'dw+md'. In MD-preferred settings ('markdown' and 'md+dw') `__` will mean GFM strong, so loading Underline there would conflict at the lexer level. Underline is unchanged in the default 'dokuwiki' setting.
2. resolveModeClass() now PascalCases every `_`-separated segment of the mode name, so `gfm_emphasis_underscore` resolves to `GfmEmphasisUnderscore`. Existing lowercase-compound names like `internallink` still resolve to `Internallink` (one segment, ucfirst-ed) — no behaviour change for current modes. This prepares the registry to load Gfm mode classes whose PascalCase filenames preserve word boundaries for readability.
3. ModeRegistryTest's multiple near-identical per-mode gating tests are consolidated into a single data-provider-driven testModeLoadingBySyntax, fed by a `\$rules` table that lists each mode against its four-setting expected load state. Adding a new gated mode now means one line in the provider. Currently only Underline is listed; upcoming Gfm-mode commits will add theirs.
show more ...
|
| 6b33ca93 | 20-Apr-2026 |
Andreas Gohr <gohr@cosmocode.de> |
add regex-primitive constants and getInstructionName() hook
Preparatory refactor for the upcoming GFM parser modes. No behaviour change for any existing mode: CONTENT_UNTIL_PARA still evaluates to t
add regex-primitive constants and getInstructionName() hook
Preparatory refactor for the upcoming GFM parser modes. No behaviour change for any existing mode: CONTENT_UNTIL_PARA still evaluates to the same regex (now factored through NOT_AT_PARA_BREAK), and getInstructionName() defaults to getModeName() so all current AbstractFormatting subclasses emit the same handler instructions as before.
AbstractMode gains four new shared regex constants:
NOT_AT_PARA_BREAK — zero-width assertion: current position is not the start of a paragraph break (blank line). Extracted from CONTENT_UNTIL_PARA for reuse in patterns that need a custom body char class.
NON_WORD_CHAR — char class: ASCII whitespace or ASCII punctuation except `_`. Multibyte-safe by construction: UTF-8 continuation bytes are >= 0x80 and thus fall outside every ASCII class, so checking positively that the surrounding context IS a non-word char correctly treats multibyte letters as word-like. No `u` flag required.
NO_WORD_BEFORE — zero-width: preceded by NON_WORD_CHAR or at start-of-input/line. For intraword-aware openers.
NO_WORD_AFTER — zero-width: followed by NON_WORD_CHAR or at end-of-input. Complement of NO_WORD_BEFORE.
AbstractFormatting gains a getInstructionName() hook that defaults to getModeName(). Subclasses that want to emit handler instructions under a different name than their lexer mode name (so a Gfm mode can share DW's `emphasis_open`/`strong_open` instructions while registering its own lexer state) override this method.
show more ...
|
| c3755410 | 20-Apr-2026 |
Andreas Gohr <gohr@cosmocode.de> |
require non-whitespace adjacency for inline formatting delimiters
An opening delimiter must now be followed by a non-whitespace character, and a closing delimiter must be preceded by one. Empty deli
require non-whitespace adjacency for inline formatting delimiters
An opening delimiter must now be followed by a non-whitespace character, and a closing delimiter must be preceded by one. Empty delimiter pairs (****, ____, '''', <sub></sub>, <sup></sup>, <del></del>) no longer match and stay literal.
Rationale: this matches Markdown's flanking-delimiter rules and eliminates accidental bolding of sequences like `** note**` at the start of a sentence. Well-formed uses (**bold**, //italic//, __underline__) are unchanged.
Affected modes: Strong, Emphasis, Underline, Monospace, Subscript, Superscript, Deleted.
BREAKING: content that was already malformed but previously rendered as formatted (e.g. `**foo bar **`) now stays literal.
show more ...
|
| 10fb3d65 | 20-Apr-2026 |
Andreas Gohr <gohr@cosmocode.de> |
prevent inline formatting from matching across paragraph boundaries
The Lexer compiles all patterns with the `s` (DOTALL) flag via ParallelRegex::getPerlMatchingFlags(), which makes `.` match newlin
prevent inline formatting from matching across paragraph boundaries
The Lexer compiles all patterns with the `s` (DOTALL) flag via ParallelRegex::getPerlMatchingFlags(), which makes `.` match newlines. Inline formatting modes use lookaheads like `\*\*(?=.*\*\*)` to verify a closing delimiter exists, so with DOTALL a lone `**` happily matched its "closer" many paragraphs later, swallowing blank lines into a single <strong> run.
Add CONTENT_UNTIL_PARA on AbstractMode — a regex snippet matching any character unless it would start a paragraph break (blank line, possibly with horizontal whitespace). Update all inline formatting entry patterns (Strong, Emphasis, Underline, Monospace, Subscript, Superscript, Deleted) to use it in their closing-delimiter lookaheads.
Emphasis also gets a real closing-`//` check; its previous lookahead just verified "content exists with a non-colon char" without requiring the closing delimiter at all.
Single newlines inside a delimiter pair still match (multi-line formatting); only blank lines end it.
BREAKING: This means you no longer can mark multiple paragraphs as bold or strike them out. On the other hand it prevents accidentally breaking the page layout by missing a closing delimiter (as reported many many times over the years) eg. #1025 #3588 #1056
show more ...
|
| 17c6179b | 20-Apr-2026 |
Andreas Gohr <gohr@cosmocode.de> |
add $conf['syntax'] setting and conditional mode loading in ModeRegistry
Introduce a new 'syntax' configuration setting (dokuwiki, markdown, dw+md, md+dw) that controls which parser modes are loaded
add $conf['syntax'] setting and conditional mode loading in ModeRegistry
Introduce a new 'syntax' configuration setting (dokuwiki, markdown, dw+md, md+dw) that controls which parser modes are loaded. Built-in modes are split into always-loaded (no Markdown equivalent), DW-only, and MD-only groups. Refactor getModes() into focused sub-methods for each group.
No Gfm mode classes exist yet, so only 'dokuwiki' is functional. The change is a strict no-op for existing behavior.
show more ...
|
| 0a5c6ce4 | 19-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
subscriptions: include diff link in plain-text list mails too
Fixes the docblock of lastRevBefore() and extends the diff link added to the HTML list mail to the plain-text variant as well, so both f
subscriptions: include diff link in plain-text list mails too
Fixes the docblock of lastRevBefore() and extends the diff link added to the HTML list mail to the plain-text variant as well, so both formats stay in sync.
show more ...
|
| f0f42d3b | 18-Apr-2026 |
Marek Adamski <fevbew@wp.pl> |
Translation update (pl) |
| 90c2f6e3 | 18-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
Clean up stale realip references after client_ip_header rename
Update docblocks in Ip.php and common.php, fix old tests to use the new config key, remove outdated translations, fix method casing in
Clean up stale realip references after client_ip_header rename
Update docblocks in Ip.php and common.php, fix old tests to use the new config key, remove outdated translations, fix method casing in test, and add example to English config description.
show more ...
|
| 04045fea | 18-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
remove unused rewriteBlocks property from Handler
This flag was added in b7c441b9 (2005) for planned wiki syntax converters but was never set to false anywhere. Remove the dead conditional and alway
remove unused rewriteBlocks property from Handler
This flag was added in b7c441b9 (2005) for planned wiki syntax converters but was never set to false anywhere. Remove the dead conditional and always run Block processing.
show more ...
|
| 3ea3771b | 18-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
supress code sniffer warning on deprecation wrapper
Previously the whole file was excluded. We can now be more precise. |
| 259e91d9 | 18-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
clean up Acronym and Preformatted
- remove stale comment, use closure for array_map, simplify compare with spaceship operator in Acronym - clarify misleading comment in Preformatted |
| 71096e46 | 18-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
move handler methods into ParserMode classes and rename Handler
Each ParserMode class now implements handle() from ModeInterface, containing the token handling logic that previously lived as individ
move handler methods into ParserMode classes and rename Handler
Each ParserMode class now implements handle() from ModeInterface, containing the token handling logic that previously lived as individual methods on Doku_Handler.
The Handler class (formerly Doku_Handler) is the single dispatch point: Lexer passes tokens to Handler::handleToken() which routes to mode objects, plugins, or returns false. The Lexer only tokenizes and resolves mapHandler aliases.
Key changes: - Add handle() to ModeInterface, implemented by all mode classes - Move Doku_Handler to dokuwiki\Parsing\Handler namespace - File extends Code (shared parsing via $type property) - Quotes uses mapHandler() + Handler::getModeName() for sub-modes - Media::parseMedia() replaces Doku_Handler_Parse_Media() - Code::parseHighlightOptions() replaces parse_highlight_options() - Per-parse state (footnote, doublequote) stays on Handler - Deprecated wrappers kept for base/header/internallink/media - Class alias and rector rules added for backward compatibility
show more ...
|
| 7958e698 | 16-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
decouple hardcoded mode names in Eol and Preformatted
Eol.php hardcoded ['listblock', 'table'] as modes to skip, and Preformatted.php hardcoded [\*\-] as a negative lookahead for list markers. Both
decouple hardcoded mode names in Eol and Preformatted
Eol.php hardcoded ['listblock', 'table'] as modes to skip, and Preformatted.php hardcoded [\*\-] as a negative lookahead for list markers. Both embed knowledge that belongs to the respective block modes, not to Eol/Preformatted. Adding a new block mode that handles its own EOL or uses different line start markers would require editing these unrelated files — a hidden coupling.
Listblock and Table now register themselves on ModeRegistry during preConnect(). Eol queries getBlockEolModes() and Preformatted queries getLineStartMarkers() to build its lookahead dynamically. Each mode owns its own data, and new block modes can participate without touching unrelated files.
show more ...
|
| 1f443476 | 16-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
split Formatting into individual classes per formatting type
Introduce AbstractFormatting as a base class and seven concrete classes (Strong, Emphasis, Underline, Monospace, Subscript, Superscript,
split Formatting into individual classes per formatting type
Introduce AbstractFormatting as a base class and seven concrete classes (Strong, Emphasis, Underline, Monospace, Subscript, Superscript, Deleted) that each define their own patterns and sort order. Delete the old Formatting class and update tests to use the new classes directly. ModeRegistry now treats formatting modes as regular built-in modes.
show more ...
|
| c8dd1b9d | 16-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
introduce ModeRegistry to encapsulate parser mode categories
Replace the global $PARSER_MODES definition in inc/parser/parser.php with a ModeRegistry singleton that initializes and manages the mode
introduce ModeRegistry to encapsulate parser mode categories
Replace the global $PARSER_MODES definition in inc/parser/parser.php with a ModeRegistry singleton that initializes and manages the mode categories. The global array is still populated for backward compatibility with plugins that access it directly.
Mode constructors now use ModeRegistry::getModesForCategories() instead of accessing the global directly. p_get_parsermodes() and p_sort_modes() are moved to inc/deprecated.php as thin wrappers.
show more ...
|
| 8c5fa126 | 16-Apr-2026 |
Andreas Gohr <gohr@cosmocode.de> |
deduplicate finalise() across rewriter subclasses
Move the identical finalise() logic into AbstractRewriter with an abstract getClosingCall() method. Lists, Quote, Table, Preformatted and Nest each
deduplicate finalise() across rewriter subclasses
Move the identical finalise() logic into AbstractRewriter with an abstract getClosingCall() method. Lists, Quote, Table, Preformatted and Nest each provide their closing call name instead of duplicating the full method.
show more ...
|
| 57d61403 | 16-Apr-2026 |
Andreas Gohr <gohr@cosmocode.de> |
remove unused Doku_Handler::fetch() method
Confirmed zero callers in core and plugin ecosystem. |
| 8ab4ec30 | 16-Apr-2026 |
Andreas Gohr <gohr@cosmocode.de> |
remove dead ParallelRegex::apply() method
Remove apply() which was never called from production code. Rewrite the inherited SimpleTest tests to use split() instead, and add a test for pre/post-match
remove dead ParallelRegex::apply() method
Remove apply() which was never called from production code. Rewrite the inherited SimpleTest tests to use split() instead, and add a test for pre/post-match splitting.
show more ...
|
| f8026da1 | 16-Apr-2026 |
Andreas Gohr <gohr@cosmocode.de> |
replace magic strings with class constants in Lexer
Introduce MODE_EXIT and MODE_SPECIAL_PREFIX constants to replace the undocumented "__exit" and "_" string conventions used for mode transitions. |
| b929bbff | 15-Apr-2026 |
Vyacheslav <bryanskmap@ya.ru> |
Translation update (ru) |
| fe6048cc | 14-Apr-2026 |
Alexander Lehmann <alexlehm@gmail.com> |
remove realip option, add default in conf/dokuwiki.php |
| bfc167db | 11-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
Limit namespace depth in io_createNamespace() #4613
Throw a RuntimeException when the given ID contains 128 or more colon-separated segments, preventing creation of excessively deep directory hierar
Limit namespace depth in io_createNamespace() #4613
Throw a RuntimeException when the given ID contains 128 or more colon-separated segments, preventing creation of excessively deep directory hierarchies.
show more ...
|
| 743a6908 | 11-Apr-2026 |
splitbrain <86426+splitbrain@users.noreply.github.com> |
Rector and PHPCS fixes |
| 867da04d | 11-Apr-2026 |
Andreas Gohr <andi@splitbrain.org> |
Less ubiquitous feed caching. addresses #4574
Instead of creating caches for each and every requested feed, only the recent feed is still cached.
The number of items is clamped to conf[recent]*5.
Less ubiquitous feed caching. addresses #4574
Instead of creating caches for each and every requested feed, only the recent feed is still cached.
The number of items is clamped to conf[recent]*5.
Plugins can influence the caching behavior via the existing FEED_OPTS_POSTPROCESS event by setting cache_allow to true and optionally adding their own cache key in cache_key
Additionally the per-namespace feed autodiscovery link from <head> pointing to list-mode feeds has been removed.
show more ...
|