Skip to content

Commit

Permalink
Implements Option/ArgumentInput::complete() for openess
Browse files Browse the repository at this point in the history
  • Loading branch information
GromNaN committed Mar 16, 2022
1 parent 373785a commit 091027a
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 43 deletions.
4 changes: 2 additions & 2 deletions src/Symfony/Component/Console/Command/Command.php
Expand Up @@ -321,9 +321,9 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti
{
$definition = $this->getDefinition();
if (CompletionInput::TYPE_OPTION_VALUE === $input->getCompletionType() && $definition->hasOption($input->getCompletionName())) {
$suggestions->suggestValues($definition->getOption($input->getCompletionName())->getSuggestedValues($input));
$definition->getOption($input->getCompletionName())->complete($input, $suggestions);
} elseif (CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType() && $definition->hasArgument($input->getCompletionName())) {
$suggestions->suggestValues($definition->getArgument($input->getCompletionName())->getSuggestedValues($input));
$definition->getArgument($input->getCompletionName())->complete($input, $suggestions);
}
}

Expand Down
19 changes: 14 additions & 5 deletions src/Symfony/Component/Console/Input/InputArgument.php
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;

Expand Down Expand Up @@ -115,17 +116,25 @@ public function getDefault(): string|bool|int|float|array|null
return $this->default;
}

public function hasCompletion(): bool
{
return [] !== $this->suggestedValues;
}

/**
* Returns suggested values for input completion.
* Adds suggestions to $suggestions for the current completion input.
*
* @see Command::complete()
*/
public function getSuggestedValues(CompletionInput $input): array
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
$values = $this->suggestedValues;
if ($values instanceof \Closure && !\is_array($values = $values($input))) {
throw new LogicException(sprintf('Closure for argument "%s" must return an array. Got "%s".', $this->name, get_debug_type($values)));
throw new LogicException(sprintf('Closure for argument "%s" must return an array or null. Got "%s".', $this->name, get_debug_type($values)));
}
if ($values) {
$suggestions->suggestValues($values);
}

return $values;
}

/**
Expand Down
30 changes: 20 additions & 10 deletions src/Symfony/Component/Console/Input/InputOption.php
Expand Up @@ -11,7 +11,9 @@

namespace Symfony\Component\Console\Input;

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\Exception\LogicException;

Expand Down Expand Up @@ -201,24 +203,32 @@ public function getDefault(): string|bool|int|float|array|null
}

/**
* Returns suggested values for input completion.
* Returns the description text.
*/
public function getSuggestedValues(CompletionInput $input): array
public function getDescription(): string
{
$values = $this->suggestedValues;
if ($values instanceof \Closure && !\is_array($values = $values($input))) {
throw new LogicException(sprintf('Closure for option "%s" must return an array. Got "%s".', $this->name, get_debug_type($values)));
}
return $this->description;
}

return $values;
public function hasCompletion(): bool
{
return [] !== $this->suggestedValues;
}

/**
* Returns the description text.
* Adds suggestions to $suggestions for the current completion input.
*
* @see Command::complete()
*/
public function getDescription(): string
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
return $this->description;
$values = $this->suggestedValues;
if ($values instanceof \Closure && !\is_array($values = $values($input))) {
throw new LogicException(sprintf('Closure for option "%s" must return an array or null. Got "%s".', $this->name, get_debug_type($values)));
}
if ($values) {
$suggestions->suggestValues($values);
}
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/Symfony/Component/Console/Tests/Command/CommandTest.php
Expand Up @@ -17,6 +17,7 @@
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Input\InputArgument;
Expand Down Expand Up @@ -96,7 +97,7 @@ public function testAddArgumentFull()
$argument = $command->getDefinition()->getArgument('foo');
$this->assertSame('Description', $argument->getDescription());
$this->assertSame('default', $argument->getDefault());
$this->assertSame(['a', 'b'], $argument->getSuggestedValues(new CompletionInput()));
$this->assertTrue($argument->hasCompletion());
}

public function testAddOption()
Expand All @@ -115,7 +116,7 @@ public function testAddOptionFull()
$this->assertSame('f', $option->getShortcut());
$this->assertSame('Description', $option->getDescription());
$this->assertSame('default', $option->getDefault());
$this->assertSame(['a', 'b'], $option->getSuggestedValues(new CompletionInput()));
$this->assertTrue($option->hasCompletion());
}

public function testSetHidden()
Expand Down
30 changes: 21 additions & 9 deletions src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php
Expand Up @@ -13,6 +13,8 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Completion\Suggestion;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Input\InputArgument;

Expand Down Expand Up @@ -98,22 +100,32 @@ public function testSetDefaultWithArrayArgument()
$argument->setDefault('default');
}

public function testGetSuggestedValues()
public function testCompleteArray()
{
$values = ['foo', 'bar'];
$option = new InputArgument('foo', null, '', null, $values);
$this->assertSame($values, $option->getSuggestedValues(new CompletionInput()));
$argument = new InputArgument('foo', null, '', null, $values);
$this->assertTrue($argument->hasCompletion());
$suggestions = new CompletionSuggestions();
$argument->complete(new CompletionInput(), $suggestions);
$this->assertSame($values, array_map(fn (Suggestion $suggestion) => $suggestion->getValue(), $suggestions->getValueSuggestions()));
}

$option = new InputArgument('foo', null, '', null, fn (CompletionInput $input): array => $values);
$this->assertSame($values, $option->getSuggestedValues(new CompletionInput()));
public function testCompleteClosure()
{
$values = ['foo', 'bar'];
$argument = new InputArgument('foo', null, '', null, fn (CompletionInput $input): array => $values);
$this->assertTrue($argument->hasCompletion());
$suggestions = new CompletionSuggestions();
$argument->complete(new CompletionInput(), $suggestions);
$this->assertSame($values, array_map(fn (Suggestion $suggestion) => $suggestion->getValue(), $suggestions->getValueSuggestions()));
}

public function testGetSuggestedValuesClosureReturnIncorrectType()
public function testCompleteClosureReturnIncorrectType()
{
$this->expectException(LogicException::class);
$this->expectExceptionMessage('Closure for argument "foo" must return an array. Got "string".');
$this->expectExceptionMessage('Closure for argument "foo" must return an array or null. Got "string".');

$option = new InputArgument('foo', InputArgument::OPTIONAL, '', null, fn (CompletionInput $input) => 'invalid');
$option->getSuggestedValues(new CompletionInput());
$argument = new InputArgument('foo', InputArgument::OPTIONAL, '', null, fn (CompletionInput $input) => 'invalid');
$argument->complete(new CompletionInput(), new CompletionSuggestions());
}
}
42 changes: 27 additions & 15 deletions src/Symfony/Component/Console/Tests/Input/InputOptionTest.php
Expand Up @@ -13,7 +13,10 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Completion\Suggestion;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

class InputOptionTest extends TestCase
Expand Down Expand Up @@ -197,31 +200,40 @@ public function testEquals()
$this->assertFalse($option->equals($option2));
}

public function testGetSuggestedValues()
public function testSuggestedValuesErrorIfNoValue()
{
$values = ['foo', 'bar'];
$option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', null, $values);
$this->assertSame($values, $option->getSuggestedValues(new CompletionInput()));
$this->expectException(LogicException::class);
$this->expectExceptionMessage('Cannot set suggested values if the option does not accept a value.');

$option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', null, fn (CompletionInput $input): array => $values);
$this->assertSame($values, $option->getSuggestedValues(new CompletionInput()));
new InputOption('foo', null, InputOption::VALUE_NONE, '', null, ['foo']);
}

public function testGetSuggestedValuesClosureReturnIncorrectType()
public function testCompleteArray()
{
$this->expectException(LogicException::class);
$this->expectExceptionMessage('Closure for option "foo" must return an array. Got "string".');
$values = ['foo', 'bar'];
$option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', null, $values);
$this->assertTrue($option->hasCompletion());
$suggestions = new CompletionSuggestions();
$option->complete(new CompletionInput(), $suggestions);
$this->assertSame($values, array_map(fn (Suggestion $suggestion) => $suggestion->getValue(), $suggestions->getValueSuggestions()));
}

$option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', null, fn (CompletionInput $input) => 'invalid');
$option->getSuggestedValues(new CompletionInput());
public function testCompleteClosure()
{
$values = ['foo', 'bar'];
$option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', null, fn (CompletionInput $input): array => $values);
$this->assertTrue($option->hasCompletion());
$suggestions = new CompletionSuggestions();
$option->complete(new CompletionInput(), $suggestions);
$this->assertSame($values, array_map(fn (Suggestion $suggestion) => $suggestion->getValue(), $suggestions->getValueSuggestions()));
}

public function testSuggestedValuesErrorIfNoValue()
public function testCompleteClosureReturnIncorrectType()
{
$this->expectException(LogicException::class);
$this->expectExceptionMessage('Cannot set suggested values if the option does not accept a value.');
$this->expectExceptionMessage('Closure for option "foo" must return an array or null. Got "string".');

$option = new InputOption('foo', null, InputOption::VALUE_NONE, '', null, ['foo']);
$option->getSuggestedValues(new CompletionInput());
$option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', null, fn (CompletionInput $input) => 'invalid');
$option->complete(new CompletionInput(), new CompletionSuggestions());
}
}

0 comments on commit 091027a

Please sign in to comment.