From 825c21f6ea5f46da82f821a64d2f36b810f139aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20TAMARELLE?= Date: Wed, 1 Dec 2021 11:22:49 +0100 Subject: [PATCH] Factorise completion + complete all installed packages --- src/Composer/Command/ArchiveCommand.php | 13 +++ src/Composer/Command/BaseCommand.php | 86 +++++++++++++++++++ src/Composer/Command/CreateProjectCommand.php | 7 ++ src/Composer/Command/DependsCommand.php | 5 +- src/Composer/Command/ExecCommand.php | 19 ++-- src/Composer/Command/GlobalCommand.php | 21 +++++ src/Composer/Command/HomeCommand.php | 7 ++ src/Composer/Command/InitCommand.php | 7 ++ src/Composer/Command/InstallCommand.php | 12 ++- src/Composer/Command/OutdatedCommand.php | 5 +- src/Composer/Command/ProhibitsCommand.php | 7 ++ src/Composer/Command/ReinstallCommand.php | 7 ++ src/Composer/Command/RemoveCommand.php | 5 +- src/Composer/Command/RequireCommand.php | 17 +--- src/Composer/Command/RunScriptCommand.php | 5 +- src/Composer/Command/ShowCommand.php | 10 ++- src/Composer/Command/SuggestsCommand.php | 7 ++ src/Composer/Command/UpdateCommand.php | 8 +- 18 files changed, 191 insertions(+), 57 deletions(-) diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index c30c2364dfad..730719aca5ad 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -26,6 +26,8 @@ use Composer\Util\Filesystem; use Composer\Util\Loop; use Composer\Util\ProcessExecutor; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -38,6 +40,17 @@ */ class ArchiveCommand extends BaseCommand { + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($this->completeAvailablePackage($input, $suggestions)) { + return; + } + + if ($input->mustSuggestOptionValuesFor('format')) { + $suggestions->suggestValues(array('tar', 'zip')); + } + } + /** * @return void */ diff --git a/src/Composer/Command/BaseCommand.php b/src/Composer/Command/BaseCommand.php index 0afcc0db1810..5deffb3af32c 100644 --- a/src/Composer/Command/BaseCommand.php +++ b/src/Composer/Command/BaseCommand.php @@ -18,10 +18,19 @@ use Composer\Factory; use Composer\IO\IOInterface; use Composer\IO\NullIO; +use Composer\Package\Package; +use Composer\Package\PackageInterface; use Composer\Plugin\PreCommandRunEvent; use Composer\Package\Version\VersionParser; use Composer\Plugin\PluginEvents; +use Composer\Repository\CompositeRepository; +use Composer\Repository\InstalledRepository; +use Composer\Repository\PlatformRepository; +use Composer\Repository\RepositoryInterface; +use Composer\Repository\RootPackageRepository; use Composer\Util\Platform; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Input\InputInterface; @@ -218,6 +227,83 @@ protected function getPreferredInstallOptions(Config $config, InputInterface $in return array($preferSource, $preferDist); } + /** + * @return bool + */ + protected function completePreferInstall(CompletionInput $input, CompletionSuggestions $suggestions) + { + if ($input->mustSuggestOptionValuesFor('prefer-install')) { + $suggestions->suggestValues(array('dist', 'source', 'auto')); + + return true; + } + + return false; + } + + /** + * @return bool + */ + protected function completeInstalledPackage(CompletionInput $input, CompletionSuggestions $suggestions, $withPlatform = false) + { + if (!$input->mustSuggestArgumentValuesFor('packages') && + !$input->mustSuggestArgumentValuesFor('package') && + !$input->mustSuggestOptionValuesFor('ignore') + ) { + return false; + } + + $composer = $this->getComposer(); + $installedRepos = array( + new RootPackageRepository(clone $composer->getPackage()), + ); + + $locker = $composer->getLocker(); + if ($locker->isLocked()) { + if ($withPlatform) { + $installedRepos[] = new PlatformRepository(array(), $locker->getPlatformOverrides()); + } + $installedRepos[] = $locker->getLockedRepository(true); + } else { + if ($withPlatform) { + $installedRepos[] = new PlatformRepository(array(), $composer->getConfig()->get('platform') ?: array()); + } + $installedRepos[] = $composer->getRepositoryManager()->getLocalRepository(); + } + + $installedRepo = new InstalledRepository($installedRepos); + $suggestions->suggestValues(array_map(function (PackageInterface $package) { + return $package->getName(); + }, $installedRepo->getPackages())); + + return true; + } + + /** + * @return bool + */ + protected function completeAvailablePackage(CompletionInput $input, CompletionSuggestions $suggestions) + { + if (!$input->mustSuggestArgumentValuesFor('packages') && + !$input->mustSuggestArgumentValuesFor('package') && + !$input->mustSuggestOptionValuesFor('require') && + !$input->mustSuggestOptionValuesFor('require-dev') + ) { + return false; + } + + $composer = $this->getComposer(); + $repos = new CompositeRepository($composer->getRepositoryManager()->getRepositories()); + + $packages = $repos->search('^'.$input->getCompletionValue(), RepositoryInterface::SEARCH_NAME); + + foreach (array_slice($packages, 0, 150) as $package) { + $suggestions->suggestValue($package['name']); + } + + return true; + } + /** * @param array $requirements * diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 233ed1a2e0ab..324262da40e4 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -32,6 +32,8 @@ use Composer\Repository\RepositorySet; use Composer\Script\ScriptEvents; use Composer\Util\Silencer; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -54,6 +56,11 @@ */ class CreateProjectCommand extends BaseCommand { + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $this->completeAvailablePackage($input, $suggestions) || $this->completePreferInstall($input, $suggestions); + } + /** * @var SuggestedPackagesReporter */ diff --git a/src/Composer/Command/DependsCommand.php b/src/Composer/Command/DependsCommand.php index 28234bee6726..fce52c7ecb0f 100644 --- a/src/Composer/Command/DependsCommand.php +++ b/src/Composer/Command/DependsCommand.php @@ -26,10 +26,7 @@ class DependsCommand extends BaseDependencyCommand { public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { - if ($input->mustSuggestArgumentValuesFor(self::ARGUMENT_PACKAGE)) { - $rootPackage = $this->getComposer(true, $input->getOption('no-plugins'))->getPackage(); - $suggestions->suggestValues(array_merge(array_keys($rootPackage->getRequires()), array_keys($rootPackage->getDevRequires()))); - } + $this->completeInstalledPackage($input, $suggestions); } /** diff --git a/src/Composer/Command/ExecCommand.php b/src/Composer/Command/ExecCommand.php index c2612e2275d7..dcb6b5ad072f 100644 --- a/src/Composer/Command/ExecCommand.php +++ b/src/Composer/Command/ExecCommand.php @@ -27,7 +27,7 @@ class ExecCommand extends BaseCommand public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('binary')) { - $suggestions->suggestValues($this->getBinaries()); + $suggestions->suggestValues($this->getBinaries(false)); } } @@ -65,7 +65,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { $composer = $this->getComposer(); if ($input->getOption('list') || !$input->getArgument('binary')) { - $bins = $this->getBinaries(); + $bins = $this->getBinaries(true); if (!$bins) { $binDir = $composer->getConfig()->get('bin-dir'); @@ -109,19 +109,23 @@ protected function execute(InputInterface $input, OutputInterface $output) } /** + * @param bool $forDisplay * @return string[] */ - private function getBinaries() + private function getBinaries($forDisplay) { $composer = $this->getComposer(); $binDir = $composer->getConfig()->get('bin-dir'); $bins = glob($binDir . '/*'); - $bins = array_merge($bins, array_map(function ($e) { - return "$e (local)"; - }, $composer->getPackage()->getBinaries())); + $localBins = $composer->getPackage()->getBinaries(); + if ($forDisplay) { + $localBins = array_map(function ($e) { + return "$e (local)"; + }, $localBins); + } $binaries = array(); - foreach ($bins as $bin) { + foreach (array_merge($bins, $localBins) as $bin) { // skip .bat copies if (isset($previousBin) && $bin === $previousBin.'.bat') { continue; @@ -129,7 +133,6 @@ private function getBinaries() $previousBin = $bin; $binaries[] = basename($bin); - } return $binaries; diff --git a/src/Composer/Command/GlobalCommand.php b/src/Composer/Command/GlobalCommand.php index fbcb77eeb616..91ebfe993bc8 100644 --- a/src/Composer/Command/GlobalCommand.php +++ b/src/Composer/Command/GlobalCommand.php @@ -15,6 +15,9 @@ use Composer\Factory; use Composer\Util\Filesystem; use Composer\Util\Platform; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\StringInput; @@ -25,6 +28,24 @@ */ class GlobalCommand extends BaseCommand { + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $application = $this->getApplication(); + if ($input->mustSuggestArgumentValuesFor('command-name')) { + $suggestions->suggestValues(array_filter(array_map(function (Command $command) { + return $command->isHidden() ? null : $command->getName(); + }, $application->all()))); + + return; + } + + // @todo delegation is tricky + if ($application->has($command = $input->getArgument('command'))) { + $input = CompletionInput::fromString(preg_replace('{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}', '', rtrim($input->__toString(), '|'), 1), 3); + $this->getApplication()->get($command)->complete($input, $suggestions); + } + } + /** * @return void */ diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index ad4a42713ed5..1f9a13d00b8f 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -18,6 +18,8 @@ use Composer\Repository\RepositoryFactory; use Composer\Util\Platform; use Composer\Util\ProcessExecutor; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; @@ -28,6 +30,11 @@ */ class HomeCommand extends BaseCommand { + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $this->completeInstalledPackage($input, $suggestions); + } + /** * @inheritDoc * diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 939a3fe38374..942d229eae9b 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -31,6 +31,8 @@ use Composer\Util\ProcessExecutor; use Composer\Semver\Constraint\Constraint; use Composer\Util\Silencer; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -54,6 +56,11 @@ class InitCommand extends BaseCommand /** @var RepositorySet[] */ private $repositorySets; + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $this->completeAvailablePackage($input, $suggestions); + } + /** * @inheritDoc * diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index bb825308ca78..683a818f65dc 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -32,6 +32,11 @@ */ class InstallCommand extends BaseCommand { + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $this->completePreferInstall($input, $suggestions); + } + /** * @return void */ @@ -142,11 +147,4 @@ protected function execute(InputInterface $input, OutputInterface $output) return $install->run(); } - - public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void - { - if ($input->mustSuggestOptionValuesFor('prefer-install')) { - $suggestions->suggestValues(array('dist', 'source', 'auto')); - } - } } diff --git a/src/Composer/Command/OutdatedCommand.php b/src/Composer/Command/OutdatedCommand.php index 18651619574a..d77cda95a9b3 100644 --- a/src/Composer/Command/OutdatedCommand.php +++ b/src/Composer/Command/OutdatedCommand.php @@ -27,10 +27,7 @@ class OutdatedCommand extends ShowCommand { public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { - if ($input->mustSuggestArgumentValuesFor('package') || $input->mustSuggestOptionValuesFor('ignore')) { - $rootPackage = $this->getComposer(true, $input->getOption('no-plugins'))->getPackage(); - $suggestions->suggestValues(array_merge(array_keys($rootPackage->getRequires()), array_keys($rootPackage->getDevRequires()))); - } + $this->completeInstalledPackage($input, $suggestions); } /** diff --git a/src/Composer/Command/ProhibitsCommand.php b/src/Composer/Command/ProhibitsCommand.php index c51d320f534a..840498460281 100644 --- a/src/Composer/Command/ProhibitsCommand.php +++ b/src/Composer/Command/ProhibitsCommand.php @@ -12,6 +12,8 @@ namespace Composer\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputArgument; @@ -22,6 +24,11 @@ */ class ProhibitsCommand extends BaseDependencyCommand { + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $this->completeAvailablePackage($input, $suggestions); + } + /** * Configure command metadata. * diff --git a/src/Composer/Command/ReinstallCommand.php b/src/Composer/Command/ReinstallCommand.php index 6d0122168549..76e9fce773aa 100644 --- a/src/Composer/Command/ReinstallCommand.php +++ b/src/Composer/Command/ReinstallCommand.php @@ -20,6 +20,8 @@ use Composer\Package\BasePackage; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; @@ -30,6 +32,11 @@ */ class ReinstallCommand extends BaseCommand { + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $this->completeInstalledPackage($input, $suggestions) || $this->completePreferInstall($input, $suggestions); + } + /** * @return void */ diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index 2174b6d0d554..60d6a0cc6c66 100644 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -36,10 +36,7 @@ class RemoveCommand extends BaseCommand { public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { - if ($input->mustSuggestArgumentValuesFor('packages')) { - $rootPackage = $this->getComposer(true, $input->getOption('no-plugins'))->getPackage(); - $suggestions->suggestValues(array_merge(array_keys($rootPackage->getRequires()), array_keys($rootPackage->getDevRequires()))); - } + $this->completeInstalledPackage($input, $suggestions); } /** diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index b10b5efa11fb..a04119333128 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -14,7 +14,6 @@ use Composer\DependencyResolver\Request; use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory; -use Composer\Repository\RepositoryInterface; use Composer\Util\Filesystem; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; @@ -62,20 +61,8 @@ class RequireCommand extends InitCommand public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { - $value = $input->getCompletionValue(); - if ($input->mustSuggestArgumentValuesFor('packages') && strlen($value) > 1) { - $composer = $this->getComposer(true, $input->getOption('no-plugins')); - $repos = new CompositeRepository($composer->getRepositoryManager()->getRepositories()); - - $packages = $repos->search('^'.$input->getCompletionValue(), RepositoryInterface::SEARCH_NAME); - - foreach (array_slice($packages, 0, 150) as $package) { - $suggestions->suggestValue($package['name']); - } - - } elseif ($input->mustSuggestOptionValuesFor('prefer-install')) { - $suggestions->suggestValues(array('dist', 'source', 'auto')); - } + // @todo add ext-* ? + $this->completeAvailablePackage($input, $suggestions); } /** diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index 2a88e305fcb1..85be1206ab73 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -49,10 +49,7 @@ class RunScriptCommand extends BaseCommand public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('script')) { - $scripts = $this->getComposer()->getPackage()->getScripts(); - foreach ($scripts as $name => $script) { - $suggestions->suggestValue($name); - } + $suggestions->suggestValue(array_keys($this->getComposer()->getPackage()->getScripts())); } } diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 6d4f76e2af9c..44fdb0186d94 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -20,7 +20,6 @@ use Composer\Package\CompletePackageInterface; use Composer\Package\Link; use Composer\Package\AliasPackage; -use Composer\Package\Package; use Composer\Package\PackageInterface; use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionSelector; @@ -65,9 +64,12 @@ class ShowCommand extends BaseCommand public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { - if ($input->mustSuggestArgumentValuesFor('package') || $input->mustSuggestOptionValuesFor('ignore')) { - $rootPackage = $this->getComposer(true, $input->getOption('no-plugins'))->getPackage(); - $suggestions->suggestValues(array_merge(array_keys($rootPackage->getRequires()), array_keys($rootPackage->getDevRequires()))); + if ($this->completeInstalledPackage($input, $suggestions)) { + return; + } + + if ($input->mustSuggestOptionValuesFor('format')) { + $suggestions->suggestValues(array('json', 'text')); } } diff --git a/src/Composer/Command/SuggestsCommand.php b/src/Composer/Command/SuggestsCommand.php index 345890e61999..34a11747a476 100644 --- a/src/Composer/Command/SuggestsCommand.php +++ b/src/Composer/Command/SuggestsCommand.php @@ -16,6 +16,8 @@ use Composer\Repository\RootPackageRepository; use Composer\Repository\InstalledRepository; use Composer\Installer\SuggestedPackagesReporter; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -23,6 +25,11 @@ class SuggestsCommand extends BaseCommand { + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $this->completeInstalledPackage($input, $suggestions); + } + /** * @return void */ diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 94a27c48729c..39a3e218551a 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -41,13 +41,7 @@ class UpdateCommand extends BaseCommand { public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { - if ($input->mustSuggestArgumentValuesFor('packages')) { - $rootPackage = $this->getComposer(true, $input->getOption('no-plugins'))->getPackage(); - $suggestions->suggestValues(array_merge(array_keys($rootPackage->getRequires()), array_keys($rootPackage->getDevRequires()))); - - } elseif ($input->mustSuggestOptionValuesFor('prefer-install')) { - $suggestions->suggestValues(array('dist', 'source', 'auto')); - } + $this->completeInstalledPackage($input, $suggestions) || $this->completePreferInstall($input, $suggestions); } /**