Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Make this less terrible.

  • Loading branch information...
commit aa69326a7e6d217f46bb295261eeecc7b1f1c0a5 1 parent 840d9be
@epriestley epriestley authored
View
2  README
@@ -0,0 +1,2 @@
+(Unstable!) Diviner is a multi-language documentation generator, used to
+generate libphutil documentation.
View
92 resources/css/default_style.css
@@ -12,6 +12,10 @@ p, blockquote, th, td, button {
border: 0;
}
+html {
+ padding-bottom: 16em;
+}
+
table {
border-collapse: collapse;
border-spacing: 0;
@@ -34,6 +38,10 @@ caption, th {
text-align: left;
}
+td, th {
+ vertical-align: top;
+}
+
h1, h2, h3, h4, h5, h6 {
font-size: 100%;
font-weight: normal;
@@ -57,12 +65,13 @@ table {
font: 100%;
}
-h1, h2, h3, h4, h5 {
+h1, h2, h3, h4, h5, h6 {
color: #333;
font-weight: bold;
padding-top: .5em;
padding-bottom: .25em;
margin-bottom: .25em;
+ line-height: 1.6em;
}
h1 {
@@ -80,11 +89,20 @@ h3 {
border-bottom: 1px solid #dddddd;
}
+h4 {
+ font-size: 16px;
+}
+
+.atom-task-list {
+ padding: 0em 1em;
+}
-h4, h5 {
- font-size: 11px;
+.atom-task-list ul {
+ margin-left: 1em;
+ margin-bottom: 1em;
}
+
a {
-moz-outline-style: none;
text-decoration: none;
@@ -173,6 +191,11 @@ a.atom-symbol {
padding: .5em 1.5em;
}
+.doc-markup code.remarkup-counterexample {
+ border: 1px solid #aa0000;
+ background-color: #ffaaaa;
+}
+
.doc-markup ul {
list-style: disc;
margin: 1em 0 1em 3em;
@@ -229,4 +252,67 @@ table.atom-info td {
.docs-diviner-mark {
float: right;
+}
+
+.atom-param-table {
+ margin: .75em 3% 1em;
+ width: 94%;
+ background: #f6fff3;
+ border: 1px solid #88aa66;
+}
+
+.atom-param-table .atom-param-table-return {
+ background: #e0ffd0;
+}
+
+.atom-param-table td {
+ padding: 2px 4px;
+}
+
+.atom-param-table .atom-param-table-group {
+ text-align: right;
+ font-weight: bold;
+ white-space: nowrap;
+ width: 15%;
+ color: #7cab4f;
+}
+
+.atom-param-table .atom-param-type {
+ white-space: nowrap;
+ width: 12%;
+ text-align: right;
+}
+
+.atom-param-table .atom-param-name {
+ font-weight: bold;
+ white-space: nowrap;
+}
+
+.atom-group-contents h3 {
+ border: none;
+ margin: 0;
+ padding: 0;
+}
+
+.atom-group-contents ul {
+ margin-left: 1em;
+}
+
+.atom-group-listing {
+ background: #f3f3f3;
+ margin: 1em 0 2em 0;
+ padding: .5em 2em;
+ border: solid #555555;
+ border-width: 1px 0px 0px;
+}
+
+.atom-group-listing h1 {
+ margin: 0 0 .25em 0;
+ padding: 0;
+}
+
+.atom-toc ul {
+ margin: 1em 2em 4em;
+ font-weight: bold;
+ font-size: 14px;
}
View
1  resources/html/default_template.html
@@ -14,6 +14,7 @@
</div>
<div class="doc">
{BODY}
+ <div style="clear: both;"></div>
</div>
</div>
</body>
View
10 src/atoms/function/DivinerFunctionAtom.php
@@ -21,7 +21,7 @@
*/
class DivinerFunctionAtom extends DivinerAtom {
- private $returnType = 'wild';
+ private $returnTypeAttributes = array();
private $parameters = array();
public function getType() {
@@ -36,13 +36,13 @@ public function getChildren() {
return array();
}
- public function setReturnType($return_type) {
- $this->returnType = $return_type;
+ public function setReturnTypeAttributes(array $dict) {
+ $this->returnTypeAttributes = $dict;
return $this;
}
- public function getReturnType() {
- return $this->returnType;
+ public function getReturnTypeAttributes() {
+ return $this->returnTypeAttributes;
}
public function getParameters() {
View
10 src/atoms/method/DivinerMethodAtom.php
@@ -21,7 +21,7 @@
*/
class DivinerMethodAtom extends DivinerAtom {
- private $returnType = 'wild';
+ private $returnTypeAttributes = array();
private $parameters = array();
public function getType() {
@@ -51,13 +51,13 @@ public function sortAttributes(array $attributes) {
return array_keys($attributes);
}
- public function setReturnType($return_type) {
- $this->returnType = $return_type;
+ public function setReturnTypeAttributes(array $dict) {
+ $this->returnTypeAttributes = $dict;
return $this;
}
- public function getReturnType() {
- return $this->returnType;
+ public function getReturnTypeAttributes() {
+ return $this->returnTypeAttributes;
}
public function getParameters() {
View
46 src/engine/xhp/DivinerXHPEngine.php
@@ -67,7 +67,7 @@ public function parseFile($file, $data) {
$atom = new DivinerFunctionAtom();
$atom->setName($name->getConcreteString());
- $atom->setLine(1);
+ $atom->setLine($func->getLineNumber());
$atom->setFile($file);
$this->findAtomDocblock($atom, $func);
@@ -87,7 +87,7 @@ public function parseFile($file, $data) {
$atom = new DivinerClassAtom();
$atom->setName($name->getConcreteString());
- $atom->setLine(1);
+ $atom->setLine($class->getLineNumber());
$atom->setFile($file);
$extends = $class->getChildByIndex(2);
@@ -120,7 +120,7 @@ public function parseFile($file, $data) {
$matom->setName($method->getChildByIndex(2)->getConcreteString());
$matom->setFile($file);
- $matom->setLine(1);
+ $matom->setLine($method->getLineNumber());
$this->parseReturnType($matom, $method);
$atom->addMethod($matom);
@@ -151,12 +151,30 @@ private function findAtomDocblock(DivinerAtom $atom, XHPASTNode $node) {
}
private function parseParams(DivinerAtom $atom, XHPASTNodeList $params) {
+ $metadata = $atom->getDocblockMetadata();
+ $docs = idx($metadata, 'param', '');
+ if ($docs) {
+ $docs = explode("\n", $docs);
+ }
+
foreach ($params as $param) {
$name = $param->getChildByIndex(1);
$dict = array(
'type' => $param->getChildByIndex(0)->getConcreteString(),
'default' => $param->getChildByIndex(2)->getConcreteString(),
);
+ if ($docs) {
+ $doc = array_shift($docs);
+ if ($doc) {
+ $split = preg_split('/\s+/', trim($doc), $limit = 2);
+ if (!empty($split[0])) {
+ $dict['doctype'] = $split[0];
+ }
+ if (!empty($split[1])) {
+ $dict['docs'] = $split[1];
+ }
+ }
+ }
$atom->addParameter($name->getConcreteString(), $dict);
}
}
@@ -164,17 +182,29 @@ private function parseParams(DivinerAtom $atom, XHPASTNodeList $params) {
private function parseReturnType(DivinerAtom $atom, XHPASTNode $decl) {
$metadata = $atom->getDocblockMetadata();
$return = idx($metadata, 'return');
- if ($return === null) {
- $return = idx($metadata, 'returns');
- }
if ($return) {
- $type = reset(preg_split('/\s+/', trim($return)));
+ $split = preg_split('/\s+/', trim($return), $limit = 2);
+ if (!empty($split[0])) {
+ $type = $split[0];
+ } else {
+ $type = 'wild';
+ }
if ($decl->getChildByIndex(1)->getTypeName() == 'n_REFERENCE') {
$type = $type.' &';
}
+
+ $docs = null;
+ if (!empty($split[1])) {
+ $docs = $split[1];
+ }
+
+ $dict = array(
+ 'doctype' => $type,
+ 'docs' => $docs,
+ );
- $atom->setReturnType($type);
+ $atom->setReturnTypeAttributes($dict);
}
}
View
162 src/generator/static/DivinerStaticGenerator.php
@@ -23,6 +23,7 @@ public function generateDocumentation(array $views) {
$renderer = new DivinerDefaultRenderer();
$this->setRenderer($renderer);
$configuration = $this->getProjectConfiguration();
+ $renderer->setProjectConfiguration($configuration);
$root = $configuration->getProjectRoot().'/docs/';
$name = $configuration->getProjectName();
@@ -50,17 +51,102 @@ public function generateDocumentation(array $views) {
$groups = array();
foreach ($views as $view) {
- $groups[$view->getAtom()->getType()][] = $view;
+ $meta = $view->getAtom()->getDocblockMetadata();
+ $group = idx($meta, 'group', 'radicals');
+ $groups[$group][] = $view;
+ }
+ // Force radicals to the end.
+ if (!empty($groups['radicals'])) {
+ $radicals = $groups['radicals'];
+ unset($groups['radicals']);
+ $groups['radicals'] = $radicals;
+ }
+
+ $groups = array_select_keys(
+ $groups,
+ array_keys($configuration->getConfig('groups', array()))) + $groups;
+
+ $renderer->setBaseURI('');
+ $index = array();
+ $index[] = $this->renderTableOfContents($groups);
+ foreach ($groups as $name => $group) {
+ $anchor = phutil_render_tag(
+ 'a',
+ array(
+ 'name' => $renderer->getNormalizedName($name),
+ ),
+ '');
+ $index[] = '<div class="atom-group-listing">';
+ $index[] = '<h1>'.$anchor.$renderer->renderGroup($name).'</h1>';
+ $index[] = $this->renderGroup($group);
+ $index[] = '</div>';
}
- // Reorder the types.
- $groups = array_select_keys($groups, array('article')) + $groups;
+ Filesystem::writeFile(
+ $root.'/index.html',
+ $this->renderTemplate(array(
+ 'TITLE' => 'Project Index',
+ 'BODY' => implode("\n", $index),
+ 'ROOT' => '',
+ )));
- $renderer->setBaseURI('');
+ $css = $root.'/css';
+ if (!Filesystem::pathExists($css)) {
+ Filesystem::createDirectory($css, $umask = 0755);
+ }
- $index = array();
- foreach ($groups as $type => $views) {
+ $sheets = array(
+ 'default_style.css' => 'diviner.css',
+ 'syntax.css' => 'syntax.css',
+ );
+ foreach ($sheets as $sheet => $target) {
+ $stylesheet = phutil_get_library_root('diviner').
+ '/../resources/css/'.
+ $sheet;
+ Filesystem::writeFile(
+ $css.'/'.$target,
+ Filesystem::readFile($stylesheet));
+ }
+ }
+
+ private function renderTableOfContents($groups) {
+ $renderer = $this->getRenderer();
+
+ $out = array();
+ foreach ($groups as $name => $group) {
+ $link = phutil_render_tag(
+ 'a',
+ array(
+ 'href' => '#'.$renderer->getNormalizedName($name),
+ ),
+ $renderer->renderGroup($name));
+ $out[] = '<li>'.$link.'</li>';
+ }
+
+ return
+ '<div class="atom-toc">'.
+ '<h1>Table of Contents</h1>'.
+ '<ul>'.implode("\n", $out).'</ul>'.
+ '</div>';
+
+ }
+
+ private function renderGroup($group) {
+ $renderer = $this->getRenderer();
+
+ $types = array();
+ foreach ($group as $view) {
+ $types[$view->getAtom()->getType()][] = $view;
+ }
+
+ // Reorder the types.
+ $types = array_select_keys(
+ $types,
+ array('article', 'class', 'function')) + $types;
+
+ $index = array();
+ foreach ($types as $type => $views) {
$ordered = array();
foreach ($views as $view) {
$ordered[$view->getAtom()->getName()] = $view;
@@ -68,17 +154,33 @@ public function generateDocumentation(array $views) {
ksort($ordered);
$views = array_values($ordered);
- $index[] = phutil_render_tag(
- 'h1',
- array(
- ),
- $this->renderTypeDisplayName($type));
+ if ($type != 'article') {
+ $index[] = phutil_render_tag(
+ 'h3',
+ array(
+ ),
+ $this->renderTypeDisplayName($type));
+ }
if ($type == 'class') {
$map = array(-1 => array());
+
+ $local = array();
+ foreach ($views as $view) {
+ $atom = $view->getAtom();
+ $local[$atom->getName()] = true;
+ }
+
foreach ($views as $view) {
$atom = $view->getAtom();
$extends = $atom->getParentClasses();
- if (!$extends) {
+ $hit = false;
+ foreach ($extends as $parent) {
+ if (isset($local[$parent])) {
+ $hit = true;
+ break;
+ }
+ }
+ if (!$hit) {
$extends = array(-1);
}
foreach ($extends as $parent) {
@@ -103,35 +205,13 @@ public function generateDocumentation(array $views) {
),
implode("\n", $list));
}
-
- Filesystem::writeFile(
- $root.'/index.html',
- $this->renderTemplate(array(
- 'TITLE' => 'Project Index',
- 'BODY' => implode("\n", $index),
- 'ROOT' => '',
- )));
-
- $css = $root.'/css';
- if (!Filesystem::pathExists($css)) {
- Filesystem::createDirectory($css, $umask = 0755);
- }
-
- $sheets = array(
- 'default_style.css' => 'diviner.css',
- 'syntax.css' => 'syntax.css',
- );
-
- foreach ($sheets as $sheet => $target) {
- $stylesheet = phutil_get_library_root('diviner').
- '/../resources/css/'.
- $sheet;
- Filesystem::writeFile(
- $css.'/'.$target,
- Filesystem::readFile($stylesheet));
- }
- }
-
+
+ return
+ '<div class="atom-group-contents">'.
+ implode("\n", $index).
+ '</div>';
+ }
+
private function renderView(DivinerBaseAtomView $view) {
$html = $view->renderView();
View
11 src/renderer/base/DivinerRenderer.php
@@ -18,5 +18,16 @@
abstract class DivinerRenderer {
+ private $projectConfiguration;
+
+ public function setProjectConfiguration(
+ DivinerProjectConfiguration $project_configuration) {
+ $this->projectConfiguration = $project_configuration;
+ return $this;
+ }
+
+ public function getProjectConfiguration() {
+ return $this->projectConfiguration;
+ }
}
View
126 src/renderer/default/DivinerDefaultRenderer.php
@@ -78,7 +78,10 @@ public function renderParameters($parameters) {
'</span>';
}
- public function renderReturnType($type) {
+ public function renderReturnTypeAttributes(array $attributes) {
+ $type = nonempty(
+ idx($attributes, 'type'),
+ idx($attributes, 'doctype'));
return phutil_render_tag(
'span',
array(
@@ -201,12 +204,64 @@ private function buildSymbolRule() {
$rule->setRenderer($this);
return $rule;
}
+
+ public function renderAtomAnchor(DivinerAtom $atom) {
+ $suffix = '';
+ switch ($atom->getType()) {
+ case 'method':
+ case 'function':
+ $suffix = '()';
+ break;
+ }
+
+ $type = $atom->getType();
+ $name = $atom->getName();
+
+ $type = $this->getNormalizedName($type);
+ $name = $this->getNormalizedName($name);
+ $anchor_name = $type.'/'.$name;
+ $link_text = phutil_escape_html($atom->getName().$suffix);
+
+ return phutil_render_tag(
+ 'a',
+ array(
+ 'href' => '#'.$anchor_name,
+ 'class' => 'atom-symbol',
+ ),
+ $link_text);
+ }
public function renderAtomLink(DivinerAtom $atom) {
+ $suffix = '';
+ switch ($atom->getType()) {
+ case 'method':
+ case 'function':
+ $suffix = '()';
+ break;
+ }
+
return $this->renderAtomLinkRaw(
$atom->getType(),
- $atom->getName());
+ $atom->getName(),
+ phutil_escape_html($atom->getName().$suffix));
}
+
+ public function renderAtomAnchorTarget(DivinerAtom $atom) {
+ $type = $atom->getType();
+ $name = $atom->getName();
+
+ $type = $this->getNormalizedName($type);
+ $name = $this->getNormalizedName($name);
+ $anchor_name = $type.'/'.$name;
+
+ return phutil_render_tag(
+ 'a',
+ array(
+ 'name' => $anchor_name,
+ ),
+ '');
+ }
+
public function renderAtomLinkRaw($type, $name, $link_text = null) {
if ($link_text === null) {
@@ -235,5 +290,72 @@ public function renderType($type) {
),
phutil_escape_html($this->getTypeDisplayName($type)));
}
+
+ public function renderParameterTable(array $params, array $return) {
+ $table = array();
+
+ $param_header = 'parameters';
+ foreach ($params as $param => $details) {
+ $type = nonempty(
+ idx($details, 'doctype'),
+ idx($details, 'type'),
+ 'wild');
+ $docs = idx($details, 'docs');
+ $table[] =
+ '<tr>'.
+ '<td class="atom-param-table-group">'.$param_header.'</td>'.
+ '<td class="atom-param-type">'.phutil_escape_html($type).'</td>'.
+ '<td class="atom-param-name">'.phutil_escape_html($param).'</td>'.
+ '<td>'.$this->markupTextInline($docs).'</td>'.
+ '</tr>';
+ $param_header = null;
+ }
+
+ $type = nonempty(
+ idx($return, 'doctype'),
+ idx($return, 'type'),
+ 'wild');
+
+ $docs = idx($return, 'docs');
+
+ $table[] =
+ '<tr class="atom-param-table-return">'.
+ '<td class="atom-param-table-group">return</td>'.
+ '<td class="atom-param-type">'.phutil_escape_html($type).'</td>'.
+ '<td class="atom-param-name"></td>'.
+ '<td>'.$this->markupTextInline($docs).'</td>'.
+ '</tr>';
+
+ return
+ '<table class="atom-param-table">'.
+ implode("\n", $table).
+ '</table>';
+ }
+
+ public function renderGroup($group) {
+ $map = $this->getProjectConfiguration()->getConfig('groups', array());
+ $map = $map + array(
+ 'radicals' => 'Free Radicals',
+ );
+ $name = idx($map, $group, $group);
+ $name = phutil_escape_html($name);
+ return '<span class="atom-group">'.$name.'</span>';
+ }
+
+ public function renderFileAndLine($file, $line) {
+
+ $src_base = $this->getProjectConfiguration()->getConfig('src_base');
+ if (!$src_base) {
+ return phutil_escape_html($file.':'.$line);
+ }
+
+ return phutil_render_tag(
+ 'a',
+ array(
+ 'href' => $src_base.'/'.$file.'#L'.$line,
+ 'target' => 'blank',
+ ),
+ phutil_escape_html($file.':'.$line));
+ }
}
View
15 src/view/base/DivinerBaseAtomView.php
@@ -93,10 +93,19 @@ protected function renderHeaderContent() {
}
protected function getAtomInfoDictionary() {
+ $renderer = $this->getRenderer();
$atom = $this->getAtom();
- return array(
- 'Defined' => $atom->getFile().':'.$atom->getLine(),
- );
+ $dict = array();
+ $dict['Defined'] = $renderer->renderFileAndLine(
+ $atom->getFile(),
+ $atom->getLine());
+ $metadata = $atom->getDocblockMetadata();
+ $group = idx($metadata, 'group');
+ if ($group) {
+ $dict['Group'] = $renderer->renderGroup($group);
+ }
+
+ return $dict;
}
}
View
64 src/view/class/DivinerClassAtomView.php
@@ -22,6 +22,7 @@ protected function renderBody() {
$renderer = $this->getRenderer();
return
$renderer->markupText($atom->getDocblockText()).
+ $this->renderTasks().
$this->renderMethods();
}
@@ -40,6 +41,60 @@ protected function getAtomInfoDictionary() {
return parent::getAtomInfoDictionary() + $dict;
}
+
+ protected function getDefinedTasks() {
+ $metadata = $this->getAtom()->getDocblockMetadata();
+ $tasks = idx($metadata, 'task');
+
+ $map = array();
+ if ($tasks) {
+ foreach (explode("\n", $tasks) as $task) {
+ $split = preg_split('/\s+/', $task, $limit = 2);
+ if (isset($split[0])) {
+ $map[$split[0]] = idx($split, 1, $split[0]);
+ }
+ }
+ }
+ $map['unspecified'] = 'Unspecified';
+
+ return $map;
+ }
+
+ protected function renderTasks() {
+ $atom = $this->getAtom();
+ $renderer = $this->getRenderer();
+ $methods = $atom->getMethods();
+ $tasks = array();
+ foreach ($methods as $method) {
+ $metadata = $method->getDocblockMetadata();
+ $task = idx($metadata, 'task', 'unspecified');
+ $tasks[$task][] = $method;
+ }
+ $def = $this->getDefinedTasks();
+ $tasks = array_select_keys($tasks, array_keys($def)) + $tasks;
+
+ $out = array();
+ foreach ($tasks as $task => $methods) {
+ $task_name = idx($def, $task, $task);
+ $out[] = '<h4>'.phutil_escape_html($task_name).'</h4>';
+ $methods = msort($methods, 'getName');
+ $out[] = '<ul>';
+ foreach ($methods as $method) {
+ $out[] =
+ '<li>'.
+ $renderer->renderAttributes($method->getAttributes()).' '.
+ $renderer->renderAtomAnchor($method).
+ '</li>';
+ }
+ $out[] = '</ul>';
+ }
+
+ return
+ '<h2>Tasks</h2>'.
+ '<div class="atom-task-list">'.
+ implode("\n", $out).
+ '</div>';
+ }
protected function renderMethods() {
@@ -50,14 +105,19 @@ protected function renderMethods() {
$markup = array();
foreach ($methods as $method) {
$attributes = $renderer->renderAttributes($method->getAttributes());
- $return = $renderer->renderReturnType($method->getReturnType());
+ $return = $renderer->renderReturnTypeAttributes(
+ $method->getReturnTypeAttributes());
$params = $renderer->renderParameters($method->getParameters());
$markup[] =
'<h3>'.
+ $renderer->renderAtomAnchorTarget($method).
$attributes.' '.
$return.' '.
- $method->getName().'('.$params.')'.
+ phutil_escape_html($method->getName()).'('.$params.')'.
'</h3>';
+ $markup[] = $renderer->renderParameterTable(
+ $method->getParameters(),
+ $method->getReturnTypeAttributes());
if (strlen($method->getDocblockText())) {
$markup[] = $renderer->markupText($method->getDocblockText());
} else {
View
6 src/view/function/DivinerFunctionAtomView.php
@@ -25,7 +25,8 @@ protected function renderBody() {
$renderer = $this->getRenderer();
$attributes = $renderer->renderAttributes($atom->getAttributes());
- $return = $renderer->renderReturnType($atom->getReturnType());
+ $return = $renderer->renderReturnTypeAttributes(
+ $atom->getReturnTypeAttributes());
$params = $renderer->renderParameters($atom->getParameters());
return
'<h3>'.
@@ -33,6 +34,9 @@ protected function renderBody() {
$return.' '.
phutil_escape_html($atom->getName()).'('.$params.')'.
'</h3>'.
+ $renderer->renderParameterTable(
+ $atom->getParameters(),
+ $atom->getReturnTypeAttributes()).
$renderer->markupText($atom->getDocblockText());
}

0 comments on commit aa69326

Please sign in to comment.
Something went wrong with that request. Please try again.