1
0
Fork 0

Add completion on commands

pull/10320/head
Jérôme TAMARELLE 2021-11-30 01:03:23 +01:00 committed by Jordi Boggiano
parent 766943c767
commit fe6be142b1
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
22 changed files with 423 additions and 28 deletions

View File

@ -27,6 +27,8 @@ use Composer\Util\Filesystem;
use Composer\Util\Loop;
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\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@ -39,6 +41,17 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class ArchiveCommand extends BaseCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($this->completeAvailablePackage($input, $suggestions)) {
return;
}
if ($input->mustSuggestOptionValuesFor('format')) {
$suggestions->suggestValues(['tar', 'tar.gz', 'tar.bz2', 'zip']);
}
}
/**
* @return void
*/

View File

@ -20,10 +20,19 @@ use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface;
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;
@ -317,7 +326,96 @@ abstract class BaseCommand extends Command
}
/**
* @param array<string> $requirements
* Suggestion values for "prefer-install" option
*/
protected function completePreferInstall(CompletionInput $input, CompletionSuggestions $suggestions): bool
{
if ($input->mustSuggestOptionValuesFor('prefer-install')) {
$suggestions->suggestValues(['dist', 'source', 'auto']);
return true;
}
return false;
}
/**
* Suggest package names from installed ones.
*/
protected function completeInstalledPackage(CompletionInput $input, CompletionSuggestions $suggestions): bool
{
if (!$input->mustSuggestArgumentValuesFor('packages') &&
!$input->mustSuggestArgumentValuesFor('package') &&
!$input->mustSuggestOptionValuesFor('ignore')
) {
return false;
}
$composer = $this->getComposer();
$installedRepos = [new RootPackageRepository(clone $composer->getPackage())];
$locker = $composer->getLocker();
if ($locker->isLocked()) {
$installedRepos[] = $locker->getLockedRepository(true);
} else {
$installedRepos[] = $composer->getRepositoryManager()->getLocalRepository();
}
$installedRepo = new InstalledRepository($installedRepos);
$suggestions->suggestValues(array_map(function (PackageInterface $package) {
return $package->getName();
}, $installedRepo->getPackages()));
return true;
}
/**
* Suggest package names available on all configured repositories.
*/
protected function completeAvailablePackage(CompletionInput $input, CompletionSuggestions $suggestions): bool
{
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('^'.preg_quote($input->getCompletionValue()), RepositoryInterface::SEARCH_NAME);
foreach (array_slice($packages, 0, 150) as $package) {
$suggestions->suggestValue($package['name']);
}
return true;
}
/**
* Suggests ext- packages from the ones available on the currently-running PHP
*/
protected function completePlatformPackage(CompletionInput $input, CompletionSuggestions $suggestions): bool
{
if (!$input->mustSuggestOptionValuesFor('require') &&
!$input->mustSuggestOptionValuesFor('require-dev') &&
!str_starts_with($input->getCompletionValue(), 'ext-')
) {
return false;
}
$repos = new PlatformRepository([], $this->getComposer()->getConfig()->get('platform') ?? []);
$suggestions->suggestValues(array_map(function (PackageInterface $package) {
return $package->getName();
}, $repos->getPackages()));
return true;
}
/**
* @param array<string, string> $requirements
*
* @return array<string, string>
*/

View File

@ -33,6 +33,8 @@ use Composer\Repository\InstalledArrayRepository;
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;
@ -55,6 +57,11 @@ use Composer\Package\Version\VersionParser;
*/
class CreateProjectCommand extends BaseCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->completeAvailablePackage($input, $suggestions) || $this->completePreferInstall($input, $suggestions);
}
/**
* @var SuggestedPackagesReporter
*/

View File

@ -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 @@ use Symfony\Component\Console\Input\InputOption;
*/
class DependsCommand extends BaseDependencyCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->completeInstalledPackage($input, $suggestions);
}
/**
* Configure command metadata.
*

View File

@ -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\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@ -22,6 +24,13 @@ use Symfony\Component\Console\Input\InputArgument;
*/
class ExecCommand extends BaseCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestArgumentValuesFor('binary')) {
$suggestions->suggestValues($this->getBinaries(false));
}
}
/**
* @return void
*/
@ -52,14 +61,11 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
$composer = $this->requireComposer();
if ($input->getOption('list') || !$input->getArgument('binary')) {
$bins = $this->getBinaries(true);
if (count($bins) > 0) {
$binDir = $composer->getConfig()->get('bin-dir');
if ($input->getOption('list') || null === $input->getArgument('binary')) {
$bins = glob($binDir . '/*');
$bins = array_merge($bins, array_map(function ($e) {
return "$e (local)";
}, $composer->getPackage()->getBinaries()));
if (!$bins) {
throw new \RuntimeException("No binaries found in composer.json or in bin-dir ($binDir)");
}
@ -70,13 +76,6 @@ EOT
);
foreach ($bins as $bin) {
// skip .bat copies
if (isset($previousBin) && $bin === $previousBin.'.bat') {
continue;
}
$previousBin = $bin;
$bin = basename($bin);
$this->getIO()->write(
<<<EOT
<info>- $bin</info>
@ -105,4 +104,30 @@ EOT
return $dispatcher->dispatchScript('__exec_command', true, $input->getArgument('args'));
}
private function getBinaries(bool $forDisplay): array
{
$composer = $this->getComposer();
$binDir = $composer->getConfig()->get('bin-dir');
$bins = glob($binDir . '/*');
$localBins = $composer->getPackage()->getBinaries();
if ($forDisplay) {
$localBins = array_map(function ($e) {
return "$e (local)";
}, $localBins);
}
$binaries = [];
foreach (array_merge($bins, $localBins) as $bin) {
// skip .bat copies
if (isset($previousBin) && $bin === $previousBin.'.bat') {
continue;
}
$previousBin = $bin;
$binaries[] = basename($bin);
}
return $binaries;
}
}

View File

@ -16,6 +16,9 @@ use Composer\Factory;
use Composer\Pcre\Preg;
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;
@ -26,6 +29,16 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
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 void
*/

View File

@ -18,6 +18,8 @@ use Composer\Repository\RootPackageRepository;
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 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class HomeCommand extends BaseCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->completeInstalledPackage($input, $suggestions);
}
/**
* @inheritDoc
*

View File

@ -23,6 +23,8 @@ use Composer\Repository\PlatformRepository;
use Composer\Repository\RepositoryFactory;
use Composer\Util\Filesystem;
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;
@ -42,6 +44,14 @@ class InitCommand extends BaseCommand
/** @var array<string, string> */
private $gitConfig;
/** @var RepositorySet[] */
private $repositorySets;
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->completeAvailablePackage($input, $suggestions);
}
/**
* @inheritDoc
*

View File

@ -16,6 +16,8 @@ use Composer\Installer;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Util\HttpDownloader;
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;
@ -29,6 +31,11 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class InstallCommand extends BaseCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->completePreferInstall($input, $suggestions) || $this->completeInstalledPackage($input, $suggestions);
}
/**
* @return void
*/

View File

@ -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\Input\InputArgument;
use Symfony\Component\Console\Input\ArrayInput;
@ -23,6 +25,11 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class OutdatedCommand extends BaseCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->completeInstalledPackage($input, $suggestions);
}
/**
* @return void
*/

View File

@ -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 @@ use Symfony\Component\Console\Input\InputOption;
*/
class ProhibitsCommand extends BaseDependencyCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->completeAvailablePackage($input, $suggestions);
}
/**
* Configure command metadata.
*

View File

@ -22,6 +22,8 @@ use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Script\ScriptEvents;
use Composer\Util\Platform;
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;
@ -32,6 +34,11 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class ReinstallCommand extends BaseCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->completeInstalledPackage($input, $suggestions) || $this->completePreferInstall($input, $suggestions);
}
/**
* @return void
*/

View File

@ -20,6 +20,8 @@ use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Json\JsonFile;
use Composer\Factory;
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;
@ -32,6 +34,11 @@ use Composer\Package\BasePackage;
*/
class RemoveCommand extends BaseCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->completeInstalledPackage($input, $suggestions);
}
/**
* @return void
*/

View File

@ -14,6 +14,8 @@ namespace Composer\Command;
use Composer\DependencyResolver\Request;
use Composer\Util\Filesystem;
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\InputOption;
@ -58,6 +60,11 @@ class RequireCommand extends BaseCommand
/** @var bool */
private $dependencyResolutionCompleted = false;
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->completePlatformPackage($input, $suggestions) || $this->completeAvailablePackage($input, $suggestions) || $this->completePreferInstall($input, $suggestions);
}
/**
* @return void
*/

View File

@ -16,6 +16,8 @@ use Composer\Script\Event as ScriptEvent;
use Composer\Script\ScriptEvents;
use Composer\Util\ProcessExecutor;
use Composer\Util\Platform;
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;
@ -44,6 +46,13 @@ class RunScriptCommand extends BaseCommand
ScriptEvents::POST_AUTOLOAD_DUMP,
);
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestArgumentValuesFor('script')) {
$suggestions->suggestValues(array_keys($this->getComposer()->getPackage()->getScripts()));
}
}
/**
* @return void
*/

View File

@ -14,6 +14,8 @@ namespace Composer\Command;
use Composer\Factory;
use Composer\Json\JsonFile;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
@ -30,6 +32,13 @@ use Composer\Plugin\PluginEvents;
*/
class SearchCommand extends BaseCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestOptionValuesFor('format')) {
$suggestions->suggestValues(['json', 'text']);
}
}
/**
* @return void
*/

View File

@ -20,7 +20,6 @@ use Composer\Package\BasePackage;
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;
@ -41,6 +40,8 @@ use Composer\Semver\Constraint\ConstraintInterface;
use Composer\Semver\Semver;
use Composer\Spdx\SpdxLicenses;
use Composer\Util\PackageInfo;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputArgument;
@ -64,6 +65,17 @@ class ShowCommand extends BaseCommand
/** @var ?RepositorySet */
private $repositorySet;
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($this->completeInstalledPackage($input, $suggestions)) {
return;
}
if ($input->mustSuggestOptionValuesFor('format')) {
$suggestions->suggestValues(['json', 'text']);
}
}
/**
* @return void
*/

View File

@ -16,6 +16,8 @@ use Composer\Repository\PlatformRepository;
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 @@ use Symfony\Component\Console\Output\OutputInterface;
class SuggestsCommand extends BaseCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->completeInstalledPackage($input, $suggestions);
}
/**
* @return void
*/

View File

@ -24,6 +24,8 @@ use Composer\Package\Version\VersionParser;
use Composer\Util\HttpDownloader;
use Composer\Semver\Constraint\MultiConstraint;
use Composer\Package\Link;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@ -37,6 +39,11 @@ use Symfony\Component\Console\Question\Question;
*/
class UpdateCommand extends BaseCommand
{
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$this->completeInstalledPackage($input, $suggestions) || $this->completePreferInstall($input, $suggestions);
}
/**
* @return void
*/

View File

@ -74,6 +74,12 @@ class VersionGuesser
return null;
}
// bypass version guessing in bash completions as it takes time to create
// new processes and the root version is usually not that important
if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] === '_complete') {
return null;
}
$versionData = $this->guessGitVersion($packageConfig, $path);
if (null !== $versionData && null !== $versionData['version']) {
return $this->postprocess($versionData);

View File

@ -44,8 +44,12 @@ class HttpDownloader
private $config;
/** @var array<Job> */
private $jobs = array();
/** @var bool */
private $disableTls;
/** @var mixed[] */
private $options = array();
/** @var mixed[]|null */
private $tlsDefaultOptions = null;
/** @var int */
private $runningJobs = 0;
/** @var int */
@ -73,22 +77,19 @@ class HttpDownloader
$this->disabled = (bool) Platform::getEnv('COMPOSER_DISABLE_NETWORK');
// Setup TLS options
// The cafile option can be set via config.json
if ($disableTls === false) {
$this->options = StreamContextFactory::getTlsDefaults($options, $io);
if ($disableTls === true) {
// make sure the tlsDefaultOptions are not loaded later
$this->tlsDefaultOptions = [];
}
// handle the other externally set options normally.
$this->options = array_replace_recursive($this->options, $options);
$this->disableTls = $disableTls;
$this->options = $options;
$this->config = $config;
if (self::isCurlEnabled()) {
$this->curl = new CurlDownloader($io, $config, $options, $disableTls);
}
$this->rfs = new RemoteFilesystem($io, $config, $options, $disableTls);
if (is_numeric($maxJobs = Platform::getEnv('COMPOSER_MAX_PARALLEL_HTTP'))) {
$this->maxJobs = max(1, min(50, (int) $maxJobs));
}
@ -171,7 +172,13 @@ class HttpDownloader
*/
public function getOptions()
{
return $this->options;
if ($this->tlsDefaultOptions === null) {
// Setup TLS options
// The cafile option can be set via config.json
$this->tlsDefaultOptions = StreamContextFactory::getTlsDefaults($this->options, $this->io);
}
return array_replace_recursive($this->tlsDefaultOptions, $this->options);
}
/**
@ -191,7 +198,7 @@ class HttpDownloader
*/
private function addJob(array $request, bool $sync = false): array
{
$request['options'] = array_replace_recursive($this->options, $request['options']);
$request['options'] = array_replace_recursive($this->getOptions(), $request['options']);
/** @var Job */
$job = array(
@ -211,8 +218,6 @@ class HttpDownloader
$this->io->setAuthentication($job['origin'], rawurldecode($match[1]), rawurldecode($match[2]));
}
$rfs = $this->rfs;
if ($this->canUseCurl($job)) {
$resolver = function ($resolve, $reject) use (&$job): void {
$job['status'] = HttpDownloader::STATUS_QUEUED;
@ -285,6 +290,15 @@ class HttpDownloader
return array($job, $promise);
}
private function getRFS(): RemoteFilesystem
{
if (null === $this->rfs) {
$this->rfs = new RemoteFilesystem($this->io, $this->config, $this->options, $this->disableTls);
}
return $this->rfs;
}
/**
* @param int $id
* @return void

View File

@ -0,0 +1,109 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test;
use Composer\Console\Application;
use Symfony\Component\Console\Tester\CommandCompletionTester;
/**
* Validate autocompletion for all commands.
*
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class CompletionFunctionalTest extends TestCase
{
public function getCommandSuggestions(): iterable
{
$randomProject = '104corp/cache';
$installedPackages = ['composer/semver', 'psr/log'];
$preferInstall = ['dist', 'source', 'auto'];
yield ['archive ', [$randomProject]];
yield ['archive symfony/http-', ['symfony/http-kernel', 'symfony/http-foundation']];
yield ['archive --format ', ['tar', 'zip']];
yield ['create-project ', [$randomProject]];
yield ['create-project symfony/skeleton --prefer-install ', $preferInstall];
yield ['depends ', $installedPackages];
yield ['why ', $installedPackages];
yield ['exec ', ['composer', 'compile']];
yield ['browse ', $installedPackages];
yield ['home -H ', $installedPackages];
yield ['init --require ', [$randomProject]];
yield ['init --require-dev foo/bar --require-dev ', [$randomProject]];
yield ['install --prefer-install ', $preferInstall];
yield ['install ', $installedPackages];
yield ['outdated ', $installedPackages];
yield ['prohibits ', [$randomProject]];
yield ['why-not symfony/http-ker', ['symfony/http-kernel']];
yield ['reinstall --prefer-install ', $preferInstall];
yield ['reinstall ', $installedPackages];
yield ['remove ', $installedPackages];
yield ['require --prefer-install ', $preferInstall];
yield ['require ', [$randomProject]];
yield ['require --dev symfony/http-', ['symfony/http-kernel', 'symfony/http-foundation']];
yield ['run-script ', ['compile', 'test', 'phpstan']];
yield ['run-script test ', null];
yield ['search --format ', ['text', 'json']];
yield ['show --format ', ['text', 'json']];
yield ['info ', $installedPackages];
yield ['suggests ', $installedPackages];
yield ['update --prefer-install ', $preferInstall];
yield ['update ', $installedPackages];
}
/**
* @dataProvider getCommandSuggestions
*
* @param string $input The command that is typed
* @param string[]|null $expectedSuggestions Sample expected suggestions. Null if nothing is expected.
*/
public function testComplete(string $input, ?array $expectedSuggestions): void
{
$input = explode(' ', $input);
$commandName = array_shift($input);
$command = $this->getApplication()->get($commandName);
$tester = new CommandCompletionTester($command);
$suggestions = $tester->complete($input);
if (null === $expectedSuggestions) {
$this->assertEmpty($suggestions);
return;
}
$diff = array_diff($expectedSuggestions, $suggestions);
$this->assertEmpty($diff, sprintf('Suggestions must contain "%s". Got "%s".', implode('", "', $diff), implode('", "', $suggestions)));
}
private function getApplication(): Application
{
return new Application();
}
}