diff --git a/src/Composer/Command/ExecCommand.php b/src/Composer/Command/ExecCommand.php
index abaa1aff3..905b28b99 100644
--- a/src/Composer/Command/ExecCommand.php
+++ b/src/Composer/Command/ExecCommand.php
@@ -51,6 +51,26 @@ EOT
;
}
+ protected function interact(InputInterface $input, OutputInterface $output): void
+ {
+ $binaries = $this->getBinaries(false);
+ if (count($binaries) === 0) {
+ return;
+ }
+
+ $io = $this->getIO();
+ /** @var string $binary */
+ $binary = $io->select(
+ 'Binary to run: ',
+ $binaries,
+ '',
+ 1,
+ 'Invalid binary name "%s"'
+ );
+
+ $input->setArgument('binary', $binaries[$binary]);
+ }
+
protected function execute(InputInterface $input, OutputInterface $output)
{
$composer = $this->requireComposer();
diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php
index 4e1bccd27..20c419500 100644
--- a/src/Composer/Command/RunScriptCommand.php
+++ b/src/Composer/Command/RunScriptCommand.php
@@ -52,7 +52,7 @@ class RunScriptCommand extends BaseCommand
->setDescription('Runs the scripts defined in composer.json')
->setDefinition([
new InputArgument('script', InputArgument::OPTIONAL, 'Script name to run.', null, function () {
- return array_keys($this->requireComposer()->getPackage()->getScripts());
+ return array_map(static function ($script) { return $script['name']; }, $this->getScripts());
}),
new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
new InputOption('timeout', null, InputOption::VALUE_REQUIRED, 'Sets script timeout in seconds, or 0 for never.'),
@@ -72,6 +72,29 @@ EOT
;
}
+ protected function interact(InputInterface $input, OutputInterface $output): void
+ {
+ $scripts = $this->getScripts();
+ if (count($scripts) === 0) {
+ return;
+ }
+
+ $options = [];
+ foreach ($scripts as $script) {
+ $options[$script['name']] = $script['description'];
+ }
+ $io = $this->getIO();
+ $script = $io->select(
+ 'Script to run: ',
+ $options,
+ '',
+ 1,
+ 'Invalid script name "%s"'
+ );
+
+ $input->setArgument('script', $script);
+ }
+
protected function execute(InputInterface $input, OutputInterface $output): int
{
if ($input->getOption('list')) {
@@ -114,15 +137,34 @@ EOT
protected function listScripts(OutputInterface $output): int
{
- $scripts = $this->requireComposer()->getPackage()->getScripts();
-
- if (!count($scripts)) {
+ $scripts = $this->getScripts();
+ if (count($scripts) === 0) {
return 0;
}
$io = $this->getIO();
$io->writeError('scripts:');
$table = [];
+ foreach ($scripts as $script) {
+ $table[] = [' '.$script['name'], $script['description']];
+ }
+
+ $this->renderTable($table, $output);
+
+ return 0;
+ }
+
+ /**
+ * @return list
+ */
+ private function getScripts(): array
+ {
+ $scripts = $this->requireComposer()->getPackage()->getScripts();
+ if (count($scripts) === 0) {
+ return [];
+ }
+
+ $result = [];
foreach ($scripts as $name => $script) {
$description = '';
try {
@@ -133,11 +175,9 @@ EOT
} catch (\Symfony\Component\Console\Exception\CommandNotFoundException $e) {
// ignore scripts that have no command associated, like native Composer script listeners
}
- $table[] = [' '.$name, $description];
+ $result[] = ['name' => $name, 'description' => $description];
}
- $this->renderTable($table, $output);
-
- return 0;
+ return $result;
}
}
diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php
index 46ba7a4a1..8ecea4268 100644
--- a/src/Composer/IO/ConsoleIO.php
+++ b/src/Composer/IO/ConsoleIO.php
@@ -310,6 +310,11 @@ class ConsoleIO extends BaseIO
$result = $helper->ask($this->input, $this->getErrorOutput(), $question);
+ $isAssoc = (bool) \count(array_filter(array_keys($choices), 'is_string'));
+ if ($isAssoc) {
+ return $result;
+ }
+
if (!is_array($result)) {
return (string) array_search($result, $choices, true);
}