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 = self::correctCommandSpelling($command, $all, 2); 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); }
public static function resolveAliases($command, ArcanistConfiguration $config, array $argv, ArcanistWorkingCopyIdentity $working_copy) { $aliases = ArcanistAliasWorkflow::getAliases($working_copy); if (!isset($aliases[$command])) { return array(null, $argv); } $new_command = head($aliases[$command]); $workflow = $config->buildWorkflow($new_command); if (!$workflow) { return array(null, $argv); } $alias_argv = array_slice($aliases[$command], 1); foreach ($alias_argv as $alias_arg) { if (!in_array($alias_arg, $argv)) { array_unshift($argv, $alias_arg); } } return array($new_command, $argv); }
public function run() { $pos = $this->getArgument('current'); $argv = $this->getArgument('argv', array()); $argc = count($argv); if ($pos === null) { $pos = $argc - 1; } if ($pos > $argc) { throw new ArcanistUsageException(pht('Specified position is greater than the number of ' . 'arguments provided.')); } // Determine which revision control system the working copy uses, so we // can filter out commands and flags which aren't supported. If we can't // figure it out, just return all flags/commands. $vcs = null; // We have to build our own because if we requiresWorkingCopy() we'll throw // if we aren't in a .arcconfig directory. We probably still can't do much, // but commands can raise more detailed errors. $configuration_manager = $this->getConfigurationManager(); $working_copy = ArcanistWorkingCopyIdentity::newFromPath(getcwd()); if ($working_copy->getVCSType()) { $configuration_manager->setWorkingCopyIdentity($working_copy); $repository_api = ArcanistRepositoryAPI::newAPIFromConfigurationManager($configuration_manager); $vcs = $repository_api->getSourceControlSystemName(); } $arc_config = $this->getArcanistConfiguration(); if ($pos <= 1) { $workflows = $arc_config->buildAllWorkflows(); $complete = array(); foreach ($workflows as $name => $workflow) { if (!$workflow->shouldShellComplete()) { continue; } $workflow->setArcanistConfiguration($this->getArcanistConfiguration()); $workflow->setConfigurationManager($this->getConfigurationManager()); if ($vcs || $workflow->requiresWorkingCopy()) { $supported_vcs = $workflow->getSupportedRevisionControlSystems(); if (!in_array($vcs, $supported_vcs)) { continue; } } $complete[] = $name; } // Also permit autocompletion of "arc alias" commands. $aliases = ArcanistAliasWorkflow::getAliases($configuration_manager); foreach ($aliases as $key => $value) { $complete[] = $key; } echo implode(' ', $complete) . "\n"; return 0; } else { $workflow = $arc_config->buildWorkflow($argv[1]); if (!$workflow) { list($new_command, $new_args) = ArcanistAliasWorkflow::resolveAliases($argv[1], $arc_config, array_slice($argv, 2), $configuration_manager); if ($new_command) { $workflow = $arc_config->buildWorkflow($new_command); } if (!$workflow) { return 1; } else { $argv = array_merge(array($argv[0]), array($new_command), $new_args); } } $arguments = $workflow->getArguments(); $prev = idx($argv, $pos - 1, null); if (!strncmp($prev, '--', 2)) { $prev = substr($prev, 2); } else { $prev = null; } if ($prev !== null && isset($arguments[$prev]) && isset($arguments[$prev]['param'])) { $type = idx($arguments[$prev], 'paramtype'); switch ($type) { case 'file': echo "FILE\n"; break; case 'complete': echo implode(' ', $workflow->getShellCompletions($argv)) . "\n"; break; default: echo "ARGUMENT\n"; break; } return 0; } else { $output = array(); foreach ($arguments as $argument => $spec) { if ($argument == '*') { continue; } if ($vcs && isset($spec['supports']) && !in_array($vcs, $spec['supports'])) { continue; } $output[] = '--' . $argument; } $cur = idx($argv, $pos, ''); $any_match = false; if (strlen($cur)) { foreach ($output as $possible) { if (!strncmp($possible, $cur, strlen($cur))) { $any_match = true; } } } if (!$any_match && isset($arguments['*'])) { // TODO: This is mega hacktown but something else probably breaks // if we use a rich argument specification; fix it when we move to // PhutilArgumentParser since everything will need to be tested then // anyway. if ($arguments['*'] == 'branch' && isset($repository_api)) { $branches = $repository_api->getAllBranches(); $branches = ipull($branches, 'name'); $output = $branches; } else { $output = array('FILE'); } } echo implode(' ', $output) . "\n"; return 0; } } }
public static function resolveAliases($command, ArcanistConfiguration $config, array $argv, ArcanistConfigurationManager $configuration_manager) { $aliases = ArcanistAliasWorkflow::getAliases($configuration_manager); if (!isset($aliases[$command])) { return array(null, $argv); } $new_command = head($aliases[$command]); if (self::isShellCommandAlias($new_command)) { return array($new_command, $argv); } $workflow = $config->buildWorkflow($new_command); if (!$workflow) { return array(null, $argv); } $alias_argv = array_slice($aliases[$command], 1); foreach (array_reverse($alias_argv) as $alias_arg) { if (!in_array($alias_arg, $argv)) { array_unshift($argv, $alias_arg); } } return array($new_command, $argv); }
$user_config = ArcanistBaseWorkflow::readUserConfigurationFile(); $config = $working_copy->getConfig('arcanist_configuration'); if ($config) { $config = new $config(); } else { $config = new ArcanistConfiguration(); } $command = strtolower($args[0]); $args = array_slice($args, 1); $workflow = $config->buildWorkflow($command); if (!$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($working_copy); list($new_command, $args) = ArcanistAliasWorkflow::resolveAliases($command, $config, $args, $working_copy); $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); if ($config_trace_mode) { echo "[alias: 'arc {$command}' -> \$ {$full_alias}]\n"; } if ($args) { $err = phutil_passthru('%C %Ls', $shell_cmd, $args); } else { $err = phutil_passthru('%C', $shell_cmd); } exit($err);
public function run() { $pos = $this->getArgument('current'); $argv = $this->getArgument('argv', array()); $argc = count($argv); if ($pos === null) { $pos = $argc - 1; } // Determine which revision control system the working copy uses, so we // can filter out commands and flags which aren't supported. If we can't // figure it out, just return all flags/commands. $vcs = null; // We have to build our own because if we requiresWorkingCopy() we'll throw // if we aren't in a .arcconfig directory. We probably still can't do much, // but commands can raise more detailed errors. $working_copy = ArcanistWorkingCopyIdentity::newFromPath(getcwd()); if ($working_copy->getProjectRoot()) { $repository_api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity($working_copy); $vcs = $repository_api->getSourceControlSystemName(); } $arc_config = $this->getArcanistConfiguration(); if ($pos == 1) { $workflows = $arc_config->buildAllWorkflows(); $complete = array(); foreach ($workflows as $name => $workflow) { if (!$workflow->shouldShellComplete()) { continue; } $supported = $workflow->getSupportedRevisionControlSystems(); $ok = in_array('any', $supported) || in_array($vcs, $supported); if (!$ok) { continue; } $complete[] = $name; } // Also permit autocompletion of "arc alias" commands. foreach (ArcanistAliasWorkflow::getAliases($working_copy) as $key => $value) { $complete[] = $key; } echo implode(' ', $complete) . "\n"; return 0; } else { $workflow = $arc_config->buildWorkflow($argv[1]); if (!$workflow) { list($new_command, $new_args) = ArcanistAliasWorkflow::resolveAliases($argv[1], $arc_config, array_slice($argv, 2), $working_copy); if ($new_command) { $workflow = $arc_config->buildWorkflow($new_command); } if (!$workflow) { return 1; } else { $argv = array_merge(array($argv[0]), array($new_command), $new_args); } } $arguments = $workflow->getArguments(); $prev = idx($argv, $pos - 1, null); if (!strncmp($prev, '--', 2)) { $prev = substr($prev, 2); } else { $prev = null; } if ($prev !== null && isset($arguments[$prev]) && isset($arguments[$prev]['param'])) { $type = idx($arguments[$prev], 'paramtype'); switch ($type) { case 'file': echo "FILE\n"; break; case 'complete': echo implode(' ', $workflow->getShellCompletions($argv)) . "\n"; break; default: echo "ARGUMENT\n"; break; } return 0; } else { $output = array(); foreach ($arguments as $argument => $spec) { if ($argument == '*') { continue; } if ($vcs && isset($spec['supports']) && !in_array($vcs, $spec['supports'])) { continue; } $output[] = '--' . $argument; } $cur = idx($argv, $pos, ''); $any_match = false; foreach ($output as $possible) { if (!strncmp($possible, $cur, strlen($cur))) { $any_match = true; } } if (!$any_match && isset($arguments['*'])) { // TODO: the '*' specifier should probably have more details about // whether or not it is a list of files. Since it almost always is in // practice, assume FILE for now. echo "FILE\n"; } else { echo implode(' ', $output) . "\n"; } return 0; } } }