@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Compose and display method information in Diviner

Summary:
Ref T988. As mentioned elsewhere, one broad goal of this iteration is to reduce the amount of boilerplate documentation we need to write. For example:

- I want to set `@group` by default in most cases.
- I want to inherit things like `@param` and `@return` by default.
- I want to inherit method documentation by default.
- I want to inherit `@task` information.

This implements most of the method inheritance stuff.

This //looks// super gross, but I believe we now compose all of the information of interest at display time and can work on rendering it sensibly in the near future.

Test Plan: {F56790}

Reviewers: btrahan, chad

Reviewed By: btrahan

CC: aran, sascha-egerer

Maniphest Tasks: T988

Differential Revision: https://secure.phabricator.com/D6849

+225 -62
+18 -18
src/applications/diviner/atomizer/DivinerPHPAtomizer.php
··· 2 2 3 3 final class DivinerPHPAtomizer extends DivinerAtomizer { 4 4 5 + protected function newAtom($type) { 6 + return parent::newAtom($type)->setLanguage('php'); 7 + } 8 + 9 + 5 10 protected function executeAtomize($file_name, $file_data) { 6 11 $future = xhpast_get_parser_future($file_data); 7 12 $tree = XHPASTTree::newFromDataAndResolvedExecFuture( ··· 17 22 18 23 $name = $func->getChildByIndex(2); 19 24 20 - $atom = id(new DivinerAtom()) 21 - ->setType('function') 25 + $atom = $this->newAtom(DivinerAtom::TYPE_FUNCTION) 22 26 ->setName($name->getConcreteString()) 23 27 ->setLine($func->getLineNumber()) 24 28 ->setFile($file_name); ··· 32 36 } 33 37 34 38 $class_types = array( 35 - 'class' => 'n_CLASS_DECLARATION', 36 - 'interface' => 'n_INTERFACE_DECLARATION', 39 + DivinerAtom::TYPE_CLASS => 'n_CLASS_DECLARATION', 40 + DivinerAtom::TYPE_INTERFACE => 'n_INTERFACE_DECLARATION', 37 41 ); 38 42 foreach ($class_types as $atom_type => $node_type) { 39 43 $class_decls = $root->selectDescendantsOfType($node_type); 40 44 foreach ($class_decls as $class) { 41 45 $name = $class->getChildByIndex(1, 'n_CLASS_NAME'); 42 46 43 - $atom = id(new DivinerAtom()) 44 - ->setType($atom_type) 47 + $atom = $this->newAtom($atom_type) 45 48 ->setName($name->getConcreteString()) 46 49 ->setFile($file_name) 47 50 ->setLine($class->getLineNumber()); 51 + 52 + // TODO: Parse "abstract" and "final". 48 53 49 54 // If this exists, it is n_EXTENDS_LIST. 50 55 $extends = $class->getChildByIndex(2); 51 56 $extends_class = $extends->selectDescendantsOfType('n_CLASS_NAME'); 52 57 foreach ($extends_class as $parent_class) { 53 58 $atom->addExtends( 54 - DivinerAtomRef::newFromDictionary( 55 - array( 56 - 'type' => 'class', 57 - 'name' => $parent_class->getConcreteString(), 58 - ))); 59 + $this->newRef( 60 + DivinerAtom::TYPE_CLASS, 61 + $parent_class->getConcreteString())); 59 62 } 60 63 61 64 // If this exists, it is n_IMPLEMENTS_LIST. ··· 63 66 $iface_names = $implements->selectDescendantsOfType('n_CLASS_NAME'); 64 67 foreach ($iface_names as $iface_name) { 65 68 $atom->addExtends( 66 - DivinerAtomRef::newFromDictionary( 67 - array( 68 - 'type' => 'interface', 69 - 'name' => $iface_name->getConcreteString(), 70 - ))); 69 + $this->newRef( 70 + DivinerAtom::TYPE_INTERFACE, 71 + $iface_name->getConcreteString())); 71 72 } 72 73 73 74 $this->findAtomDocblock($atom, $class); 74 75 75 76 $methods = $class->selectDescendantsOfType('n_METHOD_DECLARATION'); 76 77 foreach ($methods as $method) { 77 - $matom = id(new DivinerAtom()) 78 - ->setType('method'); 78 + $matom = $this->newAtom(DivinerAtom::TYPE_METHOD); 79 79 80 80 $this->findAtomDocblock($matom, $method); 81 81
+207 -44
src/applications/diviner/controller/DivinerAtomController.php
··· 76 76 id(new PhabricatorTagView()) 77 77 ->setType(PhabricatorTagView::TYPE_STATE) 78 78 ->setBackgroundColor(PhabricatorTagView::COLOR_BLUE) 79 - ->setName(DivinerAtom::getAtomTypeNameString($atom->getType()))); 79 + ->setName(DivinerAtom::getAtomTypeNameString($atom->getType()))) 80 + ->setSubheader($this->renderFullSignature($symbol)); 80 81 81 82 $properties = id(new PhabricatorPropertyListView()); 82 83 ··· 98 99 ->setSeverity(AphrontErrorView::SEVERITY_WARNING); 99 100 } 100 101 102 + $methods = $this->composeMethods($symbol); 103 + 101 104 $field = 'default'; 102 105 $engine = id(new PhabricatorMarkupEngine()) 103 106 ->setViewer($viewer) 104 - ->addObject($symbol, $field) 105 - ->process(); 106 - 107 - $content = $engine->getOutput($symbol, $field); 108 - 109 - if (strlen(trim($symbol->getMarkupText($field)))) { 110 - $content = phutil_tag( 111 - 'div', 112 - array( 113 - 'class' => 'phabricator-remarkup', 114 - ), 115 - array( 116 - $content, 117 - )); 118 - } else { 119 - $undoc = DivinerAtom::getThisAtomIsNotDocumentedString($atom->getType()); 120 - $content = id(new AphrontErrorView()) 121 - ->appendChild($undoc) 122 - ->setSeverity(AphrontErrorView::SEVERITY_NODATA); 107 + ->addObject($symbol, $field); 108 + foreach ($methods as $method) { 109 + foreach ($method['atoms'] as $matom) { 110 + $engine->addObject($matom, $field); 111 + } 123 112 } 113 + $engine->process(); 124 114 115 + $content = $this->renderDocumentationText($symbol, $engine); 125 116 126 117 $toc = $engine->getEngineMetadata( 127 118 $symbol, ··· 136 127 ->appendChild($warnings) 137 128 ->appendChild($content); 138 129 139 - $parameters = $atom->getProperty('parameters'); 140 - if ($parameters !== null) { 141 - $document->appendChild( 142 - id(new PhabricatorHeaderView()) 143 - ->setHeader(pht('Parameters'))); 130 + $document->appendChild($this->buildParametersAndReturn(array($symbol))); 144 131 145 - $document->appendChild( 146 - id(new DivinerParameterTableView()) 147 - ->setParameters($parameters)); 148 - } 149 - 150 - $return = $atom->getProperty('return'); 151 - if ($return !== null) { 152 - $document->appendChild( 153 - id(new PhabricatorHeaderView()) 154 - ->setHeader(pht('Return'))); 155 - $document->appendChild( 156 - id(new DivinerReturnTableView()) 157 - ->setReturn($return)); 158 - } 159 - 160 - $methods = $this->composeMethods($symbol); 161 132 if ($methods) { 162 - 163 133 $tasks = $this->composeTasks($symbol); 164 134 165 135 if ($tasks) { ··· 201 171 id(new PhabricatorHeaderView()) 202 172 ->setHeader(pht('Methods'))); 203 173 foreach ($methods as $spec) { 174 + $matom = last($spec['atoms']); 204 175 $method_header = id(new PhabricatorHeaderView()) 205 - ->setHeader(last($spec['atoms'])->getName()); 176 + ->setHeader($matom->getName()); 206 177 207 178 $inherited = $spec['inherited']; 208 179 if ($inherited) { ··· 213 184 ->setName(pht('Inherited'))); 214 185 } 215 186 216 - $document->appendChild($method_header); 187 + $method_header->setSubheader( 188 + $this->renderFullSignature($matom)); 189 + 190 + $document->appendChild( 191 + array( 192 + $method_header, 193 + $this->renderMethodDocumentationText($symbol, $spec, $engine), 194 + $this->buildParametersAndReturn($spec['atoms']), 195 + )); 217 196 } 218 197 } 219 198 ··· 419 398 } 420 399 421 400 return $task_specs + $extends_task_specs; 401 + } 402 + 403 + private function renderFullSignature(DivinerLiveSymbol $symbol) { 404 + switch ($symbol->getType()) { 405 + case DivinerAtom::TYPE_CLASS: 406 + case DivinerAtom::TYPE_INTERFACE: 407 + case DivinerAtom::TYPE_METHOD: 408 + case DivinerAtom::TYPE_FUNCTION: 409 + break; 410 + default: 411 + return null; 412 + } 413 + 414 + $atom = $symbol->getAtom(); 415 + 416 + $out = array(); 417 + if ($atom->getProperty('final')) { 418 + $out[] = 'final'; 419 + } 420 + 421 + if ($atom->getProperty('abstract')) { 422 + $out[] = 'abstract'; 423 + } 424 + 425 + if ($atom->getProperty('access')) { 426 + $out[] = $atom->getProperty('access'); 427 + } 428 + 429 + if ($atom->getProperty('static')) { 430 + $out[] = 'static'; 431 + } 432 + 433 + switch ($symbol->getType()) { 434 + case DivinerAtom::TYPE_CLASS: 435 + case DivinerAtom::TYPE_INTERFACE: 436 + $out[] = $symbol->getType(); 437 + break; 438 + case DivinerAtom::TYPE_FUNCTION: 439 + switch ($atom->getLanguage()) { 440 + case 'php': 441 + $out[] = $symbol->getType(); 442 + break; 443 + } 444 + break; 445 + case DivinerAtom::TYPE_METHOD: 446 + switch ($atom->getLanguage()) { 447 + case 'php': 448 + $out[] = DivinerAtom::TYPE_FUNCTION; 449 + break; 450 + } 451 + break; 452 + } 453 + 454 + $out[] = $symbol->getName(); 455 + 456 + $out = implode(' ', $out); 457 + 458 + $parameters = $atom->getProperty('parameters'); 459 + if ($parameters !== null) { 460 + $pout = array(); 461 + foreach ($parameters as $parameter) { 462 + $pout[] = $parameter['name']; 463 + } 464 + $out .= '('.implode(', ', $pout).')'; 465 + } 466 + 467 + return $out; 468 + } 469 + 470 + private function buildParametersAndReturn(array $symbols) { 471 + assert_instances_of($symbols, 'DivinerLiveSymbol'); 472 + 473 + $symbols = array_reverse($symbols); 474 + $out = array(); 475 + 476 + $collected_parameters = null; 477 + foreach ($symbols as $symbol) { 478 + $parameters = $symbol->getAtom()->getProperty('parameters'); 479 + if ($parameters !== null) { 480 + if ($collected_parameters === null) { 481 + $collected_parameters = array(); 482 + } 483 + foreach ($parameters as $key => $parameter) { 484 + if (isset($collected_parameters[$key])) { 485 + $collected_parameters[$key] += $parameter; 486 + } else { 487 + $collected_parameters[$key] = $parameter; 488 + } 489 + } 490 + } 491 + } 492 + 493 + if ($parameters !== null) { 494 + $out[] = id(new PhabricatorHeaderView()) 495 + ->setHeader(pht('Parameters')); 496 + $out[] = id(new DivinerParameterTableView()) 497 + ->setParameters($parameters); 498 + } 499 + 500 + $collected_return = null; 501 + foreach ($symbols as $symbol) { 502 + $return = $symbol->getAtom()->getProperty('return'); 503 + if ($return) { 504 + if ($collected_return) { 505 + $collected_return += $return; 506 + } else { 507 + $collected_return = $return; 508 + } 509 + } 510 + } 511 + 512 + if ($return !== null) { 513 + $out[] = id(new PhabricatorHeaderView()) 514 + ->setHeader(pht('Return')); 515 + $out[] = id(new DivinerReturnTableView()) 516 + ->setReturn($collected_return); 517 + } 518 + 519 + return $out; 520 + } 521 + 522 + private function renderDocumentationText( 523 + DivinerLiveSymbol $symbol, 524 + PhabricatorMarkupEngine $engine) { 525 + 526 + $field = 'default'; 527 + $content = $engine->getOutput($symbol, $field); 528 + 529 + if (strlen(trim($symbol->getMarkupText($field)))) { 530 + $content = phutil_tag( 531 + 'div', 532 + array( 533 + 'class' => 'phabricator-remarkup', 534 + ), 535 + $content); 536 + } else { 537 + $atom = $symbol->getAtom(); 538 + $undoc = DivinerAtom::getThisAtomIsNotDocumentedString($atom->getType()); 539 + $content = id(new AphrontErrorView()) 540 + ->appendChild($undoc) 541 + ->setSeverity(AphrontErrorView::SEVERITY_NODATA); 542 + } 543 + 544 + return $content; 545 + } 546 + 547 + private function renderMethodDocumentationText( 548 + DivinerLiveSymbol $parent, 549 + array $spec, 550 + PhabricatorMarkupEngine $engine) { 551 + 552 + $symbols = array_values($spec['atoms']); 553 + $implementations = array_values($spec['implementations']); 554 + 555 + $field = 'default'; 556 + 557 + $out = array(); 558 + foreach ($symbols as $key => $symbol) { 559 + $impl = $implementations[$key]; 560 + if ($impl !== $parent) { 561 + if (!strlen(trim($symbol->getMarkupText($field)))) { 562 + continue; 563 + } 564 + $out[] = phutil_tag( 565 + 'div', 566 + array(), 567 + pht('From parent implementation in %s:', $impl->getName())); 568 + } else if ($out) { 569 + $out[] = phutil_tag( 570 + 'div', 571 + array(), 572 + pht('From this implementation:')); 573 + } 574 + $out[] = $this->renderDocumentationText($symbol, $engine); 575 + } 576 + 577 + // If we only have inherited implementations but none have documentation, 578 + // render the last one here so we get the "this thing has no documentation" 579 + // element. 580 + if (!$out) { 581 + $out[] = $this->renderDocumentationText($symbol, $engine); 582 + } 583 + 584 + return $out; 422 585 } 423 586 424 587 }