Эх сурвалжийг харах

Interactive interface with autocompletion

SofHad 10 жил өмнө
parent
commit
aca619e130

+ 112 - 3
src/Composer/Command/UpdateCommand.php

@@ -12,6 +12,7 @@
 
 namespace Composer\Command;
 
+use Composer\Composer;
 use Composer\Installer;
 use Composer\Plugin\CommandEvent;
 use Composer\Plugin\PluginEvents;
@@ -19,6 +20,8 @@ use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Question\ConfirmationQuestion;
+use Symfony\Component\Console\Question\Question;
 
 /**
  * @author Jordi Boggiano <j.boggiano@seld.be>
@@ -50,6 +53,7 @@ class UpdateCommand extends Command
                 new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
                 new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'),
                 new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'),
+                new InputOption('interactive', 'i', InputOption::VALUE_NONE, 'interactive interface with autocompletion that can help to select the packages to update.'),
             ))
             ->setHelp(<<<EOT
 The <info>update</info> command reads the composer.json file from the
@@ -74,13 +78,18 @@ EOT
 
     protected function execute(InputInterface $input, OutputInterface $output)
     {
+        $composer = $this->getComposer(true, $input->getOption('no-plugins'));
+        $packages = $input->getOption('interactive') ? $this->getPackagesInteractively(
+            $input,
+            $output,
+            $composer
+        ) : $input->getArgument('packages');
+
         if ($input->getOption('no-custom-installers')) {
             $output->writeln('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
             $input->setOption('no-plugins', true);
         }
 
-        $composer = $this->getComposer(true, $input->getOption('no-plugins'));
-        $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
         $io = $this->getIO();
 
         $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output);
@@ -122,7 +131,7 @@ EOT
             ->setRunScripts(!$input->getOption('no-scripts'))
             ->setOptimizeAutoloader($optimize)
             ->setUpdate(true)
-            ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages'))
+            ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $packages)
             ->setWhitelistDependencies($input->getOption('with-dependencies'))
             ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
             ->setPreferStable($input->getOption('prefer-stable'))
@@ -135,4 +144,104 @@ EOT
 
         return $install->run();
     }
+
+    private function getPackagesInteractively(
+        InputInterface $input,
+        OutputInterface $output,
+        Composer $composer
+    ) {
+        if ($input->getArgument('packages')) {
+            throw new \InvalidArgumentException(
+                'The option --interactive does not require an argument'
+            );
+        }
+
+        $packagesMap = $composer->getRepositoryManager()
+            ->getLocalRepository()->getPackages();
+        $config      = $composer->getConfig();
+
+        $requiredPackageNames = array();
+        foreach (
+            array_merge(
+                $composer->getPackage()->getRequires(),
+                $composer->getPackage()->getDevRequires()
+            ) as $require) {
+            $requiredPackageNames[] = $require->getTarget();
+        }
+
+        $InstalledPackageNames = array();
+        foreach ($packagesMap as $package) {
+            $InstalledPackageNames[] = $package->getPrettyName();
+        }
+        $names = array_unique(
+            array_merge($InstalledPackageNames, $requiredPackageNames)
+        );
+
+        $vendorDir      = $config->get('vendor-dir', true);
+        $vendorWildcard = sprintf('%s/*', $vendorDir);
+
+        $autocompleterValues = array($vendorWildcard);
+        foreach ($names as $name) {
+            $autocompleterValues[] = $name;
+            $autocompleterValues[] = sprintf('%s/%s', $vendorDir, $name);
+        }
+
+        $helper       = $this->getHelper('question');
+        $question     = new Question(
+            '<comment>Add package that should be updated:</comment>',
+            null
+        );
+        $confirmation = new ConfirmationQuestion(
+            '<question>Add more packages[yes|no] ?</question><info> (no) </info> ',
+            false
+        );
+
+        $packages = array();
+        do {
+            $question->setAutocompleterValues($autocompleterValues);
+            $addedPackage = $helper->ask($input, $output, $question);
+
+            if (!is_string($addedPackage) || empty($addedPackage)) {
+                $output->writeln('<error>Invalid package.</error>');
+
+                continue;
+            }
+
+            if (!in_array($addedPackage, $packages)) {
+                $packages[] = $addedPackage;
+                if ($addedPackage === $vendorWildcard) {
+                    $autocompleterValues = array();
+                } else {
+                    $autocompleterValues = array_diff($autocompleterValues, array($addedPackage));
+                }
+            } else {
+                $output->writeln(sprintf('<error>The package "%s" was already added.</error>', $package));
+            }
+        } while ($helper->ask($input, $output, $confirmation));
+
+        $packages = array_filter($packages);
+        if (!$packages) {
+            throw new \InvalidArgumentException('You must enter minimum one package.');
+        }
+
+        $output->writeln(str_repeat('.', 40));
+        foreach ((array)$packages as $package) {
+            $output->writeln(sprintf('<info>- %s</info>', $package));
+        }
+        $output->writeln(str_repeat('.', 40));
+
+        $continue = new ConfirmationQuestion(
+            sprintf(
+                '<question>Would you like to continue and update the above package%s [yes|no] ?</question><info> (yes) </info>',
+                1 === count($packages) ? '' : 's'
+            ),
+            true
+        );
+
+        if ($helper->ask($input, $output, $continue)) {
+            return $packages;
+        }
+
+        throw new \RuntimeException('Installation aborted.');
+    }
 }