Improve package suggestions, show only vendors by default when showing all available packages, add support for -p/-a in show command
parent
1162629a17
commit
3b2745a00d
|
@ -13,7 +13,9 @@
|
|||
namespace Composer\Command;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Pcre\Preg;
|
||||
use Composer\Repository\CompositeRepository;
|
||||
use Composer\Repository\InstalledRepository;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
|
@ -46,9 +48,9 @@ trait CompletionTrait
|
|||
/**
|
||||
* Suggest package names from installed.
|
||||
*/
|
||||
private function suggestInstalledPackage(): \Closure
|
||||
private function suggestInstalledPackage(bool $includePlatformPackages = false): \Closure
|
||||
{
|
||||
return function (): array {
|
||||
return function (CompletionInput $input) use ($includePlatformPackages): array {
|
||||
$composer = $this->requireComposer();
|
||||
$installedRepos = [new RootPackageRepository(clone $composer->getPackage())];
|
||||
|
||||
|
@ -59,46 +61,113 @@ trait CompletionTrait
|
|||
$installedRepos[] = $composer->getRepositoryManager()->getLocalRepository();
|
||||
}
|
||||
|
||||
$platformHint = [];
|
||||
if ($includePlatformPackages) {
|
||||
if ($locker->isLocked()) {
|
||||
$platformRepo = new PlatformRepository(array(), $locker->getPlatformOverrides());
|
||||
} else {
|
||||
$platformRepo = new PlatformRepository(array(), $composer->getConfig()->get('platform') ?: array());
|
||||
}
|
||||
if ($input->getCompletionValue() === '') {
|
||||
// to reduce noise, when no text is yet entered we list only two entries for ext- and lib- prefixes
|
||||
$hintsToFind = ['ext-' => 0, 'lib-' => 0, 'php' => 99, 'composer' => 99];
|
||||
foreach ($platformRepo->getPackages() as $pkg) {
|
||||
foreach ($hintsToFind as $hintPrefix => $hintCount) {
|
||||
if (str_starts_with($pkg->getName(), $hintPrefix)) {
|
||||
if ($hintCount === 0 || $hintCount >= 99) {
|
||||
$platformHint[] = $pkg->getName();
|
||||
$hintsToFind[$hintPrefix]++;
|
||||
} elseif ($hintCount === 1) {
|
||||
unset($hintsToFind[$hintPrefix]);
|
||||
$platformHint[] = substr($pkg->getName(), 0, max(strlen($pkg->getName()) - 3, strlen($hintPrefix) + 1)).'...';
|
||||
}
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$installedRepos[] = $platformRepo;
|
||||
}
|
||||
}
|
||||
|
||||
$installedRepo = new InstalledRepository($installedRepos);
|
||||
|
||||
return array_map(function (PackageInterface $package) {
|
||||
return array_merge(
|
||||
array_map(function (PackageInterface $package) {
|
||||
return $package->getName();
|
||||
}, $installedRepo->getPackages());
|
||||
}, $installedRepo->getPackages()),
|
||||
$platformHint
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggest package names available on all configured repositories.
|
||||
* @todo rework to list packages from cache
|
||||
*/
|
||||
private function suggestAvailablePackage(): \Closure
|
||||
private function suggestAvailablePackage(int $max = 99): \Closure
|
||||
{
|
||||
return function (CompletionInput $input) {
|
||||
return function (CompletionInput $input) use ($max): array {
|
||||
if ($max < 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$composer = $this->requireComposer();
|
||||
$repos = new CompositeRepository($composer->getRepositoryManager()->getRepositories());
|
||||
|
||||
$packages = $repos->search('^' . preg_quote($input->getCompletionValue()), RepositoryInterface::SEARCH_NAME);
|
||||
$results = [];
|
||||
if (!str_contains($input->getCompletionValue(), '/')) {
|
||||
$results = $repos->search('^' . preg_quote($input->getCompletionValue()), RepositoryInterface::SEARCH_VENDOR);
|
||||
$vendors = true;
|
||||
}
|
||||
|
||||
return array_column(array_slice($packages, 0, 150), 'name');
|
||||
// if we get a single vendor, we expand it into its contents already
|
||||
if (\count($results) <= 1) {
|
||||
$results = $repos->search('^'.preg_quote($input->getCompletionValue()), RepositoryInterface::SEARCH_NAME);
|
||||
$vendors = false;
|
||||
}
|
||||
|
||||
$results = array_column(array_slice($results, 0, $max), 'name');
|
||||
if ($vendors) {
|
||||
$results = array_map(function (string $name): string {
|
||||
return $name.'/';
|
||||
}, $results);
|
||||
}
|
||||
|
||||
return $results;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggest package names available on all configured repositories or
|
||||
* ext- packages from the ones available on the currently-running PHP
|
||||
* platform packages from the ones available on the currently-running PHP
|
||||
*/
|
||||
private function suggestAvailablePackageOrExtension(): \Closure
|
||||
private function suggestAvailablePackageInclPlatform(): \Closure
|
||||
{
|
||||
return function (CompletionInput $input) {
|
||||
if (!str_starts_with($input->getCompletionValue(), 'ext-')) {
|
||||
return $this->suggestAvailablePackage()($input);
|
||||
return function (CompletionInput $input): array {
|
||||
if (Preg::isMatch('{^(ext|lib|php)(-|$)|^com}', $input->getCompletionValue())) {
|
||||
$matches = $this->suggestPlatformPackage()($input);
|
||||
} else {
|
||||
$matches = [];
|
||||
}
|
||||
|
||||
return array_merge($matches, $this->suggestAvailablePackage(99 - \count($matches))($input));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggest platform packages from the ones available on the currently-running PHP
|
||||
*/
|
||||
private function suggestPlatformPackage(): \Closure
|
||||
{
|
||||
return function (CompletionInput $input): array {
|
||||
$repos = new PlatformRepository([], $this->requireComposer()->getConfig()->get('platform') ?? []);
|
||||
|
||||
return array_map(function (PackageInterface $package) {
|
||||
$pattern = BasePackage::packageNameToRegexp($input->getCompletionValue().'*');
|
||||
return array_filter(array_map(function (PackageInterface $package) {
|
||||
return $package->getName();
|
||||
}, $repos->getPackages());
|
||||
}, $repos->getPackages()), function (string $name) use ($pattern): bool {
|
||||
return Preg::isMatch($pattern, $name);
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ class DependsCommand extends BaseDependencyCommand
|
|||
->setAliases(array('why'))
|
||||
->setDescription('Shows which packages cause the given package to be installed.')
|
||||
->setDefinition(array(
|
||||
new InputArgument(self::ARGUMENT_PACKAGE, InputArgument::REQUIRED, 'Package to inspect', null, $this->suggestInstalledPackage()),
|
||||
new InputArgument(self::ARGUMENT_PACKAGE, InputArgument::REQUIRED, 'Package to inspect', null, $this->suggestInstalledPackage(true)),
|
||||
new InputOption(self::OPTION_RECURSIVE, 'r', InputOption::VALUE_NONE, 'Recursively resolves up to the root package'),
|
||||
new InputOption(self::OPTION_TREE, 't', InputOption::VALUE_NONE, 'Prints the results as a nested tree'),
|
||||
))
|
||||
|
|
|
@ -59,8 +59,8 @@ class InitCommand extends BaseCommand
|
|||
new InputOption('author', null, InputOption::VALUE_REQUIRED, 'Author name of package'),
|
||||
new InputOption('type', null, InputOption::VALUE_OPTIONAL, 'Type of package (e.g. library, project, metapackage, composer-plugin)'),
|
||||
new InputOption('homepage', null, InputOption::VALUE_REQUIRED, 'Homepage of package'),
|
||||
new InputOption('require', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"', null, $this->suggestAvailablePackage()),
|
||||
new InputOption('require-dev', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"', null, $this->suggestAvailablePackage()),
|
||||
new InputOption('require', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"', null, $this->suggestAvailablePackageInclPlatform()),
|
||||
new InputOption('require-dev', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"', null, $this->suggestAvailablePackageInclPlatform()),
|
||||
new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum stability (empty or one of: '.implode(', ', array_keys(BasePackage::$stabilities)).')'),
|
||||
new InputOption('license', 'l', InputOption::VALUE_REQUIRED, 'License of package'),
|
||||
new InputOption('repository', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Add custom repositories, either by URL or using JSON arrays'),
|
||||
|
|
|
@ -43,7 +43,7 @@ class RemoveCommand extends BaseCommand
|
|||
->setName('remove')
|
||||
->setDescription('Removes a package from the require or require-dev.')
|
||||
->setDefinition(array(
|
||||
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Packages that should be removed.', null, $this->suggestInstalledPackage()),
|
||||
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Packages that should be removed.', null, $this->suggestInstalledPackage(true)),
|
||||
new InputOption('dev', null, InputOption::VALUE_NONE, 'Removes a package from the require-dev section.'),
|
||||
new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
|
||||
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
||||
|
|
|
@ -68,7 +68,7 @@ class RequireCommand extends BaseCommand
|
|||
->setName('require')
|
||||
->setDescription('Adds required packages to your composer.json and installs them.')
|
||||
->setDefinition(array(
|
||||
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Optional package name can also include a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"', null, $this->suggestAvailablePackageOrExtension()),
|
||||
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Optional package name can also include a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"', null, $this->suggestAvailablePackageInclPlatform()),
|
||||
new InputOption('dev', null, InputOption::VALUE_NONE, 'Add requirement to require-dev.'),
|
||||
new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
|
||||
new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
|
||||
|
|
|
@ -40,6 +40,7 @@ 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\Formatter\OutputFormatter;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Composer\Console\Input\InputArgument;
|
||||
|
@ -75,7 +76,7 @@ class ShowCommand extends BaseCommand
|
|||
->setAliases(array('info'))
|
||||
->setDescription('Shows information about packages.')
|
||||
->setDefinition(array(
|
||||
new InputArgument('package', InputArgument::OPTIONAL, 'Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.', null, $this->suggestInstalledPackage()),
|
||||
new InputArgument('package', InputArgument::OPTIONAL, 'Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.', null, $this->suggestPackageBasedOnMode()),
|
||||
new InputArgument('version', InputArgument::OPTIONAL, 'Version or version constraint to inspect'),
|
||||
new InputOption('all', null, InputOption::VALUE_NONE, 'List all packages'),
|
||||
new InputOption('locked', null, InputOption::VALUE_NONE, 'List all locked packages'),
|
||||
|
@ -109,6 +110,21 @@ EOT
|
|||
;
|
||||
}
|
||||
|
||||
protected function suggestPackageBasedOnMode(): \Closure
|
||||
{
|
||||
return function (CompletionInput $input) {
|
||||
if ($input->getOption('available') || $input->getOption('all')) {
|
||||
return $this->suggestAvailablePackageInclPlatform()($input);
|
||||
}
|
||||
|
||||
if ($input->getOption('platform')) {
|
||||
return $this->suggestPlatformPackage()($input);
|
||||
}
|
||||
|
||||
return $this->suggestInstalledPackage()($input);
|
||||
};
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->versionParser = new VersionParser;
|
||||
|
|
Loading…
Reference in New Issue