From c7c8b4929bcbdbb9cc703d9d00014ad3f5884879 Mon Sep 17 00:00:00 2001 From: Zander Thannhauser Date: Sun, 23 Nov 2025 16:45:07 -0600 Subject: [PATCH] added tom cells --- Michelf/Markdown.php | 196 +++++++++++++--- Michelf/MarkdownExtra.php | 331 +++++++++++++++++++++++++-- Readme.md | 458 +++++--------------------------------- Readme.php | 7 +- flake.lock | 77 +++++++ flake.nix | 35 +++ 6 files changed, 644 insertions(+), 460 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/Michelf/Markdown.php b/Michelf/Markdown.php index 746a2d0..bb86414 100644 --- a/Michelf/Markdown.php +++ b/Michelf/Markdown.php @@ -29,16 +29,19 @@ class Markdown implements MarkdownInterface { * @param string $text * @return string */ - public static function defaultTransform(string $text): string { + public static function defaultTransform(string $text): string + { // Take parser class on which this function was called. $parser_class = static::class; // Try to take parser from the static parser list static $parser_list; + $parser =& $parser_list[$parser_class]; // Create the parser it not already set - if (!$parser) { + if (!$parser) + { $parser = new $parser_class; } @@ -46,6 +49,8 @@ class Markdown implements MarkdownInterface { return $parser->transform($text); } + private int $debug_depth = 0; + /** * Configuration variables */ @@ -142,7 +147,10 @@ class Markdown implements MarkdownInterface { * Constructor function. Initialize appropriate member variables. * @return void */ - public function __construct() { + public function __construct() + { + $this->enter(__FUNCTION__); + $this->_initDetab(); $this->prepareItalicsAndBold(); @@ -160,6 +168,8 @@ class Markdown implements MarkdownInterface { asort($this->document_gamut); asort($this->block_gamut); asort($this->span_gamut); + + $this->exit(__FUNCTION__); } @@ -184,13 +194,18 @@ class Markdown implements MarkdownInterface { * Called before the transformation process starts to setup parser states. * @return void */ - protected function setup() { + protected function setup() + { + $this->enter(__FUNCTION__); + // Clear global hashes. $this->urls = $this->predef_urls; $this->titles = $this->predef_titles; $this->html_hashes = array(); $this->in_anchor = false; $this->in_emphasis_processing = false; + + $this->exit(__FUNCTION__); } /** @@ -204,6 +219,21 @@ class Markdown implements MarkdownInterface { $this->html_hashes = array(); } + public function enter(string $text) + { + // printf("%*s<%s>\n", $this->debug_depth, "", $text); $this->debug_depth += 1; + } + + public function exit(string $text) + { + // $this->debug_depth -= 1; printf("%*s\n", $this->debug_depth, "", $text); + } + + public function zprint(string $text) + { + // printf("%*s%s\n", $this->debug_depth, "", $text); + } + /** * Main function. Performs some preprocessing on the input text and pass * it through the document gamut. @@ -213,7 +243,10 @@ class Markdown implements MarkdownInterface { * @param string $text * @return string */ - public function transform(string $text): string { + public function transform(string $text): string + { + $this->enter(__FUNCTION__); + $this->setup(); # Remove UTF-8 BOM and marker character in input, if present. @@ -229,7 +262,6 @@ class Markdown implements MarkdownInterface { # Convert all tabs to spaces. $text = $this->detab($text); - # Turn block-level HTML blocks into hash entries $text = $this->hashHTMLBlocks($text); # Strip any lines consisting only of spaces and tabs. @@ -239,12 +271,15 @@ class Markdown implements MarkdownInterface { $text = preg_replace('/^[ ]+$/m', '', $text); # Run document gamut methods. - foreach ($this->document_gamut as $method => $priority) { + foreach ($this->document_gamut as $method => $priority) + { $text = $this->$method($text); } $this->teardown(); - + + $this->exit(__FUNCTION__); + return $text . "\n"; } @@ -264,7 +299,8 @@ class Markdown implements MarkdownInterface { * @return string */ protected function stripLinkDefinitions($text) { - + $this->enter(__FUNCTION__); + $less_than_tab = $this->tab_width - 1; // Link defs are in the form: ^[id]: url "optional title" @@ -293,6 +329,9 @@ class Markdown implements MarkdownInterface { array($this, '_stripLinkDefinitions_callback'), $text ); + + $this->exit(__FUNCTION__); + return $text; } @@ -301,12 +340,19 @@ class Markdown implements MarkdownInterface { * @param array $matches * @return string */ - protected function _stripLinkDefinitions_callback($matches) { + protected function _stripLinkDefinitions_callback($matches) + { + $this->enter(__FUNCTION__); $link_id = strtolower($matches[1]); + $url = $matches[2] == '' ? $matches[3] : $matches[2]; + $this->urls[$link_id] = $url; + $this->titles[$link_id] =& $matches[4]; - return ''; // String that will replace the block + + $this->exit(__FUNCTION__); + return ''; } /** @@ -314,8 +360,12 @@ class Markdown implements MarkdownInterface { * @param string $text * @return string */ - protected function hashHTMLBlocks($text) { - if ($this->no_markup) { + protected function hashHTMLBlocks($text) + { + $this->enter(__FUNCTION__); + if ($this->no_markup) + { + $this->exit(__FUNCTION__); return $text; } @@ -459,6 +509,7 @@ class Markdown implements MarkdownInterface { $text ); + $this->exit(__FUNCTION__); return $text; } @@ -496,6 +547,7 @@ class Markdown implements MarkdownInterface { static $i = 0; $key = "$boundary\x1A" . ++$i . $boundary; $this->html_hashes[$key] = $text; + return $key; // String that will replace the tag. } @@ -532,9 +584,16 @@ class Markdown implements MarkdownInterface { * @param string $text * @return string */ - protected function runBlockGamut($text) { + protected function runBlockGamut($text) + { + $this->enter(__FUNCTION__); + $text = $this->hashHTMLBlocks($text); - return $this->runBasicBlockGamut($text); + + $retval = $this->runBasicBlockGamut($text); + + $this->exit(__FUNCTION__); + return $retval; } /** @@ -545,8 +604,10 @@ class Markdown implements MarkdownInterface { * @param string $text * @return string */ - protected function runBasicBlockGamut($text) { - + protected function runBasicBlockGamut($text) + { + $this->enter(__FUNCTION__); + foreach ($this->block_gamut as $method => $priority) { $text = $this->$method($text); } @@ -554,6 +615,8 @@ class Markdown implements MarkdownInterface { // Finally form paragraph and restore hashed blocks. $text = $this->formParagraphs($text); + $this->exit(__FUNCTION__); + return $text; } @@ -605,11 +668,15 @@ class Markdown implements MarkdownInterface { * @param string $text * @return string */ - protected function runSpanGamut($text) { + protected function runSpanGamut($text) + { + $this->enter(__FUNCTION__); + foreach ($this->span_gamut as $method => $priority) { $text = $this->$method($text); } + $this->exit(__FUNCTION__); return $text; } @@ -618,14 +685,20 @@ class Markdown implements MarkdownInterface { * @param string $text * @return string */ - protected function doHardBreaks($text) { + protected function doHardBreaks($text) + { + $this->enter(__FUNCTION__); + if ($this->hard_wrap) { - return preg_replace_callback('/ *\n/', + $retval = preg_replace_callback('/ *\n/', array($this, '_doHardBreaks_callback'), $text); } else { - return preg_replace_callback('/ {2,}\n/', + $retval = preg_replace_callback('/ {2,}\n/', array($this, '_doHardBreaks_callback'), $text); } + + $this->exit(__FUNCTION__); + return $retval; } /** @@ -642,10 +715,15 @@ class Markdown implements MarkdownInterface { * @param string $text * @return string */ - protected function doAnchors($text) { - if ($this->in_anchor) { + protected function doAnchors($text) + { + $this->enter(__FUNCTION__); + + if ($this->in_anchor) + { return $text; } + $this->in_anchor = true; // First, handle reference-style links: [link text] [id] @@ -703,6 +781,8 @@ class Markdown implements MarkdownInterface { array($this, '_doAnchors_reference_callback'), $text); $this->in_anchor = false; + + $this->exit(__FUNCTION__); return $text; } @@ -711,7 +791,10 @@ class Markdown implements MarkdownInterface { * @param array $matches * @return string */ - protected function _doAnchors_reference_callback($matches) { + protected function _doAnchors_reference_callback($matches) + { + $this->enter(__FUNCTION__); + $whole_match = $matches[1]; $link_text = $matches[2]; $link_id =& $matches[3]; @@ -742,6 +825,9 @@ class Markdown implements MarkdownInterface { } else { $result = $whole_match; } + + $this->exit(__FUNCTION__); + return $result; } @@ -750,7 +836,10 @@ class Markdown implements MarkdownInterface { * @param array $matches * @return string */ - protected function _doAnchors_inline_callback($matches) { + protected function _doAnchors_inline_callback($matches) + { + $this->enter(__FUNCTION__); + $link_text = $this->runSpanGamut($matches[2]); $url = $matches[3] === '' ? $matches[4] : $matches[3]; $title =& $matches[7]; @@ -773,6 +862,7 @@ class Markdown implements MarkdownInterface { $link_text = $this->runSpanGamut($link_text); $result .= ">$link_text"; + $this->exit(__FUNCTION__); return $this->hashPart($result); } @@ -781,7 +871,10 @@ class Markdown implements MarkdownInterface { * @param string $text * @return string */ - protected function doImages($text) { + protected function doImages($text) + { + $this->enter(__FUNCTION__); + // First, handle reference-style labeled images: ![alt text][id] $text = preg_replace_callback('{ ( # wrap whole match in $1 @@ -827,6 +920,7 @@ class Markdown implements MarkdownInterface { }xs', array($this, '_doImages_inline_callback'), $text); + $this->exit(__FUNCTION__); return $text; } @@ -835,7 +929,9 @@ class Markdown implements MarkdownInterface { * @param array $matches * @return string */ - protected function _doImages_reference_callback($matches) { + protected function _doImages_reference_callback($matches) + { + $this->enter(__FUNCTION__); $whole_match = $matches[1]; $alt_text = $matches[2]; $link_id = strtolower($matches[3]); @@ -860,6 +956,7 @@ class Markdown implements MarkdownInterface { $result = $whole_match; } + $this->exit(__FUNCTION__); return $result; } @@ -891,7 +988,10 @@ class Markdown implements MarkdownInterface { * @param string $text * @return string */ - protected function doHeaders($text) { + protected function doHeaders($text) + { + $this->enter(__FUNCTION__); + /** * Setext-style headers: * Header 1 @@ -921,6 +1021,7 @@ class Markdown implements MarkdownInterface { }xm', array($this, '_doHeaders_callback_atx'), $text); + $this->exit(__FUNCTION__); return $text; } @@ -929,7 +1030,10 @@ class Markdown implements MarkdownInterface { * @param array $matches * @return string */ - protected function _doHeaders_callback_setext($matches) { + protected function _doHeaders_callback_setext($matches) + { + $this->enter(__FUNCTION__); + // Terrible hack to check we haven't found an empty list item. if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) { return $matches[0]; @@ -941,6 +1045,8 @@ class Markdown implements MarkdownInterface { $idAtt = $this->_generateIdFromHeaderValue($matches[1]); $block = "".$this->runSpanGamut($matches[1]).""; + + $this->exit(__FUNCTION__); return "\n" . $this->hashBlock($block) . "\n\n"; } @@ -949,7 +1055,10 @@ class Markdown implements MarkdownInterface { * @param array $matches * @return string */ - protected function _doHeaders_callback_atx($matches) { + protected function _doHeaders_callback_atx($matches) + { + print("_doHeaders_callback_atx\n"); + // ID attribute generation $idAtt = $this->_generateIdFromHeaderValue($matches[2]); @@ -985,7 +1094,10 @@ class Markdown implements MarkdownInterface { * @param string $text * @return string */ - protected function doLists($text) { + protected function doLists($text) + { + $this->enter(__FUNCTION__); + $less_than_tab = $this->tab_width - 1; // Re-usable patterns to match list item bullets and number markers: @@ -1044,6 +1156,7 @@ class Markdown implements MarkdownInterface { } } + $this->exit(__FUNCTION__); return $text; } @@ -1052,7 +1165,8 @@ class Markdown implements MarkdownInterface { * @param array $matches * @return string */ - protected function _doLists_callback($matches) { + protected function _doLists_callback($matches) + { // Re-usable patterns to match list item bullets and number markers: $marker_ul_re = '[*+-]'; $marker_ol_re = '\d+[\.]'; @@ -1420,7 +1534,10 @@ class Markdown implements MarkdownInterface { * @param string $text * @return string */ - protected function doBlockQuotes($text) { + protected function doBlockQuotes($text) + { + $this->enter(__FUNCTION__); + $text = preg_replace_callback('/ ( # Wrap whole match in $1 (?> @@ -1433,6 +1550,7 @@ class Markdown implements MarkdownInterface { /xm', array($this, '_doBlockQuotes_callback'), $text); + $this->exit(__FUNCTION__); return $text; } @@ -1474,7 +1592,10 @@ class Markdown implements MarkdownInterface { * @param boolean $wrap_in_p Whether paragraphs should be wrapped in

tags * @return string */ - protected function formParagraphs($text, $wrap_in_p = true) { + protected function formParagraphs($text, $wrap_in_p = true) + { + $this->enter(__FUNCTION__); + // Strip leading and trailing lines: $text = preg_replace('/\A\n+|\n+\z/', '', $text); @@ -1535,6 +1656,7 @@ class Markdown implements MarkdownInterface { } } + $this->exit(__FUNCTION__); return implode("\n\n", $grafs); } @@ -1717,7 +1839,10 @@ class Markdown implements MarkdownInterface { * @param string $str * @return string */ - protected function parseSpan($str) { + protected function parseSpan($str) + { + $this->enter(__FUNCTION__); + $output = ''; $span_re = '{ @@ -1766,6 +1891,7 @@ class Markdown implements MarkdownInterface { } } + $this->exit(__FUNCTION__); return $output; } diff --git a/Michelf/MarkdownExtra.php b/Michelf/MarkdownExtra.php index 78e265d..3dc59b6 100644 --- a/Michelf/MarkdownExtra.php +++ b/Michelf/MarkdownExtra.php @@ -106,7 +106,10 @@ class MarkdownExtra extends \Michelf\Markdown { * Constructor function. Initialize the parser object. * @return void */ - public function __construct() { + public function __construct() + { + $this->enter(__FUNCTION__); + // Add extra escapable characters before parent constructor // initialize the table. $this->escape_chars .= ':|'; @@ -116,21 +119,27 @@ class MarkdownExtra extends \Michelf\Markdown { $this->document_gamut += array( "doFencedCodeBlocks" => 5, "stripFootnotes" => 15, + "stripTomCells" => 16, "stripAbbreviations" => 25, "appendFootnotes" => 50, ); + $this->block_gamut += array( "doFencedCodeBlocks" => 5, "doTables" => 15, "doDefLists" => 45, ); + $this->span_gamut += array( "doFootnotes" => 5, "doAbbreviations" => 70, ); $this->enhanced_ordered_list = true; + parent::__construct(); + + $this->exit(__FUNCTION__); } @@ -144,6 +153,10 @@ class MarkdownExtra extends \Michelf\Markdown { protected array $abbr_desciptions = array(); protected string $abbr_word_re = ''; + protected array $tom_cells = array(); + protected array $tom_cells_type = array(); + protected array $tom_cells_stack = array(); + /** * Give the current footnote number. */ @@ -157,7 +170,10 @@ class MarkdownExtra extends \Michelf\Markdown { /** * Setting up Extra-specific variables. */ - protected function setup() { + protected function setup() + { + $this->enter(__FUNCTION__); + parent::setup(); $this->footnotes = array(); @@ -169,18 +185,26 @@ class MarkdownExtra extends \Michelf\Markdown { $this->footnote_counter = 1; $this->footnotes_assembled = null; + $this->tom_cells = array(); + $this->tom_cells_type = array(); + $this->tom_cells_stack = array(); + foreach ($this->predef_abbr as $abbr_word => $abbr_desc) { if ($this->abbr_word_re) $this->abbr_word_re .= '|'; $this->abbr_word_re .= preg_quote($abbr_word); $this->abbr_desciptions[$abbr_word] = trim($abbr_desc); } + + $this->exit(__FUNCTION__); } /** * Clearing Extra-specific variables. */ protected function teardown() { + $this->enter(__FUNCTION__); + $this->footnotes = array(); $this->footnotes_ordered = array(); $this->footnotes_ref_count = array(); @@ -192,6 +216,8 @@ class MarkdownExtra extends \Michelf\Markdown { $this->footnotes_assembled = null; parent::teardown(); + + $this->exit(__FUNCTION__); } @@ -270,7 +296,10 @@ class MarkdownExtra extends \Michelf\Markdown { * @param string $text * @return string */ - protected function stripLinkDefinitions($text) { + protected function stripLinkDefinitions($text) + { + $this->enter(__FUNCTION__); + $less_than_tab = $this->tab_width - 1; // Link defs are in the form: ^[id]: url "optional title" @@ -299,6 +328,9 @@ class MarkdownExtra extends \Michelf\Markdown { }xm', array($this, '_stripLinkDefinitions_callback'), $text); + + $this->exit(__FUNCTION__); + return $text; } @@ -307,12 +339,18 @@ class MarkdownExtra extends \Michelf\Markdown { * @param array $matches * @return string */ - protected function _stripLinkDefinitions_callback($matches) { + protected function _stripLinkDefinitions_callback($matches) + { + $this->enter(__FUNCTION__); + $link_id = strtolower($matches[1]); $url = $matches[2] == '' ? $matches[3] : $matches[2]; $this->urls[$link_id] = $url; $this->titles[$link_id] =& $matches[4]; $this->ref_attr[$link_id] = $this->doExtraAttributes("", $dummy =& $matches[5]); + + $this->exit(__FUNCTION__); + return ''; // String that will replace the block } @@ -1055,6 +1093,7 @@ class MarkdownExtra extends \Michelf\Markdown { * @return string */ protected function doHeaders($text) { + $this->enter(__FUNCTION__); // Setext-style headers: // Header 1 {#header1} // ======== @@ -1089,6 +1128,8 @@ class MarkdownExtra extends \Michelf\Markdown { }xm', array($this, '_doHeaders_callback_atx'), $text); + $this->exit(__FUNCTION__); + return $text; } @@ -1104,7 +1145,8 @@ class MarkdownExtra extends \Michelf\Markdown { $level = $matches[3][0] === '=' ? 1 : 2; - $defaultId = is_callable($this->header_id_func) ? call_user_func($this->header_id_func, $matches[1]) : null; + $defaultId = is_callable($this->header_id_func) ? + call_user_func($this->header_id_func, $matches[1]) : null; $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[2], $defaultId); $block = "" . $this->runSpanGamut($matches[1]) . ""; @@ -1116,10 +1158,12 @@ class MarkdownExtra extends \Michelf\Markdown { * @param array $matches * @return string */ - protected function _doHeaders_callback_atx($matches) { + protected function _doHeaders_callback_atx($matches) + { $level = strlen($matches[1]); - $defaultId = is_callable($this->header_id_func) ? call_user_func($this->header_id_func, $matches[2]) : null; + $defaultId = is_callable($this->header_id_func) ? + call_user_func($this->header_id_func, $matches[2]) : null; $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[3], $defaultId); $block = "" . $this->runSpanGamut($matches[2]) . ""; return "\n" . $this->hashBlock($block) . "\n\n"; @@ -1130,7 +1174,10 @@ class MarkdownExtra extends \Michelf\Markdown { * @param string $text * @return string */ - protected function doTables($text) { + protected function doTables($text) + { + $this->enter(__FUNCTION__); + $less_than_tab = $this->tab_width - 1; // Find tables with leading pipe. // @@ -1184,6 +1231,8 @@ class MarkdownExtra extends \Michelf\Markdown { }xm', array($this, '_DoTable_callback'), $text); + $this->exit(__FUNCTION__); + return $text; } @@ -1208,12 +1257,15 @@ class MarkdownExtra extends \Michelf\Markdown { * @param string $alignname * @return string */ - protected function _doTable_makeAlignAttr($alignname) { - if (empty($this->table_align_class_tmpl)) { - return " align=\"$alignname\""; + protected function _doTable_makeAlignAttr($alignname) + { + if (empty($this->table_align_class_tmpl)) + { + return " style='text-align: $alignname'"; } $classname = str_replace('%%', $alignname, $this->table_align_class_tmpl); + return " class=\"$classname\""; } @@ -1222,11 +1274,17 @@ class MarkdownExtra extends \Michelf\Markdown { * @param array $matches * @return string */ - protected function _doTable_callback($matches) { + protected function _doTable_callback($matches) + { + $this->enter(__FUNCTION__); + + // array_shift($matches); + $head = $matches[1]; $underline = $matches[2]; $content = $matches[3]; $id_class = $matches[4] ?? null; + $column_tags = []; $attr = []; // Remove any tailing pipes for each line. @@ -1236,15 +1294,48 @@ class MarkdownExtra extends \Michelf\Markdown { // Reading alignement from header underline. $separators = preg_split('/ *[|] */', $underline); - foreach ($separators as $n => $s) { + foreach ($separators as $n => $s) + { if (preg_match('/^ *-+: *$/', $s)) + { + $column_tags[$n] = "td"; $attr[$n] = $this->_doTable_makeAlignAttr('right'); + } else if (preg_match('/^ *:-+: *$/', $s)) + { + $column_tags[$n] = "td"; $attr[$n] = $this->_doTable_makeAlignAttr('center'); + } else if (preg_match('/^ *:-+ *$/', $s)) + { + $column_tags[$n] = "td"; $attr[$n] = $this->_doTable_makeAlignAttr('left'); - else + } + else if (preg_match('/^ *=+: *$/', $s)) + { + $column_tags[$n] = "th"; + $attr[$n] = $this->_doTable_makeAlignAttr('right'); + } + else if (preg_match('/^ *:=+: *$/', $s)) + { + $column_tags[$n] = "th"; + $attr[$n] = $this->_doTable_makeAlignAttr('center'); + } + else if (preg_match('/^ *:=+ *$/', $s)) + { + $column_tags[$n] = "th"; + $attr[$n] = $this->_doTable_makeAlignAttr('left'); + } + else if (preg_match('/^ *=+ *$/', $s)) + { + $column_tags[$n] = "th"; $attr[$n] = ''; + } + else + { + $column_tags[$n] = "td"; + $attr[$n] = ''; + } } // Parsing span elements, including code spans, character escapes, @@ -1259,8 +1350,41 @@ class MarkdownExtra extends \Michelf\Markdown { $text = "\n"; $text .= "\n"; $text .= "\n"; - foreach ($headers as $n => $header) { - $text .= " " . $this->runSpanGamut(trim($header)) . "\n"; + foreach ($headers as $n => $header) + { + if (preg_match('/\[([a-zA-Z]+)\]/', $header, $matches)) + { + $label = $matches[1]; + + if ( !in_array($label, $this->tom_cells_stack) + && array_key_exists($label, $this->tom_cells)) + { + array_push($this->tom_cells_stack, $label); + + $local_content = $this->tom_cells[$label]; + + if ($this->tom_cells_type[$label] == "span") + { + $processed = $this->runSpanGamut($local_content); + } + else + { + $processed = $this->runBasicBlockGamut($local_content); + } + + array_pop($this->tom_cells_stack); + + $text .= " " . $processed . "\n"; + } + else + { + $text .= " " . $header . "\n"; + } + } + else + { + $text .= " " . $this->runSpanGamut(trim($header)) . "\n"; + } } $text .= "\n"; $text .= "\n"; @@ -1269,7 +1393,8 @@ class MarkdownExtra extends \Michelf\Markdown { $rows = explode("\n", trim($content, "\n")); $text .= "\n"; - foreach ($rows as $row) { + foreach ($rows as $row) + { // Parsing span elements, including code spans, character escapes, // and inline HTML tags, so that pipes inside those gets ignored. $row = $this->parseSpan($row); @@ -1279,14 +1404,50 @@ class MarkdownExtra extends \Michelf\Markdown { $row_cells = array_pad($row_cells, $col_count, ''); $text .= "\n"; - foreach ($row_cells as $n => $cell) { - $text .= " " . $this->runSpanGamut(trim($cell)) . "\n"; + foreach ($row_cells as $n => $cell) + { + if (preg_match('/ *\[([a-zA-Z]+)\] */', $cell, $matches)) + { + $label = $matches[1]; + + if ( !in_array($label, $this->tom_cells_stack) + && array_key_exists($label, $this->tom_cells)) + { + array_push($this->tom_cells_stack, $label); + + $local_content = $this->tom_cells[$label]; + + if ($this->tom_cells_type[$label] == "span") + { + $processed = $this->runSpanGamut($local_content); + } + else + { + $processed = $this->runBasicBlockGamut($local_content); + } + + array_pop($this->tom_cells_stack); + + $text .= " <{$column_tags[$n]}$attr[$n]>" . $processed . "\n"; + } + else + { + $text .= " <{$column_tags[$n]}$attr[$n]>" . $cell . "\n"; + } + } + else + { + $text .= " <{$column_tags[$n]}$attr[$n]>" . $this->runSpanGamut(trim($cell)) . "\n"; + } } + $text .= "\n"; } $text .= "\n"; $text .= ""; + $this->exit(__FUNCTION__); + return $this->hashBlock($text) . "\n"; } @@ -1295,7 +1456,10 @@ class MarkdownExtra extends \Michelf\Markdown { * @param string $text * @return string */ - protected function doDefLists($text) { + protected function doDefLists($text) + { + $this->enter(__FUNCTION__); + $less_than_tab = $this->tab_width - 1; // Re-usable pattern to match any entire dl list: @@ -1332,6 +1496,8 @@ class MarkdownExtra extends \Michelf\Markdown { }mx', array($this, '_doDefLists_callback'), $text); + $this->exit(__FUNCTION__); + return $text; } @@ -1341,6 +1507,8 @@ class MarkdownExtra extends \Michelf\Markdown { * @return string */ protected function _doDefLists_callback($matches) { + $this->enter(__FUNCTION__); + // Re-usable patterns to match list item bullets and number markers: $list = $matches[1]; @@ -1348,6 +1516,9 @@ class MarkdownExtra extends \Michelf\Markdown { // paragraph for the last item in a list, if necessary: $result = trim($this->processDefListItems($list)); $result = "

\n" . $result . "\n
"; + + $this->exit(__FUNCTION__); + return $this->hashBlock($result) . "\n\n"; } @@ -1358,6 +1529,8 @@ class MarkdownExtra extends \Michelf\Markdown { * @return string */ protected function processDefListItems($list_str) { + $this->enter(__FUNCTION__); + $less_than_tab = $this->tab_width - 1; @@ -1395,6 +1568,8 @@ class MarkdownExtra extends \Michelf\Markdown { }xm', array($this, '_processDefListItems_callback_dd'), $list_str); + $this->exit(__FUNCTION__); + return $list_str; } @@ -1404,12 +1579,17 @@ class MarkdownExtra extends \Michelf\Markdown { * @return string */ protected function _processDefListItems_callback_dt($matches) { + $this->enter(__FUNCTION__); + $terms = explode("\n", trim($matches[1])); $text = ''; foreach ($terms as $term) { $term = $this->runSpanGamut(trim($term)); $text .= "\n
" . $term . "
"; } + + $this->exit(__FUNCTION__); + return $text . "\n"; } @@ -1419,6 +1599,8 @@ class MarkdownExtra extends \Michelf\Markdown { * @return string */ protected function _processDefListItems_callback_dd($matches) { + $this->enter(__FUNCTION__); + $leading_line = $matches[1]; $marker_space = $matches[2]; $def = $matches[3]; @@ -1434,6 +1616,8 @@ class MarkdownExtra extends \Michelf\Markdown { $def = $this->runSpanGamut($this->outdent($def)); } + $this->exit(__FUNCTION__); + return "\n
" . $def . "
\n"; } @@ -1448,7 +1632,8 @@ class MarkdownExtra extends \Michelf\Markdown { * @return string */ protected function doFencedCodeBlocks($text) { - + $this->enter(__FUNCTION__); + $text = preg_replace_callback('{ (?:\n|\A) # 1: Opening marker @@ -1478,6 +1663,8 @@ class MarkdownExtra extends \Michelf\Markdown { }xm', array($this, '_doFencedCodeBlocks_callback'), $text); + $this->exit(__FUNCTION__); + return $text; } @@ -1589,6 +1776,8 @@ class MarkdownExtra extends \Michelf\Markdown { * @return string */ protected function stripFootnotes($text) { + $this->enter(__FUNCTION__); + $less_than_tab = $this->tab_width - 1; // Link defs are in the form: [^id]: url "optional title" @@ -1609,6 +1798,9 @@ class MarkdownExtra extends \Michelf\Markdown { }xm', array($this, '_stripFootnotes_callback'), $text); + + $this->exit(__FUNCTION__); + return $text; } @@ -1617,12 +1809,85 @@ class MarkdownExtra extends \Michelf\Markdown { * @param array $matches * @return string */ - protected function _stripFootnotes_callback($matches) { + protected function _stripFootnotes_callback($matches) + { + $this->enter(__FUNCTION__); + $note_id = $this->fn_id_prefix . $matches[1]; - $this->footnotes[$note_id] = $this->outdent($matches[2]); + $content = $this->footnotes[$note_id] = $this->outdent($matches[2]); + + $this->zprint("note_id = $note_id"); + $this->zprint("content = $content"); + + $this->exit(__FUNCTION__); + return ''; // String that will replace the block } + protected function stripTomCells($text) { + $this->enter(__FUNCTION__); + + $less_than_tab = $this->tab_width - 1; + + $text = preg_replace_callback('{ + ^[ ]{0,' . $less_than_tab . '}\[(.+?)\][ ]?: [ ]* \n + + ( + (?> + (?!^\[\1\]) + .*\n+ + )+ + ) + + # Closing marker. + \[\1\][ ]* (?= \n ) + }xm', + array($this, '_stripTomCells_block_callback'), + $text); + + $text = preg_replace_callback('{ + ^[ ]{0,' . $less_than_tab . '}\[(.+?)\][ ]?: (.+) \[\1\][ ]* (?= \n ) + }xm', + array($this, '_stripTomCells_span_callback'), + $text); + + $this->exit(__FUNCTION__); + + return $text; + } + + protected function _stripTomCells_span_callback($matches) + { + $this->enter(__FUNCTION__); + + $this->zprint("matches[1] = {$matches[1]}"); + $this->zprint("matches[2] = {$matches[2]}"); + + $this->tom_cells[$matches[1]] = trim($matches[2]); + + $this->tom_cells_type[$matches[1]] = "span"; + + $this->exit(__FUNCTION__); + + return ''; + } + + protected function _stripTomCells_block_callback($matches) + { + $this->enter(__FUNCTION__); + + $this->zprint("matches[1] = {$matches[1]}"); + $this->zprint("matches[2] = {$matches[2]}"); + + $this->tom_cells[$matches[1]] = $this->outdent($matches[2]); + + $this->tom_cells_type[$matches[1]] = "block"; + + $this->exit(__FUNCTION__); + + return ''; + } + /** * Replace footnote references in $text [^id] with a special text-token * which will be replaced by the actual footnote marker in appendFootnotes. @@ -1642,11 +1907,14 @@ class MarkdownExtra extends \Michelf\Markdown { * @return string */ protected function appendFootnotes($text) { + $this->enter(__FUNCTION__); + $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', array($this, '_appendFootnotes_callback'), $text); if ( ! empty( $this->footnotes_ordered ) ) { $this->_doFootnotes(); + if ( ! $this->omit_footnotes ) { $text .= "\n\n"; $text .= "
\n"; @@ -1655,6 +1923,9 @@ class MarkdownExtra extends \Michelf\Markdown { $text .= "
"; } } + + $this->exit(__FUNCTION__); + return $text; } @@ -1665,6 +1936,8 @@ class MarkdownExtra extends \Michelf\Markdown { * @return void */ protected function _doFootnotes() { + $this->enter(__FUNCTION__); + $attr = array(); if ($this->fn_backlink_class !== "") { $class = $this->fn_backlink_class; @@ -1734,6 +2007,8 @@ class MarkdownExtra extends \Michelf\Markdown { $text .= "\n"; $this->footnotes_assembled = $text; + + $this->exit(__FUNCTION__); } /** @@ -1809,6 +2084,8 @@ class MarkdownExtra extends \Michelf\Markdown { * @return string */ protected function stripAbbreviations($text) { + $this->enter(__FUNCTION__); + $less_than_tab = $this->tab_width - 1; // Link defs are in the form: [id]*: url "optional title" @@ -1818,6 +2095,9 @@ class MarkdownExtra extends \Michelf\Markdown { }xm', array($this, '_stripAbbreviations_callback'), $text); + + $this->exit(__FUNCTION__); + return $text; } @@ -1827,6 +2107,8 @@ class MarkdownExtra extends \Michelf\Markdown { * @return string */ protected function _stripAbbreviations_callback($matches) { + $this->enter(__FUNCTION__); + $abbr_word = $matches[1]; $abbr_desc = $matches[2]; if ($this->abbr_word_re) { @@ -1834,6 +2116,9 @@ class MarkdownExtra extends \Michelf\Markdown { } $this->abbr_word_re .= preg_quote($abbr_word); $this->abbr_desciptions[$abbr_word] = trim($abbr_desc); + + $this->exit(__FUNCTION__); + return ''; // String that will replace the block } diff --git a/Readme.md b/Readme.md index b9f273a..a065384 100644 --- a/Readme.md +++ b/Readme.md @@ -1,461 +1,121 @@ -PHP Markdown -============ -![ci.yml](https://github.com/michelf/php-markdown/actions/workflows/ci.yml/badge.svg) + -based on Markdown by John Gruber - +Hello, world! -Introduction ------------- +| Header 1 | [foo] +| :------: | :-----: +| Cell 1 | Cell 2 +| [bar] | Cell 4 +| Cell 3 | [gar] -This is a library package that includes the PHP Markdown parser and its -sibling PHP Markdown Extra with additional features. -Markdown is a text-to-HTML conversion tool for web writers. Markdown -allows you to write using an easy-to-read, easy-to-write plain text -format, then convert it to structurally valid XHTML (or HTML). -"Markdown" is actually two things: a plain text markup syntax, and a -software tool, originally written in Perl, that converts the plain text -markup to HTML. PHP Markdown is a port to PHP of the original Markdown -program by John Gruber. +[foo]: +There are multiple paragraphs -* [Full documentation of the Markdown syntax]() - — Daring Fireball (John Gruber) -* [Markdown Extra syntax additions]() - — Michel Fortin +I'm another one! +[foo] +[bar]: something clever without paragraphs [bar] -Requirement ------------ +[gar]: +just one paragraph here +[gar] -This library package requires PHP 7.4 or later. +[bar]: something clever clever without paragraphs [bar] -Note: The older plugin/library hybrid package for PHP Markdown and -PHP Markdown Extra is no longer maintained but will work with PHP 4.0.5 and -later. +[gar]: +just one paragraph here here +[gar] -You might need to set pcre.backtrack_limit higher than 1 000 000 -(the default), though the default is usually fine. -Usage ------ -To use this library with Composer, first install it with: - $ composer require michelf/php-markdown + + + + + + -Then include Composer's generated vendor/autoload.php to [enable autoloading]: - require 'vendor/autoload.php'; -Without Composer, for autoloading to work, your project needs an autoloader -compatible with PSR-4 or PSR-0. See the included Readme.php file for a minimal -autoloader setup. (If you cannot use autoloading, see below.) -With class autoloading in place: + + + + - use Michelf\Markdown; - $my_html = Markdown::defaultTransform($my_text); -Markdown Extra syntax is also available the same way: + - use Michelf\MarkdownExtra; - $my_html = MarkdownExtra::defaultTransform($my_text); + -If you wish to use PHP Markdown with another text filter function -built to parse HTML, you should filter the text *after* the `transform` -function call. This is an example with [PHP SmartyPants]: - use Michelf\Markdown, Michelf\SmartyPants; - $my_html = Markdown::defaultTransform($my_text); - $my_html = SmartyPants::defaultTransform($my_html); -All these examples are using the static `defaultTransform` static function -found inside the parser class. If you want to customize the parser -configuration, you can also instantiate it directly and change some -configuration variables: - use Michelf\MarkdownExtra; - $parser = new MarkdownExtra; - $parser->fn_id_prefix = "post22-"; - $my_html = $parser->transform($my_text); -To learn more, see the full list of [configuration variables]. - [enable autoloading]: https://getcomposer.org/doc/01-basic-usage.md#autoloading - [PHP SmartyPants]: https://michelf.ca/projects/php-smartypants/ - [configuration variables]: https://michelf.ca/projects/php-markdown/configuration/ -### Usage without an autoloader -If you cannot use class autoloading, you can still use `include` or `require` -to access the parser. To load the `Michelf\Markdown` parser, do it this way: + + + + - require_once 'Michelf/Markdown.inc.php'; -Or, if you need the `Michelf\MarkdownExtra` parser: + + + + - require_once 'Michelf/MarkdownExtra.inc.php'; -While the plain `.php` files depend on autoloading to work correctly, using the -`.inc.php` files instead will eagerly load the dependencies that would be -loaded on demand if you were using autoloading. + + + + + + + + + -Public API and Versioning Policy ---------------------------------- -Version numbers are of the form *major*.*minor*.*patch*. -The public API of PHP Markdown consist of the two parser classes `Markdown` -and `MarkdownExtra`, their constructors, the `transform` and `defaultTransform` -functions and their configuration variables. The public API is stable for -a given major version number. It might get additions when the minor version -number increments. -**Protected members are not considered public API.** This is unconventional -and deserves an explanation. Incrementing the major version number every time -the underlying implementation of something changes is going to give -nonessential version numbers for the vast majority of people who just use the -parser. Protected members are meant to create parser subclasses that behave in -different ways. Very few people create parser subclasses. I don't want to -discourage it by making everything private, but at the same time I can't -guarantee any stable hook between versions if you use protected members. -**Syntax changes** will increment the minor number for new features, and the -patch number for small corrections. A *new feature* is something that needs a -change in the syntax documentation. Note that since PHP Markdown Lib includes -two parsers, a syntax change for either of them will increment the minor -number. Also note that there is nothing perfectly backward-compatible with the -Markdown syntax: all inputs are always valid, so new features always replace -something that was previously legal, although generally nonsensical to do. -Bugs ----- -To file bug reports please send email to: - -Please include with your report: (1) the example input; (2) the output you -expected; (3) the output PHP Markdown actually produced. -If you have a problem where Markdown gives you an empty result, first check -that the backtrack limit is not too low by running `php --info | grep pcre`. -See Installation and Requirement above for details. -Development and Testing ------------------------ -Pull requests for fixing bugs are welcome. Proposed new features are -going to be meticulously reviewed -- taking into account backward compatibility, -potential side effects, and future extensibility -- before deciding on -acceptance or rejection. -If you make a pull request that includes changes to the parser please add -tests for what is being changed to the `test/` directory. This can be as -simple as adding a `.text` (input) file with a corresponding `.xhtml` -(output) file to proper category under `./test/resources/`. -Traditionally tests were in a separate repository, [MDTest](https://github.com/michelf/mdtest) -but they are now located here, alongside the source code. -Donations ---------- -If you wish to make a donation that will help me devote more time to -PHP Markdown, please visit [michelf.ca/donate]. - [michelf.ca/donate]: https://michelf.ca/donate/#!Thanks%20for%20PHP%20Markdown -Version History ---------------- -PHP Markdown Lib 2.0.0 (26 Sep 2022) -* Now requiring PHP version 7.4 or later. - -* Added type annotations to configuration properties of the parser. - (Thanks to Tac Tacelosky.) - -* Fixing a TypeError in PHP 8 caused by invalid counter variable. - (Thanks to Alexey Kopytko.) - -* Composer package now excludes development files. - (Thanks to Cédric Anne.) - - -PHP Markdown Lib 1.9.1 (23 Nov 2021) - -* Now treating `
` and `` as block level so they don't - get wrapped in `

`. - (Thanks to Thomas Hochstein for the fix.) - -* Fix for unintended blank title attribute when adding supplementary attributes - to a link in Markdown Extra. - (Thanks to Richie Black for the fix.) - - -PHP Markdown Lib 1.9.0 (1 Dec 2019) - -* Added `fn_backlink_label` configuration variable to put some text in the - `aria-label` attribute. - (Thanks to Sunny Walker for the implementation.) - -* Occurances of "`^^`" in `fn_backlink_html`, `fn_backlink_class`, - `fn_backlink_title`, and `fn_backlink_label` will be replaced by the - corresponding footnote number in the HTML output. Occurances of "`%%`" will be - replaced by a number for the reference (footnotes can have multiple references). - (Thanks to Sunny Walker for the implementation.) - -* Added configuration variable `omit_footnotes`. When `true` footnotes are not - appended at the end of the generated HTML and the `footnotes_assembled` - variable will contain the HTML for the footnote list, allowing footnotes to be - moved somewhere else on the page. - (Thanks to James K. for the implementation.) - - Note: when placing the content of `footnotes_assembled` on the page, consider - adding the attribute `role="doc-endnotes"` to the `

` or `
` that will - enclose the list of footnotes so they are reachable to accessibility tools the - same way they would be with the default HTML output. - -* Fixed deprecation warnings from PHP about usage of curly braces to access - characters in text strings. - (Thanks to Remi Collet and Frans-Willem Post.) - - -PHP Markdown Lib 1.8.0 (14 Jan 2018) - -* Autoloading with Composer now uses PSR-4. - -* HTML output for Markdown Extra footnotes now include `role` attributes - with values from [WAI-ARIA](https://www.w3.org/TR/dpub-aria/) to - make them more accessible. - (Thanks to Tobias Bengfort) - -* In Markdown Extra, added the `hashtag_protection` configuration variable. - When set to `true` it prevents ATX-style headers with no space after the initial - hash from being interpreted as headers. This way your precious hashtags - are preserved. - (Thanks to Jaussoin Timothée for the implementation.) - - -PHP Markdown Lib 1.7.0 (29 Oct 2016) - -* Added a `hard_wrap` configuration variable to make all newline characters - in the text become `
` tags in the HTML output. By default, according - to the standard Markdown syntax these newlines are ignored unless they a - preceded by two spaces. Thanks to Jonathan Cohlmeyer for the implementation. - -* Improved the parsing of list items to fix problematic cases that came to - light with the addition of `hard_wrap`. This should have no effect on the - output except span-level list items that ended with two spaces (and thus - ended with a line break). - -* Added a `code_span_content_func` configuration variable which takes a - function that will convert the content of the code span to HTML. This can - be useful to implement syntax highlighting. Although contrary to its - code block equivalent, there is no syntax for specifying a language. - Credits to styxit for the implementation. - -* Fixed a Markdown Extra issue where two-space-at-end-of-line hard breaks - wouldn't work inside of HTML block elements such as `

` - where the element expects only span-level content. - -* In the parser code, switched to PHPDoc comment format. Thanks to - Robbie Averill for the help. - - -PHP Markdown Lib 1.6.0 (23 Dec 2015) - -Note: this version was incorrectly released as 1.5.1 on Dec 22, a number -that contradicted the versioning policy. - -* For fenced code blocks in Markdown Extra, can now set a class name for the - code block's language before the special attribute block. Previously, this - class name was only allowed in the absence of the special attribute block. - -* Added a `code_block_content_func` configuration variable which takes a - function that will convert the content of the code block to HTML. This is - most useful for syntax highlighting. For fenced code blocks in Markdown - Extra, the function has access to the language class name (the one outside - of the special attribute block). Credits to Mario Konrad for providing the - implementation. - -* The curled arrow character for the backlink in footnotes is now followed - by a Unicode variant selector to prevent it from being displayed in emoji - form on iOS. - - Note that in older browsers the variant selector is often interpreted as a - separate character, making it visible after the arrow. So there is now a - also a `fn_backlink_html` configuration variable that can be used to set - the link text to something else. Credits to Dana for providing the - implementation. - -* Fixed an issue in MarkdownExtra where long header lines followed by a - special attribute block would hit the backtrack limit an cause an empty - string to be returned. - - -PHP Markdown Lib 1.5.0 (1 Mar 2015) - -* Added the ability start ordered lists with a number different from 1 and - and have that reflected in the HTML output. This can be enabled with - the `enhanced_ordered_lists` configuration variable for the Markdown - parser; it is enabled by default for Markdown Extra. - Credits to Matt Gorle for providing the implementation. - -* Added the ability to insert custom HTML attributes with simple values - everywhere an extra attribute block is allowed (links, images, headers). - The value must be unquoted, cannot contains spaces and is limited to - alphanumeric ASCII characters. - Credits to Peter Droogmans for providing the implementation. - -* Added a `header_id_func` configuration variable which takes a function - that can generate an `id` attribute value from the header text. - Credits to Evert Pot for providing the implementation. - -* Added a `url_filter_func` configuration variable which takes a function - that can rewrite any link or image URL to something different. - - -PHP Markdown Lib 1.4.1 (4 May 2014) - -* The HTML block parser will now treat `

` as a block-level element - (as it should) and no longer wrap it in `

` or parse it's content with - the as Markdown syntax (although with Extra you can use `markdown="1"` - if you wish to use the Markdown syntax inside it). - -* The content of `