Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add links to type tags #94

Merged
merged 7 commits into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions phpdotnet/phd/Package/Generic/XHTML.php
Original file line number Diff line number Diff line change
Expand Up @@ -1277,10 +1277,10 @@ public function format_parameter($open, $name, $attrs, $props)
}

public function format_void($open, $name, $attrs, $props) {
if ($props['sibling'] == 'methodname') {
if (isset($props['sibling']) && $props['sibling'] == 'methodname') {
return '(';
} else {
return '<span class="type"><span class="type void">void</span></span>';
return '<span class="type"><a href="language.types.void.html" class="type void">void</a></span>';
}
}

Expand Down
181 changes: 133 additions & 48 deletions phpdotnet/phd/Package/PHP/XHTML.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ abstract class Package_PHP_XHTML extends Package_Generic_XHTML {
'colophon' => 'format_chunk',
'function' => 'format_function',
'methodname' => 'format_function',
'methodparam' => 'format_methodparam',
'methodsynopsis' => 'format_methodsynopsis',
'legalnotice' => 'format_chunk',
'parameter' => array(
/* DEFAULT */ 'format_parameter',
'methodparam' => 'format_methodparam_parameter',
),
'part' => 'format_container_chunk',
'partintro' => 'format_partintro',
'preface' => 'format_chunk',
Expand Down Expand Up @@ -70,9 +75,11 @@ abstract class Package_PHP_XHTML extends Package_Generic_XHTML {
'type' => array(
/* DEFAULT */ 'format_type',
'methodsynopsis' => 'format_methodsynopsis_type',
'methodparam' => 'format_type_methodparam',
'type' => array(
/* DEFAULT */ 'format_type',
'methodsynopsis' => 'format_suppressed_tags',
'methodparam' => 'format_suppressed_tags',
),
),
'varname' => array(
Expand Down Expand Up @@ -120,14 +127,15 @@ abstract class Package_PHP_XHTML extends Package_Generic_XHTML {
),
'refname' => 'format_refname_text',
'type' => array(
/* DEFAULT */ 'format_type_if_object_or_pseudo_text',
/* DEFAULT */ 'format_type_text',
'classsynopsisinfo' => false,
'fieldsynopsis' => 'format_type_if_object_or_pseudo_text',
'methodparam' => 'format_type_if_object_or_pseudo_text',
'fieldsynopsis' => 'format_type_text',
'methodparam' => 'format_type_methodparam_text',
'methodsynopsis' => 'format_type_methodsynopsis_text',
'type' => array(
/* DEFAULT */ 'format_type_if_object_or_pseudo_text',
/* DEFAULT */ 'format_type_text',
'methodsynopsis' => 'format_type_methodsynopsis_text',
'methodparam' => 'format_type_methodparam_text',
),
),
'titleabbrev' => array(
Expand Down Expand Up @@ -481,40 +489,19 @@ public function format_methodsynopsis($open, $name, $attrs, $props) {
$content = "";
if ($this->params["paramCount"] > 3) {
$content .= "<br>";
} else if ($this->params["paramCount"] === 0) {
$content .= "(";
}

$content .= ")";

if ($this->cchunk["methodsynopsis"]["returntypes"]) {
$types = [];
$this->type_separator = $this->cchunk["methodsynopsis"]["type_separator"];

if (
$this->type_separator === "|" &&
count($this->cchunk["methodsynopsis"]["returntypes"]) === 2 &&
in_array("null", $this->cchunk["methodsynopsis"]["returntypes"], true)
) {
$this->simple_nullable = true;
$this->type_separator = "";
$types[] = '<span class="type">?</span>';
}
$type = $this->format_types(
$this->cchunk["methodsynopsis"]["type_separator"],
$this->cchunk["methodsynopsis"]["returntypes"]
);

foreach ($this->cchunk["methodsynopsis"]["returntypes"] as $return_type) {
$formatted_type = self::format_type_if_object_or_pseudo_text($return_type, "type");
if ($formatted_type === false) {
$formatted_type = $return_type;
}
if ($formatted_type !== "") {
$types[] = '<span class="type">' . $formatted_type . '</span>';
}
}

$type = implode($this->type_separator ?? '', $types);
if (count($types) > 1) {
$type = '<span class="type">' . $type . '</span>';
}
$content .= ': ' . $type;
$this->simple_nullable = null;
}

$content .= "</div>\n";
Expand All @@ -523,29 +510,109 @@ public function format_methodsynopsis($open, $name, $attrs, $props) {
return $content;
}

private function format_types($type_separator, $paramOrReturnType) {
$types = [];
$this->type_separator = $type_separator;

if (
$this->type_separator === "|" &&
count($paramOrReturnType) === 2 &&
in_array("null", $paramOrReturnType, true)
) {
$this->simple_nullable = true;
$this->type_separator = "";
$formatted_type = self::format_type_text("?", "type");
$types[] = '<span class="type">' . $formatted_type .'</span>';
}

foreach ($paramOrReturnType as $individualType) {
$formatted_type = self::format_type_text($individualType, "type");
if ($formatted_type === false) {
$formatted_type = $individualType;
}
if ($formatted_type !== "") {
if ($individualType === "void") {
$types[] = $formatted_type;
} else {
$types[] = '<span class="type">' . $formatted_type . '</span>';
}
}
}

$type = implode($this->type_separator ?? '', $types);
if (count($types) > 1) {
$type = '<span class="type">' . $type . '</span>';
}
$this->simple_nullable = null;

return $type;
}

public function format_type_methodsynopsis_text($type, $tagname) {
$this->cchunk["methodsynopsis"]["returntypes"][] = $type;

return "";
}

public function format_type_if_object_or_pseudo_text($type, $tagname) {
if (strtolower($type) === "null" && $this->simple_nullable) {
return "";
public function format_methodparam_parameter($open, $name, $attrs, $props) {
$type = "";
if ($open) {
if ($this->cchunk["methodparam"]["paramtypes"]) {
$type = $this->format_types(
$this->cchunk["methodparam"]["type_separator"],
$this->cchunk["methodparam"]["paramtypes"]
);
}
$this->cchunk["methodparam"]["type_separator"] = "";
$this->cchunk["methodparam"]["paramtypes"] = [];
}
return $type . parent::format_methodparam_parameter($open, $name, $attrs, $props);
}

public function format_methodparam($open, $name, $attrs) {
if ($open) {
$this->cchunk["methodparam"]["paramtypes"] = [];
$this->cchunk["methodparam"]["type_separator"] = "";
return parent::format_methodparam($open, $name, $attrs);
}

if (in_array(strtolower($type), array("bool", "int", "double", "boolean", "integer", "float", "string", "array", "object", "resource", "null"))) {
return false;
if ($this->params["opt"] && !$this->params["init"]) {
return '<span class="initializer"> = ?</span></span>';
}
$this->params["init"] = false;
$this->cchunk["methodparam"]["paramtypes"] = [];
$this->cchunk["methodparam"]["type_separator"] = "";
return "</span>";
}

return self::format_type_text($type, $tagname);
public function format_type_methodparam($open, $tag, $attrs) {
if ($open) {
if (isset($attrs[Reader::XMLNS_DOCBOOK]["class"])) {
if ($attrs[Reader::XMLNS_DOCBOOK]["class"] === "union") {
$this->cchunk["methodparam"]["type_separator"] = "|";
} else {
$this->cchunk["methodparam"]["type_separator"] = "&";
}
haszi marked this conversation as resolved.
Show resolved Hide resolved
}
$this->cchunk["methodparam"]["paramtypes"] = [];
}
return "";
}

public function format_type_methodparam_text($type, $tagname) {
$this->cchunk["methodparam"]["paramtypes"][] = $type;

return "";
}

public function format_type_text($type, $tagname) {
$type = trim($type);
haszi marked this conversation as resolved.
Show resolved Hide resolved
$t = strtr(strtolower($type), ["_" => "-", "\\" => "-"]);
$href = $fragment = "";

switch($t) {
case "void":
return $this->format_void(false, '', [], []);
case "bool":
$href = "language.types.boolean";
break;
Expand All @@ -555,37 +622,55 @@ public function format_type_text($type, $tagname) {
case "double":
$href = "language.types.float";
break;
case "?":
$href = "language.types.null";
break;
case "false":
case "true":
$href = "language.types.value";
break;
case "null":
if ($this->simple_nullable) {
return "";
}
case "boolean":
case "integer":
Comment on lines 684 to 685
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those, plus the "double" case should warn IMHO, as those are an alternative spelling for the proper built-in types that should not exist in the documentation any more. But can be a follow-up PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about this and other warnings. There's a whole range of formatting and logic errors we could catch in Phd from whitespace in all sorts of tags, incompatible type combinations, etc.
If this was implemented as some sort of option that Phd could be called from the command line with, this whole doc checker could be added as an additional step to the documentations' pull request workflow. Maybe?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this makes sense, but I was certain we already had support for warnings in PhD as I had seen them 🤔

Maybe if it's a new render that runs fast and doesn't need indexing, having it run on CI would make sense. Last time it came up there were concerns about the CI taking too long, or running too frequently as a PR might be update often in quick successions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yeah. The error handler is right there in functions.php, a file included in everything Phd which I also never looked inside. :-)

So this format would be called with the --noindex option, it would have to override the createLink(), update() and appendData() methods at a minimum, and it's validation method(s) could return null and would raise warnings on validation errors. Sounds fairly simple, so it's probably far from it. :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As everything with PhD :D it looks simple and it isn't :D

case "float":
case "string":
case "array":
case "object":
case "resource":
case "null":
if ($this->simple_nullable) {
return "";
}
case "callable":
case "iterable":
$href = "language.types.$t";
break;
case "mixed":
$href = "language.types.declarations";
$fragment = "language.types.declarations.$t";
case "never":
$href = "language.types.$t";
break;
default:
/* Check if its a classname. */
$href = Format::getFilename("class.$t");
}

if ($href === false) {
return false;
}

$classNames = ($type === "?") ? ($tagname . ' null') : ($tagname . ' ' . $type);
if ($href && $this->chunked) {
return '<a href="' .$href. $this->getExt().($fragment ? "#$fragment" : ""). '" class="' .$tagname. ' ' .$type. '">' .$type. '</a>';
return '<a href="' .$href. $this->getExt().($fragment ? "#$fragment" : ""). '" class="' . $classNames . '">' .$type. '</a>';
}
if ($href) {
return '<a href="#' .($fragment ? $fragment : $href). '" class="' .$tagname. ' ' .$type. '">' .$type. '</a>';
return '<a href="#' .($fragment ? $fragment : $href). '" class="' . $classNames . '">' .$type. '</a>';
}
return '<span class="' . $classNames . '">' .$type. '</span>';
}

public function format_void($open, $name, $attrs, $props) {
if (isset($props['sibling']) && $props['sibling'] == 'methodname') {
$this->cchunk["methodsynopsis"]["returntypes"][] = "void";
return '';
}
return '<span class="' .$tagname. ' ' .$type. '">' .$type. '</span>';
return parent::format_void($open, $name, $attrs, $props);
}

public function format_example_title($open, $name, $attrs, $props) {
Expand Down
18 changes: 10 additions & 8 deletions tests/php/bug49102-1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -63,26 +63,28 @@ Content:

<div class="classsynopsisinfo classsynopsisinfo_comment">/* Methods */</div>
<div class="constructorsynopsis dc-description">
<span class="methodname"><strong>__construct</strong></span>()</div>
<span class="methodname"><strong>__construct</strong></span>(): <span class="type"><a href="language.types.void.html" class="type void">void</a></span></div>

<div class="methodsynopsis dc-description"><span class="methodname"><strong>setIteratorMode</strong></span>(<span class="methodparam"><span class="type">int</span> <code class="parameter">$mode</code></span>): <span class="type"><span class="type void">void</span></span></div>
<div class="methodsynopsis dc-description"><span class="methodname"><strong>setIteratorMode</strong></span>(<span class="methodparam"><span class="type"><a href="language.types.integer.html" class="type int">int</a></span> <code class="parameter">$mode</code></span>): <span class="type"><a href="language.types.void.html" class="type void">void</a></span></div>


<div class="classsynopsisinfo classsynopsisinfo_comment">/* Inherited methods */</div>
<div class="methodsynopsis dc-description"><span class="methodname"><strong>SplDoublyLinkedList::bottom</strong></span>(): <span class="type"><a href="language.types.declarations.html#language.types.declarations.mixed" class="type mixed">mixed</a></span></div>
<div class="methodsynopsis dc-description"><span class="methodname"><strong>SplDoublyLinkedList::bottom</strong></span>(): <span class="type"><span class="type"><a href="language.types.mixed.html" class="type mixed">mixed</a></span><span class="type"><a href="language.types.void.html" class="type void">void</a></span></span></div>

<div class="methodsynopsis dc-description"><span class="methodname"><strong>SplDoublyLinkedList::count</strong></span>(): <span class="type">int</span></div>
<div class="methodsynopsis dc-description"><span class="methodname"><strong>SplDoublyLinkedList::count</strong></span>(): <span class="type"><span class="type"><a href="language.types.integer.html" class="type int">int</a></span><span class="type"><a href="language.types.void.html" class="type void">void</a></span></span></div>

<div class="methodsynopsis dc-description"><span class="methodname"><strong>SplDoublyLinkedList::current</strong></span>(): <span class="type"><a href="language.types.declarations.html#language.types.declarations.mixed" class="type mixed">mixed</a></span></div>
<div class="methodsynopsis dc-description"><span class="methodname"><strong>SplDoublyLinkedList::current</strong></span>(): <span class="type"><span class="type"><a href="language.types.mixed.html" class="type mixed">mixed</a></span><span class="type"><a href="language.types.void.html" class="type void">void</a></span></span></div>

<div class="methodsynopsis dc-description"><span class="methodname"><strong>SplDoublyLinkedList::getIteratorMode</strong></span>(): <span class="type">int</span></div>
<div class="methodsynopsis dc-description"><span class="methodname"><strong>SplDoublyLinkedList::getIteratorMode</strong></span>(): <span class="type"><span class="type"><a href="language.types.integer.html" class="type int">int</a></span><span class="type"><a href="language.types.void.html" class="type void">void</a></span></span></div>

<div class="methodsynopsis dc-description"><span class="methodname"><strong>SplDoublyLinkedList::offsetExists</strong></span>(<span class="methodparam"><span class="type"><a href="language.types.declarations.html#language.types.declarations.mixed" class="type mixed">mixed</a></span> <code class="parameter">$index</code></span>): <span class="type">bool</span></div>
<div class="methodsynopsis dc-description"><span class="methodname"><strong>SplDoublyLinkedList::offsetExists</strong></span>(<span class="methodparam"><span class="type"><a href="language.types.mixed.html" class="type mixed">mixed</a></span> <code class="parameter">$index</code></span>): <span class="type"><a href="language.types.boolean.html" class="type bool">bool</a></span></div>

<div class="methodsynopsis dc-description"><span class="methodname"><strong>SplDoublyLinkedList::offsetGet</strong></span>(<span class="methodparam"><span class="type"><a href="language.types.declarations.html#language.types.declarations.mixed" class="type mixed">mixed</a></span> <code class="parameter">$index</code></span>): <span class="type"><a href="language.types.declarations.html#language.types.declarations.mixed" class="type mixed">mixed</a></span></div>
<div class="methodsynopsis dc-description"><span class="methodname"><strong>SplDoublyLinkedList::offsetGet</strong></span>(<span class="methodparam"><span class="type"><a href="language.types.mixed.html" class="type mixed">mixed</a></span> <code class="parameter">$index</code></span>): <span class="type"><a href="language.types.mixed.html" class="type mixed">mixed</a></span></div>

}</div>

</div>
</div>
</div>


Loading
Loading