Move Diviner further toward usability
Summary:
- Complete the "project" -> "book" stuff. This is cleaner conceptually and keeps us from having yet another meaning for the word "project".
- Normalize symbols during atomization. This simplifies publishing a great deal, and allows static documentation to link to dynamic documentation and vice versa, because the canonical names of symbols are agreed upon (we can tweak the actual algorithm).
- Give articles a specifiable name distinct from the title, and default to something like "support" instead of "Get Help! Get Support!" so URIs end up more readable (not "Get_Help!_Get_Support!").
- Have the atomizers set book information on atoms.
- Implement very basic publishers. Publishers are basically glue code between the atomization process and the rendering process -- the two we'll have initially are "static" (publish to files on disk) and "phabricator" (or similar -- publish into the database).
- Handle duplicate symbol definitions in the atomize and publish pipelines. This fixes the issue where a project defines two functions named "idx()" and we currently tell them not to do that and break. Realistically, this is common in the real world and we should just roll our eyes and do the legwork to generate documentation as best we can.
- Particularly, dirty all atoms with the same name as a dirty atom (e.g., if 'function f()' is updated, regnerate the documentation for all functions named f() in the book).
- When publishing, we publish these at "function/f/@1", "function/f/@2". The base page will offer to disambiguate ("There are 8 functions named 'f' in this codebase, which one do you want?").
- Implement a very very basic renderer. This generates the actual HTML (or text, or XML, or whatever else) for the documentation, which the publisher dumps onto disk or into a database or whatever.
- The atomize workflow actually needs to depend on books, at least sort of, so make it load config and use it properly.
- Propagate multilevel dirties through the graph. If "C extends B" and "B extends A", we should regenerate C when A changes. Prior to this diff, we would regnerate B only.
Test Plan: Generated some documentation. Named two articles "feedback", generated docs, saw "article/feedback/@1/" and "article/feedback/@2/" created.
Reviewers: btrahan, vrana, chad
Reviewed By: chad
CC: aran
Maniphest Tasks: T988
Differential Revision: https://secure.phabricator.com/D4896
2013-02-18 00:39:36 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class DivinerDefaultRenderer extends DivinerRenderer {
|
|
|
|
|
|
|
|
public function renderAtom(DivinerAtom $atom) {
|
2013-02-18 00:40:24 +01:00
|
|
|
$out = array(
|
|
|
|
$this->renderAtomTitle($atom),
|
|
|
|
$this->renderAtomProperties($atom),
|
|
|
|
$this->renderAtomDescription($atom),
|
|
|
|
);
|
|
|
|
|
|
|
|
return phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'diviner-atom',
|
|
|
|
),
|
|
|
|
$out);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function renderAtomTitle(DivinerAtom $atom) {
|
|
|
|
$name = $this->renderAtomName($atom);
|
|
|
|
$type = $this->renderAtomType($atom);
|
|
|
|
|
|
|
|
return phutil_tag(
|
|
|
|
'h1',
|
|
|
|
array(
|
|
|
|
'class' => 'atom-title',
|
|
|
|
),
|
|
|
|
array($name, ' ', $type));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function renderAtomName(DivinerAtom $atom) {
|
|
|
|
return phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'atom-name',
|
|
|
|
),
|
|
|
|
$this->getAtomName($atom));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getAtomName(DivinerAtom $atom) {
|
|
|
|
if ($atom->getDocblockMetaValue('title')) {
|
|
|
|
return $atom->getDocblockMetaValue('title');
|
|
|
|
}
|
|
|
|
|
|
|
|
return $atom->getName();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function renderAtomType(DivinerAtom $atom) {
|
|
|
|
return phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'atom-name',
|
|
|
|
),
|
|
|
|
$this->getAtomType($atom));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getAtomType(DivinerAtom $atom) {
|
|
|
|
return ucwords($atom->getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function renderAtomProperties(DivinerAtom $atom) {
|
|
|
|
$props = $this->getAtomProperties($atom);
|
|
|
|
|
|
|
|
$out = array();
|
|
|
|
foreach ($props as $prop) {
|
|
|
|
list($key, $value) = $prop;
|
|
|
|
|
|
|
|
$out[] = phutil_tag('dt', array(), $key);
|
|
|
|
$out[] = phutil_tag('dd', array(), $value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return phutil_tag(
|
|
|
|
'dl',
|
|
|
|
array(
|
|
|
|
'class' => 'atom-properties',
|
|
|
|
),
|
|
|
|
$out);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getAtomProperties(DivinerAtom $atom) {
|
|
|
|
$properties = array();
|
|
|
|
$properties[] = array(
|
|
|
|
pht('Defined'),
|
|
|
|
$atom->getFile().':'.$atom->getLine(),
|
|
|
|
);
|
|
|
|
|
|
|
|
return $properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function renderAtomDescription(DivinerAtom $atom) {
|
|
|
|
$text = $this->getAtomDescription($atom);
|
|
|
|
$engine = $this->getBlockMarkupEngine();
|
2013-02-18 18:44:43 +01:00
|
|
|
|
|
|
|
$this->pushAtomStack($atom);
|
|
|
|
$description = $engine->markupText($text);
|
|
|
|
$this->popAtomStack($atom);
|
|
|
|
|
2013-02-18 00:40:24 +01:00
|
|
|
return phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'atom-description',
|
|
|
|
),
|
2013-02-18 18:44:43 +01:00
|
|
|
$description);
|
2013-02-18 00:40:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getAtomDescription(DivinerAtom $atom) {
|
|
|
|
return $atom->getDocblockText();
|
Move Diviner further toward usability
Summary:
- Complete the "project" -> "book" stuff. This is cleaner conceptually and keeps us from having yet another meaning for the word "project".
- Normalize symbols during atomization. This simplifies publishing a great deal, and allows static documentation to link to dynamic documentation and vice versa, because the canonical names of symbols are agreed upon (we can tweak the actual algorithm).
- Give articles a specifiable name distinct from the title, and default to something like "support" instead of "Get Help! Get Support!" so URIs end up more readable (not "Get_Help!_Get_Support!").
- Have the atomizers set book information on atoms.
- Implement very basic publishers. Publishers are basically glue code between the atomization process and the rendering process -- the two we'll have initially are "static" (publish to files on disk) and "phabricator" (or similar -- publish into the database).
- Handle duplicate symbol definitions in the atomize and publish pipelines. This fixes the issue where a project defines two functions named "idx()" and we currently tell them not to do that and break. Realistically, this is common in the real world and we should just roll our eyes and do the legwork to generate documentation as best we can.
- Particularly, dirty all atoms with the same name as a dirty atom (e.g., if 'function f()' is updated, regnerate the documentation for all functions named f() in the book).
- When publishing, we publish these at "function/f/@1", "function/f/@2". The base page will offer to disambiguate ("There are 8 functions named 'f' in this codebase, which one do you want?").
- Implement a very very basic renderer. This generates the actual HTML (or text, or XML, or whatever else) for the documentation, which the publisher dumps onto disk or into a database or whatever.
- The atomize workflow actually needs to depend on books, at least sort of, so make it load config and use it properly.
- Propagate multilevel dirties through the graph. If "C extends B" and "B extends A", we should regenerate C when A changes. Prior to this diff, we would regnerate B only.
Test Plan: Generated some documentation. Named two articles "feedback", generated docs, saw "article/feedback/@1/" and "article/feedback/@2/" created.
Reviewers: btrahan, vrana, chad
Reviewed By: chad
CC: aran
Maniphest Tasks: T988
Differential Revision: https://secure.phabricator.com/D4896
2013-02-18 00:39:36 +01:00
|
|
|
}
|
|
|
|
|
2013-02-18 00:40:11 +01:00
|
|
|
public function renderAtomSummary(DivinerAtom $atom) {
|
2013-02-18 00:40:24 +01:00
|
|
|
$text = $this->getAtomSummary($atom);
|
|
|
|
$engine = $this->getInlineMarkupEngine();
|
2013-02-18 18:44:43 +01:00
|
|
|
|
|
|
|
$this->pushAtomStack($atom);
|
|
|
|
$summary = $engine->markupText($text);
|
|
|
|
$this->popAtomStack();
|
|
|
|
|
2013-02-18 00:40:24 +01:00
|
|
|
return phutil_tag(
|
|
|
|
'span',
|
|
|
|
array(
|
|
|
|
'class' => 'atom-summary',
|
|
|
|
),
|
2013-02-18 18:44:43 +01:00
|
|
|
$summary);
|
2013-02-18 00:40:24 +01:00
|
|
|
}
|
|
|
|
|
2013-09-08 18:12:33 +02:00
|
|
|
public function getAtomSummary(DivinerAtom $atom) {
|
2013-02-18 00:40:24 +01:00
|
|
|
if ($atom->getDocblockMetaValue('summary')) {
|
|
|
|
return $atom->getDocblockMetaValue('summary');
|
|
|
|
}
|
|
|
|
|
|
|
|
$text = $this->getAtomDescription($atom);
|
|
|
|
return PhabricatorMarkupEngine::summarize($text);
|
2013-02-18 00:40:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function renderAtomIndex(array $refs) {
|
|
|
|
$refs = msort($refs, 'getSortKey');
|
|
|
|
|
|
|
|
$groups = mgroup($refs, 'getGroup');
|
|
|
|
|
|
|
|
$out = array();
|
|
|
|
foreach ($groups as $group_key => $refs) {
|
2013-02-18 00:40:24 +01:00
|
|
|
$out[] = phutil_tag(
|
|
|
|
'h1',
|
|
|
|
array(
|
|
|
|
'class' => 'atom-group-name',
|
|
|
|
),
|
|
|
|
$this->getGroupName($group_key));
|
|
|
|
|
|
|
|
$items = array();
|
2013-02-18 00:40:11 +01:00
|
|
|
foreach ($refs as $ref) {
|
2013-02-18 00:40:24 +01:00
|
|
|
$items[] = phutil_tag(
|
|
|
|
'li',
|
|
|
|
array(
|
|
|
|
'class' => 'atom-index-item',
|
|
|
|
),
|
|
|
|
array(
|
2013-05-20 19:18:26 +02:00
|
|
|
$this->renderAtomRefLink($ref),
|
2013-02-18 00:40:24 +01:00
|
|
|
' - ',
|
|
|
|
$ref->getSummary(),
|
|
|
|
));
|
2013-02-18 00:40:11 +01:00
|
|
|
}
|
2013-02-18 00:40:24 +01:00
|
|
|
|
|
|
|
$out[] = phutil_tag(
|
|
|
|
'ul',
|
|
|
|
array(
|
|
|
|
'class' => 'atom-index-list',
|
|
|
|
),
|
|
|
|
$items);
|
2013-02-18 00:40:11 +01:00
|
|
|
}
|
|
|
|
|
2013-02-18 00:40:24 +01:00
|
|
|
return phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'atom-index',
|
|
|
|
),
|
|
|
|
$out);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getGroupName($group_key) {
|
|
|
|
return $group_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getBlockMarkupEngine() {
|
2013-06-06 17:36:51 +02:00
|
|
|
$engine = PhabricatorMarkupEngine::newMarkupEngine(array());
|
|
|
|
|
|
|
|
$engine->setConfig('preserve-linebreaks', false);
|
2013-03-04 21:33:05 +01:00
|
|
|
$engine->setConfig('viewer', new PhabricatorUser());
|
2013-02-18 18:44:43 +01:00
|
|
|
$engine->setConfig('diviner.renderer', $this);
|
2013-06-06 17:36:51 +02:00
|
|
|
$engine->setConfig('header.generate-toc', true);
|
|
|
|
|
2013-02-18 18:44:43 +01:00
|
|
|
return $engine;
|
2013-02-18 00:40:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getInlineMarkupEngine() {
|
|
|
|
return $this->getBlockMarkupEngine();
|
2013-02-18 00:40:11 +01:00
|
|
|
}
|
|
|
|
|
2013-02-18 18:44:43 +01:00
|
|
|
public function normalizeAtomRef(DivinerAtomRef $ref) {
|
|
|
|
if (!strlen($ref->getBook())) {
|
|
|
|
$ref->setBook($this->getConfig('name'));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($ref->getBook() != $this->getConfig('name')) {
|
|
|
|
// If the ref is from a different book, we can't normalize it. Just return
|
|
|
|
// it as-is if it has enough information to resolve.
|
|
|
|
if ($ref->getName() && $ref->getType()) {
|
|
|
|
return $ref;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$atom = $this->getPublisher()->findAtomByRef($ref);
|
|
|
|
if ($atom) {
|
|
|
|
return $atom->getRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getAtomHrefDepth(DivinerAtom $atom) {
|
|
|
|
if ($atom->getContext()) {
|
|
|
|
return 4;
|
|
|
|
} else {
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getHrefForAtomRef(DivinerAtomRef $ref) {
|
2013-05-20 19:18:26 +02:00
|
|
|
$depth = 1;
|
|
|
|
|
2013-02-18 18:44:43 +01:00
|
|
|
$atom = $this->peekAtomStack();
|
2013-05-20 19:18:26 +02:00
|
|
|
if ($atom) {
|
|
|
|
$depth = $this->getAtomHrefDepth($atom);
|
|
|
|
}
|
|
|
|
|
2013-02-18 18:44:43 +01:00
|
|
|
$href = str_repeat('../', $depth);
|
|
|
|
|
|
|
|
$book = $ref->getBook();
|
|
|
|
$type = $ref->getType();
|
|
|
|
$name = $ref->getName();
|
|
|
|
$context = $ref->getContext();
|
|
|
|
|
|
|
|
$href .= $book.'/'.$type.'/';
|
|
|
|
if ($context !== null) {
|
|
|
|
$href .= $context.'/';
|
|
|
|
}
|
2013-05-20 19:18:26 +02:00
|
|
|
$href .= $name.'/index.html';
|
2013-02-18 18:44:43 +01:00
|
|
|
|
|
|
|
return $href;
|
|
|
|
}
|
2013-02-18 00:40:11 +01:00
|
|
|
|
2013-05-20 19:18:26 +02:00
|
|
|
protected function renderAtomRefLink(DivinerAtomRef $ref) {
|
|
|
|
return phutil_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => $this->getHrefForAtomRef($ref),
|
|
|
|
),
|
|
|
|
$ref->getTitle());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Move Diviner further toward usability
Summary:
- Complete the "project" -> "book" stuff. This is cleaner conceptually and keeps us from having yet another meaning for the word "project".
- Normalize symbols during atomization. This simplifies publishing a great deal, and allows static documentation to link to dynamic documentation and vice versa, because the canonical names of symbols are agreed upon (we can tweak the actual algorithm).
- Give articles a specifiable name distinct from the title, and default to something like "support" instead of "Get Help! Get Support!" so URIs end up more readable (not "Get_Help!_Get_Support!").
- Have the atomizers set book information on atoms.
- Implement very basic publishers. Publishers are basically glue code between the atomization process and the rendering process -- the two we'll have initially are "static" (publish to files on disk) and "phabricator" (or similar -- publish into the database).
- Handle duplicate symbol definitions in the atomize and publish pipelines. This fixes the issue where a project defines two functions named "idx()" and we currently tell them not to do that and break. Realistically, this is common in the real world and we should just roll our eyes and do the legwork to generate documentation as best we can.
- Particularly, dirty all atoms with the same name as a dirty atom (e.g., if 'function f()' is updated, regnerate the documentation for all functions named f() in the book).
- When publishing, we publish these at "function/f/@1", "function/f/@2". The base page will offer to disambiguate ("There are 8 functions named 'f' in this codebase, which one do you want?").
- Implement a very very basic renderer. This generates the actual HTML (or text, or XML, or whatever else) for the documentation, which the publisher dumps onto disk or into a database or whatever.
- The atomize workflow actually needs to depend on books, at least sort of, so make it load config and use it properly.
- Propagate multilevel dirties through the graph. If "C extends B" and "B extends A", we should regenerate C when A changes. Prior to this diff, we would regnerate B only.
Test Plan: Generated some documentation. Named two articles "feedback", generated docs, saw "article/feedback/@1/" and "article/feedback/@2/" created.
Reviewers: btrahan, vrana, chad
Reviewed By: chad
CC: aran
Maniphest Tasks: T988
Differential Revision: https://secure.phabricator.com/D4896
2013-02-18 00:39:36 +01:00
|
|
|
}
|