Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. QuestionHelper.php

function QuestionHelper::autocomplete

Autocompletes a question.

Parameters

resource $inputStream:

1 call to QuestionHelper::autocomplete()
QuestionHelper::doAsk in vendor/symfony/console/Helper/QuestionHelper.php
Asks the question to the user.

File

vendor/symfony/console/Helper/QuestionHelper.php, line 239

Class

QuestionHelper
The QuestionHelper class provides helpers to interact with the user.

Namespace

Symfony\Component\Console\Helper

Code

private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete) : string {
    $cursor = new Cursor($output, $inputStream);
    $fullChoice = '';
    $ret = '';
    $i = 0;
    $ofs = -1;
    $matches = $autocomplete($ret);
    $numMatches = \count($matches);
    $sttyMode = shell_exec('stty -g');
    $isStdin = 'php://stdin' === (stream_get_meta_data($inputStream)['uri'] ?? null);
    $r = [
        $inputStream,
    ];
    $w = [];
    // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
    shell_exec('stty -icanon -echo');
    // Add highlighted text style
    $output->getFormatter()
        ->setStyle('hl', new OutputFormatterStyle('black', 'white'));
    // Read a keypress
    while (!feof($inputStream)) {
        while ($isStdin && 0 === @stream_select($r, $w, $w, 0, 100)) {
            // Give signal handlers a chance to run
            $r = [
                $inputStream,
            ];
        }
        $c = fread($inputStream, 1);
        // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false.
        if (false === $c || '' === $ret && '' === $c && null === $question->getDefault()) {
            shell_exec('stty ' . $sttyMode);
            throw new MissingInputException('Aborted.');
        }
        elseif ("" === $c) {
            // Backspace Character
            if (0 === $numMatches && 0 !== $i) {
                --$i;
                $cursor->moveLeft(s($fullChoice)->slice(-1)
                    ->width(false));
                $fullChoice = self::substr($fullChoice, 0, $i);
            }
            if (0 === $i) {
                $ofs = -1;
                $matches = $autocomplete($ret);
                $numMatches = \count($matches);
            }
            else {
                $numMatches = 0;
            }
            // Pop the last character off the end of our string
            $ret = self::substr($ret, 0, $i);
        }
        elseif ("\x1b" === $c) {
            // Did we read an escape sequence?
            $c .= fread($inputStream, 2);
            // A = Up Arrow. B = Down Arrow
            if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
                if ('A' === $c[2] && -1 === $ofs) {
                    $ofs = 0;
                }
                if (0 === $numMatches) {
                    continue;
                }
                $ofs += 'A' === $c[2] ? -1 : 1;
                $ofs = ($numMatches + $ofs) % $numMatches;
            }
        }
        elseif (\ord($c) < 32) {
            if ("\t" === $c || "\n" === $c) {
                if ($numMatches > 0 && -1 !== $ofs) {
                    $ret = (string) $matches[$ofs];
                    // Echo out remaining chars for current match
                    $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
                    $output->write($remainingCharacters);
                    $fullChoice .= $remainingCharacters;
                    $i = false === ($encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding);
                    $matches = array_filter($autocomplete($ret), fn($match) => '' === $ret || str_starts_with($match, $ret));
                    $numMatches = \count($matches);
                    $ofs = -1;
                }
                if ("\n" === $c) {
                    $output->write($c);
                    break;
                }
                $numMatches = 0;
            }
            continue;
        }
        else {
            if ("\x80" <= $c) {
                $c .= fread($inputStream, [
                    "\xc0" => 1,
                    "\xd0" => 1,
                    "\xe0" => 2,
                    "\xf0" => 3,
                ][$c & "\xf0"]);
            }
            $output->write($c);
            $ret .= $c;
            $fullChoice .= $c;
            ++$i;
            $tempRet = $ret;
            if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
                $tempRet = $this->mostRecentlyEnteredValue($fullChoice);
            }
            $numMatches = 0;
            $ofs = 0;
            foreach ($autocomplete($ret) as $value) {
                // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
                if (str_starts_with($value, $tempRet)) {
                    $matches[$numMatches++] = $value;
                }
            }
        }
        $cursor->clearLineAfter();
        if ($numMatches > 0 && -1 !== $ofs) {
            $cursor->savePosition();
            // Write highlighted text, complete the partially entered response
            $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
            $output->write('<hl>' . OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)) . '</hl>');
            $cursor->restorePosition();
        }
    }
    // Reset stty so it behaves normally again
    shell_exec('stty ' . $sttyMode);
    return $fullChoice;
}

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal