Created
February 14, 2023 14:45
-
-
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.
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
<?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