diff --git a/src/Editor.php b/src/Editor.php index 9d78ac7..6d08cb9 100644 --- a/src/Editor.php +++ b/src/Editor.php @@ -12,7 +12,7 @@ use Aviat\Kilo\Tokens\PHP8; class Editor { use Traits\MagicProperties; - private string $ab = ''; + private string $outputBuffer = ''; protected int $cursorX = 0; protected int $cursorY = 0; @@ -189,7 +189,7 @@ class Editor { protected function insertRow(int $at, string $s, bool $updateSyntax = TRUE): void { - if ($at < 0 || $at > $this->numRows) + if ($at > $this->numRows) { return; } @@ -523,103 +523,111 @@ class Editor { for ($y = 0; $y < $this->screenRows; $y++) { $filerow = $y + $this->rowOffset; - if ($filerow >= $this->numRows) + + ($filerow >= $this->numRows) + ? $this->drawPlaceholderRow($y) + : $this->drawRow($filerow); + + $this->outputBuffer .= ANSI::CLEAR_LINE; + $this->outputBuffer .= "\r\n"; + } + } + + protected function drawRow(int $rowIdx): void + { + $len = $this->rows[$rowIdx]->rsize - $this->colOffset; + if ($len < 0) + { + $len = 0; + } + if ($len > $this->screenCols) + { + $len = $this->screenCols; + } + + $chars = substr($this->rows[$rowIdx]->render, $this->colOffset, (int)$len); + $hl = array_slice($this->rows[$rowIdx]->hl, $this->colOffset, (int)$len); + + $currentColor = -1; + + for ($i = 0; $i < $len; $i++) + { + $ch = $chars[$i]; + + // Handle 'non-printable' characters + if (is_ctrl($ch)) { - if ($this->numRows === 0 && $y === (int)($this->screenRows / 2)) + $sym = (ord($ch) <= 26) + ? chr(ord('@') + ord($ch)) + : '?'; + $this->outputBuffer .= ANSI::color(Color::INVERT); + $this->outputBuffer .= $sym; + $this->outputBuffer .= ANSI::RESET_TEXT; + if ($currentColor !== -1) { - $welcome = sprintf('PHP Kilo editor -- version %s', KILO_VERSION); - $welcomelen = strlen($welcome); - if ($welcomelen > $this->screenCols) - { - $welcomelen = $this->screenCols; - } - - $padding = ($this->screenCols - $welcomelen) / 2; - if ($padding > 0) - { - $this->ab .= '~'; - $padding--; - } - for ($i = 0; $i < $padding; $i++) - { - $this->ab .= ' '; - } - - $this->ab .= substr($welcome, 0, $welcomelen); + $this->outputBuffer .= ANSI::color($currentColor); } - else + } + else if ($hl[$i] === Highlight::NORMAL) + { + if ($currentColor !== -1) { - $this->ab .= '~'; + $this->outputBuffer .= ANSI::RESET_TEXT; + $this->outputBuffer .= ANSI::color(Color::FG_WHITE); + $currentColor = -1; } + $this->outputBuffer .= $ch; } else { - $len = $this->rows[$filerow]->rsize - $this->colOffset; - if ($len < 0) + $color = syntax_to_color($hl[$i]); + if ($color !== $currentColor) { - $len = 0; - } - if ($len > $this->screenCols) - { - $len = $this->screenCols; + $currentColor = $color; + $this->outputBuffer .= ANSI::RESET_TEXT; + $this->outputBuffer .= ANSI::color($color); } + $this->outputBuffer .= $ch; + } + } - $c = substr($this->rows[$filerow]->render, $this->colOffset, $len); - $hl = array_slice($this->rows[$filerow]->hl, $this->colOffset, $len); + $this->outputBuffer .= ANSI::RESET_TEXT; + $this->outputBuffer .= ANSI::color(Color::FG_WHITE); + } - $currentColor = -1; - - for ($i = 0; $i < $len; $i++) - { - // Handle 'non-printable' characters - if (is_ctrl($c[$i])) - { - $sym = (ord($c[$i]) <= 26) - ? chr(ord('@') + ord($c[$i])) - : '?'; - $this->ab .= ANSI::color(Color::INVERT); - $this->ab .= $sym; - $this->ab .= ANSI::RESET_TEXT; - if ($currentColor !== -1) - { - $this->ab .= ANSI::color($currentColor); - } - } - else if ($hl[$i] === Highlight::NORMAL) - { - if ($currentColor !== -1) - { - $this->ab .= ANSI::RESET_TEXT; - $this->ab .= ANSI::color(Color::FG_WHITE); - $currentColor = -1; - } - $this->ab .= $c[$i]; - } - else - { - $color = syntax_to_color($hl[$i]); - if ($color !== $currentColor) - { - $currentColor = $color; - $this->ab .= ANSI::RESET_TEXT; - $this->ab .= ANSI::color($color); - } - $this->ab .= $c[$i]; - } - } - - $this->ab .= ANSI::RESET_TEXT; - $this->ab .= ANSI::color(Color::FG_WHITE); + protected function drawPlaceholderRow(int $y): void + { + if ($this->numRows === 0 && $y === (int)($this->screenRows / 2)) + { + $welcome = sprintf('PHP Kilo editor -- version %s', KILO_VERSION); + $welcomelen = strlen($welcome); + if ($welcomelen > $this->screenCols) + { + $welcomelen = $this->screenCols; } - $this->ab .= ANSI::CLEAR_LINE; - $this->ab .= "\r\n"; + $padding = ($this->screenCols - $welcomelen) / 2; + if ($padding > 0) + { + $this->outputBuffer .= '~'; + $padding--; + } + for ($i = 0; $i < $padding; $i++) + { + $this->outputBuffer .= ' '; + } + + $this->outputBuffer .= substr($welcome, 0, $welcomelen); + } + else + { + $this->outputBuffer .= '~'; } } protected function drawStatusBar(): void { - $this->ab .= ANSI::color(Color::INVERT); + $this->outputBuffer .= ANSI::color(Color::INVERT); $statusFilename = $this->filename !== '' ? $this->filename : '[No Name]'; $syntaxType = ($this->syntax !== NULL) ? $this->syntax->filetype : 'no ft'; @@ -632,25 +640,25 @@ class Editor { { $len = $this->screenCols; } - $this->ab .= substr($status, 0, $len); + $this->outputBuffer .= substr($status, 0, $len); while ($len < $this->screenCols) { if ($this->screenCols - $len === $rlen) { - $this->ab .= substr($rstatus, 0, $rlen); + $this->outputBuffer .= substr($rstatus, 0, $rlen); break; } - $this->ab .= ' '; + $this->outputBuffer .= ' '; $len++; } - $this->ab .= ANSI::RESET_TEXT; - $this->ab .= "\r\n"; + $this->outputBuffer .= ANSI::RESET_TEXT; + $this->outputBuffer .= "\r\n"; } protected function drawMessageBar(): void { - $this->ab .= ANSI::CLEAR_LINE; + $this->outputBuffer .= ANSI::CLEAR_LINE; $len = strlen($this->statusMsg); if ($len > $this->screenCols) { @@ -659,7 +667,7 @@ class Editor { if ($len > 0 && (time() - $this->statusMsgTime) < 5) { - $this->ab .= substr($this->statusMsg, 0, $len); + $this->outputBuffer .= substr($this->statusMsg, 0, $len); } } @@ -667,24 +675,24 @@ class Editor { { $this->scroll(); - $this->ab = ''; + $this->outputBuffer = ''; - $this->ab .= ANSI::HIDE_CURSOR; - $this->ab .= ANSI::RESET_CURSOR; + $this->outputBuffer .= ANSI::HIDE_CURSOR; + $this->outputBuffer .= ANSI::RESET_CURSOR; $this->drawRows(); $this->drawStatusBar(); $this->drawMessageBar(); // Specify the current cursor position - $this->ab .= ANSI::moveCursor( + $this->outputBuffer .= ANSI::moveCursor( ($this->cursorY - $this->rowOffset) + 1, ($this->renderX - $this->colOffset) + 1 ); - $this->ab .= ANSI::SHOW_CURSOR; + $this->outputBuffer .= ANSI::SHOW_CURSOR; - echo $this->ab; + echo $this->outputBuffer; } public function setStatusMessage(string $fmt, mixed ...$args): void @@ -904,7 +912,7 @@ class Editor { protected function refreshSyntax(): void { // Update the syntax highlighting for all the rows of the file - // array_walk($this->rows, static fn (Row $row) => $row->updateSyntax()); + array_walk($this->rows, static fn (Row $row) => $row->updateSyntax()); } private function refreshPHPSyntax(): void diff --git a/src/Enum/Color.php b/src/Enum/Color.php index 1fe6085..ce11d31 100644 --- a/src/Enum/Color.php +++ b/src/Enum/Color.php @@ -6,6 +6,7 @@ use Aviat\Kilo\Traits; /** * ANSI Color escape sequences + * @enum */ class Color { use Traits\ConstList; diff --git a/src/Enum/Highlight.php b/src/Enum/Highlight.php index ed37fed..48f797e 100644 --- a/src/Enum/Highlight.php +++ b/src/Enum/Highlight.php @@ -4,6 +4,9 @@ namespace Aviat\Kilo\Enum; use Aviat\Kilo\Traits; +/** + * @enum + */ class Highlight { use Traits\ConstList; diff --git a/src/Enum/KeyCode.php b/src/Enum/KeyCode.php index 5fd94df..3d2fdc0 100644 --- a/src/Enum/KeyCode.php +++ b/src/Enum/KeyCode.php @@ -7,6 +7,7 @@ use function Aviat\Kilo\ctrl_key; /** * 'Raw' input from stdin + * @enum */ class KeyCode { use Traits\ConstList; @@ -35,7 +36,7 @@ class KeyCode { * ctrl + letter combo * * @param string $char - * @return string + * @return string|null */ public static function CTRL(string $char): ?string { diff --git a/src/Enum/KeyType.php b/src/Enum/KeyType.php index ef1fd90..dee5c4e 100644 --- a/src/Enum/KeyType.php +++ b/src/Enum/KeyType.php @@ -6,6 +6,7 @@ use Aviat\Kilo\Traits; /** * Constants representing various control keys + * @enum */ class KeyType { use Traits\ConstList; diff --git a/src/Event.php b/src/Event.php index 55d5fce..6c9f9af 100644 --- a/src/Event.php +++ b/src/Event.php @@ -14,9 +14,9 @@ class Event { public const QUIT_ATTEMPT = 'QUIT_ATTEMPT'; // Mapping of events to handlers - private static $subscribeMap = []; + private static array $subscribeMap = []; - public static function fire(string $eventName, $value): void + public static function fire(string $eventName, mixed $value): void { static::validateEvent($eventName); diff --git a/src/Row.php b/src/Row.php index f502f1e..1acb919 100644 --- a/src/Row.php +++ b/src/Row.php @@ -8,6 +8,7 @@ use Aviat\Kilo\Enum\KeyCode; /** * @property-read int $size * @property-read int $rsize + * @property-read string $chars */ class Row { use Traits\MagicProperties; diff --git a/src/Traits/MagicProperties.php b/src/Traits/MagicProperties.php index 6cde0fd..92661a8 100644 --- a/src/Traits/MagicProperties.php +++ b/src/Traits/MagicProperties.php @@ -5,7 +5,7 @@ namespace Aviat\Kilo\Traits; trait MagicProperties { abstract public function __get(string $name); - public function __set(string $name, $value) + public function __set(string $name, mixed $value): void { if (property_exists($this, $name)) { diff --git a/src/functions.php b/src/functions.php index 39463a9..6536f88 100644 --- a/src/functions.php +++ b/src/functions.php @@ -289,10 +289,10 @@ function syntax_to_color(int $hl): int * Replace tabs with the specified number of spaces. * * @param string $str - * @param int|null $number + * @param int $number * @return string */ -function tabs_to_spaces(string $str, ?int $number = KILO_TAB_STOP): string +function tabs_to_spaces(string $str, int $number = KILO_TAB_STOP): string { return str_replace(KeyCode::TAB, str_repeat(KeyCode::SPACE, $number), $str); } @@ -629,6 +629,7 @@ function php_token_to_highlight(int $token): int // Invalid syntax T_BAD_CHARACTER => Highlight::INVALID, + default => Highlight::NORMAL, }; }