Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 16 |
Terminal | |
0.00% |
0 / 1 |
|
0.00% |
0 / 3 |
306 | |
0.00% |
0 / 16 |
size | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
getWindowSize | n/a |
0 / 0 |
5 | n/a |
0 / 0 |
|||||
clear | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
read | n/a |
0 / 0 |
3 | n/a |
0 / 0 |
|||||
readKey | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 14 |
|||
ding | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
write | n/a |
0 / 0 |
3 | n/a |
0 / 0 |
|||||
has_tput | n/a |
0 / 0 |
2 | n/a |
0 / 0 |
1 | <?php declare(strict_types=1); |
2 | |
3 | namespace Aviat\Kilo; |
4 | |
5 | use Aviat\Kilo\Enum\RawKeyCode; |
6 | use Aviat\Kilo\Enum\KeyType; |
7 | use Aviat\Kilo\Type\TerminalSize; |
8 | |
9 | class Terminal { |
10 | /** |
11 | * Get the size of the current terminal window |
12 | * |
13 | * @codeCoverageIgnore |
14 | * @return TerminalSize |
15 | */ |
16 | public static function size(): TerminalSize |
17 | { |
18 | return new TerminalSize(...self::getWindowSize()); |
19 | } |
20 | |
21 | /** |
22 | * Get the size of the current terminal window |
23 | * |
24 | * @codeCoverageIgnore |
25 | * @return array |
26 | */ |
27 | public static function getWindowSize(): array |
28 | { |
29 | $ffiSize = Termios::getWindowSize(); |
30 | if ($ffiSize !== NULL) |
31 | { |
32 | return $ffiSize; |
33 | } |
34 | |
35 | // Try using tput |
36 | if (self::has_tput()) |
37 | { |
38 | $rows = (int)trim((string)shell_exec('tput lines')); |
39 | $cols = (int)trim((string)shell_exec('tput cols')); |
40 | |
41 | if ($rows > 0 && $cols > 0) |
42 | { |
43 | return [$rows, $cols]; |
44 | } |
45 | } |
46 | |
47 | // Worst-case, return an arbitrary 'standard' size |
48 | return [25, 80]; |
49 | } |
50 | |
51 | /** |
52 | * Clear the screen and reset the cursor position |
53 | */ |
54 | public static function clear(): void |
55 | { |
56 | self::write(ANSI::CLEAR_SCREEN . ANSI::RESET_CURSOR); |
57 | } |
58 | |
59 | /** |
60 | * Pull input from the stdin stream. |
61 | * |
62 | * @codeCoverageIgnore |
63 | * @param int $len |
64 | * @return string |
65 | */ |
66 | public static function read(int $len = 128): string |
67 | { |
68 | $handle = fopen('php://stdin', 'rb'); |
69 | if ($handle === false) |
70 | { |
71 | return ''; |
72 | } |
73 | |
74 | $input = fread($handle, $len); |
75 | fclose($handle); |
76 | |
77 | return (is_string($input)) ? $input : ''; |
78 | } |
79 | |
80 | /** |
81 | * Get the last key input from the terminal and convert to a |
82 | * more useful format |
83 | * |
84 | * @return string |
85 | */ |
86 | public static function readKey(): string |
87 | { |
88 | $c = Terminal::read(); |
89 | |
90 | return match($c) |
91 | { |
92 | // Unambiguous mappings |
93 | RawKeyCode::ARROW_DOWN => KeyType::ARROW_DOWN, |
94 | RawKeyCode::ARROW_LEFT => KeyType::ARROW_LEFT, |
95 | RawKeyCode::ARROW_RIGHT => KeyType::ARROW_RIGHT, |
96 | RawKeyCode::ARROW_UP => KeyType::ARROW_UP, |
97 | RawKeyCode::DELETE => KeyType::DELETE, |
98 | RawKeyCode::ENTER => KeyType::ENTER, |
99 | RawKeyCode::PAGE_DOWN => KeyType::PAGE_DOWN, |
100 | RawKeyCode::PAGE_UP => KeyType::PAGE_UP, |
101 | |
102 | // Backspace |
103 | RawKeyCode::CTRL('h'), RawKeyCode::BACKSPACE => KeyType::BACKSPACE, |
104 | |
105 | // Escape |
106 | RawKeyCode::CTRL('l'), RawKeyCode::ESCAPE => KeyType::ESCAPE, |
107 | |
108 | // Home Key |
109 | "\eOH", "\e[7~", "\e[1~", ANSI::RESET_CURSOR => KeyType::HOME, |
110 | |
111 | // End Key |
112 | "\eOF", "\e[4~", "\e[8~", "\e[F" => KeyType::END, |
113 | |
114 | default => $c, |
115 | }; |
116 | } |
117 | |
118 | /** |
119 | * Ring the terminal bell |
120 | */ |
121 | public static function ding(): void |
122 | { |
123 | self::write(RawKeyCode::BELL); |
124 | } |
125 | |
126 | /** |
127 | * Write to the stdout stream |
128 | * |
129 | * @codeCoverageIgnore |
130 | * @param string $str |
131 | * @param int|NULL $len |
132 | * @return int|false |
133 | */ |
134 | public static function write(string $str, int $len = NULL): int|false |
135 | { |
136 | $handle = fopen('php://stdout', 'ab'); |
137 | if ($handle === false) |
138 | { |
139 | return false; |
140 | } |
141 | |
142 | $res = (is_int($len)) |
143 | ? fwrite($handle, $str, $len) |
144 | : fwrite($handle, $str); |
145 | |
146 | fflush($handle); |
147 | |
148 | fclose($handle); |
149 | |
150 | return $res; |
151 | } |
152 | |
153 | /** |
154 | * See if tput exists for fallback terminal size detection |
155 | * |
156 | * @return bool |
157 | * @codeCoverageIgnore |
158 | */ |
159 | private static function has_tput(): bool |
160 | { |
161 | $cmd = shell_exec('type tput'); |
162 | if ( ! is_string($cmd)) |
163 | { |
164 | return FALSE; |
165 | } |
166 | |
167 | return str_contains($cmd, ' is '); |
168 | } |
169 | } |