private function assertCommandCorrection($expect, $input, $commands) { $result = PhutilArgumentSpellingCorrector::newCommandCorrector()->correctSpelling($input, $commands); sort($result); sort($expect); $commands = implode(', ', $commands); $this->assertEqual($expect, $result, pht('Correction of %s against: %s', $input, $commands)); }
/** * Select a workflow. For commands that may operate in several modes, like * `arc`, the modes can be split into "workflows". Each workflow specifies * the arguments it accepts. This method takes a list of workflows, selects * the chosen workflow, parses its arguments, and either executes it (if it * is executable) or returns it for handling. * * See @{class:PhutilArgumentWorkflow} for details on using workflows. * * @param list List of @{class:PhutilArgumentWorkflow}s. * @return PhutilArgumentWorkflow|no Returns the chosen workflow if it is * not executable, or executes it and * exits with a return code if it is. * @task parse */ public function parseWorkflowsFull(array $workflows) { assert_instances_of($workflows, 'PhutilArgumentWorkflow'); // Clear out existing workflows. We need to do this to permit the // construction of sub-workflows. $this->workflows = array(); foreach ($workflows as $workflow) { $name = $workflow->getName(); if ($name === null) { throw new PhutilArgumentSpecificationException(pht('Workflow has no name!')); } if (isset($this->workflows[$name])) { throw new PhutilArgumentSpecificationException(pht("Two workflows with name '%s!", $name)); } $this->workflows[$name] = $workflow; } $argv = $this->argv; if (empty($argv)) { // TODO: this is kind of hacky / magical. if (isset($this->workflows['help'])) { $argv = array('help'); } else { throw new PhutilArgumentUsageException(pht('No workflow selected.')); } } $flow = array_shift($argv); if (empty($this->workflows[$flow])) { $corrected = PhutilArgumentSpellingCorrector::newCommandCorrector()->correctSpelling($flow, array_keys($this->workflows)); if (count($corrected) == 1) { $corrected = head($corrected); $this->logMessage(tsprintf("%s\n", pht('(Assuming "%s" is the British spelling of "%s".)', $flow, $corrected))); $flow = $corrected; } else { $this->raiseUnknownWorkflow($flow, $corrected); } } $workflow = $this->workflows[$flow]; if ($this->showHelp) { // Make "cmd flow --help" behave like "cmd help flow", not "cmd help". $help_flow = idx($this->workflows, 'help'); if ($help_flow) { if ($help_flow !== $workflow) { $workflow = $help_flow; $argv = array($flow); // Prevent parse() from dumping us back out to standard help. $this->showHelp = false; } } else { $this->printHelpAndExit(); } } $this->argv = array_values($argv); if ($workflow->shouldParsePartial()) { $this->parsePartial($workflow->getArguments()); } else { $this->parse($workflow->getArguments()); } if ($workflow->isExecutable()) { $workflow->setArgv($this); $err = $workflow->execute($this); exit($err); } else { return $workflow; } }
public final function selectWorkflow(&$command, array &$args, ArcanistConfigurationManager $configuration_manager, PhutilConsole $console) { // First, try to build a workflow with the exact name provided. We always // pick an exact match, and do not allow aliases to override it. $workflow = $this->buildWorkflow($command); if ($workflow) { return $workflow; } // If the user has an alias, like 'arc alias dhelp diff help', look it up // and substitute it. We do this only after trying to resolve the workflow // normally to prevent you from doing silly things like aliasing 'alias' // to something else. $aliases = ArcanistAliasWorkflow::getAliases($configuration_manager); list($new_command, $args) = ArcanistAliasWorkflow::resolveAliases($command, $this, $args, $configuration_manager); $full_alias = idx($aliases, $command, array()); $full_alias = implode(' ', $full_alias); // Run shell command aliases. if (ArcanistAliasWorkflow::isShellCommandAlias($new_command)) { $shell_cmd = substr($full_alias, 1); $console->writeLog("[%s: 'arc %s' -> \$ %s]", pht('alias'), $command, $shell_cmd); if ($args) { $err = phutil_passthru('%C %Ls', $shell_cmd, $args); } else { $err = phutil_passthru('%C', $shell_cmd); } exit($err); } // Run arc command aliases. if ($new_command) { $workflow = $this->buildWorkflow($new_command); if ($workflow) { $console->writeLog("[%s: 'arc %s' -> 'arc %s']\n", pht('alias'), $command, $full_alias); $command = $new_command; return $workflow; } } $all = array_keys($this->buildAllWorkflows()); // We haven't found a real command or an alias, so try to locate a command // by unique prefix. $prefixes = $this->expandCommandPrefix($command, $all); if (count($prefixes) == 1) { $command = head($prefixes); return $this->buildWorkflow($command); } else { if (count($prefixes) > 1) { $this->raiseUnknownCommand($command, $prefixes); } } // We haven't found a real command, alias, or unique prefix. Try similar // spellings. $corrected = PhutilArgumentSpellingCorrector::newCommandCorrector()->correctSpelling($command, $all); if (count($corrected) == 1) { $console->writeErr(pht("(Assuming '%s' is the British spelling of '%s'.)", $command, head($corrected)) . "\n"); $command = head($corrected); return $this->buildWorkflow($command); } else { if (count($corrected) > 1) { $this->raiseUnknownCommand($command, $corrected); } } $this->raiseUnknownCommand($command); }