🔧
This commit is contained in:
1
vendor/laravel/prompts/composer.json
vendored
1
vendor/laravel/prompts/composer.json
vendored
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "laravel/prompts",
|
||||
"type": "library",
|
||||
"description": "Add beautiful and user-friendly forms to your command-line applications.",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
||||
16
vendor/laravel/prompts/src/Concerns/Cursor.php
vendored
16
vendor/laravel/prompts/src/Concerns/Cursor.php
vendored
@@ -60,4 +60,20 @@ trait Cursor
|
||||
|
||||
static::writeDirectly($sequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the cursor to the given column.
|
||||
*/
|
||||
public function moveCursorToColumn(int $column): void
|
||||
{
|
||||
static::writeDirectly("\e[{$column}G");
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the cursor up by the given number of lines.
|
||||
*/
|
||||
public function moveCursorUp(int $lines): void
|
||||
{
|
||||
static::writeDirectly("\e[{$lines}A");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ trait FakesInputOutput
|
||||
$mock->shouldReceive('restoreTty')->byDefault();
|
||||
$mock->shouldReceive('cols')->byDefault()->andReturn(80);
|
||||
$mock->shouldReceive('lines')->byDefault()->andReturn(24);
|
||||
$mock->shouldReceive('initDimensions')->byDefault();
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$mock->shouldReceive('read')->once()->andReturn($key);
|
||||
@@ -34,7 +35,7 @@ trait FakesInputOutput
|
||||
|
||||
static::$terminal = $mock;
|
||||
|
||||
self::setOutput(new BufferedConsoleOutput());
|
||||
self::setOutput(new BufferedConsoleOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,7 @@ trait Scrolling
|
||||
{
|
||||
$reservedLines = ($renderer = $this->getRenderer()) instanceof ScrollingRenderer ? $renderer->reservedLines() : 0;
|
||||
|
||||
$this->scroll = min($this->scroll, $this->terminal()->lines() - $reservedLines);
|
||||
$this->scroll = max(1, min($this->scroll, $this->terminal()->lines() - $reservedLines));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ trait Termwind
|
||||
{
|
||||
protected function termwind(string $html)
|
||||
{
|
||||
renderUsing($output = new BufferedConsoleOutput());
|
||||
renderUsing($output = new BufferedConsoleOutput);
|
||||
|
||||
render($html);
|
||||
|
||||
|
||||
@@ -8,24 +8,28 @@ use Laravel\Prompts\MultiSearchPrompt;
|
||||
use Laravel\Prompts\MultiSelectPrompt;
|
||||
use Laravel\Prompts\Note;
|
||||
use Laravel\Prompts\PasswordPrompt;
|
||||
use Laravel\Prompts\PausePrompt;
|
||||
use Laravel\Prompts\Progress;
|
||||
use Laravel\Prompts\SearchPrompt;
|
||||
use Laravel\Prompts\SelectPrompt;
|
||||
use Laravel\Prompts\Spinner;
|
||||
use Laravel\Prompts\SuggestPrompt;
|
||||
use Laravel\Prompts\Table;
|
||||
use Laravel\Prompts\TextareaPrompt;
|
||||
use Laravel\Prompts\TextPrompt;
|
||||
use Laravel\Prompts\Themes\Default\ConfirmPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\MultiSearchPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\MultiSelectPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\NoteRenderer;
|
||||
use Laravel\Prompts\Themes\Default\PasswordPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\PausePromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\ProgressRenderer;
|
||||
use Laravel\Prompts\Themes\Default\SearchPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\SelectPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\SpinnerRenderer;
|
||||
use Laravel\Prompts\Themes\Default\SuggestPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\TableRenderer;
|
||||
use Laravel\Prompts\Themes\Default\TextareaPromptRenderer;
|
||||
use Laravel\Prompts\Themes\Default\TextPromptRenderer;
|
||||
|
||||
trait Themes
|
||||
@@ -43,10 +47,12 @@ trait Themes
|
||||
protected static array $themes = [
|
||||
'default' => [
|
||||
TextPrompt::class => TextPromptRenderer::class,
|
||||
TextareaPrompt::class => TextareaPromptRenderer::class,
|
||||
PasswordPrompt::class => PasswordPromptRenderer::class,
|
||||
SelectPrompt::class => SelectPromptRenderer::class,
|
||||
MultiSelectPrompt::class => MultiSelectPromptRenderer::class,
|
||||
ConfirmPrompt::class => ConfirmPromptRenderer::class,
|
||||
PausePrompt::class => PausePromptRenderer::class,
|
||||
SearchPrompt::class => SearchPromptRenderer::class,
|
||||
MultiSearchPrompt::class => MultiSearchPromptRenderer::class,
|
||||
SuggestPrompt::class => SuggestPromptRenderer::class,
|
||||
|
||||
@@ -17,4 +17,90 @@ trait Truncation
|
||||
|
||||
return mb_strwidth($string) <= $width ? $string : (mb_strimwidth($string, 0, $width - 1).'…');
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-byte version of wordwrap.
|
||||
*
|
||||
* @param non-empty-string $break
|
||||
*/
|
||||
protected function mbWordwrap(
|
||||
string $string,
|
||||
int $width = 75,
|
||||
string $break = "\n",
|
||||
bool $cut_long_words = false
|
||||
): string {
|
||||
$lines = explode($break, $string);
|
||||
$result = [];
|
||||
|
||||
foreach ($lines as $originalLine) {
|
||||
if (mb_strwidth($originalLine) <= $width) {
|
||||
$result[] = $originalLine;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$words = explode(' ', $originalLine);
|
||||
$line = null;
|
||||
$lineWidth = 0;
|
||||
|
||||
if ($cut_long_words) {
|
||||
foreach ($words as $index => $word) {
|
||||
$characters = mb_str_split($word);
|
||||
$strings = [];
|
||||
$str = '';
|
||||
|
||||
foreach ($characters as $character) {
|
||||
$tmp = $str.$character;
|
||||
|
||||
if (mb_strwidth($tmp) > $width) {
|
||||
$strings[] = $str;
|
||||
$str = $character;
|
||||
} else {
|
||||
$str = $tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if ($str !== '') {
|
||||
$strings[] = $str;
|
||||
}
|
||||
|
||||
$words[$index] = implode(' ', $strings);
|
||||
}
|
||||
|
||||
$words = explode(' ', implode(' ', $words));
|
||||
}
|
||||
|
||||
foreach ($words as $word) {
|
||||
$tmp = ($line === null) ? $word : $line.' '.$word;
|
||||
|
||||
// Look for zero-width joiner characters (combined emojis)
|
||||
preg_match('/\p{Cf}/u', $word, $joinerMatches);
|
||||
|
||||
$wordWidth = count($joinerMatches) > 0 ? 2 : mb_strwidth($word);
|
||||
|
||||
$lineWidth += $wordWidth;
|
||||
|
||||
if ($line !== null) {
|
||||
// Space between words
|
||||
$lineWidth += 1;
|
||||
}
|
||||
|
||||
if ($lineWidth <= $width) {
|
||||
$line = $tmp;
|
||||
} else {
|
||||
$result[] = $line;
|
||||
$line = $word;
|
||||
$lineWidth = $wordWidth;
|
||||
}
|
||||
}
|
||||
|
||||
if ($line !== '') {
|
||||
$result[] = $line;
|
||||
}
|
||||
|
||||
$line = null;
|
||||
}
|
||||
|
||||
return implode($break, $result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ trait TypedValue
|
||||
/**
|
||||
* Track the value as the user types.
|
||||
*/
|
||||
protected function trackTypedValue(string $default = '', bool $submit = true, ?callable $ignore = null): void
|
||||
protected function trackTypedValue(string $default = '', bool $submit = true, ?callable $ignore = null, bool $allowNewLine = false): void
|
||||
{
|
||||
$this->typedValue = $default;
|
||||
|
||||
@@ -27,7 +27,7 @@ trait TypedValue
|
||||
$this->cursorPosition = mb_strlen($this->typedValue);
|
||||
}
|
||||
|
||||
$this->on('key', function ($key) use ($submit, $ignore) {
|
||||
$this->on('key', function ($key) use ($submit, $ignore, $allowNewLine) {
|
||||
if ($key[0] === "\e" || in_array($key, [Key::CTRL_B, Key::CTRL_F, Key::CTRL_A, Key::CTRL_E])) {
|
||||
if ($ignore !== null && $ignore($key)) {
|
||||
return;
|
||||
@@ -51,10 +51,17 @@ trait TypedValue
|
||||
return;
|
||||
}
|
||||
|
||||
if ($key === Key::ENTER && $submit) {
|
||||
$this->submit();
|
||||
if ($key === Key::ENTER) {
|
||||
if ($submit) {
|
||||
$this->submit();
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($allowNewLine) {
|
||||
$this->typedValue = mb_substr($this->typedValue, 0, $this->cursorPosition).PHP_EOL.mb_substr($this->typedValue, $this->cursorPosition);
|
||||
$this->cursorPosition++;
|
||||
}
|
||||
} elseif ($key === Key::BACKSPACE || $key === Key::CTRL_H) {
|
||||
if ($this->cursorPosition === 0) {
|
||||
return;
|
||||
@@ -81,20 +88,20 @@ trait TypedValue
|
||||
/**
|
||||
* Add a virtual cursor to the value and truncate if necessary.
|
||||
*/
|
||||
protected function addCursor(string $value, int $cursorPosition, int $maxWidth): string
|
||||
protected function addCursor(string $value, int $cursorPosition, ?int $maxWidth = null): string
|
||||
{
|
||||
$before = mb_substr($value, 0, $cursorPosition);
|
||||
$current = mb_substr($value, $cursorPosition, 1);
|
||||
$after = mb_substr($value, $cursorPosition + 1);
|
||||
|
||||
$cursor = mb_strlen($current) ? $current : ' ';
|
||||
$cursor = mb_strlen($current) && $current !== PHP_EOL ? $current : ' ';
|
||||
|
||||
$spaceBefore = $maxWidth - mb_strwidth($cursor) - (mb_strwidth($after) > 0 ? 1 : 0);
|
||||
$spaceBefore = $maxWidth < 0 || $maxWidth === null ? mb_strwidth($before) : $maxWidth - mb_strwidth($cursor) - (mb_strwidth($after) > 0 ? 1 : 0);
|
||||
[$truncatedBefore, $wasTruncatedBefore] = mb_strwidth($before) > $spaceBefore
|
||||
? [$this->trimWidthBackwards($before, 0, $spaceBefore - 1), true]
|
||||
: [$before, false];
|
||||
|
||||
$spaceAfter = $maxWidth - ($wasTruncatedBefore ? 1 : 0) - mb_strwidth($truncatedBefore) - mb_strwidth($cursor);
|
||||
$spaceAfter = $maxWidth < 0 || $maxWidth === null ? mb_strwidth($after) : $maxWidth - ($wasTruncatedBefore ? 1 : 0) - mb_strwidth($truncatedBefore) - mb_strwidth($cursor);
|
||||
[$truncatedAfter, $wasTruncatedAfter] = mb_strwidth($after) > $spaceAfter
|
||||
? [mb_strimwidth($after, 0, $spaceAfter - 1), true]
|
||||
: [$after, false];
|
||||
@@ -102,6 +109,7 @@ trait TypedValue
|
||||
return ($wasTruncatedBefore ? $this->dim('…') : '')
|
||||
.$truncatedBefore
|
||||
.$this->inverse($cursor)
|
||||
.($current === PHP_EOL ? PHP_EOL : '')
|
||||
.$truncatedAfter
|
||||
.($wasTruncatedAfter ? $this->dim('…') : '');
|
||||
}
|
||||
|
||||
3
vendor/laravel/prompts/src/ConfirmPrompt.php
vendored
3
vendor/laravel/prompts/src/ConfirmPrompt.php
vendored
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Laravel\Prompts;
|
||||
|
||||
use Closure;
|
||||
|
||||
class ConfirmPrompt extends Prompt
|
||||
{
|
||||
/**
|
||||
@@ -20,6 +22,7 @@ class ConfirmPrompt extends Prompt
|
||||
public bool|string $required = false,
|
||||
public mixed $validate = null,
|
||||
public string $hint = '',
|
||||
public ?Closure $transform = null,
|
||||
) {
|
||||
$this->confirmed = $default;
|
||||
|
||||
|
||||
10
vendor/laravel/prompts/src/Exceptions/FormRevertedException.php
vendored
Normal file
10
vendor/laravel/prompts/src/Exceptions/FormRevertedException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class FormRevertedException extends RuntimeException
|
||||
{
|
||||
//
|
||||
}
|
||||
279
vendor/laravel/prompts/src/FormBuilder.php
vendored
Normal file
279
vendor/laravel/prompts/src/FormBuilder.php
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Support\Collection;
|
||||
use Laravel\Prompts\Exceptions\FormRevertedException;
|
||||
|
||||
class FormBuilder
|
||||
{
|
||||
/**
|
||||
* Each step that should be executed.
|
||||
*
|
||||
* @var array<int, \Laravel\Prompts\FormStep>
|
||||
*/
|
||||
protected array $steps = [];
|
||||
|
||||
/**
|
||||
* The responses provided by each step.
|
||||
*
|
||||
* @var array<mixed>
|
||||
*/
|
||||
protected array $responses = [];
|
||||
|
||||
/**
|
||||
* Add a new step.
|
||||
*/
|
||||
public function add(Closure $step, ?string $name = null, bool $ignoreWhenReverting = false): self
|
||||
{
|
||||
$this->steps[] = new FormStep($step, true, $name, $ignoreWhenReverting);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all of the given steps.
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function submit(): array
|
||||
{
|
||||
$index = 0;
|
||||
$wasReverted = false;
|
||||
|
||||
while ($index < count($this->steps)) {
|
||||
$step = $this->steps[$index];
|
||||
|
||||
if ($wasReverted && $index > 0 && $step->shouldIgnoreWhenReverting($this->responses)) {
|
||||
$index--;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$wasReverted = false;
|
||||
|
||||
$index > 0
|
||||
? Prompt::revertUsing(function () use (&$wasReverted) {
|
||||
$wasReverted = true;
|
||||
}) : Prompt::preventReverting();
|
||||
|
||||
try {
|
||||
$this->responses[$step->name ?? $index] = $step->run(
|
||||
$this->responses,
|
||||
$this->responses[$step->name ?? $index] ?? null,
|
||||
);
|
||||
} catch (FormRevertedException) {
|
||||
$wasReverted = true;
|
||||
}
|
||||
|
||||
$wasReverted ? $index-- : $index++;
|
||||
}
|
||||
|
||||
Prompt::preventReverting();
|
||||
|
||||
return $this->responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user for text input.
|
||||
*/
|
||||
public function text(string $label, string $placeholder = '', string $default = '', bool|string $required = false, mixed $validate = null, string $hint = '', ?string $name = null, ?Closure $transform = null): self
|
||||
{
|
||||
return $this->runPrompt(text(...), get_defined_vars());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user for multiline text input.
|
||||
*/
|
||||
public function textarea(string $label, string $placeholder = '', string $default = '', bool|string $required = false, ?Closure $validate = null, string $hint = '', int $rows = 5, ?string $name = null, ?Closure $transform = null): self
|
||||
{
|
||||
return $this->runPrompt(textarea(...), get_defined_vars());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user for input, hiding the value.
|
||||
*/
|
||||
public function password(string $label, string $placeholder = '', bool|string $required = false, mixed $validate = null, string $hint = '', ?string $name = null, ?Closure $transform = null): self
|
||||
{
|
||||
return $this->runPrompt(password(...), get_defined_vars());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to select an option.
|
||||
*
|
||||
* @param array<int|string, string>|Collection<int|string, string> $options
|
||||
* @param true|string $required
|
||||
*/
|
||||
public function select(string $label, array|Collection $options, int|string|null $default = null, int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true, ?string $name = null, ?Closure $transform = null): self
|
||||
{
|
||||
return $this->runPrompt(select(...), get_defined_vars());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to select multiple options.
|
||||
*
|
||||
* @param array<int|string, string>|Collection<int|string, string> $options
|
||||
* @param array<int|string>|Collection<int, int|string> $default
|
||||
*/
|
||||
public function multiselect(string $label, array|Collection $options, array|Collection $default = [], int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.', ?string $name = null, ?Closure $transform = null): self
|
||||
{
|
||||
return $this->runPrompt(multiselect(...), get_defined_vars());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to confirm an action.
|
||||
*/
|
||||
public function confirm(string $label, bool $default = true, string $yes = 'Yes', string $no = 'No', bool|string $required = false, mixed $validate = null, string $hint = '', ?string $name = null, ?Closure $transform = null): self
|
||||
{
|
||||
return $this->runPrompt(confirm(...), get_defined_vars());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to continue or cancel after pausing.
|
||||
*/
|
||||
public function pause(string $message = 'Press enter to continue...', ?string $name = null): self
|
||||
{
|
||||
return $this->runPrompt(pause(...), get_defined_vars());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user for text input with auto-completion.
|
||||
*
|
||||
* @param array<string>|Collection<int, string>|Closure(string): array<string> $options
|
||||
*/
|
||||
public function suggest(string $label, array|Collection|Closure $options, string $placeholder = '', string $default = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = '', ?string $name = null, ?Closure $transform = null): self
|
||||
{
|
||||
return $this->runPrompt(suggest(...), get_defined_vars());
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the user to search for an option.
|
||||
*
|
||||
* @param Closure(string): array<int|string, string> $options
|
||||
* @param true|string $required
|
||||
*/
|
||||
public function search(string $label, Closure $options, string $placeholder = '', int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true, ?string $name = null, ?Closure $transform = null): self
|
||||
{
|
||||
return $this->runPrompt(search(...), get_defined_vars());
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the user to search for multiple option.
|
||||
*
|
||||
* @param Closure(string): array<int|string, string> $options
|
||||
*/
|
||||
public function multisearch(string $label, Closure $options, string $placeholder = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.', ?string $name = null, ?Closure $transform = null): self
|
||||
{
|
||||
return $this->runPrompt(multisearch(...), get_defined_vars());
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a spinner while the given callback is executing.
|
||||
*
|
||||
* @param \Closure(): mixed $callback
|
||||
*/
|
||||
public function spin(Closure $callback, string $message = '', ?string $name = null): self
|
||||
{
|
||||
return $this->runPrompt(spin(...), get_defined_vars(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a note.
|
||||
*/
|
||||
public function note(string $message, ?string $type = null, ?string $name = null): self
|
||||
{
|
||||
return $this->runPrompt(note(...), get_defined_vars(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an error.
|
||||
*/
|
||||
public function error(string $message, ?string $name = null): self
|
||||
{
|
||||
return $this->runPrompt(error(...), get_defined_vars(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a warning.
|
||||
*/
|
||||
public function warning(string $message, ?string $name = null): self
|
||||
{
|
||||
return $this->runPrompt(warning(...), get_defined_vars(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an alert.
|
||||
*/
|
||||
public function alert(string $message, ?string $name = null): self
|
||||
{
|
||||
return $this->runPrompt(alert(...), get_defined_vars(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an informational message.
|
||||
*/
|
||||
public function info(string $message, ?string $name = null): self
|
||||
{
|
||||
return $this->runPrompt(info(...), get_defined_vars(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an introduction.
|
||||
*/
|
||||
public function intro(string $message, ?string $name = null): self
|
||||
{
|
||||
return $this->runPrompt(intro(...), get_defined_vars(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a closing message.
|
||||
*/
|
||||
public function outro(string $message, ?string $name = null): self
|
||||
{
|
||||
return $this->runPrompt(outro(...), get_defined_vars(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a table.
|
||||
*
|
||||
* @param array<int, string|array<int, string>>|Collection<int, string|array<int, string>> $headers
|
||||
* @param array<int, array<int, string>>|Collection<int, array<int, string>> $rows
|
||||
*/
|
||||
public function table(array|Collection $headers = [], array|Collection|null $rows = null, ?string $name = null): self
|
||||
{
|
||||
return $this->runPrompt(table(...), get_defined_vars(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a progress bar.
|
||||
*
|
||||
* @template TSteps of iterable<mixed>|int
|
||||
* @template TReturn
|
||||
*
|
||||
* @param TSteps $steps
|
||||
* @param ?Closure((TSteps is int ? int : value-of<TSteps>), Progress<TSteps>): TReturn $callback
|
||||
*/
|
||||
public function progress(string $label, iterable|int $steps, ?Closure $callback = null, string $hint = '', ?string $name = null): self
|
||||
{
|
||||
return $this->runPrompt(progress(...), get_defined_vars(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given prompt passing the given arguments.
|
||||
*
|
||||
* @param array<mixed> $arguments
|
||||
*/
|
||||
protected function runPrompt(callable $prompt, array $arguments, bool $ignoreWhenReverting = false): self
|
||||
{
|
||||
return $this->add(function (array $responses, mixed $previousResponse) use ($prompt, $arguments) {
|
||||
unset($arguments['name']);
|
||||
|
||||
if (array_key_exists('default', $arguments) && $previousResponse !== null) {
|
||||
$arguments['default'] = $previousResponse;
|
||||
}
|
||||
|
||||
return $prompt(...$arguments);
|
||||
}, name: $arguments['name'], ignoreWhenReverting: $ignoreWhenReverting);
|
||||
}
|
||||
}
|
||||
59
vendor/laravel/prompts/src/FormStep.php
vendored
Normal file
59
vendor/laravel/prompts/src/FormStep.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts;
|
||||
|
||||
use Closure;
|
||||
|
||||
class FormStep
|
||||
{
|
||||
protected readonly Closure $condition;
|
||||
|
||||
public function __construct(
|
||||
protected readonly Closure $step,
|
||||
bool|Closure $condition,
|
||||
public readonly ?string $name,
|
||||
protected readonly bool $ignoreWhenReverting,
|
||||
) {
|
||||
$this->condition = is_bool($condition)
|
||||
? fn () => $condition
|
||||
: $condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute this step.
|
||||
*
|
||||
* @param array<mixed> $responses
|
||||
*/
|
||||
public function run(array $responses, mixed $previousResponse): mixed
|
||||
{
|
||||
if (! $this->shouldRun($responses)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ($this->step)($responses, $previousResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the step should run based on the given condition.
|
||||
*
|
||||
* @param array<mixed> $responses
|
||||
*/
|
||||
protected function shouldRun(array $responses): bool
|
||||
{
|
||||
return ($this->condition)($responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this step should be skipped over when a subsequent step is reverted.
|
||||
*
|
||||
* @param array<mixed> $responses
|
||||
*/
|
||||
public function shouldIgnoreWhenReverting(array $responses): bool
|
||||
{
|
||||
if (! $this->shouldRun($responses)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->ignoreWhenReverting;
|
||||
}
|
||||
}
|
||||
16
vendor/laravel/prompts/src/Key.php
vendored
16
vendor/laravel/prompts/src/Key.php
vendored
@@ -6,8 +6,12 @@ class Key
|
||||
{
|
||||
const UP = "\e[A";
|
||||
|
||||
const SHIFT_UP = "\e[1;2A";
|
||||
|
||||
const DOWN = "\e[B";
|
||||
|
||||
const SHIFT_DOWN = "\e[1;2B";
|
||||
|
||||
const RIGHT = "\e[C";
|
||||
|
||||
const LEFT = "\e[D";
|
||||
@@ -20,6 +24,8 @@ class Key
|
||||
|
||||
const LEFT_ARROW = "\eOD";
|
||||
|
||||
const ESCAPE = "\e";
|
||||
|
||||
const DELETE = "\e[3~";
|
||||
|
||||
const BACKSPACE = "\177";
|
||||
@@ -71,11 +77,21 @@ class Key
|
||||
*/
|
||||
const CTRL_A = "\x01";
|
||||
|
||||
/**
|
||||
* EOF
|
||||
*/
|
||||
const CTRL_D = "\x04";
|
||||
|
||||
/**
|
||||
* End
|
||||
*/
|
||||
const CTRL_E = "\x05";
|
||||
|
||||
/**
|
||||
* Negative affirmation
|
||||
*/
|
||||
const CTRL_U = "\x15";
|
||||
|
||||
/**
|
||||
* Checks for the constant values for the given match and returns the match
|
||||
*
|
||||
|
||||
66
vendor/laravel/prompts/src/MultiSearchPrompt.php
vendored
66
vendor/laravel/prompts/src/MultiSearchPrompt.php
vendored
@@ -17,6 +17,11 @@ class MultiSearchPrompt extends Prompt
|
||||
*/
|
||||
protected ?array $matches = null;
|
||||
|
||||
/**
|
||||
* Whether the matches are initially a list.
|
||||
*/
|
||||
protected bool $isList;
|
||||
|
||||
/**
|
||||
* The selected values.
|
||||
*
|
||||
@@ -37,6 +42,7 @@ class MultiSearchPrompt extends Prompt
|
||||
public bool|string $required = false,
|
||||
public mixed $validate = null,
|
||||
public string $hint = '',
|
||||
public ?Closure $transform = null,
|
||||
) {
|
||||
$this->trackTypedValue(submit: false, ignore: fn ($key) => Key::oneOf([Key::SPACE, Key::HOME, Key::END, Key::CTRL_A, Key::CTRL_E], $key) && $this->highlighted !== null);
|
||||
|
||||
@@ -45,9 +51,11 @@ class MultiSearchPrompt extends Prompt
|
||||
$this->on('key', fn ($key) => match ($key) {
|
||||
Key::UP, Key::UP_ARROW, Key::SHIFT_TAB => $this->highlightPrevious(count($this->matches), true),
|
||||
Key::DOWN, Key::DOWN_ARROW, Key::TAB => $this->highlightNext(count($this->matches), true),
|
||||
Key::oneOf([Key::HOME, Key::CTRL_A], $key) => $this->highlighted !== null ? $this->highlight(0) : null,
|
||||
Key::oneOf([Key::END, Key::CTRL_E], $key) => $this->highlighted !== null ? $this->highlight(count($this->matches()) - 1) : null,
|
||||
Key::oneOf(Key::HOME, $key) => $this->highlighted !== null ? $this->highlight(0) : null,
|
||||
Key::oneOf(Key::END, $key) => $this->highlighted !== null ? $this->highlight(count($this->matches()) - 1) : null,
|
||||
Key::SPACE => $this->highlighted !== null ? $this->toggleHighlighted() : null,
|
||||
Key::CTRL_A => $this->highlighted !== null ? $this->toggleAll() : null,
|
||||
Key::CTRL_E => null,
|
||||
Key::ENTER => $this->submit(),
|
||||
Key::LEFT, Key::LEFT_ARROW, Key::RIGHT, Key::RIGHT_ARROW => $this->highlighted = null,
|
||||
default => $this->search(),
|
||||
@@ -96,16 +104,25 @@ class MultiSearchPrompt extends Prompt
|
||||
return $this->matches;
|
||||
}
|
||||
|
||||
if (strlen($this->typedValue) === 0) {
|
||||
$matches = ($this->options)($this->typedValue);
|
||||
$matches = ($this->options)($this->typedValue);
|
||||
|
||||
return $this->matches = [
|
||||
...array_diff($this->values, $matches),
|
||||
...$matches,
|
||||
];
|
||||
if (! isset($this->isList) && count($matches) > 0) {
|
||||
// This needs to be captured the first time we receive matches so
|
||||
// we know what we're dealing with later if matches is empty.
|
||||
$this->isList = array_is_list($matches);
|
||||
}
|
||||
|
||||
return $this->matches = ($this->options)($this->typedValue);
|
||||
if (! isset($this->isList)) {
|
||||
return $this->matches = [];
|
||||
}
|
||||
|
||||
if (strlen($this->typedValue) > 0) {
|
||||
return $this->matches = $matches;
|
||||
}
|
||||
|
||||
return $this->matches = $this->isList
|
||||
? [...array_diff(array_values($this->values), $matches), ...$matches]
|
||||
: array_diff($this->values, $matches) + $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,12 +135,33 @@ class MultiSearchPrompt extends Prompt
|
||||
return array_slice($this->matches(), $this->firstVisible, $this->scroll, preserve_keys: true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle all options.
|
||||
*/
|
||||
protected function toggleAll(): void
|
||||
{
|
||||
$allMatchesSelected = collect($this->matches)->every(fn ($label, $key) => $this->isList()
|
||||
? array_key_exists($label, $this->values)
|
||||
: array_key_exists($key, $this->values));
|
||||
|
||||
if ($allMatchesSelected) {
|
||||
$this->values = array_filter($this->values, fn ($value) => $this->isList()
|
||||
? ! in_array($value, $this->matches)
|
||||
: ! array_key_exists(array_search($value, $this->matches), $this->matches)
|
||||
);
|
||||
} else {
|
||||
$this->values = $this->isList()
|
||||
? array_merge($this->values, array_combine(array_values($this->matches), array_values($this->matches)))
|
||||
: array_merge($this->values, array_combine(array_keys($this->matches), array_values($this->matches)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the highlighted entry.
|
||||
*/
|
||||
protected function toggleHighlighted(): void
|
||||
{
|
||||
if (array_is_list($this->matches)) {
|
||||
if ($this->isList()) {
|
||||
$label = $this->matches[$this->highlighted];
|
||||
$key = $label;
|
||||
} else {
|
||||
@@ -165,4 +203,12 @@ class MultiSearchPrompt extends Prompt
|
||||
{
|
||||
return array_values($this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the matches are initially a list.
|
||||
*/
|
||||
public function isList(): bool
|
||||
{
|
||||
return $this->isList;
|
||||
}
|
||||
}
|
||||
|
||||
21
vendor/laravel/prompts/src/MultiSelectPrompt.php
vendored
21
vendor/laravel/prompts/src/MultiSelectPrompt.php
vendored
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Laravel\Prompts;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class MultiSelectPrompt extends Prompt
|
||||
@@ -43,6 +44,7 @@ class MultiSelectPrompt extends Prompt
|
||||
public bool|string $required = false,
|
||||
public mixed $validate = null,
|
||||
public string $hint = '',
|
||||
public ?Closure $transform = null,
|
||||
) {
|
||||
$this->options = $options instanceof Collection ? $options->all() : $options;
|
||||
$this->default = $default instanceof Collection ? $default->all() : $default;
|
||||
@@ -53,9 +55,10 @@ class MultiSelectPrompt extends Prompt
|
||||
$this->on('key', fn ($key) => match ($key) {
|
||||
Key::UP, Key::UP_ARROW, Key::LEFT, Key::LEFT_ARROW, Key::SHIFT_TAB, Key::CTRL_P, Key::CTRL_B, 'k', 'h' => $this->highlightPrevious(count($this->options)),
|
||||
Key::DOWN, Key::DOWN_ARROW, Key::RIGHT, Key::RIGHT_ARROW, Key::TAB, Key::CTRL_N, Key::CTRL_F, 'j', 'l' => $this->highlightNext(count($this->options)),
|
||||
Key::oneOf([Key::HOME, Key::CTRL_A], $key) => $this->highlight(0),
|
||||
Key::oneOf([Key::END, Key::CTRL_E], $key) => $this->highlight(count($this->options) - 1),
|
||||
Key::oneOf(Key::HOME, $key) => $this->highlight(0),
|
||||
Key::oneOf(Key::END, $key) => $this->highlight(count($this->options) - 1),
|
||||
Key::SPACE => $this->toggleHighlighted(),
|
||||
Key::CTRL_A => $this->toggleAll(),
|
||||
Key::ENTER => $this->submit(),
|
||||
default => null,
|
||||
});
|
||||
@@ -115,6 +118,20 @@ class MultiSelectPrompt extends Prompt
|
||||
return in_array($value, $this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle all options.
|
||||
*/
|
||||
protected function toggleAll(): void
|
||||
{
|
||||
if (count($this->values) === count($this->options)) {
|
||||
$this->values = [];
|
||||
} else {
|
||||
$this->values = array_is_list($this->options)
|
||||
? array_values($this->options)
|
||||
: array_keys($this->options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the highlighted entry.
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Laravel\Prompts;
|
||||
|
||||
use Closure;
|
||||
|
||||
class PasswordPrompt extends Prompt
|
||||
{
|
||||
use Concerns\TypedValue;
|
||||
@@ -15,6 +17,7 @@ class PasswordPrompt extends Prompt
|
||||
public bool|string $required = false,
|
||||
public mixed $validate = null,
|
||||
public string $hint = '',
|
||||
public ?Closure $transform = null,
|
||||
) {
|
||||
$this->trackTypedValue();
|
||||
}
|
||||
|
||||
28
vendor/laravel/prompts/src/PausePrompt.php
vendored
Normal file
28
vendor/laravel/prompts/src/PausePrompt.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts;
|
||||
|
||||
class PausePrompt extends Prompt
|
||||
{
|
||||
/**
|
||||
* Create a new PausePrompt instance.
|
||||
*/
|
||||
public function __construct(public string $message = 'Press enter to continue...')
|
||||
{
|
||||
$this->required = false;
|
||||
$this->validate = null;
|
||||
|
||||
$this->on('key', fn ($key) => match ($key) {
|
||||
Key::ENTER => $this->submit(),
|
||||
default => null,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the prompt.
|
||||
*/
|
||||
public function value(): bool
|
||||
{
|
||||
return static::$interactive;
|
||||
}
|
||||
}
|
||||
8
vendor/laravel/prompts/src/Progress.php
vendored
8
vendor/laravel/prompts/src/Progress.php
vendored
@@ -138,6 +138,14 @@ class Progress extends Prompt
|
||||
$this->resetSignals();
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the progress bar to re-render.
|
||||
*/
|
||||
public function render(): void
|
||||
{
|
||||
parent::render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the label.
|
||||
*/
|
||||
|
||||
161
vendor/laravel/prompts/src/Prompt.php
vendored
161
vendor/laravel/prompts/src/Prompt.php
vendored
@@ -3,6 +3,7 @@
|
||||
namespace Laravel\Prompts;
|
||||
|
||||
use Closure;
|
||||
use Laravel\Prompts\Exceptions\FormRevertedException;
|
||||
use Laravel\Prompts\Output\ConsoleOutput;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
@@ -29,6 +30,11 @@ abstract class Prompt
|
||||
*/
|
||||
public string $error = '';
|
||||
|
||||
/**
|
||||
* The cancel message displayed when this prompt is cancelled.
|
||||
*/
|
||||
public string $cancelMessage = 'Cancelled.';
|
||||
|
||||
/**
|
||||
* The previously rendered frame.
|
||||
*/
|
||||
@@ -44,6 +50,11 @@ abstract class Prompt
|
||||
*/
|
||||
public bool|string $required;
|
||||
|
||||
/**
|
||||
* The transformation callback.
|
||||
*/
|
||||
public ?Closure $transform = null;
|
||||
|
||||
/**
|
||||
* The validator callback or rules.
|
||||
*/
|
||||
@@ -52,7 +63,7 @@ abstract class Prompt
|
||||
/**
|
||||
* The cancellation callback.
|
||||
*/
|
||||
protected static Closure $cancelUsing;
|
||||
protected static ?Closure $cancelUsing;
|
||||
|
||||
/**
|
||||
* Indicates if the prompt has been validated.
|
||||
@@ -64,6 +75,11 @@ abstract class Prompt
|
||||
*/
|
||||
protected static ?Closure $validateUsing;
|
||||
|
||||
/**
|
||||
* The revert handler from the StepBuilder.
|
||||
*/
|
||||
protected static ?Closure $revertUsing = null;
|
||||
|
||||
/**
|
||||
* The output instance.
|
||||
*/
|
||||
@@ -125,7 +141,11 @@ abstract class Prompt
|
||||
}
|
||||
}
|
||||
|
||||
return $this->value();
|
||||
if ($key === Key::CTRL_U && self::$revertUsing) {
|
||||
throw new FormRevertedException;
|
||||
}
|
||||
|
||||
return $this->transformedValue();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -136,7 +156,7 @@ abstract class Prompt
|
||||
/**
|
||||
* Register a callback to be invoked when a user cancels a prompt.
|
||||
*/
|
||||
public static function cancelUsing(Closure $callback): void
|
||||
public static function cancelUsing(?Closure $callback): void
|
||||
{
|
||||
static::$cancelUsing = $callback;
|
||||
}
|
||||
@@ -172,7 +192,7 @@ abstract class Prompt
|
||||
*/
|
||||
protected static function output(): OutputInterface
|
||||
{
|
||||
return self::$output ??= new ConsoleOutput();
|
||||
return self::$output ??= new ConsoleOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,7 +212,7 @@ abstract class Prompt
|
||||
*/
|
||||
public static function terminal(): Terminal
|
||||
{
|
||||
return static::$terminal ??= new Terminal();
|
||||
return static::$terminal ??= new Terminal;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,11 +223,33 @@ abstract class Prompt
|
||||
static::$validateUsing = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert the prompt using the given callback.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function revertUsing(Closure $callback): void
|
||||
{
|
||||
static::$revertUsing = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear any previous revert callback.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function preventReverting(): void
|
||||
{
|
||||
static::$revertUsing = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the prompt.
|
||||
*/
|
||||
protected function render(): void
|
||||
{
|
||||
$this->terminal()->initDimensions();
|
||||
|
||||
$frame = $this->renderTheme();
|
||||
|
||||
if ($frame === $this->prevFrame) {
|
||||
@@ -223,35 +265,14 @@ abstract class Prompt
|
||||
return;
|
||||
}
|
||||
|
||||
$this->resetCursorPosition();
|
||||
$terminalHeight = $this->terminal()->lines();
|
||||
$previousFrameHeight = count(explode(PHP_EOL, $this->prevFrame));
|
||||
$renderableLines = array_slice(explode(PHP_EOL, $frame), abs(min(0, $terminalHeight - $previousFrameHeight)));
|
||||
|
||||
// Ensure that the full frame is buffered so subsequent output can see how many trailing newlines were written.
|
||||
if ($this->state === 'submit') {
|
||||
$this->eraseDown();
|
||||
static::output()->write($frame);
|
||||
|
||||
$this->prevFrame = '';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$diff = $this->diffLines($this->prevFrame, $frame);
|
||||
|
||||
if (count($diff) === 1) { // Update the single line that changed.
|
||||
$diffLine = $diff[0];
|
||||
$this->moveCursor(0, $diffLine);
|
||||
$this->eraseLines(1);
|
||||
$lines = explode(PHP_EOL, $frame);
|
||||
static::output()->write($lines[$diffLine]);
|
||||
$this->moveCursor(0, count($lines) - $diffLine - 1);
|
||||
} elseif (count($diff) > 1) { // Re-render everything past the first change
|
||||
$diffLine = $diff[0];
|
||||
$this->moveCursor(0, $diffLine);
|
||||
$this->eraseDown();
|
||||
$lines = explode(PHP_EOL, $frame);
|
||||
$newLines = array_slice($lines, $diffLine);
|
||||
static::output()->write(implode(PHP_EOL, $newLines));
|
||||
}
|
||||
$this->moveCursorToColumn(1);
|
||||
$this->moveCursorUp(min($terminalHeight, $previousFrameHeight) - 1);
|
||||
$this->eraseDown();
|
||||
$this->output()->write(implode(PHP_EOL, $renderableLines));
|
||||
|
||||
$this->prevFrame = $frame;
|
||||
}
|
||||
@@ -261,47 +282,13 @@ abstract class Prompt
|
||||
*/
|
||||
protected function submit(): void
|
||||
{
|
||||
$this->validate($this->value());
|
||||
$this->validate($this->transformedValue());
|
||||
|
||||
if ($this->state !== 'error') {
|
||||
$this->state = 'submit';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the cursor position to the beginning of the previous frame.
|
||||
*/
|
||||
private function resetCursorPosition(): void
|
||||
{
|
||||
$lines = count(explode(PHP_EOL, $this->prevFrame)) - 1;
|
||||
|
||||
$this->moveCursor(-999, $lines * -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the difference between two strings.
|
||||
*
|
||||
* @return array<int>
|
||||
*/
|
||||
private function diffLines(string $a, string $b): array
|
||||
{
|
||||
if ($a === $b) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$aLines = explode(PHP_EOL, $a);
|
||||
$bLines = explode(PHP_EOL, $b);
|
||||
$diff = [];
|
||||
|
||||
for ($i = 0; $i < max(count($aLines), count($bLines)); $i++) {
|
||||
if (! isset($aLines[$i]) || ! isset($bLines[$i]) || $aLines[$i] !== $bLines[$i]) {
|
||||
$diff[] = $i;
|
||||
}
|
||||
}
|
||||
|
||||
return $diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a key press and determine whether to continue.
|
||||
*/
|
||||
@@ -317,6 +304,22 @@ abstract class Prompt
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($key === Key::CTRL_U) {
|
||||
if (! self::$revertUsing) {
|
||||
$this->state = 'error';
|
||||
$this->error = 'This cannot be reverted.';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->state = 'cancel';
|
||||
$this->cancelMessage = 'Reverted.';
|
||||
|
||||
call_user_func(self::$revertUsing);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($key === Key::CTRL_C) {
|
||||
$this->state = 'cancel';
|
||||
|
||||
@@ -324,12 +327,32 @@ abstract class Prompt
|
||||
}
|
||||
|
||||
if ($this->validated) {
|
||||
$this->validate($this->value());
|
||||
$this->validate($this->transformedValue());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the input.
|
||||
*/
|
||||
private function transform(mixed $value): mixed
|
||||
{
|
||||
if (is_null($this->transform)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return call_user_func($this->transform, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transformed value of the prompt.
|
||||
*/
|
||||
protected function transformedValue(): mixed
|
||||
{
|
||||
return $this->transform($this->value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the input.
|
||||
*/
|
||||
|
||||
1
vendor/laravel/prompts/src/SearchPrompt.php
vendored
1
vendor/laravel/prompts/src/SearchPrompt.php
vendored
@@ -31,6 +31,7 @@ class SearchPrompt extends Prompt
|
||||
public mixed $validate = null,
|
||||
public string $hint = '',
|
||||
public bool|string $required = true,
|
||||
public ?Closure $transform = null,
|
||||
) {
|
||||
if ($this->required === false) {
|
||||
throw new InvalidArgumentException('Argument [required] must be true or a string.');
|
||||
|
||||
2
vendor/laravel/prompts/src/SelectPrompt.php
vendored
2
vendor/laravel/prompts/src/SelectPrompt.php
vendored
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Laravel\Prompts;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Support\Collection;
|
||||
use InvalidArgumentException;
|
||||
|
||||
@@ -29,6 +30,7 @@ class SelectPrompt extends Prompt
|
||||
public mixed $validate = null,
|
||||
public string $hint = '',
|
||||
public bool|string $required = true,
|
||||
public ?Closure $transform = null,
|
||||
) {
|
||||
if ($this->required === false) {
|
||||
throw new InvalidArgumentException('Argument [required] must be true or a string.');
|
||||
|
||||
9
vendor/laravel/prompts/src/SuggestPrompt.php
vendored
9
vendor/laravel/prompts/src/SuggestPrompt.php
vendored
@@ -14,7 +14,7 @@ class SuggestPrompt extends Prompt
|
||||
/**
|
||||
* The options for the suggest prompt.
|
||||
*
|
||||
* @var array<string>|Closure(string): array<string>
|
||||
* @var array<string>|Closure(string): (array<string>|Collection<int, string>)
|
||||
*/
|
||||
public array|Closure $options;
|
||||
|
||||
@@ -28,7 +28,7 @@ class SuggestPrompt extends Prompt
|
||||
/**
|
||||
* Create a new SuggestPrompt instance.
|
||||
*
|
||||
* @param array<string>|Collection<int, string>|Closure(string): array<string> $options
|
||||
* @param array<string>|Collection<int, string>|Closure(string): (array<string>|Collection<int, string>) $options
|
||||
*/
|
||||
public function __construct(
|
||||
public string $label,
|
||||
@@ -39,6 +39,7 @@ class SuggestPrompt extends Prompt
|
||||
public bool|string $required = false,
|
||||
public mixed $validate = null,
|
||||
public string $hint = '',
|
||||
public ?Closure $transform = null,
|
||||
) {
|
||||
$this->options = $options instanceof Collection ? $options->all() : $options;
|
||||
|
||||
@@ -91,7 +92,9 @@ class SuggestPrompt extends Prompt
|
||||
}
|
||||
|
||||
if ($this->options instanceof Closure) {
|
||||
return $this->matches = array_values(($this->options)($this->value()));
|
||||
$matches = ($this->options)($this->value());
|
||||
|
||||
return $this->matches = array_values($matches instanceof Collection ? $matches->all() : $matches);
|
||||
}
|
||||
|
||||
return $this->matches = array_values(array_filter($this->options, function ($option) {
|
||||
|
||||
26
vendor/laravel/prompts/src/Terminal.php
vendored
26
vendor/laravel/prompts/src/Terminal.php
vendored
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Laravel\Prompts;
|
||||
|
||||
use ReflectionClass;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Terminal as SymfonyTerminal;
|
||||
|
||||
@@ -13,14 +14,17 @@ class Terminal
|
||||
protected ?string $initialTtyMode;
|
||||
|
||||
/**
|
||||
* The number of columns in the terminal.
|
||||
* The Symfony Terminal instance.
|
||||
*/
|
||||
protected int $cols;
|
||||
protected SymfonyTerminal $terminal;
|
||||
|
||||
/**
|
||||
* The number of lines in the terminal.
|
||||
* Create a new Terminal instance.
|
||||
*/
|
||||
protected int $lines;
|
||||
public function __construct()
|
||||
{
|
||||
$this->terminal = new SymfonyTerminal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a line from the terminal.
|
||||
@@ -59,7 +63,7 @@ class Terminal
|
||||
*/
|
||||
public function cols(): int
|
||||
{
|
||||
return $this->cols ??= (new SymfonyTerminal())->getWidth();
|
||||
return $this->terminal->getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +71,17 @@ class Terminal
|
||||
*/
|
||||
public function lines(): int
|
||||
{
|
||||
return $this->lines ??= (new SymfonyTerminal())->getHeight();
|
||||
return $this->terminal->getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re)initialize the terminal dimensions.
|
||||
*/
|
||||
public function initDimensions(): void
|
||||
{
|
||||
(new ReflectionClass($this->terminal))
|
||||
->getMethod('initDimensions')
|
||||
->invoke($this->terminal);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
3
vendor/laravel/prompts/src/TextPrompt.php
vendored
3
vendor/laravel/prompts/src/TextPrompt.php
vendored
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Laravel\Prompts;
|
||||
|
||||
use Closure;
|
||||
|
||||
class TextPrompt extends Prompt
|
||||
{
|
||||
use Concerns\TypedValue;
|
||||
@@ -16,6 +18,7 @@ class TextPrompt extends Prompt
|
||||
public bool|string $required = false,
|
||||
public mixed $validate = null,
|
||||
public string $hint = '',
|
||||
public ?Closure $transform = null,
|
||||
) {
|
||||
$this->trackTypedValue($default);
|
||||
}
|
||||
|
||||
248
vendor/laravel/prompts/src/TextareaPrompt.php
vendored
Normal file
248
vendor/laravel/prompts/src/TextareaPrompt.php
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts;
|
||||
|
||||
use Closure;
|
||||
|
||||
class TextareaPrompt extends Prompt
|
||||
{
|
||||
use Concerns\Scrolling;
|
||||
use Concerns\Truncation;
|
||||
use Concerns\TypedValue;
|
||||
|
||||
/**
|
||||
* The width of the textarea.
|
||||
*/
|
||||
public int $width = 60;
|
||||
|
||||
/**
|
||||
* Create a new TextareaPrompt instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public string $label,
|
||||
public string $placeholder = '',
|
||||
public string $default = '',
|
||||
public bool|string $required = false,
|
||||
public mixed $validate = null,
|
||||
public string $hint = '',
|
||||
int $rows = 5,
|
||||
public ?Closure $transform = null,
|
||||
) {
|
||||
$this->scroll = $rows;
|
||||
|
||||
$this->initializeScrolling();
|
||||
|
||||
$this->trackTypedValue(
|
||||
default: $default,
|
||||
submit: false,
|
||||
allowNewLine: true,
|
||||
);
|
||||
|
||||
$this->on('key', function ($key) {
|
||||
if ($key[0] === "\e") {
|
||||
match ($key) {
|
||||
Key::UP, Key::UP_ARROW, Key::CTRL_P => $this->handleUpKey(),
|
||||
Key::DOWN, Key::DOWN_ARROW, Key::CTRL_N => $this->handleDownKey(),
|
||||
default => null,
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Keys may be buffered.
|
||||
foreach (mb_str_split($key) as $key) {
|
||||
if ($key === Key::CTRL_D) {
|
||||
$this->submit();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatted value with a virtual cursor.
|
||||
*/
|
||||
public function valueWithCursor(): string
|
||||
{
|
||||
if ($this->value() === '') {
|
||||
return $this->wrappedPlaceholderWithCursor();
|
||||
}
|
||||
|
||||
return $this->addCursor($this->wrappedValue(), $this->cursorPosition + $this->cursorOffset(), -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The word-wrapped version of the typed value.
|
||||
*/
|
||||
public function wrappedValue(): string
|
||||
{
|
||||
return $this->mbWordwrap($this->value(), $this->width, PHP_EOL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* The formatted lines.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function lines(): array
|
||||
{
|
||||
return explode(PHP_EOL, $this->wrappedValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* The currently visible lines.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function visible(): array
|
||||
{
|
||||
$this->adjustVisibleWindow();
|
||||
|
||||
$withCursor = $this->valueWithCursor();
|
||||
|
||||
return array_slice(explode(PHP_EOL, $withCursor), $this->firstVisible, $this->scroll, preserve_keys: true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the up key press.
|
||||
*/
|
||||
protected function handleUpKey(): void
|
||||
{
|
||||
if ($this->cursorPosition === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lines = collect($this->lines());
|
||||
|
||||
// Line length + 1 for the newline character
|
||||
$lineLengths = $lines->map(fn ($line, $index) => mb_strlen($line) + ($index === $lines->count() - 1 ? 0 : 1));
|
||||
|
||||
$currentLineIndex = $this->currentLineIndex();
|
||||
|
||||
if ($currentLineIndex === 0) {
|
||||
// They're already at the first line, jump them to the first position
|
||||
$this->cursorPosition = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$currentLines = $lineLengths->slice(0, $currentLineIndex + 1);
|
||||
|
||||
$currentColumn = $currentLines->last() - ($currentLines->sum() - $this->cursorPosition);
|
||||
|
||||
$destinationLineLength = ($lineLengths->get($currentLineIndex - 1) ?? $currentLines->first()) - 1;
|
||||
|
||||
$newColumn = min($destinationLineLength, $currentColumn);
|
||||
|
||||
$fullLines = $currentLines->slice(0, -2);
|
||||
|
||||
$this->cursorPosition = $fullLines->sum() + $newColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the down key press.
|
||||
*/
|
||||
protected function handleDownKey(): void
|
||||
{
|
||||
$lines = collect($this->lines());
|
||||
|
||||
// Line length + 1 for the newline character
|
||||
$lineLengths = $lines->map(fn ($line, $index) => mb_strlen($line) + ($index === $lines->count() - 1 ? 0 : 1));
|
||||
|
||||
$currentLineIndex = $this->currentLineIndex();
|
||||
|
||||
if ($currentLineIndex === $lines->count() - 1) {
|
||||
// They're already at the last line, jump them to the last position
|
||||
$this->cursorPosition = mb_strlen($lines->implode(PHP_EOL));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Lines up to and including the current line
|
||||
$currentLines = $lineLengths->slice(0, $currentLineIndex + 1);
|
||||
|
||||
$currentColumn = $currentLines->last() - ($currentLines->sum() - $this->cursorPosition);
|
||||
|
||||
$destinationLineLength = $lineLengths->get($currentLineIndex + 1) ?? $currentLines->last();
|
||||
|
||||
if ($currentLineIndex + 1 !== $lines->count() - 1) {
|
||||
$destinationLineLength--;
|
||||
}
|
||||
|
||||
$newColumn = min(max(0, $destinationLineLength), $currentColumn);
|
||||
|
||||
$this->cursorPosition = $currentLines->sum() + $newColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the visible window to ensure the cursor is always visible.
|
||||
*/
|
||||
protected function adjustVisibleWindow(): void
|
||||
{
|
||||
if (count($this->lines()) < $this->scroll) {
|
||||
return;
|
||||
}
|
||||
|
||||
$currentLineIndex = $this->currentLineIndex();
|
||||
|
||||
while ($this->firstVisible + $this->scroll <= $currentLineIndex) {
|
||||
$this->firstVisible++;
|
||||
}
|
||||
|
||||
if ($currentLineIndex === $this->firstVisible - 1) {
|
||||
$this->firstVisible = max(0, $this->firstVisible - 1);
|
||||
}
|
||||
|
||||
// Make sure there are always the scroll amount visible
|
||||
if ($this->firstVisible + $this->scroll > count($this->lines())) {
|
||||
$this->firstVisible = count($this->lines()) - $this->scroll;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of the current line that the cursor is on.
|
||||
*/
|
||||
protected function currentLineIndex(): int
|
||||
{
|
||||
$totalLineLength = 0;
|
||||
|
||||
return (int) collect($this->lines())->search(function ($line) use (&$totalLineLength) {
|
||||
$totalLineLength += mb_strlen($line) + 1;
|
||||
|
||||
return $totalLineLength > $this->cursorPosition;
|
||||
}) ?: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the cursor offset considering wrapped words.
|
||||
*/
|
||||
protected function cursorOffset(): int
|
||||
{
|
||||
$cursorOffset = 0;
|
||||
|
||||
preg_match_all('/\S{'.$this->width.',}/u', $this->value(), $matches, PREG_OFFSET_CAPTURE);
|
||||
|
||||
foreach ($matches[0] as $match) {
|
||||
if ($this->cursorPosition + $cursorOffset >= $match[1] + mb_strwidth($match[0])) {
|
||||
$cursorOffset += (int) floor(mb_strwidth($match[0]) / $this->width);
|
||||
}
|
||||
}
|
||||
|
||||
return $cursorOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapped version of the placeholder with the virtual cursor.
|
||||
*/
|
||||
protected function wrappedPlaceholderWithCursor(): string
|
||||
{
|
||||
return implode(PHP_EOL, array_map(
|
||||
$this->dim(...),
|
||||
explode(PHP_EOL, $this->addCursor(
|
||||
$this->mbWordwrap($this->placeholder, $this->width, PHP_EOL, true),
|
||||
cursorPosition: 0,
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ use Laravel\Prompts\Prompt;
|
||||
|
||||
trait DrawsBoxes
|
||||
{
|
||||
use InteractsWithStrings;
|
||||
|
||||
protected int $minWidth = 60;
|
||||
|
||||
/**
|
||||
@@ -55,39 +57,4 @@ trait DrawsBoxes
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of the longest line.
|
||||
*
|
||||
* @param array<string> $lines
|
||||
*/
|
||||
protected function longest(array $lines, int $padding = 0): int
|
||||
{
|
||||
return max(
|
||||
$this->minWidth,
|
||||
collect($lines)
|
||||
->map(fn ($line) => mb_strwidth($this->stripEscapeSequences($line)) + $padding)
|
||||
->max()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pad text ignoring ANSI escape sequences.
|
||||
*/
|
||||
protected function pad(string $text, int $length): string
|
||||
{
|
||||
$rightPadding = str_repeat(' ', max(0, $length - mb_strwidth($this->stripEscapeSequences($text))));
|
||||
|
||||
return "{$text}{$rightPadding}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip ANSI escape sequences from the given text.
|
||||
*/
|
||||
protected function stripEscapeSequences(string $text): string
|
||||
{
|
||||
$text = preg_replace("/\e[^m]*m/", '', $text);
|
||||
|
||||
return preg_replace("/<(?:(?:[fb]g|options)=[a-z,;]+)+>(.*?)<\/>/i", '$1', $text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ trait DrawsScrollbars
|
||||
|
||||
$scrollPosition = $this->scrollPosition($firstVisible, $height, $total);
|
||||
|
||||
return $visible
|
||||
return $visible // @phpstan-ignore return.type
|
||||
->values()
|
||||
->map(fn ($line) => $this->pad($line, $width))
|
||||
->map(fn ($line, $index) => match ($index) {
|
||||
|
||||
46
vendor/laravel/prompts/src/Themes/Default/Concerns/InteractsWithStrings.php
vendored
Normal file
46
vendor/laravel/prompts/src/Themes/Default/Concerns/InteractsWithStrings.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Themes\Default\Concerns;
|
||||
|
||||
trait InteractsWithStrings
|
||||
{
|
||||
/**
|
||||
* Get the length of the longest line.
|
||||
*
|
||||
* @param array<string> $lines
|
||||
*/
|
||||
protected function longest(array $lines, int $padding = 0): int
|
||||
{
|
||||
return max(
|
||||
$this->minWidth,
|
||||
collect($lines)
|
||||
->map(fn ($line) => mb_strwidth($this->stripEscapeSequences($line)) + $padding)
|
||||
->max()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pad text ignoring ANSI escape sequences.
|
||||
*/
|
||||
protected function pad(string $text, int $length, string $char = ' '): string
|
||||
{
|
||||
$rightPadding = str_repeat($char, max(0, $length - mb_strwidth($this->stripEscapeSequences($text))));
|
||||
|
||||
return "{$text}{$rightPadding}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip ANSI escape sequences from the given text.
|
||||
*/
|
||||
protected function stripEscapeSequences(string $text): string
|
||||
{
|
||||
// Strip ANSI escape sequences.
|
||||
$text = preg_replace("/\e[^m]*m/", '', $text);
|
||||
|
||||
// Strip Symfony named style tags.
|
||||
$text = preg_replace("/<(info|comment|question|error)>(.*?)<\/\\1>/", '$2', $text);
|
||||
|
||||
// Strip Symfony inline style tags.
|
||||
return preg_replace("/<(?:(?:[fb]g|options)=[a-z,;]+)+>(.*?)<\/>/i", '$1', $text);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class ConfirmPromptRenderer extends Renderer
|
||||
$this->renderOptions($prompt),
|
||||
color: 'red'
|
||||
)
|
||||
->error('Cancelled.'),
|
||||
->error($prompt->cancelMessage),
|
||||
|
||||
'error' => $this
|
||||
->box(
|
||||
|
||||
@@ -30,7 +30,7 @@ class MultiSearchPromptRenderer extends Renderer implements Scrolling
|
||||
$this->strikethrough($this->dim($this->truncate($prompt->searchValue() ?: $prompt->placeholder, $maxWidth))),
|
||||
color: 'red',
|
||||
)
|
||||
->error('Cancelled'),
|
||||
->error($prompt->cancelMessage),
|
||||
|
||||
'error' => $this
|
||||
->box(
|
||||
@@ -111,11 +111,11 @@ class MultiSearchPromptRenderer extends Renderer implements Scrolling
|
||||
|
||||
return $this->scrollbar(
|
||||
collect($prompt->visible())
|
||||
->map(fn ($label) => $this->truncate($label, $prompt->terminal()->cols() - 10))
|
||||
->map(fn ($label) => $this->truncate($label, $prompt->terminal()->cols() - 12))
|
||||
->map(function ($label, $key) use ($prompt) {
|
||||
$index = array_search($key, array_keys($prompt->matches()));
|
||||
$active = $index === $prompt->highlighted;
|
||||
$selected = array_is_list($prompt->visible())
|
||||
$selected = $prompt->isList()
|
||||
? in_array($label, $prompt->value())
|
||||
: in_array($key, $prompt->value());
|
||||
|
||||
@@ -156,7 +156,7 @@ class MultiSearchPromptRenderer extends Renderer implements Scrolling
|
||||
$info = count($prompt->value()).' selected';
|
||||
|
||||
$hiddenCount = count($prompt->value()) - collect($prompt->matches())
|
||||
->filter(fn ($label, $key) => in_array(array_is_list($prompt->matches()) ? $label : $key, $prompt->value()))
|
||||
->filter(fn ($label, $key) => in_array($prompt->isList() ? $label : $key, $prompt->value()))
|
||||
->count();
|
||||
|
||||
if ($hiddenCount > 0) {
|
||||
|
||||
@@ -28,7 +28,7 @@ class MultiSelectPromptRenderer extends Renderer implements Scrolling
|
||||
$this->renderOptions($prompt),
|
||||
color: 'red',
|
||||
)
|
||||
->error('Cancelled.'),
|
||||
->error($prompt->cancelMessage),
|
||||
|
||||
'error' => $this
|
||||
->box(
|
||||
|
||||
@@ -28,7 +28,7 @@ class PasswordPromptRenderer extends Renderer
|
||||
$this->strikethrough($this->dim($this->truncate($prompt->masked() ?: $prompt->placeholder, $maxWidth))),
|
||||
color: 'red',
|
||||
)
|
||||
->error('Cancelled.'),
|
||||
->error($prompt->cancelMessage),
|
||||
|
||||
'error' => $this
|
||||
->box(
|
||||
|
||||
25
vendor/laravel/prompts/src/Themes/Default/PausePromptRenderer.php
vendored
Normal file
25
vendor/laravel/prompts/src/Themes/Default/PausePromptRenderer.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Themes\Default;
|
||||
|
||||
use Laravel\Prompts\PausePrompt;
|
||||
|
||||
class PausePromptRenderer extends Renderer
|
||||
{
|
||||
use Concerns\DrawsBoxes;
|
||||
|
||||
/**
|
||||
* Render the pause prompt.
|
||||
*/
|
||||
public function __invoke(PausePrompt $prompt): string
|
||||
{
|
||||
match ($prompt->state) {
|
||||
'submit' => collect(explode(PHP_EOL, $prompt->message))
|
||||
->each(fn ($line) => $this->line($this->gray(" {$line}"))),
|
||||
default => collect(explode(PHP_EOL, $prompt->message))
|
||||
->each(fn ($line) => $this->line($this->green(" {$line}")))
|
||||
};
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ class ProgressRenderer extends Renderer
|
||||
color: 'red',
|
||||
info: $progress->progress.'/'.$progress->total,
|
||||
)
|
||||
->error('Cancelled.'),
|
||||
->error($progress->cancelMessage),
|
||||
|
||||
default => $this
|
||||
->box(
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace Laravel\Prompts\Themes\Default;
|
||||
use Laravel\Prompts\Concerns\Colors;
|
||||
use Laravel\Prompts\Concerns\Truncation;
|
||||
use Laravel\Prompts\Prompt;
|
||||
use RuntimeException;
|
||||
|
||||
abstract class Renderer
|
||||
{
|
||||
@@ -22,7 +21,7 @@ abstract class Renderer
|
||||
*/
|
||||
public function __construct(protected Prompt $prompt)
|
||||
{
|
||||
$this->checkTerminalSize($prompt);
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,19 +99,4 @@ abstract class Renderer
|
||||
.$this->output
|
||||
.(in_array($this->prompt->state, ['submit', 'cancel']) ? PHP_EOL : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the terminal is large enough to render the prompt.
|
||||
*/
|
||||
private function checkTerminalSize(Prompt $prompt): void
|
||||
{
|
||||
$required = 8;
|
||||
$actual = $prompt->terminal()->lines();
|
||||
|
||||
if ($actual < $required) {
|
||||
throw new RuntimeException(
|
||||
"The terminal height must be at least [$required] lines but is currently [$actual]. Please increase the height or reduce the font size."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class SearchPromptRenderer extends Renderer implements Scrolling
|
||||
$this->strikethrough($this->dim($this->truncate($prompt->searchValue() ?: $prompt->placeholder, $maxWidth))),
|
||||
color: 'red',
|
||||
)
|
||||
->error('Cancelled'),
|
||||
->error($prompt->cancelMessage),
|
||||
|
||||
'error' => $this
|
||||
->box(
|
||||
|
||||
@@ -30,7 +30,7 @@ class SelectPromptRenderer extends Renderer implements Scrolling
|
||||
$this->renderOptions($prompt),
|
||||
color: 'red',
|
||||
)
|
||||
->error('Cancelled.'),
|
||||
->error($prompt->cancelMessage),
|
||||
|
||||
'error' => $this
|
||||
->box(
|
||||
|
||||
@@ -30,7 +30,7 @@ class SuggestPromptRenderer extends Renderer implements Scrolling
|
||||
$this->strikethrough($this->dim($this->truncate($prompt->value() ?: $prompt->placeholder, $maxWidth))),
|
||||
color: 'red',
|
||||
)
|
||||
->error('Cancelled'),
|
||||
->error($prompt->cancelMessage),
|
||||
|
||||
'error' => $this
|
||||
->box(
|
||||
|
||||
@@ -14,7 +14,7 @@ class TableRenderer extends Renderer
|
||||
*/
|
||||
public function __invoke(Table $table): string
|
||||
{
|
||||
$tableStyle = (new TableStyle())
|
||||
$tableStyle = (new TableStyle)
|
||||
->setHorizontalBorderChars('─')
|
||||
->setVerticalBorderChars('│', '│')
|
||||
->setCellHeaderFormat($this->dim('<fg=default>%s</>'))
|
||||
@@ -26,7 +26,7 @@ class TableRenderer extends Renderer
|
||||
$tableStyle->setCrossingChars('┼', '<fg=gray>┌', '┬', '┐', '┤', '┘</>', '┴', '└', '├');
|
||||
}
|
||||
|
||||
$buffered = new BufferedConsoleOutput();
|
||||
$buffered = new BufferedConsoleOutput;
|
||||
|
||||
(new SymfonyTable($buffered))
|
||||
->setHeaders($table->headers)
|
||||
|
||||
@@ -28,7 +28,7 @@ class TextPromptRenderer extends Renderer
|
||||
$this->strikethrough($this->dim($this->truncate($prompt->value() ?: $prompt->placeholder, $maxWidth))),
|
||||
color: 'red',
|
||||
)
|
||||
->error('Cancelled.'),
|
||||
->error($prompt->cancelMessage),
|
||||
|
||||
'error' => $this
|
||||
->box(
|
||||
|
||||
87
vendor/laravel/prompts/src/Themes/Default/TextareaPromptRenderer.php
vendored
Normal file
87
vendor/laravel/prompts/src/Themes/Default/TextareaPromptRenderer.php
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Prompts\Themes\Default;
|
||||
|
||||
use Laravel\Prompts\TextareaPrompt;
|
||||
use Laravel\Prompts\Themes\Contracts\Scrolling;
|
||||
|
||||
class TextareaPromptRenderer extends Renderer implements Scrolling
|
||||
{
|
||||
use Concerns\DrawsBoxes;
|
||||
use Concerns\DrawsScrollbars;
|
||||
|
||||
/**
|
||||
* Render the textarea prompt.
|
||||
*/
|
||||
public function __invoke(TextareaPrompt $prompt): string
|
||||
{
|
||||
$prompt->width = $prompt->terminal()->cols() - 8;
|
||||
|
||||
return match ($prompt->state) {
|
||||
'submit' => $this
|
||||
->box(
|
||||
$this->dim($this->truncate($prompt->label, $prompt->width)),
|
||||
collect($prompt->lines())->implode(PHP_EOL),
|
||||
),
|
||||
|
||||
'cancel' => $this
|
||||
->box(
|
||||
$this->truncate($prompt->label, $prompt->width),
|
||||
collect($prompt->lines())->map(fn ($line) => $this->strikethrough($this->dim($line)))->implode(PHP_EOL),
|
||||
color: 'red',
|
||||
)
|
||||
->error($prompt->cancelMessage),
|
||||
|
||||
'error' => $this
|
||||
->box(
|
||||
$this->truncate($prompt->label, $prompt->width),
|
||||
$this->renderText($prompt),
|
||||
color: 'yellow',
|
||||
info: 'Ctrl+D to submit'
|
||||
)
|
||||
->warning($this->truncate($prompt->error, $prompt->terminal()->cols() - 5)),
|
||||
|
||||
default => $this
|
||||
->box(
|
||||
$this->cyan($this->truncate($prompt->label, $prompt->width)),
|
||||
$this->renderText($prompt),
|
||||
info: 'Ctrl+D to submit'
|
||||
)
|
||||
->when(
|
||||
$prompt->hint,
|
||||
fn () => $this->hint($prompt->hint),
|
||||
fn () => $this->newLine() // Space for errors
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the text in the prompt.
|
||||
*/
|
||||
protected function renderText(TextareaPrompt $prompt): string
|
||||
{
|
||||
$visible = collect($prompt->visible());
|
||||
|
||||
while ($visible->count() < $prompt->scroll) {
|
||||
$visible->push('');
|
||||
}
|
||||
|
||||
$longest = $this->longest($prompt->lines()) + 2;
|
||||
|
||||
return $this->scrollbar(
|
||||
$visible,
|
||||
$prompt->firstVisible,
|
||||
$prompt->scroll,
|
||||
count($prompt->lines()),
|
||||
min($longest, $prompt->width + 2),
|
||||
)->implode(PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of lines to reserve outside of the scrollable area.
|
||||
*/
|
||||
public function reservedLines(): int
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
417
vendor/laravel/prompts/src/helpers.php
vendored
417
vendor/laravel/prompts/src/helpers.php
vendored
@@ -5,182 +5,245 @@ namespace Laravel\Prompts;
|
||||
use Closure;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Prompt the user for text input.
|
||||
*/
|
||||
function text(string $label, string $placeholder = '', string $default = '', bool|string $required = false, mixed $validate = null, string $hint = ''): string
|
||||
{
|
||||
return (new TextPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user for input, hiding the value.
|
||||
*/
|
||||
function password(string $label, string $placeholder = '', bool|string $required = false, mixed $validate = null, string $hint = ''): string
|
||||
{
|
||||
return (new PasswordPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to select an option.
|
||||
*
|
||||
* @param array<int|string, string>|Collection<int|string, string> $options
|
||||
* @param true|string $required
|
||||
*/
|
||||
function select(string $label, array|Collection $options, int|string|null $default = null, int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true): int|string
|
||||
{
|
||||
return (new SelectPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to select multiple options.
|
||||
*
|
||||
* @param array<int|string, string>|Collection<int|string, string> $options
|
||||
* @param array<int|string>|Collection<int, int|string> $default
|
||||
* @return array<int|string>
|
||||
*/
|
||||
function multiselect(string $label, array|Collection $options, array|Collection $default = [], int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.'): array
|
||||
{
|
||||
return (new MultiSelectPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to confirm an action.
|
||||
*/
|
||||
function confirm(string $label, bool $default = true, string $yes = 'Yes', string $no = 'No', bool|string $required = false, mixed $validate = null, string $hint = ''): bool
|
||||
{
|
||||
return (new ConfirmPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user for text input with auto-completion.
|
||||
*
|
||||
* @param array<string>|Collection<int, string>|Closure(string): array<string> $options
|
||||
*/
|
||||
function suggest(string $label, array|Collection|Closure $options, string $placeholder = '', string $default = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = ''): string
|
||||
{
|
||||
return (new SuggestPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the user to search for an option.
|
||||
*
|
||||
* @param Closure(string): array<int|string, string> $options
|
||||
* @param true|string $required
|
||||
*/
|
||||
function search(string $label, Closure $options, string $placeholder = '', int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true): int|string
|
||||
{
|
||||
return (new SearchPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the user to search for multiple option.
|
||||
*
|
||||
* @param Closure(string): array<int|string, string> $options
|
||||
* @return array<int|string>
|
||||
*/
|
||||
function multisearch(string $label, Closure $options, string $placeholder = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.'): array
|
||||
{
|
||||
return (new MultiSearchPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a spinner while the given callback is executing.
|
||||
*
|
||||
* @template TReturn of mixed
|
||||
*
|
||||
* @param \Closure(): TReturn $callback
|
||||
* @return TReturn
|
||||
*/
|
||||
function spin(Closure $callback, string $message = ''): mixed
|
||||
{
|
||||
return (new Spinner($message))->spin($callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a note.
|
||||
*/
|
||||
function note(string $message, ?string $type = null): void
|
||||
{
|
||||
(new Note($message, $type))->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an error.
|
||||
*/
|
||||
function error(string $message): void
|
||||
{
|
||||
(new Note($message, 'error'))->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a warning.
|
||||
*/
|
||||
function warning(string $message): void
|
||||
{
|
||||
(new Note($message, 'warning'))->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an alert.
|
||||
*/
|
||||
function alert(string $message): void
|
||||
{
|
||||
(new Note($message, 'alert'))->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an informational message.
|
||||
*/
|
||||
function info(string $message): void
|
||||
{
|
||||
(new Note($message, 'info'))->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an introduction.
|
||||
*/
|
||||
function intro(string $message): void
|
||||
{
|
||||
(new Note($message, 'intro'))->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a closing message.
|
||||
*/
|
||||
function outro(string $message): void
|
||||
{
|
||||
(new Note($message, 'outro'))->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a table.
|
||||
*
|
||||
* @param array<int, string|array<int, string>>|Collection<int, string|array<int, string>> $headers
|
||||
* @param array<int, array<int, string>>|Collection<int, array<int, string>> $rows
|
||||
*/
|
||||
function table(array|Collection $headers = [], array|Collection|null $rows = null): void
|
||||
{
|
||||
(new Table($headers, $rows))->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a progress bar.
|
||||
*
|
||||
* @template TSteps of iterable<mixed>|int
|
||||
* @template TReturn
|
||||
*
|
||||
* @param TSteps $steps
|
||||
* @param ?Closure((TSteps is int ? int : value-of<TSteps>), Progress<TSteps>): TReturn $callback
|
||||
* @return ($callback is null ? Progress<TSteps> : array<TReturn>)
|
||||
*/
|
||||
function progress(string $label, iterable|int $steps, ?Closure $callback = null, string $hint = ''): array|Progress
|
||||
{
|
||||
$progress = new Progress($label, $steps, $hint);
|
||||
|
||||
if ($callback !== null) {
|
||||
return $progress->map($callback);
|
||||
if (! function_exists('\Laravel\Prompts\text')) {
|
||||
/**
|
||||
* Prompt the user for text input.
|
||||
*/
|
||||
function text(string $label, string $placeholder = '', string $default = '', bool|string $required = false, mixed $validate = null, string $hint = '', ?Closure $transform = null): string
|
||||
{
|
||||
return (new TextPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\textarea')) {
|
||||
/**
|
||||
* Prompt the user for multiline text input.
|
||||
*/
|
||||
function textarea(string $label, string $placeholder = '', string $default = '', bool|string $required = false, mixed $validate = null, string $hint = '', int $rows = 5, ?Closure $transform = null): string
|
||||
{
|
||||
return (new TextareaPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\password')) {
|
||||
/**
|
||||
* Prompt the user for input, hiding the value.
|
||||
*/
|
||||
function password(string $label, string $placeholder = '', bool|string $required = false, mixed $validate = null, string $hint = '', ?Closure $transform = null): string
|
||||
{
|
||||
return (new PasswordPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\select')) {
|
||||
/**
|
||||
* Prompt the user to select an option.
|
||||
*
|
||||
* @param array<int|string, string>|Collection<int|string, string> $options
|
||||
* @param true|string $required
|
||||
*/
|
||||
function select(string $label, array|Collection $options, int|string|null $default = null, int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true, ?Closure $transform = null): int|string
|
||||
{
|
||||
return (new SelectPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\multiselect')) {
|
||||
/**
|
||||
* Prompt the user to select multiple options.
|
||||
*
|
||||
* @param array<int|string, string>|Collection<int|string, string> $options
|
||||
* @param array<int|string>|Collection<int, int|string> $default
|
||||
* @return array<int|string>
|
||||
*/
|
||||
function multiselect(string $label, array|Collection $options, array|Collection $default = [], int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.', ?Closure $transform = null): array
|
||||
{
|
||||
return (new MultiSelectPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\confirm')) {
|
||||
/**
|
||||
* Prompt the user to confirm an action.
|
||||
*/
|
||||
function confirm(string $label, bool $default = true, string $yes = 'Yes', string $no = 'No', bool|string $required = false, mixed $validate = null, string $hint = '', ?Closure $transform = null): bool
|
||||
{
|
||||
return (new ConfirmPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\pause')) {
|
||||
/**
|
||||
* Prompt the user to continue or cancel after pausing.
|
||||
*/
|
||||
function pause(string $message = 'Press enter to continue...'): bool
|
||||
{
|
||||
return (new PausePrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\suggest')) {
|
||||
/**
|
||||
* Prompt the user for text input with auto-completion.
|
||||
*
|
||||
* @param array<string>|Collection<int, string>|Closure(string): array<string> $options
|
||||
*/
|
||||
function suggest(string $label, array|Collection|Closure $options, string $placeholder = '', string $default = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = '', ?Closure $transform = null): string
|
||||
{
|
||||
return (new SuggestPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\search')) {
|
||||
/**
|
||||
* Allow the user to search for an option.
|
||||
*
|
||||
* @param Closure(string): array<int|string, string> $options
|
||||
* @param true|string $required
|
||||
*/
|
||||
function search(string $label, Closure $options, string $placeholder = '', int $scroll = 5, mixed $validate = null, string $hint = '', bool|string $required = true, ?Closure $transform = null): int|string
|
||||
{
|
||||
return (new SearchPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\multisearch')) {
|
||||
/**
|
||||
* Allow the user to search for multiple option.
|
||||
*
|
||||
* @param Closure(string): array<int|string, string> $options
|
||||
* @return array<int|string>
|
||||
*/
|
||||
function multisearch(string $label, Closure $options, string $placeholder = '', int $scroll = 5, bool|string $required = false, mixed $validate = null, string $hint = 'Use the space bar to select options.', ?Closure $transform = null): array
|
||||
{
|
||||
return (new MultiSearchPrompt(...func_get_args()))->prompt();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\spin')) {
|
||||
/**
|
||||
* Render a spinner while the given callback is executing.
|
||||
*
|
||||
* @template TReturn of mixed
|
||||
*
|
||||
* @param \Closure(): TReturn $callback
|
||||
* @return TReturn
|
||||
*/
|
||||
function spin(Closure $callback, string $message = ''): mixed
|
||||
{
|
||||
return (new Spinner($message))->spin($callback);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\note')) {
|
||||
/**
|
||||
* Display a note.
|
||||
*/
|
||||
function note(string $message, ?string $type = null): void
|
||||
{
|
||||
(new Note($message, $type))->display();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\error')) {
|
||||
/**
|
||||
* Display an error.
|
||||
*/
|
||||
function error(string $message): void
|
||||
{
|
||||
(new Note($message, 'error'))->display();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\warning')) {
|
||||
/**
|
||||
* Display a warning.
|
||||
*/
|
||||
function warning(string $message): void
|
||||
{
|
||||
(new Note($message, 'warning'))->display();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\alert')) {
|
||||
/**
|
||||
* Display an alert.
|
||||
*/
|
||||
function alert(string $message): void
|
||||
{
|
||||
(new Note($message, 'alert'))->display();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\info')) {
|
||||
/**
|
||||
* Display an informational message.
|
||||
*/
|
||||
function info(string $message): void
|
||||
{
|
||||
(new Note($message, 'info'))->display();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\intro')) {
|
||||
/**
|
||||
* Display an introduction.
|
||||
*/
|
||||
function intro(string $message): void
|
||||
{
|
||||
(new Note($message, 'intro'))->display();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\outro')) {
|
||||
/**
|
||||
* Display a closing message.
|
||||
*/
|
||||
function outro(string $message): void
|
||||
{
|
||||
(new Note($message, 'outro'))->display();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\table')) {
|
||||
/**
|
||||
* Display a table.
|
||||
*
|
||||
* @param array<int, string|array<int, string>>|Collection<int, string|array<int, string>> $headers
|
||||
* @param array<int, array<int, string>>|Collection<int, array<int, string>> $rows
|
||||
*/
|
||||
function table(array|Collection $headers = [], array|Collection|null $rows = null): void
|
||||
{
|
||||
(new Table($headers, $rows))->display();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\progress')) {
|
||||
/**
|
||||
* Display a progress bar.
|
||||
*
|
||||
* @template TSteps of iterable<mixed>|int
|
||||
* @template TReturn
|
||||
*
|
||||
* @param TSteps $steps
|
||||
* @param ?Closure((TSteps is int ? int : value-of<TSteps>), Progress<TSteps>): TReturn $callback
|
||||
* @return ($callback is null ? Progress<TSteps> : array<TReturn>)
|
||||
*/
|
||||
function progress(string $label, iterable|int $steps, ?Closure $callback = null, string $hint = ''): array|Progress
|
||||
{
|
||||
$progress = new Progress($label, $steps, $hint);
|
||||
|
||||
if ($callback !== null) {
|
||||
return $progress->map($callback);
|
||||
}
|
||||
|
||||
return $progress;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('\Laravel\Prompts\form')) {
|
||||
function form(): FormBuilder
|
||||
{
|
||||
return new FormBuilder;
|
||||
}
|
||||
|
||||
return $progress;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user