Skip to content

Commit fdc964d

Browse files
Extract Looping Mechanisms (#162)
* extract looping mechanism in prompt * extract keypress looping mechanism in FakesInputOutput * add docblock with type * swap out `Nothing` class for proper `Result` sentinel class * formatting * swap out Closure for callable, add types * add documentation * add comment * move readonly keyword to property for php8.1 support
1 parent fa9674f commit fdc964d

File tree

3 files changed

+67
-6
lines changed

3 files changed

+67
-6
lines changed

src/Concerns/FakesInputOutput.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,32 @@ public static function fake(array $keys = []): void
2929
$mock->shouldReceive('lines')->byDefault()->andReturn(24);
3030
$mock->shouldReceive('initDimensions')->byDefault();
3131

32-
foreach ($keys as $key) {
32+
static::fakeKeyPresses($keys, function (string $key) use ($mock): void {
3333
$mock->shouldReceive('read')->once()->andReturn($key);
34-
}
34+
});
3535

3636
static::$terminal = $mock;
3737

3838
self::setOutput(new BufferedConsoleOutput);
3939
}
4040

41+
/**
42+
* Implementation of the looping mechanism for simulating key presses.
43+
*
44+
* By ignoring the `$callable` parameter which contains the default logic
45+
* for simulating fake key presses, we can use a custom implementation
46+
* to emit key presses instead, allowing us to use different inputs.
47+
*
48+
* @param array<string> $keys
49+
* @param callable(string $key): void $callable
50+
*/
51+
public static function fakeKeyPresses(array $keys, callable $callable): void
52+
{
53+
foreach ($keys as $key) {
54+
$callable($key);
55+
}
56+
}
57+
4158
/**
4259
* Assert that the output contains the given string.
4360
*/

src/Prompt.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Closure;
66
use Laravel\Prompts\Exceptions\FormRevertedException;
77
use Laravel\Prompts\Output\ConsoleOutput;
8+
use Laravel\Prompts\Support\Result;
89
use RuntimeException;
910
use Symfony\Component\Console\Output\OutputInterface;
1011
use Throwable;
@@ -127,15 +128,15 @@ public function prompt(): mixed
127128
$this->hideCursor();
128129
$this->render();
129130

130-
while (($key = static::terminal()->read()) !== null) {
131+
$result = $this->runLoop(function (string $key): ?Result {
131132
$continue = $this->handleKeyPress($key);
132133

133134
$this->render();
134135

135136
if ($continue === false || $key === Key::CTRL_C) {
136137
if ($key === Key::CTRL_C) {
137138
if (isset(static::$cancelUsing)) {
138-
return (static::$cancelUsing)();
139+
return Result::from((static::$cancelUsing)());
139140
} else {
140141
static::terminal()->exit();
141142
}
@@ -145,14 +146,35 @@ public function prompt(): mixed
145146
throw new FormRevertedException;
146147
}
147148

148-
return $this->transformedValue();
149+
return Result::from($this->transformedValue());
149150
}
150-
}
151+
152+
// Continue looping.
153+
return null;
154+
});
155+
156+
return $result;
151157
} finally {
152158
$this->clearListeners();
153159
}
154160
}
155161

162+
/**
163+
* Implementation of the prompt looping mechanism.
164+
*
165+
* @param callable(string $key): ?Result $callable
166+
*/
167+
public function runLoop(callable $callable): mixed
168+
{
169+
while(($key = static::terminal()->read()) !== null) {
170+
$result = $callable($key);
171+
172+
if ($result instanceof Result) {
173+
return $result->value;
174+
}
175+
}
176+
}
177+
156178
/**
157179
* Register a callback to be invoked when a user cancels a prompt.
158180
*/

src/Support/Result.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Laravel\Prompts\Support;
4+
5+
/**
6+
* Result.
7+
*
8+
* This is a 'sentinel' value. It wraps a return value, which can
9+
* allow us to differentiate between a `null` return value and
10+
* a `null` return value that's intended to continue a loop.
11+
*/
12+
final class Result
13+
{
14+
public function __construct(
15+
public readonly mixed $value,
16+
) {}
17+
18+
public static function from(mixed $value): self
19+
{
20+
return new self($value);
21+
}
22+
}

0 commit comments

Comments
 (0)