Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15407890
D20996.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
D20996.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
@@ -24,7 +24,7 @@
'ArcanistAliasEngine' => 'toolset/ArcanistAliasEngine.php',
'ArcanistAliasFunctionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistAliasFunctionXHPASTLinterRule.php',
'ArcanistAliasFunctionXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistAliasFunctionXHPASTLinterRuleTestCase.php',
- 'ArcanistAliasWorkflow' => 'workflow/ArcanistAliasWorkflow.php',
+ 'ArcanistAliasWorkflow' => 'toolset/workflow/ArcanistAliasWorkflow.php',
'ArcanistAliasesConfigOption' => 'config/option/ArcanistAliasesConfigOption.php',
'ArcanistAmendWorkflow' => 'workflow/ArcanistAmendWorkflow.php',
'ArcanistAnoidWorkflow' => 'workflow/ArcanistAnoidWorkflow.php',
diff --git a/src/config/option/ArcanistAliasesConfigOption.php b/src/config/option/ArcanistAliasesConfigOption.php
--- a/src/config/option/ArcanistAliasesConfigOption.php
+++ b/src/config/option/ArcanistAliasesConfigOption.php
@@ -22,7 +22,18 @@
protected function didReadStorageValueList(array $list) {
assert_instances_of($list, 'ArcanistConfigurationSourceValue');
- return mpull($list, 'getValue');
+
+ $results = array();
+ foreach ($list as $spec) {
+ $source = $spec->getConfigurationSource();
+ $value = $spec->getValue();
+
+ $value->setConfigurationSource($source);
+
+ $results[] = $value;
+ }
+
+ return $results;
}
public function getDisplayValueFromValue($value) {
diff --git a/src/configuration/ArcanistConfiguration.php b/src/configuration/ArcanistConfiguration.php
--- a/src/configuration/ArcanistConfiguration.php
+++ b/src/configuration/ArcanistConfiguration.php
@@ -81,53 +81,6 @@
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
diff --git a/src/configuration/ArcanistConfigurationManager.php b/src/configuration/ArcanistConfigurationManager.php
--- a/src/configuration/ArcanistConfigurationManager.php
+++ b/src/configuration/ArcanistConfigurationManager.php
@@ -270,7 +270,13 @@
}
public function readUserArcConfig() {
- return idx($this->readUserConfigurationFile(), 'config', array());
+ $config = $this->readUserConfigurationFile();
+
+ if (isset($config['config'])) {
+ $config = $config['config'];
+ }
+
+ return $config;
}
public function writeUserArcConfig(array $options) {
diff --git a/src/runtime/ArcanistRuntime.php b/src/runtime/ArcanistRuntime.php
--- a/src/runtime/ArcanistRuntime.php
+++ b/src/runtime/ArcanistRuntime.php
@@ -521,7 +521,7 @@
$message = $effect->getMessage();
if ($message !== null) {
- $log->writeInfo(pht('ALIAS'), $message);
+ $log->writeHint(pht('ALIAS'), $message);
}
if ($effect->getCommand()) {
diff --git a/src/toolset/ArcanistAlias.php b/src/toolset/ArcanistAlias.php
--- a/src/toolset/ArcanistAlias.php
+++ b/src/toolset/ArcanistAlias.php
@@ -36,7 +36,7 @@
$is_list = false;
$is_dict = false;
if ($value && is_array($value)) {
- if (array_keys($value) === range(0, count($value) - 1)) {
+ if (phutil_is_natural_list($value)) {
$is_list = true;
} else {
$is_dict = true;
diff --git a/src/toolset/ArcanistAliasEffect.php b/src/toolset/ArcanistAliasEffect.php
--- a/src/toolset/ArcanistAliasEffect.php
+++ b/src/toolset/ArcanistAliasEffect.php
@@ -17,6 +17,7 @@
const EFFECT_NOTFOUND = 'not-found';
const EFFECT_CYCLE = 'cycle';
const EFFECT_STACK = 'stack';
+ const EFFECT_IGNORED = 'ignored';
public function setType($type) {
$this->type = $type;
diff --git a/src/toolset/ArcanistAliasEngine.php b/src/toolset/ArcanistAliasEngine.php
--- a/src/toolset/ArcanistAliasEngine.php
+++ b/src/toolset/ArcanistAliasEngine.php
@@ -172,15 +172,31 @@
}
$alias = array_pop($toolset_matches);
- foreach ($toolset_matches as $ignored_match) {
+
+ if ($toolset_matches) {
+ $source = $alias->getConfigurationSource();
+
$results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_IGNORED)
->setMessage(
pht(
'Multiple configuration sources define an alias for "%s %s". '.
- 'The definition in "%s" will be ignored.',
+ 'The last definition in the most specific source ("%s") will '.
+ 'be used.',
$toolset_key,
$command,
- $ignored_match->getConfigurationSource()->getSourceDisplayName()));
+ $source->getSourceDisplayName()));
+
+ foreach ($toolset_matches as $ignored_match) {
+ $source = $ignored_match->getConfigurationSource();
+
+ $results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_IGNORED)
+ ->setMessage(
+ pht(
+ 'A definition of "%s %s" in "%s" will be ignored.',
+ $toolset_key,
+ $command,
+ $source->getSourceDisplayName()));
+ }
}
if ($alias->isShellCommandAlias()) {
@@ -227,14 +243,17 @@
return $results;
}
+ $display_argv = (string)csprintf('%LR', $alias_argv);
+
$results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_ALIAS)
->setMessage(
pht(
- '%s %s -> %s %s',
+ '%s %s -> %s %s %s',
$toolset_key,
$command,
$toolset_key,
- $alias_command));
+ $alias_command,
+ $display_argv));
$argv = array_merge($alias_argv, $argv);
diff --git a/src/toolset/workflow/ArcanistAliasWorkflow.php b/src/toolset/workflow/ArcanistAliasWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/toolset/workflow/ArcanistAliasWorkflow.php
@@ -0,0 +1,200 @@
+<?php
+
+/**
+ * Manages aliases for commands with options.
+ */
+final class ArcanistAliasWorkflow extends ArcanistWorkflow {
+
+ public function getWorkflowName() {
+ return 'alias';
+ }
+
+ public function supportsToolset(ArcanistToolset $toolset) {
+ return true;
+ }
+
+ public function getWorkflowInformation() {
+ $help = pht(<<<EOTEXT
+Create an alias from __command__ to __target__ (optionally, with __options__).
+
+Aliases allow you to create shorthands for commands and sets of flags you
+commonly use, like defining "arc draft" as a shorthand for "arc diff --draft".
+
+**Creating Aliases**
+
+You can define "arc draft" as a shorthand for "arc diff --draft" like this:
+
+ $ arc alias draft diff -- --draft
+
+Now, when you run "arc draft", the command will function like
+"arc diff --draft".
+
+<bg:yellow> NOTE: </bg> Make sure you use "--" before specifying any flags you
+want to pass to the command! Otherwise, the flags will be interpreted as flags
+to "arc alias".
+
+**Listing Aliases**
+
+Without any arguments, "arc alias" will list aliases.
+
+**Removing Aliases**
+
+To remove an alias, run:
+
+ $ arc alias <alias-name>
+
+You will be prompted to remove the alias.
+
+**Shell Commands**
+
+If you begin an alias with "!", the remainder of the alias will be invoked as
+a shell command. For example, if you want to implement "arc ls", you can do so
+like this:
+
+ $ arc alias ls '!ls'
+
+When run, "arc ls" will now behave like "ls".
+
+**Multiple Toolsets**
+
+This workflow supports any toolset, even though the examples in this help text
+use "arc". If you are working with another toolset, use the binary for that
+toolset define aliases for it:
+
+ $ phage alias ...
+
+Aliases are bound to the toolset which was used to define them. If you define
+an "arc draft" alias, that does not also define a "phage draft" alias.
+
+**Builtins**
+
+You can not overwrite the behavior of builtin workflows, including "alias"
+itself, and if you install a new workflow it will take precedence over any
+existing aliases with the same name.
+EOTEXT
+);
+
+ return $this->newWorkflowInformation()
+ ->addExample(pht('**alias**'))
+ ->addExample(pht('**alias** __command__'))
+ ->addExample(pht('**alias** __command__ __target__ -- [__options__]'))
+ ->setHelp($help);
+ }
+
+ public function getWorkflowArguments() {
+ return array(
+ $this->newWorkflowArgument('json')
+ ->setHelp(pht('Output aliases in JSON format.')),
+ $this->newWorkflowArgument('argv')
+ ->setWildcard(true),
+ );
+ }
+
+ public function runWorkflow() {
+ $argv = $this->getArgument('argv');
+
+ $is_list = false;
+ $is_delete = false;
+
+ if (!$argv) {
+ $is_list = true;
+ } else if (count($argv) === 1) {
+ $is_delete = true;
+ }
+
+ $is_json = $this->getArgument('json');
+ if ($is_json && !$is_list) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'The "--json" argument may only be used when listing aliases.'));
+ }
+
+ if ($is_list) {
+ return $this->runListAliases();
+ }
+
+ if ($is_delete) {
+ return $this->runDeleteAlias($argv[0]);
+ }
+
+ return $this->runCreateAlias($argv);
+ }
+
+ private function runListAliases() {
+ // TOOLSETS: Actually list aliases.
+ return 1;
+ }
+
+ private function runDeleteAlias($alias) {
+ // TOOLSETS: Actually delete aliases.
+ return 1;
+ }
+
+ private function runCreateAlias(array $argv) {
+ $trigger = array_shift($argv);
+ $this->validateAliasTrigger($trigger);
+
+ $alias = id(new ArcanistAlias())
+ ->setToolset($this->getToolsetKey())
+ ->setTrigger($trigger)
+ ->setCommand($argv);
+
+ $aliases = $this->readAliasesForWrite();
+
+ // TOOLSETS: Check if the user already has an alias for this trigger, and
+ // prompt them to overwrite it. Needs prompting to work.
+
+ $aliases[] = $alias;
+
+ $this->writeAliases($aliases);
+
+ // TOOLSETS: Print out a confirmation that we added the alias.
+
+ return 0;
+ }
+
+ private function validateAliasTrigger($trigger) {
+ $workflows = $this->getRuntime()->getWorkflows();
+
+ if (isset($workflows[$trigger])) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'You can not define an alias for "%s" because it is a builtin '.
+ 'workflow for the current toolset ("%s"). The "alias" workflow '.
+ 'can only define new commands as aliases; it can not redefine '.
+ 'existing commands to mean something else.',
+ $trigger,
+ $this->getToolsetKey()));
+ }
+ }
+
+ private function getEditScope() {
+ return ArcanistConfigurationSource::SCOPE_USER;
+ }
+
+ private function getAliasesConfigKey() {
+ return ArcanistArcConfigurationEngineExtension::KEY_ALIASES;
+ }
+
+ private function readAliasesForWrite() {
+ $key = $this->getAliasesConfigKey();
+ $scope = $this->getEditScope();
+ $source_list = $this->getConfigurationSourceList();
+
+ return $source_list->getConfigFromScopes($key, array($scope));
+ }
+
+ private function writeAliases(array $aliases) {
+ assert_instances_of($aliases, 'ArcanistAlias');
+
+ $key = $this->getAliasesConfigKey();
+ $scope = $this->getEditScope();
+
+ $source_list = $this->getConfigurationSourceList();
+ $source = $source_list->getWritableSourceFromScope($scope);
+ $option = $source_list->getConfigOption($key);
+
+ $option->writeValue($source, $aliases);
+ }
+
+}
diff --git a/src/workflow/ArcanistAliasWorkflow.php b/src/workflow/ArcanistAliasWorkflow.php
deleted file mode 100644
--- a/src/workflow/ArcanistAliasWorkflow.php
+++ /dev/null
@@ -1,240 +0,0 @@
-<?php
-
-/**
- * Manages aliases for commands with options.
- */
-final class ArcanistAliasWorkflow extends ArcanistWorkflow {
-
- public function getWorkflowName() {
- return 'alias';
- }
-
- public function getCommandSynopses() {
- return phutil_console_format(<<<EOTEXT
- **alias**
- **alias** __command__
- **alias** __command__ __target__ -- [__options__]
-EOTEXT
- );
- }
-
- public function getCommandHelp() {
- return phutil_console_format(<<<EOTEXT
- Supports: cli
- Create an alias from __command__ to __target__ (optionally, with
- __options__). For example:
-
- arc alias fpatch patch -- --force
-
- ...will create a new 'arc' command, 'arc fpatch', which invokes
- 'arc patch --force ...' when run. NOTE: use "--" before specifying
- options!
-
- If you start an alias with "!", the remainder of the alias will be
- invoked as a shell command. For example, if you want to implement
- 'arc ls', you can do so like this:
-
- arc alias ls '!ls'
-
- You can now run "arc ls" and it will behave like "ls". Of course, this
- example is silly and would make your life worse.
-
- You can not overwrite builtins, including 'alias' itself. The builtin
- will always execute, even if it was added after your alias.
-
- To remove an alias, run:
-
- arc alias fpatch
-
- Without any arguments, 'arc alias' will list aliases.
-EOTEXT
- );
- }
-
- public function getArguments() {
- return array(
- '*' => 'argv',
- );
- }
-
- public static function getAliases(
- ArcanistConfigurationManager $configuration_manager) {
- $sources = $configuration_manager->getConfigFromAllSources('aliases');
-
- $aliases = array();
- foreach ($sources as $source) {
- $aliases += $source;
- }
-
- return $aliases;
- }
-
- private function writeAliases(array $aliases) {
- $config = $this->getConfigurationManager()->readUserConfigurationFile();
- $config['aliases'] = $aliases;
- $this->getConfigurationManager()->writeUserConfigurationFile($config);
- }
-
- public function run() {
- $aliases = self::getAliases($this->getConfigurationManager());
-
- $argv = $this->getArgument('argv');
- if (count($argv) == 0) {
- $this->printAliases($aliases);
- } else if (count($argv) == 1) {
- $this->removeAlias($aliases, $argv[0]);
- } else {
- $arc_config = $this->getArcanistConfiguration();
- $alias = $argv[0];
-
- if ($arc_config->buildWorkflow($alias)) {
- throw new ArcanistUsageException(
- pht(
- 'You can not create an alias for "%s" because it is a '.
- 'builtin command. "%s" can only create new commands.',
- "arc {$alias}",
- 'arc alias'));
- }
-
- $new_alias = array_slice($argv, 1);
-
- $command = implode(' ', $new_alias);
- if (self::isShellCommandAlias($command)) {
- echo tsprintf(
- "%s\n",
- pht(
- 'Aliased "%s" to shell command "%s".',
- "arc {$alias}",
- substr($command, 1)));
- } else {
- echo tsprintf(
- "%s\n",
- pht(
- 'Aliased "%s" to "%s".',
- "arc {$alias}",
- "arc {$command}"));
- }
-
- $aliases[$alias] = $new_alias;
- $this->writeAliases($aliases);
- }
-
- return 0;
- }
-
- public static function isShellCommandAlias($command) {
- return preg_match('/^!/', $command);
- }
-
- public static function resolveAliases(
- $command,
- ArcanistConfiguration $config,
- array $argv,
- ArcanistConfigurationManager $configuration_manager) {
-
- $aliases = self::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);
- }
-
- private function printAliases(array $aliases) {
- if (!$aliases) {
- echo tsprintf(
- "%s\n",
- pht('You have not defined any aliases yet.'));
- return;
- }
-
- $table = id(new PhutilConsoleTable())
- ->addColumn('input', array('title' => pht('Alias')))
- ->addColumn('command', array('title' => pht('Command')))
- ->addColumn('type', array('title' => pht('Type')));
-
- ksort($aliases);
-
- foreach ($aliases as $alias => $binding) {
- $command = implode(' ', $binding);
- if (self::isShellCommandAlias($command)) {
- $command = substr($command, 1);
- $type = pht('Shell Command');
- } else {
- $command = "arc {$command}";
- $type = pht('Arcanist Command');
- }
-
- $row = array(
- 'input' => "arc {$alias}",
- 'type' => $type,
- 'command' => $command,
- );
-
- $table->addRow($row);
- }
-
- $table->draw();
- }
-
- private function removeAlias(array $aliases, $alias) {
- if (empty($aliases[$alias])) {
- echo tsprintf(
- "%s\n",
- pht('No alias "%s" to remove.', $alias));
- return;
- }
-
- $command = implode(' ', $aliases[$alias]);
-
- if (self::isShellCommandAlias($command)) {
- echo tsprintf(
- "%s\n",
- pht(
- '"%s" is currently aliased to shell command "%s".',
- "arc {$alias}",
- substr($command, 1)));
- } else {
- echo tsprintf(
- "%s\n",
- pht(
- '"%s" is currently aliased to "%s".',
- "arc {$alias}",
- "arc {$command}"));
- }
-
-
- $ok = phutil_console_confirm(pht('Delete this alias?'));
- if (!$ok) {
- throw new ArcanistUserAbortException();
- }
-
- unset($aliases[$alias]);
- $this->writeAliases($aliases);
-
- echo tsprintf(
- "%s\n",
- pht(
- 'Removed alias "%s".',
- "arc {$alias}"));
- }
-
-}
diff --git a/src/workflow/ArcanistWorkflow.php b/src/workflow/ArcanistWorkflow.php
--- a/src/workflow/ArcanistWorkflow.php
+++ b/src/workflow/ArcanistWorkflow.php
@@ -2271,4 +2271,8 @@
return $this->repositoryRef;
}
+ final public function getToolsetKey() {
+ return $this->getToolset()->getToolsetKey();
+ }
+
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Mar 19, 8:05 PM (6 d, 17 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7711922
Default Alt Text
D20996.diff (19 KB)
Attached To
Mode
D20996: Update "arc alias" to modern workflows
Attached
Detach File
Event Timeline
Log In to Comment