@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.

Minor tidying of `DivinerWorkflow` classes

Summary: Minor tidying and modernizing a few things.

Test Plan: Ran `./bin/diviner atomize` and `./bin/diviner generate`.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley, #blessed_reviewers

Subscribers: Korvin, epriestley

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

+58 -71
+14 -12
src/applications/diviner/workflow/DivinerAtomizeWorkflow.php
··· 11 11 array( 12 12 'name' => 'atomizer', 13 13 'param' => 'class', 14 - 'help' => pht('Specify a subclass of DivinerAtomizer.'), 14 + 'help' => pht('Specify a subclass of %s.', 'DivinerAtomizer'), 15 15 ), 16 16 array( 17 17 'name' => 'book', ··· 36 36 37 37 $atomizer_class = $args->getArg('atomizer'); 38 38 if (!$atomizer_class) { 39 - throw new Exception('Specify an atomizer class with --atomizer.'); 39 + throw new Exception( 40 + pht('Specify an atomizer class with %s.', '--atomizer')); 40 41 } 41 42 42 43 $symbols = id(new PhutilSymbolLoader()) ··· 46 47 ->selectAndLoadSymbols(); 47 48 if (!$symbols) { 48 49 throw new Exception( 49 - "Atomizer class '{$atomizer_class}' must be a concrete subclass of ". 50 - "DivinerAtomizer."); 50 + pht( 51 + "Atomizer class '%s' must be a concrete subclass of %s.", 52 + $atomizer_class, 53 + 'DivinerAtomizer')); 51 54 } 52 55 53 56 $atomizer = newv($atomizer_class, array()); 54 57 55 58 $files = $args->getArg('files'); 56 59 if (!$files) { 57 - throw new Exception('Specify one or more files to atomize.'); 60 + throw new Exception(pht('Specify one or more files to atomize.')); 58 61 } 59 62 60 63 $file_atomizer = new DivinerFileAtomizer(); ··· 80 83 $data = Filesystem::readFile($abs_path); 81 84 82 85 if (!$this->shouldAtomizeFile($file, $data)) { 83 - $console->writeLog("Skipping %s...\n", $file); 86 + $console->writeLog("%s\n", pht('Skipping %s...', $file)); 84 87 continue; 85 88 } else { 86 - $console->writeLog("Atomizing %s...\n", $file); 89 + $console->writeLog("%s\n", pht('Atomizing %s...', $file)); 87 90 } 88 91 89 92 $context['group'] = null; ··· 98 101 $all_atoms[] = $file_atoms; 99 102 100 103 if (count($file_atoms) !== 1) { 101 - throw new Exception('Expected exactly one atom from file atomizer.'); 104 + throw new Exception( 105 + pht('Expected exactly one atom from file atomizer.')); 102 106 } 103 107 $file_atom = head($file_atoms); 104 108 ··· 121 125 if ($args->getArg('ugly')) { 122 126 $json = json_encode($all_atoms); 123 127 } else { 124 - $json_encoder = new PhutilJSON(); 125 - $json = $json_encoder->encodeFormatted($all_atoms); 128 + $json = id(new PhutilJSON())->encodeFormatted($all_atoms); 126 129 } 127 130 128 131 $console->writeOut('%s', $json); 129 - 130 132 return 0; 131 133 } 132 134 133 135 private function shouldAtomizeFile($file_name, $file_data) { 134 - return (strpos($file_data, '@'.'undivinable') === false); 136 + return strpos($file_data, '@'.'undivinable') === false; 135 137 } 136 138 137 139 }
+35 -50
src/applications/diviner/workflow/DivinerGenerateWorkflow.php
··· 12 12 array( 13 13 array( 14 14 'name' => 'clean', 15 - 'help' => 'Clear the caches before generating documentation.', 15 + 'help' => pht('Clear the caches before generating documentation.'), 16 16 ), 17 17 array( 18 18 'name' => 'book', 19 19 'param' => 'path', 20 - 'help' => 'Path to a Diviner book configuration.', 20 + 'help' => pht('Path to a Diviner book configuration.'), 21 21 ), 22 22 )); 23 23 } ··· 52 52 if (!$books) { 53 53 throw new PhutilArgumentUsageException( 54 54 pht( 55 - "There are no Diviner '.book' files anywhere beneath the ". 56 - "current directory. Use '--book <book>' to specify a ". 57 - "documentation book to generate.")); 55 + "There are no Diviner '%s' files anywhere beneath the current ". 56 + "directory. Use '%s' to specify a documentation book to generate.", 57 + '.book', 58 + '--book <book>')); 58 59 } else { 59 60 $this->log(pht('Found %s book(s).', new PhutilNumber(count($books)))); 60 61 } ··· 85 86 // amount of work we can, so that regenerating documentation after minor 86 87 // changes is quick. 87 88 // 88 - // ATOM CACHE 89 + // = ATOM CACHE = 89 90 // 90 91 // In the first stage, we find all the direct changes to source code since 91 92 // the last run. This stage relies on two data structures: 92 93 // 93 - // - File Hash Map: map<file_hash, node_hash> 94 - // - Atom Map: map<node_hash, true> 94 + // - File Hash Map: `map<file_hash, node_hash>` 95 + // - Atom Map: `map<node_hash, true>` 95 96 // 96 97 // First, we hash all the source files in the project to detect any which 97 98 // have changed since the previous run (i.e., their hash is not present in ··· 111 112 // its methods). The File Hash Map contains an exhaustive list of all atoms 112 113 // with type "file", but not child atoms of those top-level atoms.) 113 114 // 114 - // GRAPH CACHE 115 + // = GRAPH CACHE = 115 116 // 116 117 // We now know which atoms exist, and can compare the Atom Map to some 117 118 // existing cache to figure out what has changed. However, this isn't 118 119 // sufficient to figure out which documentation actually needs to be 119 - // regnerated, because atoms depend on other atoms. For example, if "B 120 - // extends A" and the definition for A changes, we need to regenerate the 121 - // documentation in B. Similarly, if X links to Y and Y changes, we should 122 - // regenerate X. (In both these cases, the documentation for the connected 123 - // atom may not acutally change, but in some cases it will, and the extra 124 - // work we need to do is generally very small compared to the size of the 125 - // project.) 120 + // regenerated, because atoms depend on other atoms. For example, if `B 121 + // extends A` and the definition for `A` changes, we need to regenerate the 122 + // documentation in `B`. Similarly, if `X` links to `Y` and `Y` changes, we 123 + // should regenerate `X`. (In both these cases, the documentation for the 124 + // connected atom may not actually change, but in some cases it will, and 125 + // the extra work we need to do is generally very small compared to the 126 + // size of the project.) 126 127 // 127 128 // To figure out which other nodes have changed, we compute a "graph hash" 128 129 // for each node. This hash combines the "node hash" with the node hashes ··· 134 135 // 135 136 // In this stage, we rely on three data structures: 136 137 // 137 - // - Symbol Map: map<node_hash, symbol_hash> 138 - // - Edge Map: map<node_hash, list<symbol_hash>> 139 - // - Graph Map: map<node_hash, graph_hash> 138 + // - Symbol Map: `map<node_hash, symbol_hash>` 139 + // - Edge Map: `map<node_hash, list<symbol_hash>>` 140 + // - Graph Map: `map<node_hash, graph_hash>` 140 141 // 141 142 // Calculating the graph hash requires several steps, because we need to 142 143 // figure out which nodes an atom is attached to. The atom contains symbolic 143 - // references to other nodes by name (e.g., "extends SomeClass") in the form 144 - // of DivinerAtomRefs. We can also build a symbolic reference for any atom 145 - // from the atom itself. Each DivinerAtomRef generates a symbol hash, 146 - // which ends with an "S", for "symbol". 144 + // references to other nodes by name (e.g., `extends SomeClass`) in the form 145 + // of @{class:DivinerAtomRefs}. We can also build a symbolic reference for 146 + // any atom from the atom itself. Each @{class:DivinerAtomRef} generates a 147 + // symbol hash, which ends with an "S", for "symbol". 147 148 // 148 149 // First, we update the symbol map. We remove (and mark dirty) any symbols 149 150 // associated with node hashes which no longer exist (e.g., old/dead nodes). 150 151 // Second, we add (and mark dirty) any symbols associated with new nodes. 151 152 // We also add edges defined by new nodes to the graph. 152 153 // 153 - // We initialize a list of dirty nodes to the list of new nodes, then 154 - // find all nodes connected to dirty symbols and add them to the dirty 155 - // node list. This list now contains every node with a new or changed 156 - // graph hash. 154 + // We initialize a list of dirty nodes to the list of new nodes, then find 155 + // all nodes connected to dirty symbols and add them to the dirty node list. 156 + // This list now contains every node with a new or changed graph hash. 157 157 // 158 158 // We walk the dirty list and compute the new graph hashes, adding them 159 159 // to the graph hash map. This Graph Map can then be passed to an actual ··· 173 173 $this->log(pht('BUILDING ATOM CACHE')); 174 174 175 175 $file_hashes = $this->findFilesInProject(); 176 - 177 176 $this->log(pht('Found %d file(s) in project.', count($file_hashes))); 178 177 179 178 $this->deleteDeadAtoms($file_hashes); 180 - 181 179 $atomize = $this->getFilesToAtomize($file_hashes); 182 - 183 180 $this->log(pht('Found %d unatomized, uncached file(s).', count($atomize))); 184 181 185 182 $file_atomizers = $this->getAtomizersForFiles($atomize); 186 - 187 183 $this->log(pht('Found %d file(s) to atomize.', count($file_atomizers))); 188 - 189 184 $futures = $this->buildAtomizerFutures($file_atomizers); 190 - 191 185 $this->log(pht('Atomizing %d file(s).', count($file_atomizers))); 192 186 193 187 if ($futures) { ··· 198 192 } 199 193 200 194 $this->log(pht('Writing atom cache.')); 201 - 202 195 $this->getAtomCache()->saveAtoms(); 203 - 204 196 $this->log(pht('Done.')."\n"); 205 197 } 206 198 207 199 private function getAtomizersForFiles(array $files) { 208 200 $rules = $this->getRules(); 209 201 $exclude = $this->getExclude(); 210 - 211 202 $atomizers = array(); 212 203 213 204 foreach ($files as $file) { ··· 221 212 $ok = preg_match($rule, $file); 222 213 if ($ok === false) { 223 214 throw new Exception( 224 - "Rule '{$rule}' is not a valid regular expression."); 215 + pht("Rule '%s' is not a valid regular expression.", $rule)); 225 216 } 226 217 if ($ok) { 227 218 $atomizers[$file] = $atomizer; ··· 234 225 } 235 226 236 227 private function getRules() { 237 - $rules = $this->getConfig('rules', array( 228 + return $this->getConfig('rules', array( 238 229 '/\\.diviner$/' => 'DivinerArticleAtomizer', 239 230 '/\\.php$/' => 'DivinerPHPAtomizer', 240 231 )); 241 - 242 - return $rules; 243 232 } 244 233 245 234 private function getExclude() { 246 235 $exclude = (array)$this->getConfig('exclude', array()); 247 236 return $exclude; 248 237 } 249 - 250 238 251 239 private function findFilesInProject() { 252 240 $raw_hashes = id(new FileFinder($this->getConfig('root'))) ··· 355 343 $bar->done(); 356 344 } 357 345 358 - 359 346 /** 360 347 * Get a global version number, which changes whenever any atom or atomizer 361 348 * implementation changes in a way which is not backward-compatible. ··· 388 375 389 376 /* -( Graph Cache )-------------------------------------------------------- */ 390 377 391 - 392 378 private function buildGraphCache() { 393 379 $this->log(pht('BUILDING GRAPH CACHE')); 394 380 ··· 401 387 402 388 $del_atoms = array_diff_key($symbol_map, $atoms); 403 389 $this->log(pht('Found %d obsolete atom(s) in graph.', count($del_atoms))); 390 + 404 391 foreach ($del_atoms as $nhash => $shash) { 405 392 $atom_cache->deleteSymbol($nhash); 406 393 $dirty_symbols[$shash] = true; ··· 411 398 412 399 $new_atoms = array_diff_key($atoms, $symbol_map); 413 400 $this->log(pht('Found %d new atom(s) in graph.', count($new_atoms))); 401 + 414 402 foreach ($new_atoms as $nhash => $ignored) { 415 403 $shash = $this->computeSymbolHash($nhash); 416 404 $atom_cache->addSymbol($nhash, $shash); 417 405 $dirty_symbols[$shash] = true; 418 406 419 - $atom_cache->addEdges( 420 - $nhash, 421 - $this->getEdges($nhash)); 407 + $atom_cache->addEdges($nhash, $this->getEdges($nhash)); 422 408 423 409 $dirty_nhashes[$nhash] = true; 424 410 } ··· 467 453 $atom = $atom_cache->getAtom($node_hash); 468 454 469 455 if (!$atom) { 470 - throw new Exception("No such atom with node hash '{$node_hash}'!"); 456 + throw new Exception( 457 + pht("No such atom with node hash '%s'!", $node_hash)); 471 458 } 472 459 473 460 $ref = DivinerAtomRef::newFromDictionary($atom['ref']); ··· 481 468 $refs = array(); 482 469 483 470 // Make the atom depend on its own symbol, so that all atoms with the same 484 - // symbol are dirtied (e.g., if a codebase defines the function "f()" 471 + // symbol are dirtied (e.g., if a codebase defines the function `f()` 485 472 // several times, all of them should be dirtied when one is dirtied). 486 473 $refs[DivinerAtomRef::newFromDictionary($atom)->toHash()] = true; 487 474 ··· 510 497 return md5(serialize($inputs)).'G'; 511 498 } 512 499 513 - 514 500 private function publishDocumentation($clean) { 515 501 $atom_cache = $this->getAtomCache(); 516 502 $graph_map = $atom_cache->getGraphMap(); ··· 526 512 527 513 $this->log(pht('Done.')); 528 514 } 529 - 530 515 531 516 }
+9 -9
src/applications/diviner/workflow/DivinerWorkflow.php
··· 20 20 protected function readBookConfiguration($book_path) { 21 21 if ($book_path === null) { 22 22 throw new PhutilArgumentUsageException( 23 - 'Specify a Diviner book configuration file with --book.'); 23 + pht( 24 + 'Specify a Diviner book configuration file with %s.', 25 + '--book')); 24 26 } 25 27 26 28 $book_data = Filesystem::readFile($book_path); 27 - $book = json_decode($book_data, true); 28 - if (!is_array($book)) { 29 - throw new PhutilArgumentUsageException( 30 - "Book configuration '{$book_path}' is not in JSON format."); 31 - } 29 + $book = phutil_json_decode($book_data); 32 30 33 31 PhutilTypeSpec::checkMap( 34 32 $book, ··· 55 53 if (!preg_match('/^[a-z][a-z-]*\z/', $book['name'])) { 56 54 $name = $book['name']; 57 55 throw new PhutilArgumentUsageException( 58 - "Book configuration '{$book_path}' has name '{$name}', but book names ". 59 - "must include only lowercase letters and hyphens."); 56 + pht( 57 + "Book configuration '%s' has name '%s', but book names must ". 58 + "include only lowercase letters and hyphens.", 59 + $book_path, 60 + $name)); 60 61 } 61 62 62 63 foreach (idx($book, 'groups', array()) as $group) { ··· 66 67 'name' => 'string', 67 68 'include' => 'optional regex|list<regex>', 68 69 )); 69 - 70 70 } 71 71 72 72 $this->bookConfigPath = $book_path;