Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9951124

Browse filesBrowse files
committed
[Console] Add completion values to input definition
1 parent a037eca commit 9951124
Copy full SHA for 9951124

File tree

Expand file treeCollapse file tree

12 files changed

+216
-62
lines changed
Filter options
Expand file treeCollapse file tree

12 files changed

+216
-62
lines changed

‎UPGRADE-6.1.md

Copy file name to clipboardExpand all lines: UPGRADE-6.1.md
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Console
1212
-------
1313

1414
* Deprecate `Command::$defaultName` and `Command::$defaultDescription`, use the `AsCommand` attribute instead
15+
* Add argument `$suggestedValues` to `Command::addArgument` and `Command::addOption`
16+
* Add argument `$suggestedValues` to `InputArgument` and `InputOption` constructors
1517

1618
HttpKernel
1719
----------

‎src/Symfony/Component/Console/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* Add method `__toString()` to `InputInterface`
88
* Deprecate `Command::$defaultName` and `Command::$defaultDescription`, use the `AsCommand` attribute instead
9+
* Add suggested values for arguments and options in input definition, for input completion
910

1011
6.0
1112
---

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Command/Command.php
+25-8Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Console\Attribute\AsCommand;
1616
use Symfony\Component\Console\Completion\CompletionInput;
1717
use Symfony\Component\Console\Completion\CompletionSuggestions;
18+
use Symfony\Component\Console\Completion\Suggestion;
1819
use Symfony\Component\Console\Exception\ExceptionInterface;
1920
use Symfony\Component\Console\Exception\InvalidArgumentException;
2021
use Symfony\Component\Console\Exception\LogicException;
@@ -319,6 +320,12 @@ public function run(InputInterface $input, OutputInterface $output): int
319320
*/
320321
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
321322
{
323+
$definition = $this->getDefinition();
324+
if (CompletionInput::TYPE_OPTION_VALUE === $input->getCompletionType() && $definition->hasOption($input->getCompletionName())) {
325+
$definition->getOption($input->getCompletionName())->complete($input, $suggestions);
326+
} elseif (CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType() && $definition->hasArgument($input->getCompletionName())) {
327+
$definition->getArgument($input->getCompletionName())->complete($input, $suggestions);
328+
}
322329
}
323330

324331
/**
@@ -427,17 +434,22 @@ public function getNativeDefinition(): InputDefinition
427434
/**
428435
* Adds an argument.
429436
*
430-
* @param int|null $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
431-
* @param mixed $default The default value (for InputArgument::OPTIONAL mode only)
437+
* @param $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
438+
* @param $default The default value (for InputArgument::OPTIONAL mode only)
439+
* @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
432440
*
433441
* @throws InvalidArgumentException When argument mode is not valid
434442
*
435443
* @return $this
436444
*/
437-
public function addArgument(string $name, int $mode = null, string $description = '', mixed $default = null): static
445+
public function addArgument(string $name, int $mode = null, string $description = '', mixed $default = null, /*array|\Closure $suggestedValues = null*/): static
438446
{
439-
$this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
440-
$this->fullDefinition?->addArgument(new InputArgument($name, $mode, $description, $default));
447+
$suggestedValues = 5 <= \func_num_args() ? func_get_arg(4) : [];
448+
if (!\is_array($suggestedValues) && !$suggestedValues instanceof \Closure) {
449+
throw new \TypeError(sprintf('Argument 5 passed to "%s()" must be array or \Closure, "%s" given.', __METHOD__, get_debug_type($suggestedValues)));
450+
}
451+
$this->definition->addArgument(new InputArgument($name, $mode, $description, $default, $suggestedValues));
452+
$this->fullDefinition?->addArgument(new InputArgument($name, $mode, $description, $default, $suggestedValues));
441453

442454
return $this;
443455
}
@@ -448,15 +460,20 @@ public function addArgument(string $name, int $mode = null, string $description
448460
* @param $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
449461
* @param $mode The option mode: One of the InputOption::VALUE_* constants
450462
* @param $default The default value (must be null for InputOption::VALUE_NONE)
463+
* @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
451464
*
452465
* @throws InvalidArgumentException If option mode is invalid or incompatible
453466
*
454467
* @return $this
455468
*/
456-
public function addOption(string $name, string|array $shortcut = null, int $mode = null, string $description = '', mixed $default = null): static
469+
public function addOption(string $name, string|array $shortcut = null, int $mode = null, string $description = '', mixed $default = null, /*array|\Closure $suggestedValues = []*/): static
457470
{
458-
$this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
459-
$this->fullDefinition?->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
471+
$suggestedValues = 6 <= \func_num_args() ? func_get_arg(5) : [];
472+
if (!\is_array($suggestedValues) && !$suggestedValues instanceof \Closure) {
473+
throw new \TypeError(sprintf('Argument 5 passed to "%s()" must be array or \Closure, "%s" given.', __METHOD__, get_debug_type($suggestedValues)));
474+
}
475+
$this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default, $suggestedValues));
476+
$this->fullDefinition?->addOption(new InputOption($name, $shortcut, $mode, $description, $default, $suggestedValues));
460477

461478
return $this;
462479
}

‎src/Symfony/Component/Console/Command/DumpCompletionCommand.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Command/DumpCompletionCommand.php
+3-10Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
namespace Symfony\Component\Console\Command;
1313

1414
use Symfony\Component\Console\Attribute\AsCommand;
15-
use Symfony\Component\Console\Completion\CompletionInput;
16-
use Symfony\Component\Console\Completion\CompletionSuggestions;
1715
use Symfony\Component\Console\Input\InputArgument;
1816
use Symfony\Component\Console\Input\InputInterface;
1917
use Symfony\Component\Console\Input\InputOption;
@@ -39,12 +37,7 @@ final class DumpCompletionCommand extends Command
3937
*/
4038
protected static $defaultDescription = 'Dump the shell completion script';
4139

42-
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
43-
{
44-
if ($input->mustSuggestArgumentValuesFor('shell')) {
45-
$suggestions->suggestValues($this->getSupportedShells());
46-
}
47-
}
40+
private array $supportedShells;
4841

4942
protected function configure()
5043
{
@@ -82,7 +75,7 @@ protected function configure()
8275
<info>eval "$(${fullCommand} completion bash)"</>
8376
EOH
8477
)
85-
->addArgument('shell', InputArgument::OPTIONAL, 'The shell type (e.g. "bash"), the value of the "$SHELL" env var will be used if this is not given')
78+
->addArgument('shell', InputArgument::OPTIONAL, 'The shell type (e.g. "bash"), the value of the "$SHELL" env var will be used if this is not given', null, $this->getSupportedShells(...))
8679
->addOption('debug', null, InputOption::VALUE_NONE, 'Tail the completion debug log')
8780
;
8881
}
@@ -135,7 +128,7 @@ private function tailDebugLog(string $commandName, OutputInterface $output): voi
135128
*/
136129
private function getSupportedShells(): array
137130
{
138-
return array_map(function ($f) {
131+
return $this->supportedShells ??= array_map(function ($f) {
139132
return pathinfo($f, \PATHINFO_EXTENSION);
140133
}, glob(__DIR__.'/../Resources/completion.*'));
141134
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Command/HelpCommand.php
+6-19Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
namespace Symfony\Component\Console\Command;
1313

14-
use Symfony\Component\Console\Completion\CompletionInput;
15-
use Symfony\Component\Console\Completion\CompletionSuggestions;
1614
use Symfony\Component\Console\Descriptor\ApplicationDescription;
1715
use Symfony\Component\Console\Helper\DescriptorHelper;
1816
use Symfony\Component\Console\Input\InputArgument;
@@ -39,8 +37,12 @@ protected function configure()
3937
$this
4038
->setName('help')
4139
->setDefinition([
42-
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
43-
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
40+
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help', function () {
41+
return array_keys((new ApplicationDescription($this->getApplication()))->getCommands());
42+
}),
43+
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', function () {
44+
return (new DescriptorHelper())->getFormats();
45+
}),
4446
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
4547
])
4648
->setDescription('Display help for a command')
@@ -81,19 +83,4 @@ protected function execute(InputInterface $input, OutputInterface $output): int
8183

8284
return 0;
8385
}
84-
85-
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
86-
{
87-
if ($input->mustSuggestArgumentValuesFor('command_name')) {
88-
$descriptor = new ApplicationDescription($this->getApplication());
89-
$suggestions->suggestValues(array_keys($descriptor->getCommands()));
90-
91-
return;
92-
}
93-
94-
if ($input->mustSuggestOptionValuesFor('format')) {
95-
$helper = new DescriptorHelper();
96-
$suggestions->suggestValues($helper->getFormats());
97-
}
98-
}
9986
}

‎src/Symfony/Component/Console/Command/LazyCommand.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Command/LazyCommand.php
+17-4Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Console\Application;
1515
use Symfony\Component\Console\Completion\CompletionInput;
1616
use Symfony\Component\Console\Completion\CompletionSuggestions;
17+
use Symfony\Component\Console\Completion\Suggestion;
1718
use Symfony\Component\Console\Helper\HelperSet;
1819
use Symfony\Component\Console\Input\InputDefinition;
1920
use Symfony\Component\Console\Input\InputInterface;
@@ -108,16 +109,28 @@ public function getNativeDefinition(): InputDefinition
108109
return $this->getCommand()->getNativeDefinition();
109110
}
110111

111-
public function addArgument(string $name, int $mode = null, string $description = '', mixed $default = null): static
112+
/**
113+
* {@inheritdoc}
114+
*
115+
* @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
116+
*/
117+
public function addArgument(string $name, int $mode = null, string $description = '', mixed $default = null, /*array|\Closure $suggestedValues = []*/): static
112118
{
113-
$this->getCommand()->addArgument($name, $mode, $description, $default);
119+
$suggestedValues = 5 <= \func_num_args() ? func_get_arg(4) : [];
120+
$this->getCommand()->addArgument($name, $mode, $description, $default, $suggestedValues);
114121

115122
return $this;
116123
}
117124

118-
public function addOption(string $name, string|array $shortcut = null, int $mode = null, string $description = '', mixed $default = null): static
125+
/**
126+
* {@inheritdoc}
127+
*
128+
* @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
129+
*/
130+
public function addOption(string $name, string|array $shortcut = null, int $mode = null, string $description = '', mixed $default = null, /*array|\Closure $suggestedValues = []*/): static
119131
{
120-
$this->getCommand()->addOption($name, $shortcut, $mode, $description, $default);
132+
$suggestedValues = 6 <= \func_num_args() ? func_get_arg(5) : [];
133+
$this->getCommand()->addOption($name, $shortcut, $mode, $description, $default, $suggestedValues);
121134

122135
return $this;
123136
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Command/ListCommand.php
+6-19Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
namespace Symfony\Component\Console\Command;
1313

14-
use Symfony\Component\Console\Completion\CompletionInput;
15-
use Symfony\Component\Console\Completion\CompletionSuggestions;
1614
use Symfony\Component\Console\Descriptor\ApplicationDescription;
1715
use Symfony\Component\Console\Helper\DescriptorHelper;
1816
use Symfony\Component\Console\Input\InputArgument;
@@ -35,9 +33,13 @@ protected function configure()
3533
$this
3634
->setName('list')
3735
->setDefinition([
38-
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
36+
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name', null, function () {
37+
return array_keys((new ApplicationDescription($this->getApplication()))->getNamespaces());
38+
}),
3939
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
40-
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
40+
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', function () {
41+
return (new DescriptorHelper())->getFormats();
42+
}),
4143
new InputOption('short', null, InputOption::VALUE_NONE, 'To skip describing commands\' arguments'),
4244
])
4345
->setDescription('List commands')
@@ -77,19 +79,4 @@ protected function execute(InputInterface $input, OutputInterface $output): int
7779

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

‎src/Symfony/Component/Console/Input/InputArgument.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Input/InputArgument.php
+29-1Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
namespace Symfony\Component\Console\Input;
1313

14+
use Symfony\Component\Console\Command\Command;
15+
use Symfony\Component\Console\Completion\CompletionInput;
16+
use Symfony\Component\Console\Completion\CompletionSuggestions;
17+
use Symfony\Component\Console\Completion\Suggestion;
1418
use Symfony\Component\Console\Exception\InvalidArgumentException;
1519
use Symfony\Component\Console\Exception\LogicException;
1620

@@ -28,17 +32,19 @@ class InputArgument
2832
private string $name;
2933
private int $mode;
3034
private string|int|bool|array|null|float $default;
35+
private array|\Closure $suggestedValues;
3136
private string $description;
3237

3338
/**
3439
* @param string $name The argument name
3540
* @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL
3641
* @param string $description A description text
3742
* @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only)
43+
* @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
3844
*
3945
* @throws InvalidArgumentException When argument mode is not valid
4046
*/
41-
public function __construct(string $name, int $mode = null, string $description = '', string|bool|int|float|array $default = null)
47+
public function __construct(string $name, int $mode = null, string $description = '', string|bool|int|float|array $default = null, \Closure|array $suggestedValues = [])
4248
{
4349
if (null === $mode) {
4450
$mode = self::OPTIONAL;
@@ -49,6 +55,7 @@ public function __construct(string $name, int $mode = null, string $description
4955
$this->name = $name;
5056
$this->mode = $mode;
5157
$this->description = $description;
58+
$this->suggestedValues = $suggestedValues;
5259

5360
$this->setDefault($default);
5461
}
@@ -111,6 +118,27 @@ public function getDefault(): string|bool|int|float|array|null
111118
return $this->default;
112119
}
113120

121+
public function hasCompletion(): bool
122+
{
123+
return [] !== $this->suggestedValues;
124+
}
125+
126+
/**
127+
* Adds suggestions to $suggestions for the current completion input.
128+
*
129+
* @see Command::complete()
130+
*/
131+
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
132+
{
133+
$values = $this->suggestedValues;
134+
if ($values instanceof \Closure && !\is_array($values = $values($input))) {
135+
throw new LogicException(sprintf('Closure for argument "%s" must return an array. Got "%s".', $this->name, get_debug_type($values)));
136+
}
137+
if ($values) {
138+
$suggestions->suggestValues($values);
139+
}
140+
}
141+
114142
/**
115143
* Returns the description text.
116144
*/

‎src/Symfony/Component/Console/Input/InputOption.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Input/InputOption.php
+32-1Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
namespace Symfony\Component\Console\Input;
1313

14+
use Symfony\Component\Console\Command\Command;
15+
use Symfony\Component\Console\Completion\CompletionInput;
16+
use Symfony\Component\Console\Completion\CompletionSuggestions;
17+
use Symfony\Component\Console\Completion\Suggestion;
1418
use Symfony\Component\Console\Exception\InvalidArgumentException;
1519
use Symfony\Component\Console\Exception\LogicException;
1620

@@ -50,16 +54,18 @@ class InputOption
5054
private string|array|null $shortcut;
5155
private int $mode;
5256
private string|int|bool|array|null|float $default;
57+
private array|\Closure $suggestedValues;
5358
private string $description;
5459

5560
/**
5661
* @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
5762
* @param int|null $mode The option mode: One of the VALUE_* constants
5863
* @param string|bool|int|float|array|null $default The default value (must be null for self::VALUE_NONE)
64+
* @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
5965
*
6066
* @throws InvalidArgumentException If option mode is invalid or incompatible
6167
*/
62-
public function __construct(string $name, string|array $shortcut = null, int $mode = null, string $description = '', string|bool|int|float|array $default = null)
68+
public function __construct(string $name, string|array $shortcut = null, int $mode = null, string $description = '', string|bool|int|float|array $default = null, array|\Closure $suggestedValues = [])
6369
{
6470
if (str_starts_with($name, '--')) {
6571
$name = substr($name, 2);
@@ -96,7 +102,11 @@ public function __construct(string $name, string|array $shortcut = null, int $mo
96102
$this->shortcut = $shortcut;
97103
$this->mode = $mode;
98104
$this->description = $description;
105+
$this->suggestedValues = $suggestedValues;
99106

107+
if ($suggestedValues && !$this->acceptValue()) {
108+
throw new LogicException('Cannot set suggested values if the option does not accept a value.');
109+
}
100110
if ($this->isArray() && !$this->acceptValue()) {
101111
throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
102112
}
@@ -201,6 +211,27 @@ public function getDescription(): string
201211
return $this->description;
202212
}
203213

214+
public function hasCompletion(): bool
215+
{
216+
return [] !== $this->suggestedValues;
217+
}
218+
219+
/**
220+
* Adds suggestions to $suggestions for the current completion input.
221+
*
222+
* @see Command::complete()
223+
*/
224+
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
225+
{
226+
$values = $this->suggestedValues;
227+
if ($values instanceof \Closure && !\is_array($values = $values($input))) {
228+
throw new LogicException(sprintf('Closure for option "%s" must return an array. Got "%s".', $this->name, get_debug_type($values)));
229+
}
230+
if ($values) {
231+
$suggestions->suggestValues($values);
232+
}
233+
}
234+
204235
/**
205236
* Checks whether the given option equals this one.
206237
*/

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.