[TikiWiki-commits] [Git][tikiwiki/tiki][master] [FIX][ENH] enhance parserlib heading and autonumbering
"Victor Emanouilov \(@kroky\) via TikiWiki-cvs" <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <69cd047635594_3b18fd9476dd@gitlab-sidekiq-low-urgency-cpu-bound-v2-999554d69-4str5.mail> |
Victor Emanouilov pushed to branch master at Tiki Wiki CMS Groupware / Tiki
Commits:
739229c2 by Sammy Ndabo at 2026-04-01T11:34:18+00:00
[FIX][ENH] enhance parserlib heading and autonumbering
---
* [FIX] parserlib: preserve legacy heading spacing for unnumbered headings
* [FIX,ENH] parselib.php: restore numbered-heading tests and parser ids
* [FIX] formatting of heading in ParseToWysiwyg_TextTest to ensure consistent spacing
* [REM] parselib.php: Remove unused variables in ParserLib to clean up code
* Fix heading IDs in WikiParser tests for collapsible sections and ensure consistent formatting
* Enhance tests for numbered headings and improve parsing logic for complex heading structures
* Fix heading when there is no numbering
* [REF] Refactor and Remove multilevel header numbering preference and clean up related tests
* Update pref name and the reset numbering symbol
* [ENH] Parserlib: update test to expect multilevel headings only when the pref that enable it is enabled
* [ENH] parselib.php: cleaned and reviewed code
* [ENH] Wiki parser: Add ^# syntax to reset heading numbering and ensures proper sequence continuation after numbering reset
* Enhance parselib heading and autonumbering
See merge request tikiwiki/tiki!7609
- - - - -
2 changed files:
- lib/parser/parserlib.php
- lib/test/editlib/ParseToWysiwyg_TextTest.php
Changes:
=====================================
lib/parser/parserlib.php
=====================================
@@ -2530,11 +2530,9 @@ class ParserLib extends TikiDb_Bridge
$data = '';
$listbeg = [];
$divdepth = [];
- $hdr_structure = [];
- $show_title_level = [];
- $last_hdr = [];
+ $numbering = [];
+ $counters = [];
$all_anchors = [];
- $nb_last_hdr = 0;
$nb_hdrs = 0;
$nb_lists = 0;
$inTable = 0;
@@ -2548,6 +2546,16 @@ class ParserLib extends TikiDb_Bridge
$in_paragraph = 0;
$in_empty_paragraph = 0;
+ $min_level = null;
+ foreach ($lines as $line) {
+ if (preg_match('/^(!+)[\+\-]?#/', $line, $m)) {
+ $level = strlen($m[1]);
+ if ($min_level === null || $level < $min_level) {
+ $min_level = $level;
+ }
+ }
+ }
+
foreach ($lines as $line) {
// Add newlines between lines
if (isset($current_title_num)) { // Exclude the first line
@@ -2755,76 +2763,56 @@ class ParserLib extends TikiDb_Bridge
$hdrlevel = $tikilib->how_many_at_start($line, '!');
if ($litype == '!' && $hdrlevel > 0 && $hdrlevel <= 6 /* HTML has h1 to h6, but no h7 or above */) { // If the line starts with 1 to 6 exclamation marks ("!")
+ ++$nb_hdrs;
+ if (preg_match('/^!+[\+\-]?#\)/', $line)) {
+ $counters = [];
+ $line = preg_replace('/(^!+[\+\-]?)(#)\)/', '$1$2', $line);
+ }
+
+ $level = $hdrlevel;
+ $current_title_real_num = '';
+ $current_title_num = '';
+
/*
* Handle headings autonumbering syntax (i.e. !#Text, !!#Text, ...)
- * Note :
- * this needs to be done even if the current header has no '#'
- * in order to generate the right numbers when they are not specified for every headers.
- * This is the case, for example, when you want to add numbers to headers of level 2 but not to level 1
+ * Note:
+ * this needs to be done even if the current header has no '#'
+ * in order to generate the right numbers when they are not specified for every headers.
+ * This is the case, for example, when you want to add numbers to headers of level 2 but not to level 1.
*/
-
- $line_lenght = strlen($line);
-
- // Generate an array containing the squeleton of maketoc (based on headers levels)
- // i.e. hdr_structure will contain something lile this :
- // array( 1, 2, 2.1, 2.1.1, 2.1.2, 2.2, ... , X.Y.Z... )
- //
-
- $hdr_structure[$nb_hdrs] = [];
-
- // Generate the number (e.g. 1.2.1.1) of the current title, based on the previous title number :
- // - if the current title deepest level is lesser than (or equal to)
- // the deepest level of the previous title : then we increment the last level number,
- // - else : we simply add new levels with value '1' (only if the previous level number was shown),
- //
- if ($nb_last_hdr > 0 && $hdrlevel <= $nb_last_hdr) {
- $hdr_structure[$nb_hdrs] = array_slice($last_hdr, 0, $hdrlevel);
- if (! empty($show_title_level[$hdrlevel]) || ! $need_autonumbering) {
- //
- // Increment the level number only if :
- // - the last title of the same level number has a displayed number
- // or - no title has a displayed number (no autonumbering)
- //
- $hdr_structure[$nb_hdrs][$hdrlevel - 1]++;
- }
- } else {
- if ($nb_last_hdr > 0) {
- $hdr_structure[$nb_hdrs] = $last_hdr;
- }
- for ($h = 0; $h < $hdrlevel - $nb_last_hdr; $h++) {
- $hdr_structure[$nb_hdrs][$h + $nb_last_hdr] = '1';
+ $support_numbering = preg_match('/^!+[\+\-]?#/', $line);
+ if ($support_numbering) {
+ // Normalize level (so the lowest becomes level 1)
+ $relative_level = $level - $min_level + 1;
+
+ // Reset deeper levels
+ for ($i = $relative_level + 1; $i <= 10; $i++) {
+ unset($counters[$i]);
}
- }
- $show_title_level[$hdrlevel] = preg_match('/^!+[\+\-]?#/', $line);
- // Update last_hdr info for the next header
- $last_hdr = $hdr_structure[$nb_hdrs];
- $nb_last_hdr = count($last_hdr);
+ // Increment this level
+ if (! isset($counters[$relative_level])) {
+ $counters[$relative_level] = 1;
+ } else {
+ $counters[$relative_level]++;
+ }
- if (is_array($last_hdr)) {
- $current_title_real_num = implode('.', $last_hdr) . '. ';
- } else {
- $current_title_real_num = $last_hdr . '. ';
- }
+ // Ensure parent levels exist (initialize with 1 if missing)
+ for ($i = 1; $i < $relative_level; $i++) {
+ if (! isset($counters[$i])) {
+ $counters[$i] = 1;
+ }
+ }
- // Update the current title number to hide all parents levels numbers if the parent has no autonumbering
- $hideall = false;
- for ($j = $hdrlevel; $j > 0; $j--) {
- if ($hideall || empty($show_title_level[$j])) {
- unset($hdr_structure[$j - 1]);
- $hideall = true;
+ $numbering = [];
+ for ($i = 1; $i <= $relative_level; $i++) {
+ $numbering[] = $counters[$i];
}
- }
- // Store the title number to use only if it has to be shown (if the '#' char is used)
- $current_title_num = '';
- if (isset($show_title_level[$hdrlevel]) && isset($hdr_structure[$nb_hdrs])) {
- $current_title_num = $show_title_level[$hdrlevel] ? implode('.', $hdr_structure[$nb_hdrs]) . '. ' : '';
+ $current_title_num = implode('.', $numbering) . ".";
+ $current_title_real_num = $current_title_num . ' ';
}
- $nb_hdrs++;
-
-
// Close open paragraph (lists already closed above)
$this->close_blocks($data, $in_paragraph, $listbeg, $divdepth, 1, 0, 0);
// Close lower level divs if opened
@@ -2842,7 +2830,10 @@ class ParserLib extends TikiDb_Bridge
$anchor = '';
$aclose = '';
$aclose2 = '';
- $addremove = $show_title_level[$hdrlevel] ? 1 : 0; // If needed, also remove '#' sign from title beginning
+ $addremove = 0;
+ if ($support_numbering) {
+ $addremove = 1; // If needed, also remove '#' sign from title beginning
+ }
// May be special signs present after '!'s?
$divstate = substr($line, $hdrlevel, 1);
@@ -2865,14 +2856,16 @@ class ParserLib extends TikiDb_Bridge
}
// Generate the final title text
- $title_text_base = substr($line, $hdrlevel + $addremove);
- $title_text = $current_title_num . $title_text_base;
+ $title_text_base_raw = substr($line, $hdrlevel + $addremove);
+ $title_text_base = $support_numbering ? ltrim($title_text_base_raw) : $title_text_base_raw;
+ $title_text = $current_title_real_num . $title_text_base;
// Remove HTML tags from Tiki syntax
$title = preg_replace('#\<(.*?)\>#', '', $title_text);
$thisid = $this->getCleanAnchor($title, $all_anchors);
- // Collect TOC entry if any {maketoc} is present on the page
+ // Collect TOC entry if any {maketoc} is present on the page.
+ // Generates the skeleton of the TOC (e.g. 1, 2, 2.1, 2.1.1, ...).
//if ( $need_maketoc !== false ) {
$anch[] = [
'id' => $thisid,
=====================================
lib/test/editlib/ParseToWysiwyg_TextTest.php
=====================================
@@ -185,6 +185,7 @@ class EditLib_ParseToWysiwyg_TextTest extends TikiTestCase
*/
public function testNumberedHeadings(): void
{
+ // Legacy expectations to keep backward compatibility intact.
$inData = '!#Heading Level 1';
$ex = '<h1 class="showhide_heading d-flex justify-content-start" id="Heading_Level_1">1. Heading Level 1</h1>';
$out = trim($this->el->parseToWysiwyg($inData));
@@ -224,6 +225,92 @@ class EditLib_ParseToWysiwyg_TextTest extends TikiTestCase
$ex .= '<h6 class="showhide_heading d-flex justify-content-start" id="Heading_Level_6">1.1.1.1.1.1. Heading Level 6</h6>';
$out = trim($this->el->parseToWysiwyg($inData));
$this->assertEquals($ex, $out);
+
+ // Additional coverage validating extended numbering behavior.
+ $inData = <<<TEST
+!# title 1
+!!# title 1.1
+!!!# title 1.2
+!!!!# title 1.3
+!!!!!# title 1.4
+!!!!!!# title 1.4
+!# title 2
+!!# title 2.1
+TEST;
+ $ex = <<<EX
+<h1 class="showhide_heading d-flex justify-content-start" id="title_1">1. title 1</h1>
+<h2 class="showhide_heading d-flex justify-content-start" id="title_1.1">1.1. title 1.1</h2>
+<h3 class="showhide_heading d-flex justify-content-start" id="title_1.2">1.1.1. title 1.2</h3>
+<h4 class="showhide_heading d-flex justify-content-start" id="title_1.3">1.1.1.1. title 1.3</h4>
+<h5 class="showhide_heading d-flex justify-content-start" id="title_1.4">1.1.1.1.1. title 1.4</h5>
+<h6 class="showhide_heading d-flex justify-content-start" id="title_1.4_2">1.1.1.1.1.1. title 1.4</h6>
+<h1 class="showhide_heading d-flex justify-content-start" id="title_2">2. title 2</h1>
+<h2 class="showhide_heading d-flex justify-content-start" id="title_2.1">2.1. title 2.1</h2>
+EX;
+ $out = trim($this->el->parseToWysiwyg($inData));
+ $this->assertEquals($ex, $out);
+
+ // Any highest level heading should have a number 1
+ $inData = <<<TEST
+!!!# title 1
+!!!!# title 1.1
+!!!!# title 1.2
+!!!# title 2
+!!!# title 2.1
+TEST;
+ $ex = <<<EX
+<h3 class="showhide_heading d-flex justify-content-start" id="title_1">1. title 1</h3>
+<h4 class="showhide_heading d-flex justify-content-start" id="title_1.1">1.1. title 1.1</h4>
+<h4 class="showhide_heading d-flex justify-content-start" id="title_1.2">1.2. title 1.2</h4>
+<h3 class="showhide_heading d-flex justify-content-start" id="title_2">2. title 2</h3>
+<h3 class="showhide_heading d-flex justify-content-start" id="title_2.1">3. title 2.1</h3>
+EX;
+ $out = trim($this->el->parseToWysiwyg($inData));
+ $this->assertEquals($ex, $out);
+
+ // Heading reset so it starts from 1
+ $inData = <<<TEST
+!!!# title 1
+!!!# title 2
+!!!#) title 1
+!!!# title 2
+TEST;
+ $ex = <<<EX
+<h3 class="showhide_heading d-flex justify-content-start" id="title_1">1. title 1</h3>
+<h3 class="showhide_heading d-flex justify-content-start" id="title_2">2. title 2</h3>
+<h3 class="showhide_heading d-flex justify-content-start" id="title_1_2">1. title 1</h3>
+<h3 class="showhide_heading d-flex justify-content-start" id="title_2_2">2. title 2</h3>
+EX;
+ $out = trim($this->el->parseToWysiwyg($inData));
+ $this->assertEquals($ex, $out);
+
+ // More complex example combining numbered and unnumbered headings
+ $inData = <<<TEST
+! Unnumbered Heading
+!!# title 1
+!!!# title 1.1
+!!!!# title 1.2
+!!# title 2
+!!!# title 2
+
+!!!#) Title 1
+!!!# Title 2
+!!!!!# Title 1.2.1.1.
+TEST;
+ $ex = <<<EX
+<h1 class="showhide_heading d-flex justify-content-start" id="Unnumbered_Heading"> Unnumbered Heading</h1>
+<h2 class="showhide_heading d-flex justify-content-start" id="title_1">1. title 1</h2>
+<h3 class="showhide_heading d-flex justify-content-start" id="title_1.1">1.1. title 1.1</h3>
+<h4 class="showhide_heading d-flex justify-content-start" id="title_1.2">1.1.1. title 1.2</h4>
+<h2 class="showhide_heading d-flex justify-content-start" id="title_2">2. title 2</h2>
+<h3 class="showhide_heading d-flex justify-content-start" id="title_2_2">2.1. title 2</h3>
+<br />
+<h3 class="showhide_heading d-flex justify-content-start" id="Title_1">1.1. Title 1</h3>
+<h3 class="showhide_heading d-flex justify-content-start" id="Title_2">1.2. Title 2</h3>
+<h5 class="showhide_heading d-flex justify-content-start" id="Title_1.2.1.1.">1.2.1.1. Title 1.2.1.1.</h5>
+EX;
+ $out = trim($this->el->parseToWysiwyg($inData));
+ $this->assertEquals($ex, $out);
}
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/739229c27c805c273e9534676083157c3b8ab88e
--
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/739229c27c805c273e9534676083157c3b8ab88e
You're receiving this email because of your account on gitlab.com. Manage all notifications: https://gitlab.com/-/profile/notifications | Help: https://gitlab.com/help
_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs