123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- <?php
- namespace Symfony\Component\Console\Completion;
- use Symfony\Component\Console\Exception\RuntimeException;
- use Symfony\Component\Console\Input\ArgvInput;
- use Symfony\Component\Console\Input\InputDefinition;
- use Symfony\Component\Console\Input\InputOption;
- final class CompletionInput extends ArgvInput
- {
- public const TYPE_ARGUMENT_VALUE = 'argument_value';
- public const TYPE_OPTION_VALUE = 'option_value';
- public const TYPE_OPTION_NAME = 'option_name';
- public const TYPE_NONE = 'none';
- private $tokens;
- private $currentIndex;
- private $completionType;
- private $completionName = null;
- private $completionValue = '';
-
- public static function fromString(string $inputStr, int $currentIndex): self
- {
- preg_match_all('/(?<=^|\s)([\'"]?)(.+?)(?<!\\\\)\1(?=$|\s)/', $inputStr, $tokens);
- return self::fromTokens($tokens[0], $currentIndex);
- }
-
- public static function fromTokens(array $tokens, int $currentIndex): self
- {
- $input = new self($tokens);
- $input->tokens = $tokens;
- $input->currentIndex = $currentIndex;
- return $input;
- }
-
- public function bind(InputDefinition $definition): void
- {
- parent::bind($definition);
- $relevantToken = $this->getRelevantToken();
- if ('-' === $relevantToken[0]) {
-
- [$optionToken, $optionValue] = explode('=', $relevantToken, 2) + ['', ''];
- $option = $this->getOptionFromToken($optionToken);
- if (null === $option && !$this->isCursorFree()) {
- $this->completionType = self::TYPE_OPTION_NAME;
- $this->completionValue = $relevantToken;
- return;
- }
- if (null !== $option && $option->acceptValue()) {
- $this->completionType = self::TYPE_OPTION_VALUE;
- $this->completionName = $option->getName();
- $this->completionValue = $optionValue ?: (!str_starts_with($optionToken, '--') ? substr($optionToken, 2) : '');
- return;
- }
- }
- $previousToken = $this->tokens[$this->currentIndex - 1];
- if ('-' === $previousToken[0] && '' !== trim($previousToken, '-')) {
-
- $previousOption = $this->getOptionFromToken($previousToken);
- if (null !== $previousOption && $previousOption->acceptValue()) {
- $this->completionType = self::TYPE_OPTION_VALUE;
- $this->completionName = $previousOption->getName();
- $this->completionValue = $relevantToken;
- return;
- }
- }
-
- $this->completionType = self::TYPE_ARGUMENT_VALUE;
- foreach ($this->definition->getArguments() as $argumentName => $argument) {
- if (!isset($this->arguments[$argumentName])) {
- break;
- }
- $argumentValue = $this->arguments[$argumentName];
- $this->completionName = $argumentName;
- if (\is_array($argumentValue)) {
- $this->completionValue = $argumentValue ? $argumentValue[array_key_last($argumentValue)] : null;
- } else {
- $this->completionValue = $argumentValue;
- }
- }
- if ($this->currentIndex >= \count($this->tokens)) {
- if (!isset($this->arguments[$argumentName]) || $this->definition->getArgument($argumentName)->isArray()) {
- $this->completionName = $argumentName;
- $this->completionValue = '';
- } else {
-
- $this->completionType = self::TYPE_NONE;
- $this->completionName = null;
- $this->completionValue = '';
- }
- }
- }
-
- public function getCompletionType(): string
- {
- return $this->completionType;
- }
-
- public function getCompletionName(): ?string
- {
- return $this->completionName;
- }
-
- public function getCompletionValue(): string
- {
- return $this->completionValue;
- }
- public function mustSuggestOptionValuesFor(string $optionName): bool
- {
- return self::TYPE_OPTION_VALUE === $this->getCompletionType() && $optionName === $this->getCompletionName();
- }
- public function mustSuggestArgumentValuesFor(string $argumentName): bool
- {
- return self::TYPE_ARGUMENT_VALUE === $this->getCompletionType() && $argumentName === $this->getCompletionName();
- }
- protected function parseToken(string $token, bool $parseOptions): bool
- {
- try {
- return parent::parseToken($token, $parseOptions);
- } catch (RuntimeException $e) {
-
- }
- return $parseOptions;
- }
- private function getOptionFromToken(string $optionToken): ?InputOption
- {
- $optionName = ltrim($optionToken, '-');
- if (!$optionName) {
- return null;
- }
- if ('-' === ($optionToken[1] ?? ' ')) {
-
- return $this->definition->hasOption($optionName) ? $this->definition->getOption($optionName) : null;
- }
-
- return $this->definition->hasShortcut($optionName[0]) ? $this->definition->getOptionForShortcut($optionName[0]) : null;
- }
-
- private function getRelevantToken(): string
- {
- return $this->tokens[$this->isCursorFree() ? $this->currentIndex - 1 : $this->currentIndex];
- }
-
- private function isCursorFree(): bool
- {
- $nrOfTokens = \count($this->tokens);
- if ($this->currentIndex > $nrOfTokens) {
- throw new \LogicException('Current index is invalid, it must be the number of input tokens or one more.');
- }
- return $this->currentIndex >= $nrOfTokens;
- }
- public function __toString()
- {
- $str = '';
- foreach ($this->tokens as $i => $token) {
- $str .= $token;
- if ($this->currentIndex === $i) {
- $str .= '|';
- }
- $str .= ' ';
- }
- if ($this->currentIndex > $i) {
- $str .= '|';
- }
- return rtrim($str);
- }
- }
|