Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14386131
D19700.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
27 KB
Referenced Files
None
Subscribers
None
D19700.diff
View Options
diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -19,7 +19,6 @@
/support/xhpast/parser.yacc.output
/support/xhpast/node_names.hpp
/support/xhpast/xhpast
-/support/xhpast/xhpast.exe
/src/parser/xhpast/bin/xhpast
## NOTE: Don't .gitignore these files! Even though they're build artifacts, we
@@ -32,9 +31,8 @@
# This is an OS X build artifact.
/support/xhpast/xhpast.dSYM
-# libphutil
-/support/phutiltestlib/.phutil_module_cache
-
# This file overrides "default.pem" if present.
/resources/ssl/custom.pem
+# Generated shell completion rules.
+/support/shell/rules/*
diff --git a/src/log/ArcanistLogEngine.php b/src/log/ArcanistLogEngine.php
--- a/src/log/ArcanistLogEngine.php
+++ b/src/log/ArcanistLogEngine.php
@@ -77,5 +77,13 @@
return $trace;
}
+ public function writeHint($label, $message) {
+ return $this->writeMessage(
+ $this->newMessage()
+ ->setColor('cyan')
+ ->setLabel($label)
+ ->setMessage($message));
+ }
+
}
diff --git a/src/toolset/ArcanistWorkflowArgument.php b/src/toolset/ArcanistWorkflowArgument.php
--- a/src/toolset/ArcanistWorkflowArgument.php
+++ b/src/toolset/ArcanistWorkflowArgument.php
@@ -6,6 +6,8 @@
private $key;
private $help;
private $wildcard;
+ private $parameter;
+ private $isPathArgument;
public function setKey($key) {
$this->key = $key;
@@ -34,6 +36,11 @@
$spec['wildcard'] = true;
}
+ $parameter = $this->getParameter();
+ if ($parameter !== null) {
+ $spec['param'] = $parameter;
+ }
+
return $spec;
}
@@ -46,5 +53,23 @@
return $this->help;
}
+ public function setParameter($parameter) {
+ $this->parameter = $parameter;
+ return $this;
+ }
+
+ public function getParameter() {
+ return $this->parameter;
+ }
+
+ public function setIsPathArgument($is_path_argument) {
+ $this->isPathArgument = $is_path_argument;
+ return $this;
+ }
+
+ public function getIsPathArgument() {
+ return $this->isPathArgument;
+ }
+
}
diff --git a/src/toolset/workflow/ArcanistShellCompleteWorkflow.php b/src/toolset/workflow/ArcanistShellCompleteWorkflow.php
--- a/src/toolset/workflow/ArcanistShellCompleteWorkflow.php
+++ b/src/toolset/workflow/ArcanistShellCompleteWorkflow.php
@@ -1,203 +1,623 @@
<?php
-/**
- * Powers shell-completion scripts.
- */
-final class ArcanistShellCompleteWorkflow extends ArcanistWorkflow {
-
- public function supportsToolset(ArcanistToolset $toolset) {
- return true;
- }
+final class ArcanistShellCompleteWorkflow
+ extends ArcanistWorkflow {
public function getWorkflowName() {
return 'shell-complete';
}
- public function getWorkflowSynopses() {
+ 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(
- pht('**shell-complete** __--current__ __N__ -- [__argv__]'),
+ $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 getWorkflowHelp() {
- return pht(<<<EOTEXT
-Implements shell completion. To use shell completion, source the appropriate
-script from 'resources/shell/' in your .shellrc.
-EOTEXT
- );
+ 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();
}
- public function getArguments() {
- return array(
- 'current' => array(
- 'param' => 'cursor_position',
- 'paramtype' => 'int',
- 'help' => pht('Current term in the argument list being completed.'),
+ private function runInstall() {
+ $log = $this->getLogEngine();
+
+ $shells = array(
+ array(
+ 'key' => 'bash',
+ 'path' => '/bin/bash',
+ 'file' => '.profile',
+ 'source' => 'hooks/bash-completion.sh',
),
- '*' => 'argv',
);
+ $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?');
+ }
+ }
+
+ // TOOLSETS: Generalize prompting.
+
+ if (!phutil_console_confirm($prompt, false)) {
+ throw new PhutilArgumentUsageException(pht('Aborted.'));
+ }
+
+ Filesystem::writeFile($file_path, $new_data);
+
+ $log->writeSuccess(
+ pht('INSTALLED'),
+ pht(
+ 'Installed shell completion support for "%s" to "%s".',
+ $shell,
+ $file_display));
}
- protected function shouldShellComplete() {
- return false;
+ 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);
}
- public function run() {
- $pos = $this->getArgument('current');
- $argv = $this->getArgument('argv', array());
+ 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);
- if ($pos === null) {
+
+ $pos = $this->getArgument('current');
+ if (!$pos) {
$pos = $argc - 1;
}
if ($pos > $argc) {
throw new ArcanistUsageException(
pht(
- 'Specified position is greater than the number of '.
- 'arguments provided.'));
+ 'Argument position specified with "--current" ("%s") is greater '.
+ 'than the number of arguments provided ("%s").',
+ new PhutilNumber($pos),
+ new PhutilNumber($argc)));
}
- // 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;
+ $workflows = $this->getRuntime()->getWorkflows();
- // 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);
+ // 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".
- $vcs = $repository_api->getSourceControlSystemName();
- }
+ $is_workflow = ($pos <= 1);
- $arc_config = $this->getArcanistConfiguration();
+ 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.
- if ($pos <= 1) {
- $workflows = $arc_config->buildAllWorkflows();
+ // 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 $name => $workflow) {
- if (!$workflow->shouldShellComplete()) {
+ foreach ($workflows as $workflow) {
+ $complete[] = $workflow->getWorkflowName();
+ }
+
+ foreach ($this->getConfig('aliases') as $alias) {
+ if ($alias->getException()) {
continue;
}
- $workflow->setArcanistConfiguration($this->getArcanistConfiguration());
- $workflow->setConfigurationManager($this->getConfigurationManager());
-
- if ($vcs || $workflow->requiresWorkingCopy()) {
- $supported_vcs = $workflow->getSupportedRevisionControlSystems();
- if (!in_array($vcs, $supported_vcs)) {
- continue;
- }
+ if ($alias->getToolset() !== $this->getToolsetKey()) {
+ continue;
}
- $complete[] = $name;
+ $complete[] = $alias->getTrigger();
}
- // Also permit autocompletion of "arc alias" commands.
- $aliases = ArcanistAliasWorkflow::getAliases($configuration_manager);
- foreach ($aliases as $key => $value) {
- $complete[] = $key;
+ // Remove invalid possibilities. For example, if the user has typed
+ // "skun<tab>", it obviously can't complete to "zebra". We don't really
+ // need to do this filtering ourselves: the shell completion will
+ // automatically match things for us even if we emit impossible results.
+ // However, it's a little easier to debug the raw output if we clean it
+ // up here before printing it out.
+ $partial = $argv[$pos];
+ foreach ($complete as $key => $candidate) {
+ if (strncmp($partial, $candidate, strlen($partial))) {
+ unset($complete[$key]);
+ }
+ }
+
+ if ($complete) {
+ return $this->suggestStrings($complete);
+ } else {
+ return $this->suggestNothing();
}
- echo implode(' ', $complete)."\n";
- return 0;
} else {
- $workflow = $arc_config->buildWorkflow($argv[1]);
+ // 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) {
- 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);
- }
+ return $this->suggestNothing();
}
- $arguments = $workflow->getArguments();
+ $arguments = $workflow->getWorkflowArguments();
+ $arguments = mpull($arguments, null, 'getKey');
+ $argument = null;
$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;
+ $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();
+ } else {
+ return $this->suggestNothing();
}
- $cur = idx($argv, $pos, '');
- $any_match = false;
+ // TOOLSETS: We can allow workflows and arguments to provide a specific
+ // list of completeable values, like the "--shell" argument for this
+ // workflow.
+ }
- if (strlen($cur)) {
- foreach ($output as $possible) {
- if (!strncmp($possible, $cur, strlen($cur))) {
- $any_match = true;
- }
- }
+ $flags = array();
+ $wildcard = null;
+ foreach ($arguments as $argument) {
+ if ($argument->getWildcard()) {
+ $wildcard = $argument;
+ continue;
}
- 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');
+ $flags[] = '--'.$argument->getKey();
+ }
+
+ $current = idx($argv, $pos, '');
+ $matches = array();
+ if (strlen($current)) {
+ foreach ($flags as $possible) {
+ if (!strncmp($possible, $current, strlen($current))) {
+ $matches[] = $possible;
}
}
+ }
+
+ // 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) {
- echo implode(' ', $output)."\n";
+ // TOOLSETS: There was previously some very questionable support for
+ // autocompleting branches here. This could be moved into Arguments
+ // and Workflows.
- return 0;
+ if ($wildcard->getIsPathArgument()) {
+ return $this->suggestPaths();
+ }
}
+
+ return $this->suggestStrings($matches);
}
}
+ private function suggestPaths() {
+ echo "FILE\n";
+ return 0;
+ }
+
+ private function suggestNothing() {
+ echo "ARGUMENT\n";
+ return 0;
+ }
+
+ private function suggestStrings(array $strings) {
+ echo implode(' ', $strings)."\n";
+ return 0;
+ }
+
}
diff --git a/support/shell/hooks/bash-completion.sh b/support/shell/hooks/bash-completion.sh
new file mode 100644
--- /dev/null
+++ b/support/shell/hooks/bash-completion.sh
@@ -0,0 +1,9 @@
+SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
+
+# Try to generate the shell completion rules if they do not yet exist.
+if [ ! -f "${SCRIPTDIR}/bash-rules.sh" ]; then
+ arc shell-complete --generate >/dev/null 2>/dev/null
+fi;
+
+# Source the shell completion rules.
+source "${SCRIPTDIR}/../rules/bash-rules.sh"
diff --git a/support/shell/rules/.keep b/support/shell/rules/.keep
new file mode 100644
diff --git a/resources/shell/bash-completion b/support/shell/templates/bash-template.sh
rename from resources/shell/bash-completion
rename to support/shell/templates/bash-template.sh
--- a/resources/shell/bash-completion
+++ b/support/shell/templates/bash-template.sh
@@ -1,12 +1,9 @@
-if [[ -n ${ZSH_VERSION-} ]]; then
- autoload -U +X bashcompinit && bashcompinit
-fi
-
-_arc ()
+_arcanist_complete_{{{BIN}}} ()
{
- CUR="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=()
- OPTS=$(echo | arc shell-complete --current ${COMP_CWORD} -- ${COMP_WORDS[@]})
+
+ CUR="${COMP_WORDS[COMP_CWORD]}"
+ OPTS=$(echo | {{{BIN}}} shell-complete --current ${COMP_CWORD} -- ${COMP_WORDS[@]} 2>/dev/null)
if [ $? -ne 0 ]; then
return $?
@@ -23,4 +20,4 @@
COMPREPLY=( $(compgen -W "${OPTS}" -- ${CUR}) )
}
-complete -F _arc -o filenames arc
+complete -F _arcanist_complete_{{{BIN}}} -o filenames {{{BIN}}}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Dec 22, 1:12 PM (10 h, 20 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6918330
Default Alt Text
D19700.diff (27 KB)
Attached To
Mode
D19700: [Wilds] Update "arc shell-complete" for toolsets
Attached
Detach File
Event Timeline
Log In to Comment