Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
88ca717
committing this insanity before i probably destroy it
joetannenbaum Sep 25, 2023
48ed227
ok we're getting closer
joetannenbaum Sep 25, 2023
40eb551
width is more stable
joetannenbaum Sep 25, 2023
79dd87e
Fix code styling
joetannenbaum Sep 25, 2023
3453abd
remove old bad stuff
joetannenbaum Sep 25, 2023
6a94030
remove unused method
joetannenbaum Sep 25, 2023
3504cf5
Merge branch 'feature/multi-line-input' of github.com:joetannenbaum/p…
joetannenbaum Sep 25, 2023
6859163
Fix code styling
joetannenbaum Sep 25, 2023
c4d0b9c
i think we've reached some level of stability
joetannenbaum Sep 25, 2023
31b00b9
fixed value with cursor method
joetannenbaum Sep 26, 2023
8cf183d
submit state
joetannenbaum Sep 26, 2023
eae29b2
cancel and error states
joetannenbaum Sep 26, 2023
4e0993b
fix cursor position if current is a new line
joetannenbaum Sep 26, 2023
b16042a
do a final check to make sure we still have the minimum number of rows
joetannenbaum Sep 26, 2023
8454a41
fixed error state
joetannenbaum Sep 26, 2023
c9fd450
actually fixed error display
joetannenbaum Sep 26, 2023
7f340a4
allow new lines as text input
joetannenbaum Sep 26, 2023
268cb2f
fixing formatting again
joetannenbaum Sep 26, 2023
d7db85f
handle scroll bottom buffer in renderer
joetannenbaum Sep 26, 2023
969fe3b
Update textarea.php
joetannenbaum Sep 26, 2023
bd0a785
Merge branch 'feature/multi-line-input' of github.com:joetannenbaum/p…
joetannenbaum Sep 26, 2023
d5a80a9
Fix code styling
joetannenbaum Sep 26, 2023
2b086b8
Update TextareaPrompt.php
joetannenbaum Sep 26, 2023
1106730
rows param + docs
joetannenbaum Sep 26, 2023
abf66eb
Merge branch 'feature/multi-line-input' of github.com:joetannenbaum/p…
joetannenbaum Sep 26, 2023
4fde7da
Create TextareaPromptTest.php
joetannenbaum Sep 26, 2023
86e98a4
Fix code styling
joetannenbaum Sep 26, 2023
12e02d7
fix static analysis
joetannenbaum Sep 26, 2023
ae54130
Merge branch 'feature/multi-line-input' of github.com:joetannenbaum/p…
joetannenbaum Sep 26, 2023
08b0f7b
Merge branch 'main' into feature/multi-line-input
jessarcher Oct 20, 2023
d20434e
Update scrolling initialization
jessarcher Oct 20, 2023
768125d
Fix test
jessarcher Oct 20, 2023
013a3ad
Formatting
jessarcher Oct 20, 2023
d513b90
Fix issue with empty last line
jessarcher Oct 20, 2023
7591fbe
fixed cancelled state so that the strikethrough doesn't affect the box
joetannenbaum Oct 21, 2023
7ad75f9
calculate proper width with each render
joetannenbaum Oct 21, 2023
a242eeb
pass max width as a negative number to avoid truncation
joetannenbaum Oct 21, 2023
973211b
Merge branch 'main' into feature/multi-line-input
jessarcher Dec 27, 2023
9758e82
Fix code styling
jessarcher Dec 27, 2023
20246ad
Merge branch 'laravel:main' into feature/multi-line-input
joetannenbaum Dec 27, 2023
508a275
fix long word wrapping + cursor position
joetannenbaum Dec 28, 2023
b130780
Fix code styling
joetannenbaum Dec 28, 2023
0c8ea67
mb_wordwrap
joetannenbaum Jan 4, 2024
8cf7e50
getting closer to consistent
joetannenbaum Jan 4, 2024
c6dce58
fix scroll width
joetannenbaum Jan 4, 2024
3347909
Fix code styling
joetannenbaum Jan 4, 2024
41cda9c
appease phpstan
joetannenbaum Jan 4, 2024
de41739
Merge branch 'feature/multi-line-input' of github.com:joetannenbaum/p…
joetannenbaum Jan 4, 2024
e8fdac9
Fix code styling
joetannenbaum Jan 4, 2024
5663c18
Create MultiByteWordWrapTest.php
joetannenbaum Jan 11, 2024
eb94a6c
remove maxLineWIdth property
joetannenbaum Jan 31, 2024
2a2c670
actually remove maxLineWidth property
joetannenbaum Jan 31, 2024
c16cfb7
fixed the off by one errors when using the up/down keys
joetannenbaum Jan 31, 2024
4a047ca
fixed bug where pasting a bunch of content didn't put the cursor in t…
joetannenbaum Jan 31, 2024
e1d46b4
Fix code styling
joetannenbaum Jan 31, 2024
f240b38
changed visiblity of validate property
joetannenbaum Jan 31, 2024
8a677ce
Fix code styling
joetannenbaum Jan 31, 2024
3ff9af4
Merge branch 'laravel:main' into feature/multi-line-input
joetannenbaum Jan 31, 2024
8fe583b
Merge branch 'laravel:main' into feature/multi-line-input
joetannenbaum Mar 5, 2024
3df51ff
validate property should be public
joetannenbaum Mar 5, 2024
9d72523
validate property should be mixed
joetannenbaum Mar 5, 2024
4d8a63d
Fix code styling
joetannenbaum Mar 5, 2024
1a7a8c6
add ability to reset cancel using + reset when using it in tests
joetannenbaum Mar 6, 2024
66330e6
fix for strange down arrow behavior
joetannenbaum Mar 20, 2024
d5699cf
move mb_wordwrap to truncation trait
joetannenbaum Mar 31, 2024
4d5c182
move rows param to last position
joetannenbaum Mar 31, 2024
1efa24d
Fix code styling
joetannenbaum Mar 31, 2024
4515c98
Formatting
jessarcher Apr 3, 2024
3c04832
Allow placeholder to wrap and contain newlines
jessarcher Apr 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions playground/textarea.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

use function Laravel\Prompts\textarea;

require __DIR__.'/../vendor/autoload.php';

$email = textarea(
label: 'Tell me a story',
placeholder: 'Weave me a tale',
);

var_dump($email);

echo str_repeat(PHP_EOL, 5);
3 changes: 3 additions & 0 deletions src/Concerns/Themes.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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;
Expand All @@ -28,6 +29,7 @@
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
Expand All @@ -45,6 +47,7 @@ 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,
Expand Down
86 changes: 86 additions & 0 deletions src/Concerns/Truncation.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,90 @@ protected function truncate(string $string, int $width): string

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);
}
}
26 changes: 17 additions & 9 deletions src/Concerns/TypedValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ 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;

if ($this->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;
Expand All @@ -51,10 +51,17 @@ protected function trackTypedValue(string $default = '', bool $submit = true, ?c
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;
Expand All @@ -81,27 +88,28 @@ public function value(): string
/**
* 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];

return ($wasTruncatedBefore ? $this->dim('…') : '')
.$truncatedBefore
.$this->inverse($cursor)
.($current === PHP_EOL ? PHP_EOL : '')
.$truncatedAfter
.($wasTruncatedAfter ? $this->dim('…') : '');
}
Expand Down
5 changes: 5 additions & 0 deletions src/Key.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ class Key
*/
const CTRL_A = "\x01";

/**
* EOF
*/
const CTRL_D = "\x04";

/**
* End
*/
Expand Down
4 changes: 2 additions & 2 deletions src/Prompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ abstract class Prompt
/**
* The cancellation callback.
*/
protected static Closure $cancelUsing;
protected static ?Closure $cancelUsing;

/**
* Indicates if the prompt has been validated.
Expand Down Expand Up @@ -136,7 +136,7 @@ public function prompt(): mixed
/**
* 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;
}
Expand Down
Loading