From 092fdac8adff1c868eaf5935d9823f64f530bfdd Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Thu, 6 Jul 2023 16:30:40 +0100 Subject: [PATCH 1/2] Add support for rendering more logical classsynopsis markup while allowing the old markup to render --- phpdotnet/phd/Package/Generic/XHTML.php | 159 +++++++++++++++++++++--- phpdotnet/phd/Package/PHP/XHTML.php | 9 +- phpdotnet/phd/Render.php | 8 +- 3 files changed, 153 insertions(+), 23 deletions(-) diff --git a/phpdotnet/phd/Package/Generic/XHTML.php b/phpdotnet/phd/Package/Generic/XHTML.php index 7e3b1fcf..16e6632f 100644 --- a/phpdotnet/phd/Package/Generic/XHTML.php +++ b/phpdotnet/phd/Package/Generic/XHTML.php @@ -66,7 +66,7 @@ abstract class Package_Generic_XHTML extends Format_Abstract_XHTML { 'classname' => array( /* DEFAULT */ 'span', 'ooclass' => array( - /* DEFAULT */ 'strong', + 'classsynopsis' => 'format_classsynopsis_ooclass_classname', 'classsynopsisinfo' => 'format_classsynopsisinfo_ooclass_classname', ), ), @@ -159,16 +159,18 @@ abstract class Package_Generic_XHTML extends Format_Abstract_XHTML { 'othercredit' => 'format_div', 'ooclass' => array( /* DEFAULT */ 'span', - 'classsynopsis' => 'format_div', + 'classsynopsis' => 'format_classsynopsis_ooclass',//'format_div',// ), 'oointerface' => array( /* DEFAULT */ 'span', + 'classsynopsis' => 'format_classsynopsis_oointerface', 'classsynopsisinfo' => 'format_classsynopsisinfo_oointerface', ), 'interfacename' => array( /* DEFAULT */ 'span', 'oointerface' => array( /* DEFAULT */ 'span', + 'classsynopsis' => 'format_classsynopsis_oointerface_interfacename', 'classsynopsisinfo' => 'format_classsynopsisinfo_oointerface_interfacename', ), ), @@ -383,6 +385,7 @@ abstract class Package_Generic_XHTML extends Format_Abstract_XHTML { /* DEFAULT */ false, 'ooclass' => array( /* DEFAULT */ false, + // We keep this to work around the legacy behaviour of not displaying it 'classsynopsis' => 'format_classsynopsis_ooclass_classname_text', ), ), @@ -422,11 +425,14 @@ abstract class Package_Generic_XHTML extends Format_Abstract_XHTML { protected $cchunk = array(); /* Default Chunk variables */ private $dchunk = array( - "classsynopsis" => array( - "close" => false, - "classname" => false, - "interface" => false, // bool: true when in interface - ), + "classsynopsis" => [ + "close" => false, + "classname" => false, + "interface" => false, // bool: true when in interface + "ooclass" => false, + "oointerface" => false, + "legacy" => true, // legacy rendering + ], "classsynopsisinfo" => array( "implements" => false, "ooclass" => false, @@ -896,6 +902,7 @@ public function format_refsect($open, $name, $attrs) { return "\n"; } + /** Legacy rendering functions for class synopsis tags that wraps the definition in a class synopsis info tag */ public function format_classsynopsisinfo_oointerface($open, $name, $attrs) { if ($open) { if ($this->cchunk["classsynopsisinfo"]["ooclass"] === false) { @@ -971,29 +978,139 @@ public function format_classsynopsisinfo($open, $name, $attrs) if (isset($attrs[Reader::XMLNS_DOCBOOK]["role"]) && $attrs[Reader::XMLNS_DOCBOOK]["role"] == "comment") { return ' */'; } + + assert($this->cchunk["classsynopsis"]["legacy"] === true); $this->cchunk["classsynopsis"]["close"] = true; return ' {'; } - public function format_classsynopsis($open, $name, $attrs) { + /** Class synopsis rendering for new/better markup */ + public function format_classsynopsis_oointerface_interfacename($open, $name, $attrs, $props) + { + if ($this->cchunk["classsynopsis"]["legacy"] === true) { + return $this->transformFromMap($open, 'strong', $name, $attrs, $props); + } + if ($open) { - // Think this just needs to be set on open and it will persist - // Will remove comment after review - if ( - isset($attrs[Reader::XMLNS_DOCBOOK]["class"]) && - $attrs[Reader::XMLNS_DOCBOOK]["class"] == "interface" - ) { - $this->cchunk["classsynopsis"]["interface"] = true; + /* If there has been a class prior this means that we are the first implementing interface + * thus mark the oointerface as already been rendered as the primary tag */ + if ($this->cchunk["classsynopsis"]["ooclass"] === true) { + $this->cchunk["classsynopsis"]["oointerface"] = true; } + /** Actual interface name in bold */ + if ($this->cchunk["classsynopsis"]["oointerface"] === false) { - return '
'; + return 'interface ' . $this->transformFromMap($open, 'strong', $name, $attrs, $props); + } + /* Whitespace for next word */ + return ' '; + } + /** Actual interface name in bold */ + if ($this->cchunk["classsynopsis"]["oointerface"] === false) { + $this->cchunk["classsynopsis"]["oointerface"] = true; + return ''; + } + /** We don't wrap extended interface in a tag */ + if ($this->cchunk["classsynopsis"]['nb_list'] > 1) { + return ','; + } + return ''; + } + + public function format_classsynopsis_ooclass_classname($open, $name, $attrs, $props) + { + if ($this->cchunk["classsynopsis"]["legacy"] === true) { + return $this->transformFromMap($open, 'strong', $name, $attrs, $props); + } + + if ($open) { + /** Actual class name in bold */ + if ($this->cchunk["classsynopsis"]["ooclass"] === false) { + + return 'class ' . $this->transformFromMap($open, 'strong', $name, $attrs, $props); + } + /* Whitespace for next word */ + return ' '; + } + /** Actual class name in bold */ + if ($this->cchunk["classsynopsis"]["ooclass"] === false) { + $this->cchunk["classsynopsis"]["ooclass"] = true; + return ''; + } + /** We don't wrap extended class in a tag */ + return ''; + } + + public function format_classsynopsis_ooclass($open, $name, $attrs, $props) + { + if ($this->cchunk["classsynopsis"]["legacy"] === true) { + return $this->format_div($open, $name, $attrs, $props); } - if ($this->cchunk["classsynopsis"]["close"] === true) { - $this->cchunk["classsynopsis"]["close"] = false; + /* Close list of classes + interfaces by "opening" class def with { */ + if (!$open && --$this->cchunk["classsynopsis"]['nb_list'] === 0) { + return ' {
'; + } + return ''; + } + + public function format_classsynopsis_oointerface($open, $name, $attrs, $props) + { + /* Used to be converted to a span */ + if ($this->cchunk["classsynopsis"]["legacy"] === true) { + return $this->transformFromMap($open, 'span', $name, $attrs, $props); + } + + /* Close list of classes + interfaces by "opening" class def with { */ + if (!$open) { + if (--$this->cchunk["classsynopsis"]['nb_list'] === 0) { + return ' {'; + } + } + return ''; + } + + public function format_classsynopsis($open, $name, $attrs, $props) { + $this->cchunk["classsynopsis"] = $this->dchunk["classsynopsis"]; + + /** Legacy presentation does not use the class attribute */ + $this->cchunk["classsynopsis"]['legacy'] = !isset($attrs[Reader::XMLNS_DOCBOOK]["class"]); + + if ($this->cchunk["classsynopsis"]['legacy']) { + if ($open) { + // Think this just needs to be set on open and it will persist + // Will remove comment after review + if ( + isset($attrs[Reader::XMLNS_DOCBOOK]["class"]) && + $attrs[Reader::XMLNS_DOCBOOK]["class"] == "interface" + ) { + $this->cchunk["classsynopsis"]["interface"] = true; + } + + return '
'; + } + + /* Just always force the ending } to close the class as an opening { should always be present + if ($this->cchunk["classsynopsis"]["close"] === true) { + $this->cchunk["classsynopsis"]["close"] = false; + return "}
"; + } + return ""; + */ return "}"; } - return ""; + + /* New rendering for more sensible markup: + * We open a fake classsynopsisinfo div to not break the CSS expectations */ + if ($open) { + $occurrences = substr_count($props['innerXml'], '') + + substr_count($props['innerXml'], '') + + substr_count($props['innerXml'], ''); + $this->cchunk["classsynopsis"]['nb_list'] = $occurrences; + return '
'; + } else { + return '}
'; + } } public function format_classsynopsis_methodsynopsis_methodname_text($value, $tag) { @@ -1020,6 +1137,10 @@ public function format_classsynopsis_methodsynopsis_methodname_text($value, $tag public function format_classsynopsis_ooclass_classname_text($value, $tag) { $this->cchunk["classsynopsis"]["classname"] = $value; + // Do not render outside ooclass class name in legacy rendering. + if ($this->cchunk["classsynopsis"]["legacy"]) { + return ''; + } return $this->TEXT($value); } diff --git a/phpdotnet/phd/Package/PHP/XHTML.php b/phpdotnet/phd/Package/PHP/XHTML.php index fdd2dee7..e2da4fc5 100644 --- a/phpdotnet/phd/Package/PHP/XHTML.php +++ b/phpdotnet/phd/Package/PHP/XHTML.php @@ -11,6 +11,7 @@ abstract class Package_PHP_XHTML extends Package_Generic_XHTML { /* DEFAULT */ 'span', 'ooclass' => array( /* DEFAULT */ 'format_suppressed_tags', + 'classsynopsis' => 'format_classsynopsis_ooclass_classname', 'classsynopsisinfo' => 'format_classsynopsisinfo_ooclass_classname', ), ), @@ -708,8 +709,12 @@ public function format_grep_classname_text($value, $tag) { } public function format_classsynopsis_ooclass_classname_text($value, $tag) { - /* intentionally not return the value, it will be printed out by "soon" */ - parent::format_classsynopsis_ooclass_classname_text($value, $tag); + $content = parent::format_classsynopsis_ooclass_classname_text($value, $tag); + /** Legacy behaviour for crappy markup */ + if ($content === '') { + return ''; + } + return $this->format_classname_text($content, $tag); } public function format_classname_text($value, $tag) { diff --git a/phpdotnet/phd/Render.php b/phpdotnet/phd/Render.php index 4117b4f4..1b3b0fdf 100644 --- a/phpdotnet/phd/Render.php +++ b/phpdotnet/phd/Render.php @@ -72,8 +72,12 @@ public function execute(Reader $r) { /* {{{ */ $innerXml = ""; if ( - ($open && $r->name === "type") || - ($open && in_array($r->name, ["methodsynopsis", "constructorsynopsis", "destructorsynopsis"], true)) + $open && + ( + $r->name === "type" || + $r->name === "classsynopsis" || + in_array($r->name, ["methodsynopsis", "constructorsynopsis", "destructorsynopsis"], true) + ) ) { $innerXml = $r->readInnerXml(); } From ad7e0e4d8bd8abed18cd60fca9c1ae161444703a Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Fri, 7 Jul 2023 03:15:50 +0100 Subject: [PATCH 2/2] Fix interface names not being removed from methods + exceptions --- phpdotnet/phd/Package/Generic/XHTML.php | 118 +++++++++++++++--------- phpdotnet/phd/Package/PHP/XHTML.php | 43 +++++---- phpdotnet/phd/Render.php | 2 +- 3 files changed, 102 insertions(+), 61 deletions(-) diff --git a/phpdotnet/phd/Package/Generic/XHTML.php b/phpdotnet/phd/Package/Generic/XHTML.php index 16e6632f..e3035c15 100644 --- a/phpdotnet/phd/Package/Generic/XHTML.php +++ b/phpdotnet/phd/Package/Generic/XHTML.php @@ -63,15 +63,6 @@ abstract class Package_Generic_XHTML extends Format_Abstract_XHTML { 'caution' => 'format_admonition', 'citation' => 'format_citation', 'citerefentry' => 'span', - 'classname' => array( - /* DEFAULT */ 'span', - 'ooclass' => array( - 'classsynopsis' => 'format_classsynopsis_ooclass_classname', - 'classsynopsisinfo' => 'format_classsynopsisinfo_ooclass_classname', - ), - ), - 'classsynopsis' => 'format_classsynopsis', - 'classsynopsisinfo' => 'format_classsynopsisinfo', 'code' => 'code', 'collab' => 'span', 'collabname' => 'span', @@ -157,15 +148,37 @@ abstract class Package_Generic_XHTML extends Format_Abstract_XHTML { 'note' => 'format_note', 'orgname' => 'span', 'othercredit' => 'format_div', + /** Class Synopsis related tags */ + 'classsynopsis' => 'format_classsynopsis', + 'classsynopsisinfo' => 'format_classsynopsisinfo', 'ooclass' => array( /* DEFAULT */ 'span', - 'classsynopsis' => 'format_classsynopsis_ooclass',//'format_div',// + 'classsynopsis' => 'format_classsynopsis_generic_oo_tag', ), + 'ooexception' => [ + /* DEFAULT */ 'span', + 'classsynopsis' => 'format_classsynopsis_generic_oo_tag', + ], 'oointerface' => array( /* DEFAULT */ 'span', - 'classsynopsis' => 'format_classsynopsis_oointerface', - 'classsynopsisinfo' => 'format_classsynopsisinfo_oointerface', + 'classsynopsis' => 'format_classsynopsis_generic_oo_tag', + 'classsynopsisinfo' => 'format_classsynopsisinfo_oointerface', ), + 'classname' => [ + /* DEFAULT */ 'span', + 'ooclass' => [ + /* DEFAULT */ 'span', + 'classsynopsis' => 'format_classsynopsis_ooclass_classname', + 'classsynopsisinfo' => 'format_classsynopsisinfo_ooclass_classname', + ], + ], + 'exceptionname' => [ + /* DEFAULT */ 'span', + 'ooexception' => [ + /* DEFAULT */ 'span', + 'classsynopsis' => 'format_classsynopsis_ooclass_classname', + ], + ], 'interfacename' => array( /* DEFAULT */ 'span', 'oointerface' => array( @@ -174,7 +187,6 @@ abstract class Package_Generic_XHTML extends Format_Abstract_XHTML { 'classsynopsisinfo' => 'format_classsynopsisinfo_oointerface_interfacename', ), ), - 'exceptionname' => 'span', 'option' => 'format_option', 'orderedlist' => 'format_orderedlist', 'para' => array( @@ -381,14 +393,29 @@ abstract class Package_Generic_XHTML extends Format_Abstract_XHTML { /* DEFAULT */ false, 'fieldsynopsis' => 'format_fieldsynopsis_modifier_text', ), - 'classname' => array( - /* DEFAULT */ false, - 'ooclass' => array( + /** Those are used to retrieve the class/interface name to be able to remove it from method names */ + 'classname' => [ + /* DEFAULT */ false, + 'ooclass' => [ /* DEFAULT */ false, - // We keep this to work around the legacy behaviour of not displaying it + /** This is also used by the legacy display to not display the class name at all */ 'classsynopsis' => 'format_classsynopsis_ooclass_classname_text', - ), - ), + ] + ], + 'exceptionname' => [ + /* DEFAULT */ false, + 'ooexception' => [ + /* DEFAULT */ false, + 'classsynopsis' => 'format_classsynopsis_oo_name_text', + ] + ], + 'interfacename' => [ + /* DEFAULT */ false, + 'oointerface' => [ + /* DEFAULT */ false, + 'classsynopsis' => 'format_classsynopsis_oo_name_text', + ] + ], 'methodname' => array( /* DEFAULT */ false, 'constructorsynopsis' => array( @@ -984,7 +1011,32 @@ public function format_classsynopsisinfo($open, $name, $attrs) return ' {
'; } + /** This method is common between both legacy and new rendering for setting up the classname in the current chunk */ + public function format_classsynopsis_ooclass_classname_text($value, $tag) { + /** If this is not defined this is the first ooclass/oointerface/ooexception and thus needs to + * set the class name to be able to remove it from the methods + */ + if (!$this->cchunk["classsynopsis"]["classname"]) { + $this->cchunk["classsynopsis"]["classname"] = $value; + } + // Do not render outside ooclass class name in legacy rendering. + if ($this->cchunk["classsynopsis"]["legacy"]) { + return ''; + } + return $this->TEXT($value); + } + /** Class synopsis rendering for new/better markup */ + public function format_classsynopsis_oo_name_text($value, $tag) { + /** If this is not defined this is the first ooclass/oointerface/ooexception and thus needs to + * set the class name to be able to remove it from the methods + */ + if (!$this->cchunk["classsynopsis"]["classname"]) { + $this->cchunk["classsynopsis"]["classname"] = $value; + } + return $this->TEXT($value); + } + public function format_classsynopsis_oointerface_interfacename($open, $name, $attrs, $props) { if ($this->cchunk["classsynopsis"]["legacy"] === true) { @@ -1026,8 +1078,8 @@ public function format_classsynopsis_ooclass_classname($open, $name, $attrs, $pr if ($open) { /** Actual class name in bold */ if ($this->cchunk["classsynopsis"]["ooclass"] === false) { - - return 'class ' . $this->transformFromMap($open, 'strong', $name, $attrs, $props); + /** We force the name: parameter to 'classname' to not break CSS expectations for exceptionanme tags */ + return 'class ' . $this->transformFromMap($open, 'strong', 'classname', $attrs, $props); } /* Whitespace for next word */ return ' '; @@ -1041,22 +1093,8 @@ public function format_classsynopsis_ooclass_classname($open, $name, $attrs, $pr return ''; } - public function format_classsynopsis_ooclass($open, $name, $attrs, $props) + public function format_classsynopsis_generic_oo_tag($open, $name, $attrs, $props) { - if ($this->cchunk["classsynopsis"]["legacy"] === true) { - return $this->format_div($open, $name, $attrs, $props); - } - - /* Close list of classes + interfaces by "opening" class def with { */ - if (!$open && --$this->cchunk["classsynopsis"]['nb_list'] === 0) { - return ' {'; - } - return ''; - } - - public function format_classsynopsis_oointerface($open, $name, $attrs, $props) - { - /* Used to be converted to a span */ if ($this->cchunk["classsynopsis"]["legacy"] === true) { return $this->transformFromMap($open, 'span', $name, $attrs, $props); } @@ -1135,14 +1173,6 @@ public function format_classsynopsis_methodsynopsis_methodname_text($value, $tag return $method; } - public function format_classsynopsis_ooclass_classname_text($value, $tag) { - $this->cchunk["classsynopsis"]["classname"] = $value; - // Do not render outside ooclass class name in legacy rendering. - if ($this->cchunk["classsynopsis"]["legacy"]) { - return ''; - } - return $this->TEXT($value); - } public function format_fieldsynopsis($open, $name, $attrs) { $this->cchunk["fieldsynopsis"] = $this->dchunk["fieldsynopsis"]; diff --git a/phpdotnet/phd/Package/PHP/XHTML.php b/phpdotnet/phd/Package/PHP/XHTML.php index e2da4fc5..a1b42d33 100644 --- a/phpdotnet/phd/Package/PHP/XHTML.php +++ b/phpdotnet/phd/Package/PHP/XHTML.php @@ -7,14 +7,6 @@ abstract class Package_PHP_XHTML extends Package_Generic_XHTML { 'appendix' => 'format_container_chunk', 'article' => 'format_container_chunk', 'book' => 'format_root_chunk', - 'classname' => array( - /* DEFAULT */ 'span', - 'ooclass' => array( - /* DEFAULT */ 'format_suppressed_tags', - 'classsynopsis' => 'format_classsynopsis_ooclass_classname', - 'classsynopsisinfo' => 'format_classsynopsisinfo_ooclass_classname', - ), - ), 'chapter' => 'format_container_chunk', 'colophon' => 'format_chunk', 'function' => 'format_function', @@ -91,15 +83,29 @@ abstract class Package_PHP_XHTML extends Package_Generic_XHTML { private $mytextmap = array( 'acronym' => 'format_acronym_text', 'function' => 'format_function_text', - 'interfacename' => 'format_classname_text', - 'exceptionname' => 'format_classname_text', - 'classname' => array( - /* DEFAULT */ 'format_classname_text', - 'ooclass' => array( - /* DEFAULT */ 'format_classname_text', + /** Those are used to retrieve the class/interface name to be able to remove it from method names */ + 'classname' => [ + /* DEFAULT */ 'format_classname_text', + 'ooclass' => [ + /* DEFAULT */ 'format_classname_text', + /** This is also used by the legacy display to not display the class name at all */ 'classsynopsis' => 'format_classsynopsis_ooclass_classname_text', - ), - ), + ] + ], + 'exceptionname' => [ + /* DEFAULT */ 'format_classname_text', + 'ooexception' => [ + /* DEFAULT */ 'format_classname_text', + 'classsynopsis' => 'format_classsynopsis_oo_name_text', + ] + ], + 'interfacename' => [ + /* DEFAULT */ 'format_classname_text', + 'oointerface' => [ + /* DEFAULT */ 'format_classname_text', + 'classsynopsis' => 'format_classsynopsis_oo_name_text', + ] + ], 'methodname' => array( /* DEFAULT */ 'format_function_text', 'constructorsynopsis' => array( @@ -717,6 +723,11 @@ public function format_classsynopsis_ooclass_classname_text($value, $tag) { return $this->format_classname_text($content, $tag); } + public function format_classsynopsis_oo_name_text($value, $tag) { + $content = parent::format_classsynopsis_oo_name_text($value, $tag); + return $this->format_classname_text($content, $tag); + } + public function format_classname_text($value, $tag) { if (($filename = $this->getClassnameLink(strtolower($value))) !== null && $this->cchunk["phpdoc:classref"] !== strtolower($value)) { $href = $this->chunked ? $filename.$this->ext : "#$filename"; diff --git a/phpdotnet/phd/Render.php b/phpdotnet/phd/Render.php index 1b3b0fdf..a588dc99 100644 --- a/phpdotnet/phd/Render.php +++ b/phpdotnet/phd/Render.php @@ -117,7 +117,7 @@ public function execute(Reader $r) { /* {{{ */ continue; } - if (strncmp($tag ?? '', "format_", 7) !== 0) { + if (/* !($tag instanceof \Closure) && */ !str_starts_with($tag ?? '', "format_")) { $data = $format->transformFromMap($open, $tag, $name, $attrs, $props); } else { $data = $format->{$tag}($open, $name, $attrs, $props);