1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-03-29 04:28:12 +01:00

Allow Diviner groups to be configured in .book files

Summary:
Ref T988. Currently, every class/function needs to be annotated with `@group`, but 99% of this data can be inferred from file structure, at least in this project. Allow group specifications like:

  "paste" : {
    "name" : "Paste",
    "include" : "(^src/applications/paste/)"
  }

..to automatically put everything defined there in the "paste" group. A list of regexps is also supported. Depends on D6855.

Test Plan: Regenerated documentation with `bin/diviner generate --book src/docs/book/phabricator.book --clean`, observed all Paste stuff go in the paste group.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T988

Differential Revision: https://secure.phabricator.com/D6856
This commit is contained in:
epriestley 2013-09-02 11:33:02 -07:00
parent fef3e074d8
commit b7f6956ec9
8 changed files with 80 additions and 31 deletions

View file

@ -295,10 +295,8 @@ final class DivinerAtom {
}
public function getRef() {
$group = null;
$title = null;
if ($this->docblockMeta) {
$group = $this->getDocblockMetaValue('group');
$title = $this->getDocblockMetaValue('title');
}
@ -308,7 +306,7 @@ final class DivinerAtom {
->setType($this->getType())
->setName($this->getName())
->setTitle($title)
->setGroup($group);
->setGroup($this->getProperty('group'));
}
public static function newFromDictionary(array $dictionary) {

View file

@ -7,6 +7,7 @@ abstract class DivinerAtomizer {
private $book;
private $fileName;
private $atomContext;
/**
* If you make a significant change to an atomizer, you can bump this
@ -16,9 +17,33 @@ abstract class DivinerAtomizer {
return 1;
}
final public function atomize($file_name, $file_data) {
final public function atomize($file_name, $file_data, array $context) {
$this->fileName = $file_name;
return $this->executeAtomize($file_name, $file_data);
$this->atomContext = $context;
$atoms = $this->executeAtomize($file_name, $file_data);
// Promote the "@group" special to a property. If there's no "@group" on
// an atom but the file it's in matches a group pattern, associate it with
// the right group.
foreach ($atoms as $atom) {
$group = null;
try {
$group = $atom->getDocblockMetaValue('group');
} catch (Exception $ex) {
// There's no docblock metadata.
}
// If there's no group, but the file matches a group, use that group.
if ($group === null && isset($context['group'])) {
$group = $context['group'];
}
if ($group !== null) {
$atom->setProperty('group', $group);
}
}
return $atoms;
}
abstract protected function executeAtomize($file_name, $file_data);

View file

@ -89,6 +89,8 @@ final class DivinerPHPAtomizer extends DivinerAtomizer {
foreach ($attributes as $attribute) {
$attr = strtolower($attribute->getConcreteString());
switch ($attr) {
case 'final':
case 'abstract':
case 'static':
$matom->setProperty($attr, true);
break;

View file

@ -81,7 +81,7 @@ final class DivinerAtomController extends DivinerController {
$properties = id(new PhabricatorPropertyListView());
$group = $atom->getDocblockMetaValue('group');
$group = $atom->getProperty('group');
if ($group) {
$group_name = $book->getGroupName($group);
} else {

View file

@ -63,7 +63,18 @@ final class DivinerAtomizeWorkflow extends DivinerWorkflow {
$configure->setBook($this->getConfig('name'));
}
$group_rules = array();
foreach ($this->getConfig('groups', array()) as $group => $spec) {
$include = (array)idx($spec, 'include', array());
foreach ($include as $pattern) {
$group_rules[$pattern] = $group;
}
}
$all_atoms = array();
$context = array(
'group' => null,
);
foreach ($files as $file) {
$abs_path = Filesystem::resolvePath($file, $this->getConfig('root'));
$data = Filesystem::readFile($abs_path);
@ -75,7 +86,15 @@ final class DivinerAtomizeWorkflow extends DivinerWorkflow {
$console->writeLog("Atomizing %s...\n", $file);
}
$file_atoms = $file_atomizer->atomize($file, $data);
$context['group'] = null;
foreach ($group_rules as $rule => $group) {
if (preg_match($rule, $file)) {
$context['group'] = $group;
break;
}
}
$file_atoms = $file_atomizer->atomize($file, $data, $context);
$all_atoms[] = $file_atoms;
if (count($file_atoms) !== 1) {
@ -83,7 +102,7 @@ final class DivinerAtomizeWorkflow extends DivinerWorkflow {
}
$file_atom = head($file_atoms);
$atoms = $atomizer->atomize($file, $data);
$atoms = $atomizer->atomize($file, $data, $context);
foreach ($atoms as $atom) {
if (!$atom->getParentHash()) {

View file

@ -205,26 +205,11 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
'/\\.php$/' => 'DivinerPHPAtomizer',
));
foreach ($rules as $rule => $atomizer) {
if (@preg_match($rule, '') === false) {
throw new Exception(
"Rule '{$rule}' is not a valid regular expression!");
}
}
return $rules;
}
private function getExclude() {
$exclude = $this->getConfig('exclude', array());
foreach ($exclude as $rule) {
if (@preg_match($rule, '') === false) {
throw new Exception(
"Exclude rule '{$rule}' is not a valid regular expression!");
}
}
$exclude = (array)$this->getConfig('exclude', array());
return $exclude;
}

View file

@ -35,6 +35,19 @@ abstract class DivinerWorkflow extends PhutilArgumentWorkflow {
"Book configuration '{$book_path}' is not in JSON format.");
}
PhutilTypeSpec::checkMap(
$book,
array(
'name' => 'string',
'title' => 'optional string',
'short' => 'optional string',
'root' => 'optional string',
'uri.source' => 'optional string',
'rules' => 'optional map<regex, string>',
'exclude' => 'optional regex|list<regex>',
'groups' => 'optional map<string, map<string, wild>>',
));
// If the book specifies a "root", resolve it; otherwise, use the directory
// the book configuration file lives in.
$full_path = dirname(Filesystem::resolvePath($book_path));
@ -43,13 +56,6 @@ abstract class DivinerWorkflow extends PhutilArgumentWorkflow {
}
$book['root'] = Filesystem::resolvePath($book['root'], $full_path);
// Make sure we have a valid book name.
if (!isset($book['name'])) {
throw new PhutilArgumentUsageException(
"Book configuration '{$book_path}' is missing required ".
"property 'name'.");
}
if (!preg_match('/^[a-z][a-z-]*$/', $book['name'])) {
$name = $book['name'];
throw new PhutilArgumentUsageException(
@ -57,6 +63,16 @@ abstract class DivinerWorkflow extends PhutilArgumentWorkflow {
"must include only lowercase letters and hyphens.");
}
foreach (idx($book, 'groups', array()) as $group) {
PhutilTypeSpec::checkmap(
$group,
array(
'name' => 'string',
'include' => 'optional regex|list<regex>',
));
}
$this->bookConfigPath = $book_path;
$this->config = $book;
}

View file

@ -17,5 +17,9 @@
"(^src/docs/user/)"
],
"groups" : {
"paste" : {
"name" : "Paste",
"include" : "(^src/applications/paste/)"
}
}
}