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);
 }
Example #5
0
 $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;
         }
     }
 }