diff --git a/src/Concerns/FakesInputOutput.php b/src/Concerns/FakesInputOutput.php index 2df265f1..e136db1d 100644 --- a/src/Concerns/FakesInputOutput.php +++ b/src/Concerns/FakesInputOutput.php @@ -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); diff --git a/src/Prompt.php b/src/Prompt.php index 1f50b102..caf8fb51 100644 --- a/src/Prompt.php +++ b/src/Prompt.php @@ -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) { @@ -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; } @@ -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 - */ - 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. */