diff --git a/src/Concerns/FakesInputOutput.php b/src/Concerns/FakesInputOutput.php index 5bbf53f..ebee895 100644 --- a/src/Concerns/FakesInputOutput.php +++ b/src/Concerns/FakesInputOutput.php @@ -29,15 +29,32 @@ public static function fake(array $keys = []): void $mock->shouldReceive('lines')->byDefault()->andReturn(24); $mock->shouldReceive('initDimensions')->byDefault(); - foreach ($keys as $key) { + static::fakeKeyPresses($keys, function (string $key) use ($mock): void { $mock->shouldReceive('read')->once()->andReturn($key); - } + }); static::$terminal = $mock; self::setOutput(new BufferedConsoleOutput); } + /** + * Implementation of the looping mechanism for simulating key presses. + * + * By ignoring the `$callable` parameter which contains the default logic + * for simulating fake key presses, we can use a custom implementation + * to emit key presses instead, allowing us to use different inputs. + * + * @param array $keys + * @param callable(string $key): void $callable + */ + public static function fakeKeyPresses(array $keys, callable $callable): void + { + foreach ($keys as $key) { + $callable($key); + } + } + /** * Assert that the output contains the given string. */ diff --git a/src/Prompt.php b/src/Prompt.php index 560bc15..11a7960 100644 --- a/src/Prompt.php +++ b/src/Prompt.php @@ -5,6 +5,7 @@ use Closure; use Laravel\Prompts\Exceptions\FormRevertedException; use Laravel\Prompts\Output\ConsoleOutput; +use Laravel\Prompts\Support\Result; use RuntimeException; use Symfony\Component\Console\Output\OutputInterface; use Throwable; @@ -127,7 +128,7 @@ public function prompt(): mixed $this->hideCursor(); $this->render(); - while (($key = static::terminal()->read()) !== null) { + $result = $this->runLoop(function (string $key): ?Result { $continue = $this->handleKeyPress($key); $this->render(); @@ -135,7 +136,7 @@ public function prompt(): mixed if ($continue === false || $key === Key::CTRL_C) { if ($key === Key::CTRL_C) { if (isset(static::$cancelUsing)) { - return (static::$cancelUsing)(); + return Result::from((static::$cancelUsing)()); } else { static::terminal()->exit(); } @@ -145,14 +146,35 @@ public function prompt(): mixed throw new FormRevertedException; } - return $this->transformedValue(); + return Result::from($this->transformedValue()); } - } + + // Continue looping. + return null; + }); + + return $result; } finally { $this->clearListeners(); } } + /** + * Implementation of the prompt looping mechanism. + * + * @param callable(string $key): ?Result $callable + */ + public function runLoop(callable $callable): mixed + { + while(($key = static::terminal()->read()) !== null) { + $result = $callable($key); + + if ($result instanceof Result) { + return $result->value; + } + } + } + /** * Register a callback to be invoked when a user cancels a prompt. */ diff --git a/src/Support/Result.php b/src/Support/Result.php new file mode 100644 index 0000000..c08273b --- /dev/null +++ b/src/Support/Result.php @@ -0,0 +1,22 @@ +