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\HelperCode
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;
}