8000 Add support for arrow keys in QuestionHelper · symfony/symfony@6b73d87 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6b73d87

Browse files
committed
Add support for arrow keys in QuestionHelper
1 parent 11f4e8b commit 6b73d87

File tree

2 files changed

+79
-3
lines changed

2 files changed

+79
-3
lines changed

src/Symfony/Component/Console/Cursor.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ public function getCurrentPosition(): array
163163

164164
sscanf($code, "\033[%d;%dR", $row, $col);
165165

166+
if (null === $row && null === $col) {
167+
sscanf($code, "~\033[%d;%dR", $row, $col);
168+
}
169+
166170
return [$col, $row];
167171
}
168172
}

src/Symfony/Component/Console/Helper/QuestionHelper.php

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ private function doAsk(OutputInterface $output, Question $question)
109109
$inputStream = $this->inputStream ?: STDIN;
110110
$autocomplete = $question->getAutocompleterCallback();
111111

112+
$cursor = new Cursor($output);
113+
112114
if (null === $autocomplete || !Terminal::hasSttyAvailable()) {
113115
$ret = false;
114116
if ($question->isHidden()) {
@@ -123,10 +125,80 @@ private function doAsk(OutputInterface $output, Question $question)
123125
}
124126

125127
if (false === $ret) {
126-
$ret = fgets($inputStream, 4096);
127-
if (false === $ret) {
128-
throw new MissingInputException('Aborted.');
128+
if (!Terminal::hasSttyAvailable()) {
129+
$ret = fgets($inputStream, 4096);
130+
if (false === $ret) {
131+
throw new MissingInputException('Aborted.');
132+
}
133+
} else {
134+
$sttyMode = shell_exec('stty -g');
135+
136+
shell_exec('stty -icanon -echo');
137+
138+
$k = null;
139+
$string = '';
140+
141+
[$x] = $cursor->getCurrentPosition();
142+
143+
while (10 !== $k && 0 !== $k) {
144+
[$pos] = $cursor->getCurrentPosition();
145+
$pos -= $x;
146+
147+
$ret = fgetc($inputStream);
148+
if (false === $ret && '' === $string) {
149+
shell_exec(sprintf('stty %s', $sttyMode));
150+
throw new MissingInputException('Aborted.');
151+
}
152+
153+
$k = ord($ret);
154+
155+
if (27 === $k) {
156+
fgetc($inputStream);
157+
$k = ord(fgetc($inputStream));
158+
159+
switch (true) {
160+
case 67 === $k && $pos < self::strlen($string):
161+
$cursor->moveRight();
162+
break;
163+
case 68 === $k && $pos > 0:
164+
$cursor->moveLeft();
165+
break;
166+
case 51 === $k && $pos >= 0:
167+
$string = self::substr($string, 0, $pos).self::substr($string, $pos + 1);
168+
$cursor->clearLineAfter();
169+
$output->write(self::substr($string, $pos));
170+
$cursor->moveToColumn($pos + $x);
171+
break;
172+
}
173+
} else if (127 === $k) {
174+
if ($pos > 0) {
175+
$string = self::substr($string, 0, $pos - 1).self::substr($string, $pos);
176+
177+
$cursor->moveToColumn($x);
178+
$output->write($string);
179+
$cursor->clearLineAfter()
180+
->moveToColumn(($pos + $x) - 1);
181+
}
182+
} else if ($k >= 32 && $k <= 126) {
183+
if ($pos > 0 && $pos < \strlen($string)) {
184+
$string = self::substr($string, 0, $pos).$ret.self::substr($string, $pos);
185+
$output->write($ret.self::substr($string, $pos + 1));
186+
$cursor->clearLineAfter()
187+
->moveToColumn($pos + $x + 1);
188+
} else {
189+
$string .= $ret;
190+
$output->write($ret);
191+
}
192+
} else {
193+
$output->write($ret);
194+
}
195+
}
196+
197+
shell_exec(sprintf('stty %s', $sttyMode));
198+
199+
$ret = $string;
129200
}
201+
130202
if ($question->isTrimmable()) {
131203
$ret = trim($ret);
132204
}

0 commit comments

Comments
 (0)
0