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

tempestphp / tempest-framework / 14049246919

24 Mar 2025 09:42PM UTC coverage: 79.353% (-0.04%) from 79.391%
14049246919

push

github

web-flow
feat(support): support array parameters in string manipulations (#1073)

48 of 48 new or added lines in 2 files covered. (100.0%)

735 existing lines in 126 files now uncovered.

10492 of 13222 relevant lines covered (79.35%)

90.78 hits per line

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

65.33
/src/Tempest/Console/src/Components/Interactive/SearchComponent.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Tempest\Console\Components\Interactive;
6

7
use Closure;
8
use Tempest\Console\Components\Concerns\HasErrors;
9
use Tempest\Console\Components\Concerns\HasState;
10
use Tempest\Console\Components\Concerns\HasTextBuffer;
11
use Tempest\Console\Components\Concerns\RendersControls;
12
use Tempest\Console\Components\OptionCollection;
13
use Tempest\Console\Components\Renderers\ChoiceRenderer;
14
use Tempest\Console\Components\Static\StaticSearchComponent;
15
use Tempest\Console\Components\TextBuffer;
16
use Tempest\Console\HandlesKey;
17
use Tempest\Console\HasCursor;
18
use Tempest\Console\HasStaticComponent;
19
use Tempest\Console\InteractiveConsoleComponent;
20
use Tempest\Console\Key;
21
use Tempest\Console\Point;
22
use Tempest\Console\StaticConsoleComponent;
23
use Tempest\Console\Terminal\Terminal;
24

25
use function Tempest\Support\Arr\wrap;
26

27
final class SearchComponent implements InteractiveConsoleComponent, HasCursor, HasStaticComponent
28
{
29
    use HasErrors;
30
    use HasState;
31
    use HasTextBuffer;
32
    use RendersControls;
33

34
    private ChoiceRenderer $renderer;
35

36
    public OptionCollection $options;
37

38
    private ?string $previousQuery = null;
39

40
    public function __construct(
11✔
41
        public string $label,
42
        public Closure $search,
43
        public bool $multiple = false,
44
        public null|array|string $default = null,
45
    ) {
46
        $this->bufferEnabled = ! $this->multiple;
11✔
47
        $this->buffer = new TextBuffer();
11✔
48
        $this->renderer = new ChoiceRenderer(multiple: $multiple, default: (string) $default);
11✔
49
        $this->options = new OptionCollection([]);
11✔
50

51
        if ($this->multiple) {
11✔
52
            $this->default = wrap($this->default);
4✔
53
        }
54

55
        $this->updateQuery();
11✔
56
    }
57

58
    public StaticConsoleComponent $staticComponent {
59
        get => new StaticSearchComponent(
60
            label: $this->label,
61
            search: $this->search,
62
            multiple: $this->multiple,
63
            default: $this->default,
64
        );
65
    }
66

67
    public function render(Terminal $terminal): string
4✔
68
    {
69
        $this->updateQuery();
4✔
70

71
        return $this->renderer->render(
4✔
72
            terminal: $terminal,
4✔
73
            state: $this->state,
4✔
74
            label: $this->label,
4✔
75
            query: $this->buffer,
4✔
76
            options: $this->options,
4✔
77
            filtering: $this->bufferEnabled,
4✔
78
        );
4✔
79
    }
80

81
    private function updateQuery(): void
11✔
82
    {
83
        if ($this->previousQuery === $this->buffer->text) {
11✔
84
            return;
6✔
85
        }
86

87
        $this->options->setCollection(array_values(($this->search)($this->buffer->text)));
11✔
88
        $this->previousQuery = $this->buffer->text;
11✔
89
    }
90

91
    private function getControls(): array
×
92
    {
93
        $controls = [];
×
94

95
        if ($this->multiple && $this->bufferEnabled) {
×
96
            $controls['esc'] = 'select';
×
97
        } elseif ($this->multiple) {
×
98
            $controls['/'] = 'filter';
×
99
            $controls['space'] = 'select';
×
100
        }
101

102
        return [
×
103
            ...$controls,
×
104
            '↑' => 'up',
×
105
            '↓' => 'down',
×
106
            'enter' =>
×
107
                $this->multiple && $this->default && $this->options->getSelectedOptions() === []
×
108
                    ? 'skip'
×
109
                    : 'confirm',
×
110
            'ctrl+c' => 'cancel',
×
UNCOV
111
        ];
×
112
    }
113

UNCOV
114
    public function getCursorPosition(Terminal $terminal): Point
×
115
    {
UNCOV
116
        return $this->renderer->getCursorPosition($terminal, $this->buffer);
×
117
    }
118

UNCOV
119
    public function cursorVisible(): bool
×
120
    {
UNCOV
121
        return $this->bufferEnabled;
×
122
    }
123

124
    #[HandlesKey]
5✔
125
    public function input(string $key): void
126
    {
127
        if ($this->multiple) {
5✔
128
            if (! $this->bufferEnabled && $key === '/') {
3✔
129
                $this->bufferEnabled = true;
1✔
130
                $this->updateQuery();
1✔
131

132
                return;
1✔
133
            }
134

135
            if (! $this->bufferEnabled) {
3✔
136
                match (mb_strtolower($key)) {
3✔
137
                    ' ' => $this->options->toggleCurrent(),
2✔
138
                    'h', 'k' => $this->options->previous(),
×
UNCOV
139
                    'j', 'l' => $this->options->next(),
×
140
                    default => null,
1✔
141
                };
3✔
142

143
                $this->updateQuery();
3✔
144

145
                return;
3✔
146
            }
147
        }
148

149
        $this->buffer->input($key);
3✔
150
        $this->updateQuery();
3✔
151
    }
152

153
    #[HandlesKey(Key::ENTER)]
6✔
154
    public function enter(): mixed
155
    {
156
        if ($this->multiple) {
6✔
157
            return $this->options->getRawSelectedOptions() ?: $this->default;
4✔
158
        }
159

160
        if (($active = $this->options->getActive()) !== null) {
2✔
161
            return $active->value;
1✔
162
        }
163

164
        return null;
1✔
165
    }
166

167
    #[HandlesKey(Key::ESCAPE)]
1✔
168
    public function stopFiltering(): void
169
    {
170
        if (! $this->multiple) {
1✔
UNCOV
171
            return;
×
172
        }
173

174
        $this->bufferEnabled = false;
1✔
175
    }
176

UNCOV
177
    #[HandlesKey(Key::UP)]
×
178
    #[HandlesKey(Key::HOME)]
179
    #[HandlesKey(Key::START_OF_LINE)]
180
    public function up(): void
181
    {
UNCOV
182
        $this->options->previous();
×
183
    }
184

185
    #[HandlesKey(Key::DOWN)]
2✔
186
    #[HandlesKey(Key::END)]
187
    #[HandlesKey(Key::END_OF_LINE)]
188
    public function down(): void
189
    {
190
        $this->options->next();
2✔
191
    }
192
}
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