Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/diviner/atomizer/DivinerPHPAtomizer.php b/src/applications/diviner/atomizer/DivinerPHPAtomizer.php
index 30928fe319..7b3f2d639b 100644
--- a/src/applications/diviner/atomizer/DivinerPHPAtomizer.php
+++ b/src/applications/diviner/atomizer/DivinerPHPAtomizer.php
@@ -1,313 +1,313 @@
<?php
final class DivinerPHPAtomizer extends DivinerAtomizer {
+ protected function newAtom($type) {
+ return parent::newAtom($type)->setLanguage('php');
+ }
+
+
protected function executeAtomize($file_name, $file_data) {
$future = xhpast_get_parser_future($file_data);
$tree = XHPASTTree::newFromDataAndResolvedExecFuture(
$file_data,
$future->resolve());
$atoms = array();
$root = $tree->getRootNode();
$func_decl = $root->selectDescendantsOfType('n_FUNCTION_DECLARATION');
foreach ($func_decl as $func) {
$name = $func->getChildByIndex(2);
- $atom = id(new DivinerAtom())
- ->setType('function')
+ $atom = $this->newAtom(DivinerAtom::TYPE_FUNCTION)
->setName($name->getConcreteString())
->setLine($func->getLineNumber())
->setFile($file_name);
$this->findAtomDocblock($atom, $func);
$this->parseParams($atom, $func);
$this->parseReturnType($atom, $func);
$atoms[] = $atom;
}
$class_types = array(
- 'class' => 'n_CLASS_DECLARATION',
- 'interface' => 'n_INTERFACE_DECLARATION',
+ DivinerAtom::TYPE_CLASS => 'n_CLASS_DECLARATION',
+ DivinerAtom::TYPE_INTERFACE => 'n_INTERFACE_DECLARATION',
);
foreach ($class_types as $atom_type => $node_type) {
$class_decls = $root->selectDescendantsOfType($node_type);
foreach ($class_decls as $class) {
$name = $class->getChildByIndex(1, 'n_CLASS_NAME');
- $atom = id(new DivinerAtom())
- ->setType($atom_type)
+ $atom = $this->newAtom($atom_type)
->setName($name->getConcreteString())
->setFile($file_name)
->setLine($class->getLineNumber());
+ // TODO: Parse "abstract" and "final".
+
// If this exists, it is n_EXTENDS_LIST.
$extends = $class->getChildByIndex(2);
$extends_class = $extends->selectDescendantsOfType('n_CLASS_NAME');
foreach ($extends_class as $parent_class) {
$atom->addExtends(
- DivinerAtomRef::newFromDictionary(
- array(
- 'type' => 'class',
- 'name' => $parent_class->getConcreteString(),
- )));
+ $this->newRef(
+ DivinerAtom::TYPE_CLASS,
+ $parent_class->getConcreteString()));
}
// If this exists, it is n_IMPLEMENTS_LIST.
$implements = $class->getChildByIndex(3);
$iface_names = $implements->selectDescendantsOfType('n_CLASS_NAME');
foreach ($iface_names as $iface_name) {
$atom->addExtends(
- DivinerAtomRef::newFromDictionary(
- array(
- 'type' => 'interface',
- 'name' => $iface_name->getConcreteString(),
- )));
+ $this->newRef(
+ DivinerAtom::TYPE_INTERFACE,
+ $iface_name->getConcreteString()));
}
$this->findAtomDocblock($atom, $class);
$methods = $class->selectDescendantsOfType('n_METHOD_DECLARATION');
foreach ($methods as $method) {
- $matom = id(new DivinerAtom())
- ->setType('method');
+ $matom = $this->newAtom(DivinerAtom::TYPE_METHOD);
$this->findAtomDocblock($matom, $method);
$attribute_list = $method->getChildByIndex(0);
$attributes = $attribute_list->selectDescendantsOfType('n_STRING');
if ($attributes) {
foreach ($attributes as $attribute) {
$attr = strtolower($attribute->getConcreteString());
switch ($attr) {
case 'static':
$matom->setProperty($attr, true);
break;
case 'public':
case 'protected':
case 'private':
$matom->setProperty('access', $attr);
break;
}
}
} else {
$matom->setProperty('access', 'public');
}
$this->parseParams($matom, $method);
$matom->setName($method->getChildByIndex(2)->getConcreteString());
$matom->setLine($method->getLineNumber());
$matom->setFile($file_name);
$this->parseReturnType($matom, $method);
$atom->addChild($matom);
$atoms[] = $matom;
}
$atoms[] = $atom;
}
}
return $atoms;
}
private function parseParams(DivinerAtom $atom, AASTNode $func) {
$params = $func
->getChildByIndex(3, 'n_DECLARATAION_PARAMETER_LIST')
->selectDescendantsOfType('n_DECLARATION_PARAMETER');
$param_spec = array();
if ($atom->getDocblockRaw()) {
$metadata = $atom->getDocblockMeta();
} else {
$metadata = array();
}
$docs = idx($metadata, 'param');
if ($docs) {
$docs = explode("\n", $docs);
$docs = array_filter($docs);
} else {
$docs = array();
}
if (count($docs)) {
if (count($docs) < count($params)) {
$atom->addWarning(
pht(
'This call takes %d parameters, but only %d are documented.',
count($params),
count($docs)));
}
}
foreach ($params as $param) {
$name = $param->getChildByIndex(1)->getConcreteString();
$dict = array(
'type' => $param->getChildByIndex(0)->getConcreteString(),
'default' => $param->getChildByIndex(2)->getConcreteString(),
);
if ($docs) {
$doc = array_shift($docs);
if ($doc) {
$dict += $this->parseParamDoc($atom, $doc, $name);
}
}
$param_spec[] = array(
'name' => $name,
) + $dict;
}
if ($docs) {
foreach ($docs as $doc) {
if ($doc) {
$param_spec[] = $this->parseParamDoc($atom, $doc, null);
}
}
}
// TODO: Find `assert_instances_of()` calls in the function body and
// add their type information here. See T1089.
$atom->setProperty('parameters', $param_spec);
}
private function findAtomDocblock(DivinerAtom $atom, XHPASTNode $node) {
$token = $node->getDocblockToken();
if ($token) {
$atom->setDocblockRaw($token->getValue());
return true;
} else {
$tokens = $node->getTokens();
if ($tokens) {
$prev = head($tokens);
while ($prev = $prev->getPrevToken()) {
if ($prev->isAnyWhitespace()) {
continue;
}
break;
}
if ($prev && $prev->isComment()) {
$value = $prev->getValue();
$matches = null;
if (preg_match('/@(return|param|task|author)/', $value, $matches)) {
$atom->addWarning(
pht(
'Atom "%s" is preceded by a comment containing "@%s", but the '.
'comment is not a documentation comment. Documentation '.
'comments must begin with "/**", followed by a newline. Did '.
'you mean to use a documentation comment? (As the comment is '.
'not a documentation comment, it will be ignored.)',
$atom->getName(),
$matches[1]));
}
}
}
$atom->setDocblockRaw('');
return false;
}
}
protected function parseParamDoc(DivinerAtom $atom, $doc, $name) {
$dict = array();
$split = preg_split('/\s+/', trim($doc), $limit = 2);
if (!empty($split[0])) {
$dict['doctype'] = $split[0];
}
if (!empty($split[1])) {
$docs = $split[1];
// If the parameter is documented like "@param int $num Blah blah ..",
// get rid of the `$num` part (which Diviner considers optional). If it
// is present and different from the declared name, raise a warning.
$matches = null;
if (preg_match('/^(\\$\S+)\s+/', $docs, $matches)) {
if ($name !== null) {
if ($matches[1] !== $name) {
$atom->addWarning(
pht(
'Parameter "%s" is named "%s" in the documentation. The '.
'documentation may be out of date.',
$name,
$matches[1]));
}
}
$docs = substr($docs, strlen($matches[0]));
}
$dict['docs'] = $docs;
}
return $dict;
}
private function parseReturnType(DivinerAtom $atom, XHPASTNode $decl) {
$return_spec = array();
$metadata = $atom->getDocblockMeta();
$return = idx($metadata, 'return');
if (!$return) {
$return = idx($metadata, 'returns');
if ($return) {
$atom->addWarning(
pht('Documentation uses `@returns`, but should use `@return`.'));
}
}
if ($atom->getName() == '__construct' && $atom->getType() == 'method') {
$return_spec = array(
'doctype' => 'this',
'docs' => '//Implicit.//',
);
if ($return) {
$atom->addWarning(
'Method __construct() has explicitly documented @return. The '.
'__construct() method always returns $this. Diviner documents '.
'this implicitly.');
}
} else if ($return) {
$split = preg_split('/\s+/', trim($return), $limit = 2);
if (!empty($split[0])) {
$type = $split[0];
}
if ($decl->getChildByIndex(1)->getTypeName() == 'n_REFERENCE') {
$type = $type.' &';
}
$docs = null;
if (!empty($split[1])) {
$docs = $split[1];
}
$return_spec = array(
'doctype' => $type,
'docs' => $docs,
);
} else {
$return_spec = array(
'type' => 'wild',
);
}
$atom->setProperty('return', $return_spec);
}
}
diff --git a/src/applications/diviner/controller/DivinerAtomController.php b/src/applications/diviner/controller/DivinerAtomController.php
index d923d3d641..3117b3e9e2 100644
--- a/src/applications/diviner/controller/DivinerAtomController.php
+++ b/src/applications/diviner/controller/DivinerAtomController.php
@@ -1,424 +1,587 @@
<?php
final class DivinerAtomController extends DivinerController {
private $bookName;
private $atomType;
private $atomName;
private $atomContext;
private $atomIndex;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->bookName = $data['book'];
$this->atomType = $data['type'];
$this->atomName = $data['name'];
$this->atomContext = nonempty(idx($data, 'context'), null);
$this->atomIndex = nonempty(idx($data, 'index'), null);
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$book = id(new DivinerBookQuery())
->setViewer($viewer)
->withNames(array($this->bookName))
->executeOne();
if (!$book) {
return new Aphront404Response();
}
// TODO: This query won't load ghosts, because they'll fail `needAtoms()`.
// Instead, we might want to load ghosts and render a message like
// "this thing existed in an older version, but no longer does", especially
// if we add content like comments.
$symbol = id(new DivinerAtomQuery())
->setViewer($viewer)
->withBookPHIDs(array($book->getPHID()))
->withTypes(array($this->atomType))
->withNames(array($this->atomName))
->withContexts(array($this->atomContext))
->withIndexes(array($this->atomIndex))
->needAtoms(true)
->needExtends(true)
->needChildren(true)
->executeOne();
if (!$symbol) {
return new Aphront404Response();
}
$atom = $symbol->getAtom();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($book->getShortTitle())
->setHref('/book/'.$book->getName().'/'));
$atom_short_title = $atom->getDocblockMetaValue(
'short',
$symbol->getTitle());
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($atom_short_title));
$header = id(new PhabricatorHeaderView())
->setHeader($symbol->getTitle())
->addTag(
id(new PhabricatorTagView())
->setType(PhabricatorTagView::TYPE_STATE)
->setBackgroundColor(PhabricatorTagView::COLOR_BLUE)
- ->setName(DivinerAtom::getAtomTypeNameString($atom->getType())));
+ ->setName(DivinerAtom::getAtomTypeNameString($atom->getType())))
+ ->setSubheader($this->renderFullSignature($symbol));
$properties = id(new PhabricatorPropertyListView());
$group = $atom->getDocblockMetaValue('group');
if ($group) {
$group_name = $book->getGroupName($group);
} else {
$group_name = null;
}
$this->buildDefined($properties, $symbol);
$this->buildExtendsAndImplements($properties, $symbol);
$warnings = $atom->getWarnings();
if ($warnings) {
$warnings = id(new AphrontErrorView())
->setErrors($warnings)
->setTitle(pht('Documentation Warnings'))
->setSeverity(AphrontErrorView::SEVERITY_WARNING);
}
+ $methods = $this->composeMethods($symbol);
+
$field = 'default';
$engine = id(new PhabricatorMarkupEngine())
->setViewer($viewer)
- ->addObject($symbol, $field)
- ->process();
-
- $content = $engine->getOutput($symbol, $field);
-
- if (strlen(trim($symbol->getMarkupText($field)))) {
- $content = phutil_tag(
- 'div',
- array(
- 'class' => 'phabricator-remarkup',
- ),
- array(
- $content,
- ));
- } else {
- $undoc = DivinerAtom::getThisAtomIsNotDocumentedString($atom->getType());
- $content = id(new AphrontErrorView())
- ->appendChild($undoc)
- ->setSeverity(AphrontErrorView::SEVERITY_NODATA);
+ ->addObject($symbol, $field);
+ foreach ($methods as $method) {
+ foreach ($method['atoms'] as $matom) {
+ $engine->addObject($matom, $field);
+ }
}
+ $engine->process();
+ $content = $this->renderDocumentationText($symbol, $engine);
$toc = $engine->getEngineMetadata(
$symbol,
$field,
PhutilRemarkupEngineRemarkupHeaderBlockRule::KEY_HEADER_TOC,
array());
$document = id(new PHUIDocumentView())
->setBook($book->getTitle(), $group_name)
->setHeader($header)
->appendChild($properties)
->appendChild($warnings)
->appendChild($content);
- $parameters = $atom->getProperty('parameters');
- if ($parameters !== null) {
- $document->appendChild(
- id(new PhabricatorHeaderView())
- ->setHeader(pht('Parameters')));
-
- $document->appendChild(
- id(new DivinerParameterTableView())
- ->setParameters($parameters));
- }
+ $document->appendChild($this->buildParametersAndReturn(array($symbol)));
- $return = $atom->getProperty('return');
- if ($return !== null) {
- $document->appendChild(
- id(new PhabricatorHeaderView())
- ->setHeader(pht('Return')));
- $document->appendChild(
- id(new DivinerReturnTableView())
- ->setReturn($return));
- }
-
- $methods = $this->composeMethods($symbol);
if ($methods) {
-
$tasks = $this->composeTasks($symbol);
if ($tasks) {
$methods_by_task = igroup($methods, 'task');
$document->appendChild(
id(new PhabricatorHeaderView())
->setHeader(pht('Tasks')));
if (isset($methods_by_task[''])) {
$tasks[''] = array(
'name' => '',
'title' => pht('Other Methods'),
'defined' => $symbol,
);
}
foreach ($tasks as $spec) {
$document->appendChild(
id(new PhabricatorHeaderView())
->setHeader($spec['title']));
$task_methods = idx($methods_by_task, $spec['name'], array());
if ($task_methods) {
$document->appendChild(hsprintf('<ul>'));
foreach ($task_methods as $task_method) {
$atom = last($task_method['atoms']);
$document->appendChild(
hsprintf('<li>%s()</li>', $atom->getName()));
}
$document->appendChild(hsprintf('</ul>'));
} else {
$document->appendChild("No methods for this task.");
}
}
}
$document->appendChild(
id(new PhabricatorHeaderView())
->setHeader(pht('Methods')));
foreach ($methods as $spec) {
+ $matom = last($spec['atoms']);
$method_header = id(new PhabricatorHeaderView())
- ->setHeader(last($spec['atoms'])->getName());
+ ->setHeader($matom->getName());
$inherited = $spec['inherited'];
if ($inherited) {
$method_header->addTag(
id(new PhabricatorTagView())
->setType(PhabricatorTagView::TYPE_STATE)
->setBackgroundColor(PhabricatorTagView::COLOR_GREY)
->setName(pht('Inherited')));
}
- $document->appendChild($method_header);
+ $method_header->setSubheader(
+ $this->renderFullSignature($matom));
+
+ $document->appendChild(
+ array(
+ $method_header,
+ $this->renderMethodDocumentationText($symbol, $spec, $engine),
+ $this->buildParametersAndReturn($spec['atoms']),
+ ));
}
}
if ($toc) {
$side = new PHUIListView();
$side->addMenuItem(
id(new PHUIListItemView())
->setName(pht('Contents'))
->setType(PHUIListItemView::TYPE_LABEL));
foreach ($toc as $key => $entry) {
$side->addMenuItem(
id(new PHUIListItemView())
->setName($entry[1])
->setHref('#'.$key));
}
$document->setSideNav($side, PHUIDocumentView::NAV_TOP);
}
return $this->buildApplicationPage(
array(
$crumbs,
$document,
),
array(
'title' => $symbol->getTitle(),
'device' => true,
));
}
private function buildExtendsAndImplements(
PhabricatorPropertyListView $view,
DivinerLiveSymbol $symbol) {
$lineage = $this->getExtendsLineage($symbol);
if ($lineage) {
$lineage = mpull($lineage, 'getName');
$lineage = implode(' > ', $lineage);
$view->addProperty(pht('Extends'), $lineage);
}
$implements = $this->getImplementsLineage($symbol);
if ($implements) {
$items = array();
foreach ($implements as $spec) {
$via = $spec['via'];
$iface = $spec['interface'];
if ($via == $symbol) {
$items[] = $iface->getName();
} else {
$items[] = $iface->getName().' (via '.$via->getName().')';
}
}
$view->addProperty(
pht('Implements'),
phutil_implode_html(phutil_tag('br'), $items));
}
}
private function getExtendsLineage(DivinerLiveSymbol $symbol) {
foreach ($symbol->getExtends() as $extends) {
if ($extends->getType() == 'class') {
$lineage = $this->getExtendsLineage($extends);
$lineage[] = $extends;
return $lineage;
}
}
return array();
}
private function getImplementsLineage(DivinerLiveSymbol $symbol) {
$implements = array();
// Do these first so we get interfaces ordered from most to least specific.
foreach ($symbol->getExtends() as $extends) {
if ($extends->getType() == 'interface') {
$implements[$extends->getName()] = array(
'interface' => $extends,
'via' => $symbol,
);
}
}
// Now do parent interfaces.
foreach ($symbol->getExtends() as $extends) {
if ($extends->getType() == 'class') {
$implements += $this->getImplementsLineage($extends);
}
}
return $implements;
}
private function buildDefined(
PhabricatorPropertyListView $view,
DivinerLiveSymbol $symbol) {
$atom = $symbol->getAtom();
$defined = $atom->getFile().':'.$atom->getLine();
$link = $symbol->getBook()->getConfig('uri.source');
if ($link) {
$link = strtr(
$link,
array(
'%%' => '%',
'%f' => phutil_escape_uri($atom->getFile()),
'%l' => phutil_escape_uri($atom->getLine()),
));
$defined = phutil_tag(
'a',
array(
'href' => $link,
'target' => '_blank',
),
$defined);
}
$view->addProperty(pht('Defined'), $defined);
}
private function composeMethods(DivinerLiveSymbol $symbol) {
$methods = $this->findMethods($symbol);
if (!$methods) {
return $methods;
}
foreach ($methods as $name => $method) {
// Check for "@task" on each parent, to find the most recently declared
// "@task".
$task = null;
foreach ($method['atoms'] as $key => $method_symbol) {
$atom = $method_symbol->getAtom();
if ($atom->getDocblockMetaValue('task')) {
$task = $atom->getDocblockMetaValue('task');
}
}
$methods[$name]['task'] = $task;
// Set 'inherited' if this atom has no implementation of the method.
if (last($method['implementations']) !== $symbol) {
$methods[$name]['inherited'] = true;
} else {
$methods[$name]['inherited'] = false;
}
}
return $methods;
}
private function findMethods(DivinerLiveSymbol $symbol) {
$child_specs = array();
foreach ($symbol->getExtends() as $extends) {
if ($extends->getType() == DivinerAtom::TYPE_CLASS) {
$child_specs = $this->findMethods($extends);
}
}
foreach ($symbol->getChildren() as $child) {
if ($child->getType() == DivinerAtom::TYPE_METHOD) {
$name = $child->getName();
if (isset($child_specs[$name])) {
$child_specs[$name]['atoms'][] = $child;
$child_specs[$name]['implementations'][] = $symbol;
} else {
$child_specs[$name] = array(
'atoms' => array($child),
'defined' => $symbol,
'implementations' => array($symbol),
);
}
}
}
return $child_specs;
}
private function composeTasks(DivinerLiveSymbol $symbol) {
$extends_task_specs = array();
foreach ($symbol->getExtends() as $extends) {
$extends_task_specs += $this->composeTasks($extends);
}
$task_specs = array();
$tasks = $symbol->getAtom()->getDocblockMetaValue('task');
if (strlen($tasks)) {
$tasks = phutil_split_lines($tasks, $retain_endings = false);
foreach ($tasks as $task) {
list($name, $title) = explode(' ', $task, 2);
$name = trim($name);
$title = trim($title);
$task_specs[$name] = array(
'name' => $name,
'title' => $title,
'defined' => $symbol,
);
}
}
return $task_specs + $extends_task_specs;
}
+ private function renderFullSignature(DivinerLiveSymbol $symbol) {
+ switch ($symbol->getType()) {
+ case DivinerAtom::TYPE_CLASS:
+ case DivinerAtom::TYPE_INTERFACE:
+ case DivinerAtom::TYPE_METHOD:
+ case DivinerAtom::TYPE_FUNCTION:
+ break;
+ default:
+ return null;
+ }
+
+ $atom = $symbol->getAtom();
+
+ $out = array();
+ if ($atom->getProperty('final')) {
+ $out[] = 'final';
+ }
+
+ if ($atom->getProperty('abstract')) {
+ $out[] = 'abstract';
+ }
+
+ if ($atom->getProperty('access')) {
+ $out[] = $atom->getProperty('access');
+ }
+
+ if ($atom->getProperty('static')) {
+ $out[] = 'static';
+ }
+
+ switch ($symbol->getType()) {
+ case DivinerAtom::TYPE_CLASS:
+ case DivinerAtom::TYPE_INTERFACE:
+ $out[] = $symbol->getType();
+ break;
+ case DivinerAtom::TYPE_FUNCTION:
+ switch ($atom->getLanguage()) {
+ case 'php':
+ $out[] = $symbol->getType();
+ break;
+ }
+ break;
+ case DivinerAtom::TYPE_METHOD:
+ switch ($atom->getLanguage()) {
+ case 'php':
+ $out[] = DivinerAtom::TYPE_FUNCTION;
+ break;
+ }
+ break;
+ }
+
+ $out[] = $symbol->getName();
+
+ $out = implode(' ', $out);
+
+ $parameters = $atom->getProperty('parameters');
+ if ($parameters !== null) {
+ $pout = array();
+ foreach ($parameters as $parameter) {
+ $pout[] = $parameter['name'];
+ }
+ $out .= '('.implode(', ', $pout).')';
+ }
+
+ return $out;
+ }
+
+ private function buildParametersAndReturn(array $symbols) {
+ assert_instances_of($symbols, 'DivinerLiveSymbol');
+
+ $symbols = array_reverse($symbols);
+ $out = array();
+
+ $collected_parameters = null;
+ foreach ($symbols as $symbol) {
+ $parameters = $symbol->getAtom()->getProperty('parameters');
+ if ($parameters !== null) {
+ if ($collected_parameters === null) {
+ $collected_parameters = array();
+ }
+ foreach ($parameters as $key => $parameter) {
+ if (isset($collected_parameters[$key])) {
+ $collected_parameters[$key] += $parameter;
+ } else {
+ $collected_parameters[$key] = $parameter;
+ }
+ }
+ }
+ }
+
+ if ($parameters !== null) {
+ $out[] = id(new PhabricatorHeaderView())
+ ->setHeader(pht('Parameters'));
+ $out[] = id(new DivinerParameterTableView())
+ ->setParameters($parameters);
+ }
+
+ $collected_return = null;
+ foreach ($symbols as $symbol) {
+ $return = $symbol->getAtom()->getProperty('return');
+ if ($return) {
+ if ($collected_return) {
+ $collected_return += $return;
+ } else {
+ $collected_return = $return;
+ }
+ }
+ }
+
+ if ($return !== null) {
+ $out[] = id(new PhabricatorHeaderView())
+ ->setHeader(pht('Return'));
+ $out[] = id(new DivinerReturnTableView())
+ ->setReturn($collected_return);
+ }
+
+ return $out;
+ }
+
+ private function renderDocumentationText(
+ DivinerLiveSymbol $symbol,
+ PhabricatorMarkupEngine $engine) {
+
+ $field = 'default';
+ $content = $engine->getOutput($symbol, $field);
+
+ if (strlen(trim($symbol->getMarkupText($field)))) {
+ $content = phutil_tag(
+ 'div',
+ array(
+ 'class' => 'phabricator-remarkup',
+ ),
+ $content);
+ } else {
+ $atom = $symbol->getAtom();
+ $undoc = DivinerAtom::getThisAtomIsNotDocumentedString($atom->getType());
+ $content = id(new AphrontErrorView())
+ ->appendChild($undoc)
+ ->setSeverity(AphrontErrorView::SEVERITY_NODATA);
+ }
+
+ return $content;
+ }
+
+ private function renderMethodDocumentationText(
+ DivinerLiveSymbol $parent,
+ array $spec,
+ PhabricatorMarkupEngine $engine) {
+
+ $symbols = array_values($spec['atoms']);
+ $implementations = array_values($spec['implementations']);
+
+ $field = 'default';
+
+ $out = array();
+ foreach ($symbols as $key => $symbol) {
+ $impl = $implementations[$key];
+ if ($impl !== $parent) {
+ if (!strlen(trim($symbol->getMarkupText($field)))) {
+ continue;
+ }
+ $out[] = phutil_tag(
+ 'div',
+ array(),
+ pht('From parent implementation in %s:', $impl->getName()));
+ } else if ($out) {
+ $out[] = phutil_tag(
+ 'div',
+ array(),
+ pht('From this implementation:'));
+ }
+ $out[] = $this->renderDocumentationText($symbol, $engine);
+ }
+
+ // If we only have inherited implementations but none have documentation,
+ // render the last one here so we get the "this thing has no documentation"
+ // element.
+ if (!$out) {
+ $out[] = $this->renderDocumentationText($symbol, $engine);
+ }
+
+ return $out;
+ }
+
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Oct 16, 9:45 AM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
272593
Default Alt Text
(28 KB)

Event Timeline