🔧
This commit is contained in:
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('…') : '');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user