diff --git a/action.php b/action.php index 71f02a1..0c2a918 100644 --- a/action.php +++ b/action.php @@ -59,6 +59,10 @@ public function register($controller) { * */ public function handle($event, $param) { + if ($this->isSubParser()) { + return; + } + refnotes_parser_core::getInstance()->exitParsingContext($event->data); /* We need a new instance of mangler for each event because we can trigger it recursively @@ -68,6 +72,27 @@ public function handle($event, $param) { $mangler->process(); } + + private function isSubParser() { + if (!class_exists('dokuwiki\Parsing\ModeRegistry')) return false; + + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + foreach ($trace as $i => $frame) { + $func = $frame['function'] ?? ''; + if ($func === 'withSubParser' || $func === 'acquireSubParser') { + return true; + } + $class = $frame['class'] ?? ''; + if (($class === 'dokuwiki\Parsing\Parser' || $class === 'Doku_Parser') && $func === 'parse') { + $caller = $trace[$i + 1] ?? array(); + $callerFunc = $caller['function'] ?? ''; + if ($callerFunc !== 'p_get_instructions' && $callerFunc !== 'getInstructions') { + return true; + } + } + } + return false; + } } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/bibtex.php b/bibtex.php index 04b335f..bb52851 100644 --- a/bibtex.php +++ b/bibtex.php @@ -49,6 +49,15 @@ private function addBibtexMode($mode) { $this->addMode($mode->getName(), $mode); } + /** + * Override addMode to avoid DokuWiki Mort dependency injection + * which expects a DokuWiki ModeRegistry and Handler. + */ + public function addMode($name, \dokuwiki\Parsing\ParserMode\AbstractMode $Mode) { + $Mode->setLexer($this->lexer); + $this->modes[$name] = $Mode; + } + /** * */ @@ -83,26 +92,35 @@ class refnotes_bibtex_lexer extends \dokuwiki\Parsing\Lexer\Lexer { */ public function parse($text) { $lastMode = ''; + // Offset tracking is required by DokuWiki Mort Lexer which doesn't slice the input string + $offset = 0; + + // Reset the mode stack for DokuWiki Mort lexer + $this->modeStack = new \dokuwiki\Parsing\Lexer\StateStack('base'); - while (is_array($parsed = $this->reduce($text))) { + while (is_array($parsed = $this->reduce($text, $offset))) { list($unmatched, $matched, $mode) = $parsed; + $matchPos = $offset + strlen($unmatched); - if (!$this->dispatchTokens($unmatched, $matched, $mode, 0, 0)) { + if (!$this->dispatchTokens($unmatched, $matched, $mode, $offset, $matchPos)) { return false; } + $newOffset = $matchPos + strlen($matched); + if (empty($unmatched) && empty($matched) && ($lastMode == $this->modeStack->getCurrent())) { return false; } $lastMode = $this->modeStack->getCurrent(); + $offset = $newOffset; } if (!$parsed) { return false; } - return $this->invokeHandler($text, DOKU_LEXER_UNMATCHED, 0); + return $this->invokeHandler(substr($text, $offset), DOKU_LEXER_UNMATCHED, $offset); } /** @@ -141,6 +159,13 @@ public function __construct() { $this->exitPattern = array(); } + /** + * Required by AbstractMode but not used by refnotes custom parser. + */ + public function handle($match, $state, $pos, Doku_Handler $handler) { + return false; + } + /** * */ @@ -218,7 +243,7 @@ public function __construct($type) { list($open, $close) = ($type == 'parented') ? array('\(', '\)') : array('{', '}'); - $this->entryPattern[] = '^@\w+\s*' . $open . '(?=.*' . $close . ')'; + $this->entryPattern[] = '@\w+\s*' . $open . '(?=.*' . $close . ')'; $this->exitPattern[] = '\s*(?:' . $close . '|(?=@))'; $this->allowedModes = array('field'); @@ -234,7 +259,7 @@ class refnotes_bibtex_field_mode extends refnotes_bibtex_mode { public function __construct() { parent::__construct(); - $this->entryPattern[] = '^\s*\w[\w-]+\s*=\s*'; + $this->entryPattern[] = '\s*\w[\w-]+\s*=\s*'; $this->exitPattern[] = '\s*(?:,|(?=[\)}@]))'; $this->allowedModes = array('integer_value', 'string_value_quoted', 'string_value_braced', 'concatenation'); @@ -250,7 +275,7 @@ class refnotes_bibtex_integer_value_mode extends refnotes_bibtex_mode { public function __construct() { parent::__construct(); - $this->specialPattern[] = '^\d+'; + $this->specialPattern[] = '\G\d+'; } } @@ -268,7 +293,7 @@ public function __construct($type) { list($open, $close, $exit) = ($type == 'quoted') ? array('"', '"', '"') : array('{', '}', '(?:}|(?=@))'); - $this->entryPattern[] = '^' . $open . '(?=.*' . $close . ')'; + $this->entryPattern[] = $open . '(?=.*' . $close . ')'; $this->exitPattern[] = $exit; $this->allowedModes = array('nested_braces_' . $type); diff --git a/core.php b/core.php index 887579a..5d9d0e0 100644 --- a/core.php +++ b/core.php @@ -67,13 +67,39 @@ public function enterParsingContext() { public function exitParsingContext($handler) { $this->handler = $handler; - unset($this->context[count($this->context) - 1]); + // Fail-safe: NEVER pop the base context, guaranteeing we always have a context to fallback to. + if (count($this->context) > 1) { + unset($this->context[count($this->context) - 1]); + } } /** * */ public function getInstructions($text) { + if (method_exists($this->handler, 'getModeRegistry')) { + $registry = $this->handler->getModeRegistry(); + $calls = $registry->withSubParser( + array(), + array(), + function ($subParser) use ($text) { + $subParser->getHandler()->reset(); + $subParser->parse($text); + return $subParser->getHandler()->calls; + } + ); + + $filtered = array(); + if (is_array($calls)) { + foreach ($calls as $call) { + if ($call[0] == 'document_start' || $call[0] == 'document_end') continue; + if ($call[0] == 'p_open' || $call[0] == 'p_close') continue; + $filtered[] = $call; + } + } + return $filtered; + } + $this->callWriter = new refnotes_nested_call_writer($this->handler->getCallWriter(), $this->handler); $this->callWriter->connect(); diff --git a/database.php b/database.php index 89324c3..c2ebb28 100644 --- a/database.php +++ b/database.php @@ -105,10 +105,24 @@ private function normalizeKeyText($text) { private function loadPages() { global $conf; - if (file_exists($conf['indexdir'] . '/page.idx')) { - require_once(DOKU_INC . 'inc/indexer.php'); + $pageIndex = array(); + + if (class_exists('\dokuwiki\Search\Indexer')) { + $indexer = new \dokuwiki\Search\Indexer(); + if (method_exists($indexer, 'getAllPages')) { + $pageIndex = $indexer->getAllPages(); + } elseif (method_exists($indexer, 'getPages')) { + $pageIndex = $indexer->getPages(); + } + } elseif (file_exists($conf['indexdir'] . '/page.idx')) { + if (file_exists(DOKU_INC . 'inc/indexer.php')) { + require_once(DOKU_INC . 'inc/indexer.php'); + } $pageIndex = idx_getIndex('page', ''); + } + + if (!empty($pageIndex)) { $namespace = refnotes_configuration::getSetting('reference-db-namespace'); $namespacePattern = '/^' . trim($namespace, ':') . ':/'; $cache = new refnotes_reference_database_cache();