diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php
index e51ba1a7c..b843a9c70 100644
--- a/src/Composer/Command/ConfigCommand.php
+++ b/src/Composer/Command/ConfigCommand.php
@@ -904,10 +904,19 @@ EOT
if ($showSource) {
$source = ' (' . $this->config->getSourceOfValue($k . $key) . ')';
}
- if (is_string($rawVal) && $rawVal != $value) {
- $io->write('[' . $k . $key . '] ' . $rawVal . ' (' . $value . ')' . $source, true, IOInterface::QUIET);
+
+ if (null !== $k && 0 === strpos($k, 'repositories')) {
+ $link = 'https://getcomposer.org/doc/05-repositories.md';
} else {
- $io->write('[' . $k . $key . '] ' . $value . '' . $source, true, IOInterface::QUIET);
+ $id = Preg::replace('{\..*$}', '', $k === '' || $k === null ? (string) $key : $k);
+ $id = Preg::replace('{[^a-z0-9]}i', '-', strtolower(trim($id)));
+ $id = Preg::replace('{-+}', '-', $id);
+ $link = 'https://getcomposer.org/doc/06-config.md#' . $id;
+ }
+ if (is_string($rawVal) && $rawVal != $value) {
+ $io->write('[' . $k . $key . '>] ' . $rawVal . ' (' . $value . ')' . $source, true, IOInterface::QUIET);
+ } else {
+ $io->write('[' . $k . $key . '>] ' . $value . '' . $source, true, IOInterface::QUIET);
}
}
}
diff --git a/src/Composer/Command/FundCommand.php b/src/Composer/Command/FundCommand.php
index 96c2c25d8..d8bccb620 100644
--- a/src/Composer/Command/FundCommand.php
+++ b/src/Composer/Command/FundCommand.php
@@ -19,6 +19,7 @@ use Composer\Package\CompletePackageInterface;
use Composer\Pcre\Preg;
use Composer\Repository\CompositeRepository;
use Composer\Semver\Constraint\MatchAllConstraint;
+use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@@ -116,7 +117,7 @@ class FundCommand extends BaseCommand
$prev = $line;
}
- $io->write(sprintf(' %s', $url));
+ $io->write(sprintf(' %s>', OutputFormatter::escape($url), $url));
}
}
diff --git a/src/Composer/Command/LicensesCommand.php b/src/Composer/Command/LicensesCommand.php
index 3c7c8265e..a50a6bae8 100644
--- a/src/Composer/Command/LicensesCommand.php
+++ b/src/Composer/Command/LicensesCommand.php
@@ -18,6 +18,8 @@ use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Package\PackageInterface;
use Composer\Repository\RepositoryInterface;
+use Composer\Util\PackageInfo;
+use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -89,8 +91,15 @@ EOT
$tableStyle->setCellRowContentFormat('%s ');
$table->setHeaders(array('Name', 'Version', 'License'));
foreach ($packages as $package) {
+ $link = PackageInfo::getViewSourceOrHomepageUrl($package);
+ if ($link !== null) {
+ $name = ''.$package->getPrettyName().'>';
+ } else {
+ $name = $package->getPrettyName();
+ }
+
$table->addRow(array(
- $package->getPrettyName(),
+ $name,
$package->getFullPrettyVersion(),
implode(', ', $package instanceof CompletePackageInterface ? $package->getLicense() : array()) ?: 'none',
));
diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php
index 285fd4221..04d5e549d 100644
--- a/src/Composer/Command/SearchCommand.php
+++ b/src/Composer/Command/SearchCommand.php
@@ -14,6 +14,7 @@ namespace Composer\Command;
use Composer\Factory;
use Composer\Json\JsonFile;
+use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
@@ -113,7 +114,12 @@ EOT
$description = substr($description, 0, $remaining - 3) . '...';
}
- $io->write(str_pad($result['name'], $nameLength, ' ') . $warning . $description);
+ $link = $result['url'] ?? null;
+ if ($link !== null) {
+ $io->write(''.$result['name'].'>'. str_repeat(' ', $nameLength - strlen($result['name'])) . $warning . $description);
+ } else {
+ $io->write(str_pad($result['name'], $nameLength, ' ') . $warning . $description);
+ }
}
} elseif ($format === 'json') {
$io->write(JsonFile::encode($results));
diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php
index 112f057d9..5f89c0d86 100644
--- a/src/Composer/Command/ShowCommand.php
+++ b/src/Composer/Command/ShowCommand.php
@@ -40,6 +40,8 @@ use Composer\Repository\RootPackageRepository;
use Composer\Semver\Constraint\ConstraintInterface;
use Composer\Semver\Semver;
use Composer\Spdx\SpdxLicenses;
+use Composer\Util\PackageInfo;
+use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -439,12 +441,8 @@ EOT
$packageViewData['name'] = $package->getPrettyName();
if ($format !== 'json' || true !== $input->getOption('name-only')) {
- $packageViewData['homepage'] = $package->getHomepage();
- if (isset($package->getSupport()['source'])) {
- $packageViewData['source'] = $package->getSupport()['source'];
- } elseif (null !== $package->getSourceUrl()) {
- $packageViewData['source'] = $package->getSourceUrl();
- }
+ $packageViewData['homepage'] = $package instanceof CompletePackageInterface ? $package->getHomepage() : null;
+ $packageViewData['source'] = PackageInfo::getViewSourceUrl($package);
}
$nameLength = max($nameLength, strlen($package->getPrettyName()));
if ($writeVersion) {
@@ -538,15 +536,15 @@ EOT
foreach ($packages as $package) {
$link = $package['source'] ?? $package['homepage'] ?? '';
- if ($link) {
- $io->write($indent . '', '', $link).'>'.$package['name'].'>'. str_repeat(' ', $nameLength - strlen($package['name'])), false);
+ if ($link !== '') {
+ $io->write($indent . ''.$package['name'].'>'. str_repeat(' ', $nameLength - strlen($package['name'])), false);
} else {
$io->write($indent . str_pad($package['name'], $nameLength, ' '), false);
}
if (isset($package['version']) && $writeVersion) {
$io->write(' ' . str_pad($package['version'], $versionLength, ' '), false);
}
- if (isset($package['latest']) && $writeLatest) {
+ if (isset($package['latest']) && isset($package['latest-status']) && $writeLatest) {
$latestVersion = $package['latest'];
$updateStatus = $package['latest-status'];
$style = $this->updateStatusToVersionStyle($updateStatus);
diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php
index 7972e9417..9debf589d 100644
--- a/src/Composer/Repository/RepositoryInterface.php
+++ b/src/Composer/Repository/RepositoryInterface.php
@@ -91,7 +91,7 @@ interface RepositoryInterface extends \Countable
* @param string $type The type of package to search for. Defaults to all types of packages
*
* @return array[] an array of array('name' => '...', 'description' => '...'|null, 'abandoned' => 'string'|true|unset) For SEARCH_VENDOR the name will be in "vendor" form
- * @phpstan-return list
+ * @phpstan-return list
*/
public function search($query, $mode = 0, $type = null);
diff --git a/src/Composer/Util/PackageInfo.php b/src/Composer/Util/PackageInfo.php
new file mode 100644
index 000000000..84277c731
--- /dev/null
+++ b/src/Composer/Util/PackageInfo.php
@@ -0,0 +1,33 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Util;
+
+use Composer\Package\CompletePackageInterface;
+use Composer\Package\PackageInterface;
+
+class PackageInfo
+{
+ public static function getViewSourceUrl(PackageInterface $package): ?string
+ {
+ if ($package instanceof CompletePackageInterface && isset($package->getSupport()['source'])) {
+ return $package->getSupport()['source'];
+ }
+
+ return $package->getSourceUrl();
+ }
+
+ public static function getViewSourceOrHomepageUrl(PackageInterface $package): ?string
+ {
+ return self::getViewSourceUrl($package) ?? ($package instanceof CompletePackageInterface ? $package->getHomepage() : null);
+ }
+}