Skip to content

Commit

Permalink
Only re-render within the visible terminal height
Browse files Browse the repository at this point in the history
  • Loading branch information
jessarcher committed Apr 3, 2024
1 parent 0cc41a5 commit f102cdb
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 62 deletions.
1 change: 1 addition & 0 deletions src/Concerns/FakesInputOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public static function fake(array $keys = []): void
$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);
Expand Down
71 changes: 9 additions & 62 deletions src/Prompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ public static function validateUsing(Closure $callback): void
*/
protected function render(): void
{
$this->terminal()->initDimensions();

$frame = $this->renderTheme();

if ($frame === $this->prevFrame) {
Expand All @@ -223,35 +225,14 @@ protected function render(): void
return;
}

$this->resetCursorPosition();

// Ensure that the full frame is buffered so subsequent output can see how many trailing newlines were written.
if ($this->state === 'submit') {
$this->eraseDown();
static::output()->write($frame);
$terminalHeight = $this->terminal()->lines();
$previousFrameHeight = count(explode(PHP_EOL, $this->prevFrame));
$renderableLines = array_slice(explode(PHP_EOL, $frame), abs(min(0, $terminalHeight - $previousFrameHeight)));

$this->prevFrame = '';

return;
}

$diff = $this->diffLines($this->prevFrame, $frame);

if (count($diff) === 1) { // Update the single line that changed.
$diffLine = $diff[0];
$this->moveCursor(0, $diffLine);
$this->eraseLines(1);
$lines = explode(PHP_EOL, $frame);
static::output()->write($lines[$diffLine]);
$this->moveCursor(0, count($lines) - $diffLine - 1);
} elseif (count($diff) > 1) { // Re-render everything past the first change
$diffLine = $diff[0];
$this->moveCursor(0, $diffLine);
$this->eraseDown();
$lines = explode(PHP_EOL, $frame);
$newLines = array_slice($lines, $diffLine);
static::output()->write(implode(PHP_EOL, $newLines));
}
$this->moveCursorToColumn(1);
$this->moveCursorUp(min($terminalHeight, $previousFrameHeight) - 1);
$this->eraseDown();
$this->output()->write(implode(PHP_EOL, $renderableLines));

$this->prevFrame = $frame;
}
Expand All @@ -268,40 +249,6 @@ protected function submit(): void
}
}

/**
* Reset the cursor position to the beginning of the previous frame.
*/
private function resetCursorPosition(): void
{
$lines = count(explode(PHP_EOL, $this->prevFrame)) - 1;

$this->moveCursor(-999, $lines * -1);
}

/**
* Get the difference between two strings.
*
* @return array<int>
*/
private function diffLines(string $a, string $b): array
{
if ($a === $b) {
return [];
}

$aLines = explode(PHP_EOL, $a);
$bLines = explode(PHP_EOL, $b);
$diff = [];

for ($i = 0; $i < max(count($aLines), count($bLines)); $i++) {
if (! isset($aLines[$i]) || ! isset($bLines[$i]) || $aLines[$i] !== $bLines[$i]) {
$diff[] = $i;
}
}

return $diff;
}

/**
* Handle a key press and determine whether to continue.
*/
Expand Down

0 comments on commit f102cdb

Please sign in to comment.