8000 feature #43596 [Console] Add completion to help & list commands (Grom… · symfony/symfony@e3e5946 · GitHub
[go: up one dir, main page]

Skip to content

Commit e3e5946

Browse files
committed
feature #43596 [Console] Add completion to help & list commands (GromNaN)
This PR was squashed before being merged into the 5.4 branch. Discussion ---------- [Console] Add completion to help & list commands | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | #43594 | License | MIT | Doc PR | no - Add completion for commands `help` and `list` - Create a tester class to wrap completion code in tests - To autocomplete option `--format`, supported formats are exposed by `DescriptorHelper` Commits ------- dc94c72 [Console] Add completion to help & list commands
2 parents 1562c98 + dc94c72 commit e3e5946

File tree

7 files changed

+200
-2
lines changed

7 files changed

+200
-2
lines changed

src/Symfony/Component/Console/Command/HelpCommand.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
namespace Symfony\Component\Console\Command;
1313

14+
use Symfony\Component\Console\Completion\CompletionInput;
15+
use Symfony\Component\Console\Completion\CompletionInterface;
16+
use Symfony\Component\Console\Completion\CompletionSuggestions;
17+
use Symfony\Component\Console\Descriptor\ApplicationDescription;
1418
use Symfony\Component\Console\Helper\DescriptorHelper;
1519
use Symfony\Component\Console\Input\InputArgument;
1620
use Symfony\Component\Console\Input\InputInterface;
@@ -22,7 +26,7 @@
2226
*
2327
* @author Fabien Potencier <fabien@symfony.com>
2428
*/
25-
class HelpCommand extends Command
29+
class HelpCommand extends Command implements CompletionInterface
2630
{
2731
private $command;
2832

@@ -80,4 +84,19 @@ protected function execute(InputInterface $input, OutputInterface $output)
8084

8185
return 0;
8286
}
87+
88+
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
89+
{
90+
if ($input->mustSuggestArgumentValuesFor('command_name')) {
91+
$descriptor = new ApplicationDescription($this->getApplication());
92+
$suggestions->suggestValues(array_keys($descriptor->getCommands()));
93+
94+
return;
95+
}
96+
97+
if ($input->mustSuggestOptionValuesFor('format')) {
98+
$helper = new DescriptorHelper();
99+
$suggestions->suggestValues($helper->getFormats());
100+
}
101+
}
83102
}

src/Symfony/Component/Console/Command/ListCommand.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
namespace Symfony\Component\Console\Command;
1313

14+
use Symfony\Component\Console\Completion\CompletionInput;
15+
use Symfony\Component\Console\Completion\CompletionInterface;
16+
use Symfony\Component\Console\Completion\CompletionSuggestions;
17+
use Symfony\Component\Console\Descriptor\ApplicationDescription;
1418
use Symfony\Component\Console\Helper\DescriptorHelper;
1519
use Symfony\Component\Console\Input\InputArgument;
1620
use Symfony\Component\Console\Input\InputInterface;
@@ -22,7 +26,7 @@
2226
*
2327
* @author Fabien Potencier <fabien@symfony.com>
2428 */
25-
class ListCommand extends Command
29+
class ListCommand extends Command implements CompletionInterface
2630
{
2731
/**
2832
* {@inheritdoc}
@@ -74,4 +78,19 @@ protected function execute(InputInterface $input, OutputInterface $output)
7478

7579
return 0;
7680
}
81+
82+
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
83+
{
84+
if ($input->mustSuggestArgumentValuesFor('namespace')) {
85+
$descriptor = new ApplicationDescription($this->getApplication());
86+
$suggestions->suggestValues(array_keys($descriptor->getNamespaces()));
87+
88+
return;
89+
}
90+
91+
if ($input->mustSuggestOptionValuesFor('format')) {
92+
$helper = new DescriptorHelper();
93+
$suggestions->suggestValues($helper->getFormats());
94+
}
95+
}
7796
}

src/Symfony/Component/Console/Helper/DescriptorHelper.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,9 @@ public function getName()
8484
{
8585
return 'descriptor';
8686
}
87+
88+
public function getFormats(): array
89+
{
90+
return array_keys($this->descriptors);
91+
}
8792
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Console\Tester;
13+
14+
use Symfony\Component\Console\Command\Command;
15+
use Symfony\Component\Console\Completion\CompletionInput;
16+
use Symfony\Component\Console\Completion\CompletionInterface;
17+
use Symfony\Component\Console\Completion\CompletionSuggestions;
18+
19+
/**
20+
* Eases the testing of command completion.
21+
*
22+
* @author Jérôme Tamarelle <jerome@tamarelle.net>
23+
*/
24+
class CommandCompletionTester
25+
{
26+
private $command;
27+
28+
public function __construct(Command $command)
29+
{
30+
$this->command = $command;
31+
}
32+
33+
/**
34+
* Create completion suggestions from input tokens.
35+
*/
36+
public function complete(array $input): array
37+
{
38+
if (!$this->command instanceof CompletionInterface) {
39+
throw new \LogicException(sprintf('Command "%s" must implement "%s" to support completion.', \get_class($this->command), CompletionInput::class));
40+
}
41+
42+
$currentIndex = \count($input);
43+
if ('' === end($input)) {
44+
array_pop($input);
45+
}
46+
array_unshift($input, $this->command->getName());
47+
48+
$completionInput = CompletionInput::fromTokens($input, $currentIndex);
49+
$completionInput->bind($this->command->getDefinition());
50+
$suggestions = new CompletionSuggestions();
51+
52+
$this->command->complete($completionInput, $suggestions);
53+
54+
$options = [];
55+
foreach ($suggestions->getOptionSuggestions() as $option) {
56+
$options[] = '--'.$option->getName();
57+
}
58+
59+
return array_merge($options, $suggestions->getValueSuggestions());
60+
}
61+
}

src/Symfony/Component/Console/Tests/Command/HelpCommandTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Console\Application;
1616
use Symfony\Component\Console\Command\HelpCommand;
1717
use Symfony\Component\Console\Command\ListCommand;
18+
use Symfony\Component\Console\Tester\CommandCompletionTester;
1819
use Symfony\Component\Console\Tester\CommandTester;
1920

2021
class HelpCommandTest extends TestCase
@@ -68,4 +69,35 @@ public function testExecuteForApplicationCommandWithXmlOption()
6869
$this->assertStringContainsString('list [--raw] [--format FORMAT] [--short] [--] [&lt;namespace&gt;]', $commandTester->getDisplay(), '->execute() returns a text help for the given command');
6970
$this->assertStringContainsString('<command', $commandTester->getDisplay(), '->execute() returns an XML help text if --format=xml is passed');
7071
}
72+
73+
/**
74+
* @dataProvider provideCompletionSuggestions
75+
*/
76+
public function testComplete(array $input, array $expectedSuggestions)
77+
{
78+
require_once realpath(__DIR__.'/../Fixtures/FooCommand.php');
79+
$application = new Application();
80+
$application->add(new \FooCommand());
81+
$tester = new CommandCompletionTester($application->get('help'));
82+
$suggestions = $tester->complete($input, 2);
83+
$this->assertSame($expectedSuggestions, $suggestions);
84+
}
85+
86+
public function provideCompletionSuggestions()
87+
{
88+
yield 'option --format' => [
89+
['--format', ''],
90+
['txt', 'xml', 'json', 'md'],
91+
];
92+
93+
yield 'nothing' => [
94+
[''],
95+
[],
96+
];
97+
98+
yield 'command_name' => [
99+
['f'],
100+
['completion', 'help', 'list', 'foo:bar'],
101+
];
102+
}
71103
}

src/Symfony/Component/Console/Tests/Command/ListCommandTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Console\Application;
16+
use Symfony\Component\Console\Tester\CommandCompletionTester;
1617
use Symfony\Component\Console\Tester\CommandTester;
1718

1819
class ListCommandTest extends TestCase
@@ -112,4 +113,35 @@ public function testExecuteListsCommandsOrderRaw()
112113

113114
$this->assertEquals($output, trim($commandTester->getDisplay(true)));
114115
}
116+
117+
/**
118+
* @dataProvider provideCompletionSuggestions
119+
*/
120+
public function testComplete(array $input, array $expectedSuggestions)
121+
{
122+
require_once realpath(__DIR__.'/../Fixtures/FooCommand.php');
123+
$application = new Application();
124+
$application->add(new \FooCommand());
125+
$tester = new CommandCompletionTester($application->get('list'));
126+
$suggestions = $tester->complete($input, 2);
127+
$this->assertSame($expectedSuggestions, $suggestions);
128+
}
129+
130+
public function provideCompletionSuggestions()
131+
{
132+
yield 'option --format' => [
133+
['--format', ''],
134+
['txt', 'xml', 'json', 'md'],
135+
];
136+
137+
yield 'namespace' => [
138+
[''],
139+
['_global', 'foo'],
140+
];
141+
142+
yield 'namespace started' => [
143+
['f'],
144+
['_global', 'foo'],
145+
];
146+
}
115147
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Console\Tests\Helper;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Console\Helper\DescriptorHelper;
16+
17+
class DescriptorHelperTest extends TestCase
18+
{
19+
public function testGetFormats()
20+
{
21+
$helper = new DescriptorHelper();
22+
$expectedFormats = [
23+
'txt',
24+
'xml',
25+
'json',
26+
'md',
27+
];
28+
$this->assertSame($expectedFormats, $helper->getFormats());
29+
}
30+
}

0 commit comments

Comments
 (0)
0