Skip to content

Instantly share code, notes, and snippets.

@joetannenbaum
Created February 14, 2023 14:45
Show Gist options
  • Save joetannenbaum/9696956460a46815dbbbbc506117d44d to your computer and use it in GitHub Desktop.
Save joetannenbaum/9696956460a46815dbbbbc506117d44d to your computer and use it in GitHub Desktop.
A Laravel Artisan macro for showing a spinner in the terminal while you perform a task in the background.
<?php
namespace App\Mixins;
use Spatie\Async\Pool;
class Console
{
public function withSpinner()
{
return function (
string $title,
callable $task,
string|callable $successDisplay = null,
array $longProcessMessages = []
) {
$this->hideCursor();
$running = true;
$animation = collect(mb_str_split('⢿⣻⣽⣾⣷⣯⣟⡿'));
$index = 0;
$this->output->write($title . ': ' . $animation->get($index));
$pool = Pool::create();
$process = $pool->add($task);
$process->then(function ($output) use (
&$running,
$title,
$successDisplay,
) {
$running = false;
if (is_callable($successDisplay)) {
$message = $successDisplay($output);
} else if (is_string($successDisplay)) {
$message = $successDisplay;
} else if (is_string($output)) {
$message = $output;
} else {
$message = '✓';
}
$this->overwriteLine(
$title . ': <info>' . $message . '</info>',
true,
);
})->catch(function ($exception) use (
&$running,
$title,
) {
$running = false;
$this->overwriteLine(
$title . ': <error>' . $exception->getMessage() . '</error>',
true,
);
throw $exception;
});
// If they quit, wrap things up nicely
$this->trap([SIGTERM, SIGQUIT], function () use ($process) {
$this->showCursor();
$process->stop();
});
$reversedLongProcessMessages = collect($longProcessMessages)
->reverse()
->map(fn ($v) => ' ' . $v);
while ($running) {
$runningTime = floor($process->getCurrentExecutionTime());
$longProcessMessage = $reversedLongProcessMessages->first(
fn ($v, $k) => $runningTime >= $k
) ?? '';
$index = ($index === $animation->count() - 1) ? 0 : $index + 1;
$this->overwriteLine(
$title . ': <comment>' . $animation->get($index) . $longProcessMessage . '</comment>'
);
usleep(200000);
}
$this->showCursor();
return $process->getOutput();
};
}
public function overwriteLine()
{
return function (string $message, bool $newLine = false) {
// Move the cursor to the beginning of the line
$this->output->write("\x0D");
// Erase the line
$this->output->write("\x1B[2K");
$this->output->write($message);
if ($newLine) {
$this->newLine();
}
};
}
public function hideCursor()
{
return function () {
$this->output->write("\e[?25l");
};
}
public function showCursor()
{
return function () {
$this->output->write("\e[?25h");
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment