Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15382027
D20997.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
26 KB
Referenced Files
None
Subscribers
None
D20997.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -402,7 +402,7 @@
'ArcanistSetConfigWorkflow' => 'workflow/ArcanistSetConfigWorkflow.php',
'ArcanistSetting' => 'configuration/ArcanistSetting.php',
'ArcanistSettings' => 'configuration/ArcanistSettings.php',
- 'ArcanistShellCompleteWorkflow' => 'workflow/ArcanistShellCompleteWorkflow.php',
+ 'ArcanistShellCompleteWorkflow' => 'toolset/workflow/ArcanistShellCompleteWorkflow.php',
'ArcanistSingleLintEngine' => 'lint/engine/ArcanistSingleLintEngine.php',
'ArcanistSlownessXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistSlownessXHPASTLinterRule.php',
'ArcanistSlownessXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistSlownessXHPASTLinterRuleTestCase.php',
diff --git a/src/toolset/workflow/ArcanistShellCompleteWorkflow.php b/src/toolset/workflow/ArcanistShellCompleteWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/toolset/workflow/ArcanistShellCompleteWorkflow.php
@@ -0,0 +1,656 @@
+<?php
+
+final class ArcanistShellCompleteWorkflow
+ extends ArcanistWorkflow {
+
+ public function supportsToolset(ArcanistToolset $toolset) {
+ return true;
+ }
+
+ public function getWorkflowName() {
+ return 'shell-complete';
+ }
+
+ public function getWorkflowInformation() {
+ $help = pht(<<<EOTEXT
+Install shell completion so you can use the "tab" key to autocomplete
+commands and flags in your shell for Arcanist toolsets and workflows.
+
+The **bash** shell is supported.
+
+**Installing Completion**
+
+To install shell completion, run the command:
+
+ $ arc shell-complete
+
+This will install shell completion into your current shell. After installing,
+you may need to start a new shell (or open a new terminal window) to pick up
+the updated configuration.
+
+Once installed, completion should work across all Arcanist toolsets.
+
+**Using Completion**
+
+After completion is installed, use the "tab" key to automatically complete
+workflows and flags. For example, if you type:
+
+ $ arc diff --draf<tab>
+
+...your shell should automatically expand the flag to:
+
+ $ arc diff --draft
+
+**Updating Completion**
+
+To update shell completion, run the same command:
+
+ $ arc shell-complete
+
+You can update shell completion without reinstalling it by running:
+
+ $ arc shell-complete --generate
+
+You may need to update shell completion if:
+
+ - you install new Arcanist toolsets; or
+ - you move the Arcanist directory; or
+ - you upgrade Arcanist and the new version fixes shell completion bugs.
+EOTEXT
+);
+
+ return $this->newWorkflowInformation()
+ ->setHelp($help);
+ }
+
+ public function getWorkflowArguments() {
+ return array(
+ $this->newWorkflowArgument('current')
+ ->setParameter('cursor-position')
+ ->setHelp(
+ pht(
+ 'Internal. Current term in the argument list being completed.')),
+ $this->newWorkflowArgument('generate')
+ ->setHelp(
+ pht(
+ 'Regenerate shell completion rules, without installing any '.
+ 'configuration.')),
+ $this->newWorkflowArgument('shell')
+ ->setParameter('shell-name')
+ ->setHelp(
+ pht(
+ 'Install completion support for a particular shell.')),
+ $this->newWorkflowArgument('argv')
+ ->setWildcard(true),
+ );
+ }
+
+ public function runWorkflow() {
+ $log = $this->getLogEngine();
+
+ $argv = $this->getArgument('argv');
+
+ $is_generate = $this->getArgument('generate');
+ $is_shell = (bool)strlen($this->getArgument('shell'));
+ $is_current = $this->getArgument('current');
+
+ if ($argv) {
+ $should_install = false;
+ $should_generate = false;
+
+ if ($is_generate) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'You can not use "--generate" when completing arguments.'));
+ }
+
+ if ($is_shell) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'You can not use "--shell" when completing arguments.'));
+ }
+
+ } else if ($is_generate) {
+ $should_install = false;
+ $should_generate = true;
+
+ if ($is_current) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'You can not use "--current" when generating rules.'));
+ }
+
+ if ($is_shell) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'The flags "--generate" and "--shell" are mutually exclusive. '.
+ 'The "--shell" flag selects which shell to install support for, '.
+ 'but the "--generate" suppresses installation.'));
+ }
+
+ } else {
+ $should_install = true;
+ $should_generate = true;
+
+ if ($is_current) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'You can not use "--current" when installing support.'));
+ }
+ }
+
+ if ($should_install) {
+ $this->runInstall();
+ }
+
+ if ($should_generate) {
+ $this->runGenerate();
+ }
+
+ if ($should_install || $should_generate) {
+ $log->writeHint(
+ pht('NOTE'),
+ pht(
+ 'You may need to open a new terminal window or launch a new shell '.
+ 'before the changes take effect.'));
+ return 0;
+ }
+
+ $this->runAutocomplete();
+ }
+
+ protected function newPrompts() {
+ return array(
+ $this->newPrompt('arc.shell-complete.install')
+ ->setDescription(
+ pht(
+ 'Confirms writing to to "~/.profile" (or another similar file) '.
+ 'to install shell completion.')),
+ );
+ }
+
+ private function runInstall() {
+ $log = $this->getLogEngine();
+
+ $shells = array(
+ array(
+ 'key' => 'bash',
+ 'path' => '/bin/bash',
+ 'file' => '.profile',
+ 'source' => 'hooks/bash-completion.sh',
+ ),
+ );
+ $shells = ipull($shells, null, 'key');
+
+ $shell = $this->getArgument('shell');
+ if (!$shell) {
+ $shell = $this->detectShell($shells);
+ } else {
+ $shell = $this->selectShell($shells, $shell);
+ }
+
+ $spec = $shells[$shell];
+ $file = $spec['file'];
+ $home = getenv('HOME');
+
+ if (!strlen($home)) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'The "HOME" environment variable is not defined, so this workflow '.
+ 'can not identify where to install shell completion.'));
+ }
+
+ $file_path = getenv('HOME').'/'.$file;
+ $file_display = '~/'.$file;
+
+ if (Filesystem::pathExists($file_path)) {
+ $file_path = Filesystem::resolvePath($file_path);
+ $data = Filesystem::readFile($file_path);
+ $is_new = false;
+ } else {
+ $data = '';
+ $is_new = true;
+ }
+
+ $line = csprintf(
+ 'source %R # arcanist-shell-complete',
+ $this->getShellPath($spec['source']));
+
+ $matches = null;
+ $replace = preg_match(
+ '/(\s*\n)?[^\n]+# arcanist-shell-complete\s*(\n\s*)?/',
+ $data,
+ $matches,
+ PREG_OFFSET_CAPTURE);
+
+ $log->writeSuccess(
+ pht('INSTALL'),
+ pht('Installing shell completion support for "%s".', $shell));
+
+ if ($replace) {
+ $replace_pos = $matches[0][1];
+ $replace_line = $matches[0][0];
+ $replace_len = strlen($replace_line);
+ $replace_display = trim($replace_line);
+
+ if ($replace_pos === 0) {
+ $new_line = $line."\n";
+ } else {
+ $new_line = "\n\n".$line."\n";
+ }
+
+ $new_data = substr_replace($data, $new_line, $replace_pos, $replace_len);
+
+ if ($new_data === $data) {
+ // If we aren't changing anything in the file, just skip the write
+ // completely.
+ $needs_write = false;
+
+ $log->writeStatus(
+ pht('SKIP'),
+ pht('Shell completion for "%s" is already installed.', $shell));
+
+ return;
+ }
+
+ echo tsprintf(
+ "%s\n\n %s\n\n%s\n\n %s\n",
+ pht(
+ 'To update shell completion support for "%s", your existing '.
+ '"%s" file will be modified. This line will be removed:',
+ $shell,
+ $file_display),
+ $replace_display,
+ pht('This line will be added:'),
+ $line);
+
+ $prompt = pht('Rewrite this file?');
+ } else {
+ if ($is_new) {
+ $new_data = $line."\n";
+
+ echo tsprintf(
+ "%s\n\n %s\n",
+ pht(
+ 'To install shell completion support for "%s", a new "%s" file '.
+ 'will be created with this content:',
+ $shell,
+ $file_display),
+ $line);
+
+ $prompt = pht('Create this file?');
+ } else {
+ $new_data = rtrim($data)."\n\n".$line."\n";
+
+ echo tsprintf(
+ "%s\n\n %s\n",
+ pht(
+ 'To install shell completion support for "%s", this line will be '.
+ 'added to your existing "%s" file:',
+ $shell,
+ $file_display),
+ $line);
+
+ $prompt = pht('Append to this file?');
+ }
+ }
+
+ $this->getPrompt('arc.shell-complete.install')
+ ->setQuery($prompt)
+ ->execute();
+
+ Filesystem::writeFile($file_path, $new_data);
+
+ $log->writeSuccess(
+ pht('INSTALLED'),
+ pht(
+ 'Installed shell completion support for "%s" to "%s".',
+ $shell,
+ $file_display));
+ }
+
+ private function selectShell(array $shells, $shell_arg) {
+ foreach ($shells as $shell) {
+ if ($shell['key'] === $shell_arg) {
+ return $shell_arg;
+ }
+ }
+
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'The shell "%s" is not supported. Supported shells are: %s.',
+ $shell_arg,
+ implode(', ', ipull($shells, 'key'))));
+ }
+
+ private function detectShell(array $shells) {
+ // NOTE: The "BASH_VERSION" and "ZSH_VERSION" shell variables are not
+ // passed to subprocesses, so we can't inspect them to figure out which
+ // shell launched us. If we could figure this out in some other way, it
+ // would be nice to do so.
+
+ // Instead, just look at "SHELL" (the user's startup shell).
+
+ $log = $this->getLogEngine();
+
+ $detected = array();
+ $log->writeStatus(
+ pht('DETECT'),
+ pht('Detecting current shell...'));
+
+ $shell_env = getenv('SHELL');
+ if (!strlen($shell_env)) {
+ $log->writeWarning(
+ pht('SHELL'),
+ pht(
+ 'The "SHELL" environment variable is not defined, so it can '.
+ 'not be used to detect the shell to install rules for.'));
+ } else {
+ $found = false;
+ foreach ($shells as $shell) {
+ if ($shell['path'] !== $shell_env) {
+ continue;
+ }
+
+ $found = true;
+ $detected[] = $shell['key'];
+
+ $log->writeSuccess(
+ pht('SHELL'),
+ pht(
+ 'The "SHELL" environment variable has value "%s", so the '.
+ 'target shell was detected as "%s".',
+ $shell_env,
+ $shell['key']));
+ }
+
+ if (!$found) {
+ $log->writeStatus(
+ pht('SHELL'),
+ pht(
+ 'The "SHELL" environment variable does not match any recognized '.
+ 'shell.'));
+ }
+ }
+
+ if (!$detected) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Unable to detect any supported shell, so autocompletion rules '.
+ 'can not be installed. Use "--shell" to select a shell.'));
+ } else if (count($detected) > 1) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Multiple supported shells were detected. Unable to determine '.
+ 'which shell to install autocompletion rules for. Use "--shell" '.
+ 'to select a shell.'));
+ }
+
+ return head($detected);
+ }
+
+ private function runGenerate() {
+ $log = $this->getLogEngine();
+
+ $toolsets = ArcanistToolset::newToolsetMap();
+
+ $log->writeStatus(
+ pht('GENERATE'),
+ pht('Generating shell completion rules...'));
+
+ $shells = array('bash');
+ foreach ($shells as $shell) {
+
+ $rules = array();
+ foreach ($toolsets as $toolset) {
+ $rules[] = $this->newCompletionRules($toolset, $shell);
+ }
+ $rules = implode("\n", $rules);
+
+ $rules_path = $this->getShellPath('rules/'.$shell.'-rules.sh');
+
+ // If a write wouldn't change anything, skip the write. This allows
+ // "arc shell-complete" to work if "arcanist/" is on a read-only NFS
+ // filesystem or something unusual like that.
+
+ $skip_write = false;
+ if (Filesystem::pathExists($rules_path)) {
+ $current = Filesystem::readFile($rules_path);
+ if ($current === $rules) {
+ $skip_write = true;
+ }
+ }
+
+ if ($skip_write) {
+ $log->writeStatus(
+ pht('SKIP'),
+ pht(
+ 'Rules are already up to date for "%s" in: %s',
+ $shell,
+ Filesystem::readablePath($rules_path)));
+ } else {
+ Filesystem::writeFile($rules_path, $rules);
+ $log->writeStatus(
+ pht('RULES'),
+ pht(
+ 'Wrote updated completion rules for "%s" to: %s.',
+ $shell,
+ Filesystem::readablePath($rules_path)));
+ }
+ }
+ }
+
+ private function newCompletionRules(ArcanistToolset $toolset, $shell) {
+ $template_path = $this->getShellPath('templates/'.$shell.'-template.sh');
+ $template = Filesystem::readFile($template_path);
+
+ $variables = array(
+ 'BIN' => $toolset->getToolsetKey(),
+ );
+
+ foreach ($variables as $key => $value) {
+ $template = str_replace('{{{'.$key.'}}}', $value, $template);
+ }
+
+ return $template;
+ }
+
+ private function getShellPath($to_file = null) {
+ $arc_root = dirname(phutil_get_library_root('arcanist'));
+ return $arc_root.'/support/shell/'.$to_file;
+ }
+
+ private function runAutocomplete() {
+ $argv = $this->getArgument('argv');
+ $argc = count($argv);
+
+ $pos = $this->getArgument('current');
+ if (!$pos) {
+ $pos = $argc - 1;
+ }
+
+ if ($pos >= $argc) {
+ throw new ArcanistUsageException(
+ pht(
+ 'Argument position specified with "--current" ("%s") is greater '.
+ 'than the number of arguments provided ("%s").',
+ new PhutilNumber($pos),
+ new PhutilNumber($argc)));
+ }
+
+ $workflows = $this->getRuntime()->getWorkflows();
+
+ // NOTE: This isn't quite right. For example, "arc --con<tab>" will
+ // try to autocomplete workflows named "--con", but it should actually
+ // autocomplete global flags and suggest "--config".
+
+ $is_workflow = ($pos <= 1);
+
+ if ($is_workflow) {
+ // NOTE: There was previously some logic to try to filter impossible
+ // workflows out of the completion list based on the VCS in the current
+ // working directory: for example, if you're in an SVN working directory,
+ // "arc a" is unlikely to complete to "arc amend" because "amend" does
+ // not support SVN. It's not clear this logic is valuable, but we could
+ // consider restoring it if good use cases arise.
+
+ // TOOLSETS: Restore the ability for workflows to opt out of shell
+ // completion. It is exceptionally unlikely that users want to shell
+ // complete obscure or internal workflows, like "arc shell-complete"
+ // itself. Perhaps a good behavior would be to offer these as
+ // completions if they are the ONLY available completion, since a user
+ // who has typed "arc shell-comp<tab>" likely does want "shell-complete".
+
+ $complete = array();
+ foreach ($workflows as $workflow) {
+ $complete[] = $workflow->getWorkflowName();
+ }
+
+ foreach ($this->getConfig('aliases') as $alias) {
+ if ($alias->getException()) {
+ continue;
+ }
+
+ if ($alias->getToolset() !== $this->getToolsetKey()) {
+ continue;
+ }
+
+ $complete[] = $alias->getTrigger();
+ }
+
+ $partial = $argv[$pos];
+ $complete = $this->getMatches($complete, $partial);
+
+ if ($complete) {
+ return $this->suggestStrings($complete);
+ } else {
+ return $this->suggestNothing();
+ }
+
+ } else {
+ // TOOLSETS: We should resolve aliases before picking a workflow, so
+ // that if you alias "arc draft" to "arc diff --draft", we can suggest
+ // other "diff" flags when you type "arc draft --q<tab>".
+
+ // TOOLSETS: It's possible the workflow isn't in position 1. The user
+ // may be running "arc --trace diff --dra<tab>", for example.
+
+ $workflow = idx($workflows, $argv[1]);
+ if (!$workflow) {
+ return $this->suggestNothing();
+ }
+
+ $arguments = $workflow->getWorkflowArguments();
+ $arguments = mpull($arguments, null, 'getKey');
+ $current = idx($argv, $pos, '');
+
+ $argument = null;
+ $prev = idx($argv, $pos - 1, null);
+ if (!strncmp($prev, '--', 2)) {
+ $prev = substr($prev, 2);
+ $argument = idx($arguments, $prev);
+ }
+
+ // If the last argument was a "--thing" argument, test if "--thing" is
+ // a parameterized argument. If it is, the next argument should be a
+ // parameter.
+
+ if ($argument && strlen($argument->getParameter())) {
+ if ($argument->getIsPathArgument()) {
+ return $this->suggestPaths($current);
+ } else {
+ return $this->suggestNothing();
+ }
+
+ // TOOLSETS: We can allow workflows and arguments to provide a specific
+ // list of completeable values, like the "--shell" argument for this
+ // workflow.
+ }
+
+ $flags = array();
+ $wildcard = null;
+ foreach ($arguments as $argument) {
+ if ($argument->getWildcard()) {
+ $wildcard = $argument;
+ continue;
+ }
+
+ $flags[] = '--'.$argument->getKey();
+ }
+
+ $matches = $this->getMatches($flags, $current);
+
+ // If whatever the user is completing does not match the prefix of any
+ // flag, try to autcomplete a wildcard argument if it has some kind of
+ // meaningful completion. For example, "arc lint READ<tab>" should
+ // autocomplete a file.
+
+ if (!$matches && $wildcard) {
+
+ // TOOLSETS: There was previously some very questionable support for
+ // autocompleting branches here. This could be moved into Arguments
+ // and Workflows.
+
+ if ($wildcard->getIsPathArgument()) {
+ return $this->suggestPaths($current);
+ }
+ }
+
+ // TODO: If a command has only one flag, like "--json", don't suggest
+ // it if the user hasn't typed anything or has only typed "--".
+
+ // TODO: Don't suggest "--flag" arguments which aren't repeatable if
+ // they are already present in the argument list.
+
+ return $this->suggestStrings($matches);
+ }
+ }
+
+ private function suggestPaths($prefix) {
+ // NOTE: We are returning a directive to the bash script to run "compgen"
+ // for us rather than running it ourselves. If we run:
+ //
+ // compgen -A file -- %s
+ //
+ // ...from this context, it fails (exits with error code 1 and no output)
+ // if the prefix is "foo\ ", on my machine. See T9116 for some dicussion.
+ echo '<compgen:file>';
+ return 0;
+ }
+
+ private function suggestNothing() {
+ return $this->suggestStrings(array());
+ }
+
+ private function suggestStrings(array $strings) {
+ sort($strings);
+ echo implode("\n", $strings);
+ return 0;
+ }
+
+ private function getMatches(array $candidates, $prefix) {
+ $matches = array();
+
+ if (strlen($prefix)) {
+ foreach ($candidates as $possible) {
+ if (!strncmp($possible, $prefix, strlen($prefix))) {
+ $matches[] = $possible;
+ }
+ }
+
+ // If we matched nothing, try a case-insensitive match.
+ if (!$matches) {
+ foreach ($candidates as $possible) {
+ if (!strncasecmp($possible, $prefix, strlen($prefix))) {
+ $matches[] = $possible;
+ }
+ }
+ }
+ } else {
+ $matches = $candidates;
+ }
+
+ return $matches;
+ }
+
+}
diff --git a/src/workflow/ArcanistShellCompleteWorkflow.php b/src/workflow/ArcanistShellCompleteWorkflow.php
deleted file mode 100644
--- a/src/workflow/ArcanistShellCompleteWorkflow.php
+++ /dev/null
@@ -1,201 +0,0 @@
-<?php
-
-/**
- * Powers shell-completion scripts.
- */
-final class ArcanistShellCompleteWorkflow extends ArcanistWorkflow {
-
- public function getWorkflowName() {
- return 'shell-complete';
- }
-
- public function getCommandSynopses() {
- return phutil_console_format(<<<EOTEXT
- **shell-complete** __--current__ __N__ -- [__argv__]
-EOTEXT
- );
- }
-
- public function getCommandHelp() {
- return phutil_console_format(<<<EOTEXT
- Supports: bash, etc.
- Implements shell completion. To use shell completion, source the
- appropriate script from 'resources/shell/' in your .shellrc.
-EOTEXT
- );
- }
-
- public function getArguments() {
- return array(
- 'current' => array(
- 'param' => 'cursor_position',
- 'paramtype' => 'int',
- 'help' => pht('Current term in the argument list being completed.'),
- ),
- '*' => 'argv',
- );
- }
-
- protected function shouldShellComplete() {
- return false;
- }
-
- 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;
- }
- }
- }
-
-}
diff --git a/src/workflow/ArcanistWorkflow.php b/src/workflow/ArcanistWorkflow.php
--- a/src/workflow/ArcanistWorkflow.php
+++ b/src/workflow/ArcanistWorkflow.php
@@ -2275,4 +2275,8 @@
return $this->getToolset()->getToolsetKey();
}
+ final public function getConfig($key) {
+ return $this->getConfigurationSourceList()->getConfig($key);
+ }
+
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Mar 15, 10:24 AM (1 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7397202
Default Alt Text
D20997.diff (26 KB)
Attached To
Mode
D20997: Port "arc shell-complete" to Toolsets
Attached
Detach File
Event Timeline
Log In to Comment