From 086a8b473fd2ee0e1cc70d4ec743ba7340b3b848 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 21 Oct 2021 12:13:19 +0200 Subject: [PATCH] [Console] Add completion to debug:translation command --- .../Command/TranslationDebugCommand.php | 76 +++++++++++++--- .../Resources/config/console.php | 1 + .../Command/TranslationDebugCommandTest.php | 86 ++++++++++++++++--- 3 files changed, 140 insertions(+), 23 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php index 95cf7bf7d9259..d56897d76029e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php @@ -12,6 +12,8 @@ namespace Symfony\Bundle\FrameworkBundle\Command; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -56,8 +58,9 @@ class TranslationDebugCommand extends Command private $defaultViewsPath; private $transPaths; private $codePaths; + private $enabledLocales; - public function __construct(TranslatorInterface $translator, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultTransPath = null, string $defaultViewsPath = null, array $transPaths = [], array $codePaths = []) + public function __construct(TranslatorInterface $translator, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultTransPath = null, string $defaultViewsPath = null, array $transPaths = [], array $codePaths = [], array $enabledLocales = []) { parent::__construct(); @@ -68,6 +71,7 @@ public function __construct(TranslatorInterface $translator, TranslationReaderIn $this->defaultViewsPath = $defaultViewsPath; $this->transPaths = $transPaths; $this->codePaths = $codePaths; + $this->enabledLocales = $enabledLocales; } /** @@ -135,15 +139,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $kernel = $this->getApplication()->getKernel(); // Define Root Paths - $transPaths = $this->transPaths; - if ($this->defaultTransPath) { - $transPaths[] = $this->defaultTransPath; - } - $codePaths = $this->codePaths; - $codePaths[] = $kernel->getProjectDir().'/src'; - if ($this->defaultViewsPath) { - $codePaths[] = $this->defaultViewsPath; - } + $transPaths = $this->getRootTransPaths(); + $codePaths = $this->getRootCodePaths($kernel); // Override with provided Bundle info if (null !== $input->getArgument('bundle')) { @@ -259,6 +256,44 @@ protected function execute(InputInterface $input, OutputInterface $output): int return $exitCode; } + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestArgumentValuesFor('locale')) { + $suggestions->suggestValues($this->enabledLocales); + + return; + } + + /** @var KernelInterface $kernel */ + $kernel = $this->getApplication()->getKernel(); + + if ($input->mustSuggestArgumentValuesFor('bundle')) { + $availableBundles = []; + foreach ($kernel->getBundles() as $bundle) { + $availableBundles[] = $bundle->getName(); + + if ($extension = $bundle->getContainerExtension()) { + $availableBundles[] = $extension->getAlias(); + } + } + + $suggestions->suggestValues($availableBundles); + + return; + } + + if ($input->mustSuggestOptionValuesFor('domain')) { + $locale = $input->getArgument('locale'); + + $mergeOperation = new MergeOperation( + $this->extractMessages($locale, $this->getRootCodePaths($kernel)), + $this->loadCurrentMessages($locale, $this->getRootTransPaths()) + ); + + $suggestions->suggestValues($mergeOperation->getDomains()); + } + } + private function formatState(int $state): string { if (self::MESSAGE_MISSING === $state) { @@ -354,4 +389,25 @@ private function loadFallbackCatalogues(string $locale, array $transPaths): arra return $fallbackCatalogues; } + + private function getRootTransPaths(): array + { + $transPaths = $this->transPaths; + if ($this->defaultTransPath) { + $transPaths[] = $this->defaultTransPath; + } + + return $transPaths; + } + + private function getRootCodePaths(KernelInterface $kernel): array + { + $codePaths = $this->codePaths; + $codePaths[] = $kernel->getProjectDir().'/src'; + if ($this->defaultViewsPath) { + $codePaths[] = $this->defaultViewsPath; + } + + return $codePaths; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php index a6ee7dc098015..e6dee5e7ab21e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php @@ -221,6 +221,7 @@ null, // twig.default_path [], // Translator paths [], // Twig paths + param('kernel.enabled_locales'), ]) ->tag('console.command') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php index 5cdf62e470066..d755e11e730af 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php @@ -14,6 +14,8 @@ use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand; use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\ExtensionWithoutConfigTestBundle\ExtensionWithoutConfigTestBundle; +use Symfony\Component\Console\Tester\CommandCompletionTester; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\Filesystem\Filesystem; @@ -139,7 +141,12 @@ protected function tearDown(): void $this->fs->remove($this->translationDir); } - private function createCommandTester($extractedMessages = [], $loadedMessages = [], $kernel = null, array $transPaths = [], array $codePaths = []): CommandTester + private function createCommandTester(array $extractedMessages = [], array $loadedMessages = [], KernelInterface $kernel = null, array $transPaths = [], array $codePaths = []): CommandTester + { + return new CommandTester($this->createCommand($extractedMessages, $loadedMessages, $kernel, $transPaths, $codePaths)); + } + + private function createCommand(array $extractedMessages = [], array $loadedMessages = [], KernelInterface $kernel = null, array $transPaths = [], array $codePaths = [], ExtractorInterface $extractor = null, array $bundles = [], array $enabledLocales = []): TranslationDebugCommand { $translator = $this->createMock(Translator::class); $translator @@ -147,15 +154,17 @@ private function createCommandTester($extractedMessages = [], $loadedMessages = ->method('getFallbackLocales') ->willReturn(['en']); - $extractor = $this->createMock(ExtractorInterface::class); - $extractor - ->expects($this->any()) - ->method('extract') - ->willReturnCallback( - function ($path, $catalogue) use ($extractedMessages) { - $catalogue->add($extractedMessages); - } - ); + if (!$extractor) { + $extractor = $this->createMock(ExtractorInterface::class); + $extractor + ->expects($this->any()) + ->method('extract') + ->willReturnCallback( + function ($path, $catalogue) use ($extractedMessages) { + $catalogue->add($extractedMessages); + } + ); + } $loader = $this->createMock(TranslationReader::class); $loader @@ -182,7 +191,7 @@ function ($path, $catalogue) use ($loadedMessages) { $kernel ->expects($this->any()) ->method('getBundles') - ->willReturn([]); + ->willReturn($bundles); $container = new Container(); $kernel @@ -190,12 +199,12 @@ function ($path, $catalogue) use ($loadedMessages) { ->method('getContainer') ->willReturn($container); - $command = new TranslationDebugCommand($translator, $loader, $extractor, $this->translationDir.'/translations', $this->translationDir.'/templates', $transPaths, $codePaths); + $command = new TranslationDebugCommand($translator, $loader, $extractor, $this->translationDir.'/translations', $this->translationDir.'/templates', $transPaths, $codePaths, $enabledLocales); $application = new Application($kernel); $application->add($command); - return new CommandTester($application->find('debug:translation')); + return $application->find('debug:translation'); } private function getBundle($path) @@ -209,4 +218,55 @@ private function getBundle($path) return $bundle; } + + /** + * @dataProvider provideCompletionSuggestions + */ + public function testComplete(array $input, array $expectedSuggestions) + { + $extractedMessagesWithDomains = [ + 'messages' => [ + 'foo' => 'foo', + ], + 'validators' => [ + 'foo' => 'foo', + ], + 'custom_domain' => [ + 'foo' => 'foo', + ], + ]; + $extractor = $this->createMock(ExtractorInterface::class); + $extractor + ->expects($this->any()) + ->method('extract') + ->willReturnCallback( + function ($path, $catalogue) use ($extractedMessagesWithDomains) { + foreach ($extractedMessagesWithDomains as $domain => $message) { + $catalogue->add($message, $domain); + } + } + ); + + $tester = new CommandCompletionTester($this->createCommand([], [], null, [], [], $extractor, [new ExtensionWithoutConfigTestBundle()], ['fr', 'nl'])); + $suggestions = $tester->complete($input); + $this->assertSame($expectedSuggestions, $suggestions); + } + + public function provideCompletionSuggestions() + { + yield 'locale' => [ + [''], + ['fr', 'nl'], + ]; + + yield 'bundle' => [ + ['fr', '--domain', 'messages', ''], + ['ExtensionWithoutConfigTestBundle', 'extension_without_config_test'], + ]; + + yield 'option --domain' => [ + ['en', '--domain', ''], + ['messages', 'validators', 'custom_domain'], + ]; + } }