-
Notifications
You must be signed in to change notification settings - Fork 93
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* added progress bar * progress bar -> progress * support lazy collections * Formatting * Fix progress bar on narrow terminals * Restore cursor after Ctrl+C * Remove LazyCollection support * Allow passing steps as an integer * Allow advancing multiple steps * Allow returning the results of the callback * Allow passing a hint * Display progress * messing with updating the label in real time * add canceled state to progress on ctrl c * Add setters for label and hint * allow for an empty title * label is now optional * Make progress evenly spaced * Improve method name * Alternative approach to optional label * Fix code styling * Remove overloading * Remove overloaded methods --------- Co-authored-by: Jess Archer <[email protected]> Co-authored-by: jessarcher <[email protected]>
- Loading branch information
1 parent
ab5afba
commit b603410
Showing
7 changed files
with
515 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<?php | ||
|
||
use function Laravel\Prompts\progress; | ||
|
||
require __DIR__.'/../vendor/autoload.php'; | ||
|
||
$states = [ | ||
'Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', | ||
'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', | ||
]; | ||
|
||
progress( | ||
label: 'Adding States', | ||
steps: $states, | ||
callback: function ($item, $progress) { | ||
usleep(250_000); | ||
|
||
if ($item === 'Arkansas') { | ||
$progress->label = 'Arkansas is not a state! Nice try.'; | ||
} | ||
|
||
return $item.' added.'; | ||
}, | ||
); | ||
|
||
progress( | ||
label: 'Adding States With Label', | ||
steps: $states, | ||
callback: function ($item, $progress) { | ||
usleep(250_000); | ||
$progress | ||
->label('Adding '.$item) | ||
->hint("{$item} has ".strlen($item).' characters'); | ||
}, | ||
); | ||
|
||
$progress = progress( | ||
label: 'Adding States Manually', | ||
steps: $states, | ||
); | ||
|
||
$progress->start(); | ||
|
||
foreach ($states as $state) { | ||
usleep(250_000); | ||
$progress | ||
->hint($state) | ||
->advance(); | ||
} | ||
|
||
$progress->finish(); | ||
|
||
progress( | ||
'Processing with Exception', | ||
$states, | ||
fn ($item) => $item === 'Arkansas' ? throw new Exception('Issue with Arkansas!') : usleep(250_000), | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
<?php | ||
|
||
namespace Laravel\Prompts; | ||
|
||
use Closure; | ||
use InvalidArgumentException; | ||
use RuntimeException; | ||
use Throwable; | ||
|
||
/** | ||
* @template TSteps of iterable<mixed>|int | ||
*/ | ||
class Progress extends Prompt | ||
{ | ||
/** | ||
* The current progress bar item count. | ||
*/ | ||
public int $progress = 0; | ||
|
||
/** | ||
* The total number of steps. | ||
*/ | ||
public int $total = 0; | ||
|
||
/** | ||
* The original value of pcntl_async_signals | ||
*/ | ||
protected bool $originalAsync; | ||
|
||
/** | ||
* Create a new ProgressBar instance. | ||
* | ||
* @param TSteps $steps | ||
*/ | ||
public function __construct(public string $label, public iterable|int $steps, public string $hint = '') | ||
{ | ||
$this->total = match (true) { | ||
is_int($this->steps) => $this->steps, | ||
is_countable($this->steps) => count($this->steps), | ||
is_iterable($this->steps) => iterator_count($this->steps), | ||
default => throw new InvalidArgumentException('Unable to count steps.'), | ||
}; | ||
|
||
if ($this->total === 0) { | ||
throw new InvalidArgumentException('Progress bar must have at least one item.'); | ||
} | ||
} | ||
|
||
/** | ||
* Map over the steps while rendering the progress bar. | ||
* | ||
* @template TReturn | ||
* | ||
* @param Closure((TSteps is int ? int : value-of<TSteps>), $this): TReturn $callback | ||
* @return array<TReturn> | ||
*/ | ||
public function map(Closure $callback): array | ||
{ | ||
$this->start(); | ||
|
||
$result = []; | ||
|
||
try { | ||
if (is_int($this->steps)) { | ||
for ($i = 0; $i < $this->steps; $i++) { | ||
$result[] = $callback($i, $this); | ||
$this->advance(); | ||
} | ||
} else { | ||
foreach ($this->steps as $step) { | ||
$result[] = $callback($step, $this); | ||
$this->advance(); | ||
} | ||
} | ||
} catch (Throwable $e) { | ||
$this->state = 'error'; | ||
$this->render(); | ||
$this->restoreCursor(); | ||
$this->resetSignals(); | ||
|
||
throw $e; | ||
} | ||
|
||
if ($this->hint !== '') { | ||
// Just pause for one moment to show the final hint | ||
// so it doesn't look like it was skipped | ||
usleep(250_000); | ||
} | ||
|
||
$this->finish(); | ||
|
||
return $result; | ||
} | ||
|
||
/** | ||
* Start the progress bar. | ||
*/ | ||
public function start(): void | ||
{ | ||
$this->capturePreviousNewLines(); | ||
|
||
if (function_exists('pcntl_signal')) { | ||
$this->originalAsync = pcntl_async_signals(true); | ||
pcntl_signal(SIGINT, function () { | ||
$this->state = 'cancel'; | ||
$this->render(); | ||
exit(); | ||
}); | ||
} | ||
|
||
$this->state = 'active'; | ||
$this->hideCursor(); | ||
$this->render(); | ||
} | ||
|
||
/** | ||
* Advance the progress bar. | ||
*/ | ||
public function advance(int $step = 1): void | ||
{ | ||
$this->progress += $step; | ||
|
||
if ($this->progress > $this->total) { | ||
$this->progress = $this->total; | ||
} | ||
|
||
$this->render(); | ||
} | ||
|
||
/** | ||
* Finish the progress bar. | ||
*/ | ||
public function finish(): void | ||
{ | ||
$this->state = 'submit'; | ||
$this->render(); | ||
$this->restoreCursor(); | ||
$this->resetSignals(); | ||
} | ||
|
||
/** | ||
* Update the label. | ||
*/ | ||
public function label(string $label): static | ||
{ | ||
$this->label = $label; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Update the hint. | ||
*/ | ||
public function hint(string $hint): static | ||
{ | ||
$this->hint = $hint; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Get the completion percentage. | ||
*/ | ||
public function percentage(): int|float | ||
{ | ||
return $this->progress / $this->total; | ||
} | ||
|
||
/** | ||
* Disable prompting for input. | ||
* | ||
* @throws \RuntimeException | ||
*/ | ||
public function prompt(): never | ||
{ | ||
throw new RuntimeException('Progress Bar cannot be prompted.'); | ||
} | ||
|
||
/** | ||
* Get the value of the prompt. | ||
*/ | ||
public function value(): bool | ||
{ | ||
return true; | ||
} | ||
|
||
/** | ||
* Reset the signal handling. | ||
*/ | ||
protected function resetSignals(): void | ||
{ | ||
if (isset($this->originalAsync)) { | ||
pcntl_async_signals($this->originalAsync); | ||
pcntl_signal(SIGINT, SIG_DFL); | ||
} | ||
} | ||
|
||
/** | ||
* Restore the cursor. | ||
*/ | ||
public function __destruct() | ||
{ | ||
$this->restoreCursor(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php | ||
|
||
namespace Laravel\Prompts\Themes\Default; | ||
|
||
use Laravel\Prompts\Progress; | ||
|
||
class ProgressRenderer extends Renderer | ||
{ | ||
use Concerns\DrawsBoxes; | ||
|
||
/** | ||
* The character to use for the progress bar. | ||
*/ | ||
protected string $barCharacter = '█'; | ||
|
||
/** | ||
* Render the progress bar. | ||
* | ||
* @param Progress<int|iterable<mixed>> $progress | ||
*/ | ||
public function __invoke(Progress $progress): string | ||
{ | ||
$filled = str_repeat($this->barCharacter, (int) ceil($progress->percentage() * min($this->minWidth, $progress->terminal()->cols() - 6))); | ||
|
||
return match ($progress->state) { | ||
'submit' => $this | ||
->box( | ||
$this->dim($this->truncate($progress->label, $progress->terminal()->cols() - 6)), | ||
$this->dim($filled), | ||
info: $progress->progress.'/'.$progress->total, | ||
), | ||
|
||
'error' => $this | ||
->box( | ||
$this->truncate($progress->label, $progress->terminal()->cols() - 6), | ||
$this->dim($filled), | ||
color: 'red', | ||
info: $progress->progress.'/'.$progress->total, | ||
), | ||
|
||
'cancel' => $this | ||
->box( | ||
$this->truncate($progress->label, $progress->terminal()->cols() - 6), | ||
$this->dim($filled), | ||
color: 'red', | ||
info: $progress->progress.'/'.$progress->total, | ||
) | ||
->error('Cancelled.'), | ||
|
||
default => $this | ||
->box( | ||
$this->cyan($this->truncate($progress->label, $progress->terminal()->cols() - 6)), | ||
$this->dim($filled), | ||
info: $progress->progress.'/'.$progress->total, | ||
) | ||
->when( | ||
$progress->hint, | ||
fn () => $this->hint($progress->hint), | ||
fn () => $this->newLine() // Space for errors | ||
) | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.