• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tempestphp / tempest-framework / 14393323144

10 Apr 2025 04:27PM UTC coverage: 81.196% (-0.2%) from 81.363%
14393323144

push

github

web-flow
refactor(core): rename `DoNotDiscover` to `SkipDiscovery` (#1142)

7 of 9 new or added lines in 9 files covered. (77.78%)

52 existing lines in 11 files now uncovered.

11529 of 14199 relevant lines covered (81.2%)

106.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.09
/src/Tempest/Console/src/Components/Renderers/TextInputRenderer.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Tempest\Console\Components\Renderers;
6

7
use Tempest\Console\Components\ComponentState;
8
use Tempest\Console\Components\TextBuffer;
9
use Tempest\Console\Point;
10
use Tempest\Console\Terminal\Terminal;
11
use Tempest\Support\Str\ImmutableString;
12

13
use function Tempest\Support\str;
14

15
final class TextInputRenderer
16
{
17
    use RendersInput;
18

19
    private int $scrollOffset = 0;
20

21
    public function __construct(
63✔
22
        private ?bool $multiline = false,
23
        private int $maximumLines = 4,
24
    ) {}
63✔
25

26
    public function render(
5✔
27
        Terminal $terminal,
28
        ComponentState $state,
29
        TextBuffer $buffer,
30
        ?string $label,
31
        ?string $placeholder = null,
32
        ?string $hint = null,
33
    ): string {
34
        $this->prepareRender($terminal, $state);
5✔
35
        $this->label($label);
5✔
36

37
        if ($hint) {
5✔
38
            $this->offsetY++;
×
39
            $this->line($this->style('fg-gray', $hint))->newLine();
×
40
        }
41

42
        // splits the text to an array so we can work with individual lines
43
        $lines = str($buffer->text ?: ($placeholder ?: ''))
5✔
44
            ->explode("\n")
5✔
45
            ->flatMap(fn (string $line) => str($line)->chunk($this->maxLineCharacters)->toArray())
5✔
46
            ->map(static fn (string $line) => str($line)->replaceEnd("\n", ' '));
5✔
47

48
        // calculates scroll offset based on cursor position
49
        $this->scrollOffset = $this->calculateScrollOffset($lines, $this->maximumLines, $buffer->getRelativeCursorPosition($this->maxLineCharacters)->y);
5✔
50

51
        // slices lines to display only the visible portion
52
        $displayLines = $lines->slice($this->scrollOffset, $this->state->isFinished() ? 1 : $this->maximumLines);
5✔
53

54
        // if there is nothing to display after the component is done, show "no input"
55
        // TODO(innocenzi): clean up
56
        if ($this->state->isFinished() && $lines->filter(fn (ImmutableString $line) => $line->trim()->isNotEmpty())->isEmpty()) {
5✔
57
            $displayLines = $displayLines->filter(fn (ImmutableString $line) => $line->trim()->isNotEmpty());
×
58

UNCOV
59
            $this->line($this->style('italic dim', 'No input.'))->newLine();
×
60
        }
61

62
        // renders visible lines
63
        foreach ($displayLines as $line) {
5✔
64
            $this->line(
5✔
65
                // Add a symbol depending on the state
66
                match ($this->state) {
5✔
67
                    ComponentState::DONE => '<style="fg-green">✓</style> ',
5✔
68
                    default => '',
5✔
69
                },
5✔
70
                // Prints the actual line
71
                $this->style(
5✔
72
                    style: match (true) {
5✔
73
                        $this->state === ComponentState::DONE => 'dim',
5✔
74
                        $this->state === ComponentState::CANCELLED => 'italic dim strikethrough',
5✔
75
                        default => null,
5✔
76
                    },
5✔
77
                    content: $line,
5✔
78
                ),
5✔
79
                // Add an ellipsis if there is more than one line but we're done with the input
80
                $this->state->isFinished() && $this->multiline && $lines->count() > 1
5✔
UNCOV
81
                    ? '<style="dim">…</style>'
×
82
                    : '',
5✔
83
            )->newLine();
5✔
84
        }
85

86
        // fills remaining lines if less than max display lines
87
        if (! $this->state->isFinished()) {
5✔
88
            $lines = $this->multiline ? $this->maximumLines : 1;
5✔
89

90
            for ($i = $displayLines->count(); $i < $lines; $i++) {
5✔
91
                $this->line("\n");
2✔
92
            }
93
        }
94

95
        return $this->finishRender();
5✔
96
    }
97

98
    public function getCursorPosition(Terminal $terminal, TextBuffer $buffer): Point
×
99
    {
100
        $position = $buffer->getRelativeCursorPosition(((($terminal->width - self::MARGIN_X) - 1) - self::PADDING_X) - self::MARGIN_X);
×
101

102
        return new Point(
×
103
            x: $position->x + self::MARGIN_X + 1 + self::PADDING_X, // +1 is the border width
×
UNCOV
104
            y: ($position->y - $this->scrollOffset) + self::MARGIN_TOP + $this->offsetY, // subtract scroll offset
×
UNCOV
105
        );
×
106
    }
107
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc