Refactor related class properties into their own objects
This commit is contained in:
parent
0914265492
commit
c5e1b6f1b2
11
README.md
11
README.md
@ -2,13 +2,18 @@
|
|||||||
|
|
||||||
[![Build Status](https://jenkins.timshome.page/buildStatus/icon?job=timw4mail%2Fphp-kilo%2Fmaster)](https://jenkins.timshome.page/job/timw4mail/job/php-kilo/job/master/)
|
[![Build Status](https://jenkins.timshome.page/buildStatus/icon?job=timw4mail%2Fphp-kilo%2Fmaster)](https://jenkins.timshome.page/job/timw4mail/job/php-kilo/job/master/)
|
||||||
|
|
||||||
A reimplementation of the [Kilo](https://viewsourcecode.org/snaptoken/kilo/index.html) tutorial in PHP. Requires PHP 8 and FFI.
|
A reimplementation of the [Kilo](https://viewsourcecode.org/snaptoken/kilo/index.html) tutorial in PHP. Also has some inspiration from the [Hecto](https://www.philippflenker.com/hecto/) text editor tutorial. Requires PHP 8 and FFI.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
* PHP 8
|
||||||
|
* FFI enabled
|
||||||
|
|
||||||
## Implementation notes:
|
## Implementation notes:
|
||||||
|
|
||||||
* The `editor` prefix has been removed from all the relevant functions, instead they are methods on the `Editor` class.
|
* The `editor` prefix has been removed from all the relevant functions, instead they are methods on one of the implementation classes.
|
||||||
* Enums are faked with class constants
|
* Enums are faked with class constants
|
||||||
* Composer is used for autoloading
|
* Composer is used for autoloading
|
||||||
* Properties that must be manually updated in the C version (like counts/string length) are implemented with magic methods,
|
* Properties that must be manually updated in the C version (like counts/string length) are implemented with magic methods,
|
||||||
so they are essentially calculated on read.
|
so they are essentially calculated on read.
|
||||||
* Generally, if a function exists in PHP, with the same name as the C function, the PHP version will be used.
|
* Generally, if a function exists in PHP, with the same name as the C function, the PHP version will be used.
|
||||||
|
* Classes are used to modularize functionality, and reduce the amount of functions with global side effects
|
||||||
|
18
kilo
18
kilo
@ -22,23 +22,7 @@ return (static function (int $argc, array $argv): int {
|
|||||||
Termios::enableRawMode();
|
Termios::enableRawMode();
|
||||||
register_shutdown_function([Termios::class, 'disableRawMode']);
|
register_shutdown_function([Termios::class, 'disableRawMode']);
|
||||||
|
|
||||||
$editor = Editor::new();
|
Editor::new($argc, $argv)->run();
|
||||||
$editor->setStatusMessage('HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find');
|
|
||||||
|
|
||||||
if ($argc >= 2)
|
|
||||||
{
|
|
||||||
$editor->open($argv[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Input Loop
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
$editor->refreshScreen();
|
|
||||||
if ($editor->processKeypress() === NULL)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
})($argc, $argv);
|
})($argc, $argv);
|
||||||
|
@ -2,17 +2,64 @@
|
|||||||
|
|
||||||
namespace Aviat\Kilo;
|
namespace Aviat\Kilo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The representation of the current document being edited
|
||||||
|
*
|
||||||
|
* @property-read int $numRows
|
||||||
|
*/
|
||||||
class Document {
|
class Document {
|
||||||
|
public ?Syntax $syntax = NULL;
|
||||||
|
|
||||||
private function __construct(
|
private function __construct(
|
||||||
public array $rows = [],
|
public array $rows = [],
|
||||||
public ?string $filename = NULL,
|
public ?string $filename = NULL,
|
||||||
private bool $dirty = FALSE,
|
private bool $dirty = FALSE,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
public function __get(string $name): ?int
|
||||||
|
{
|
||||||
|
if ($name === 'numRows')
|
||||||
|
{
|
||||||
|
return count($this->rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function new(): self
|
||||||
|
{
|
||||||
|
return new self();
|
||||||
|
}
|
||||||
|
|
||||||
public static function open(?string $filename = NULL): self
|
public static function open(?string $filename = NULL): self
|
||||||
{
|
{
|
||||||
// @TODO move logic from Editor
|
// @TODO move logic from Editor
|
||||||
return new self(filename: $filename);
|
return new self(filename: $filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public function save(): bool
|
||||||
|
{
|
||||||
|
// @TODO move logic
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insertChar(Point $at, string $c): void
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isDirty(): bool
|
||||||
|
{
|
||||||
|
return $this->dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteChar(Point $at): void
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function insertNewline(Point $at): void
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
167
src/Editor.php
167
src/Editor.php
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace Aviat\Kilo;
|
namespace Aviat\Kilo;
|
||||||
|
|
||||||
|
use Aviat\Kilo\Type\TerminalSize;
|
||||||
use Aviat\Kilo\Enum\{Color, KeyCode, KeyType, Highlight};
|
use Aviat\Kilo\Enum\{Color, KeyCode, KeyType, Highlight};
|
||||||
use Aviat\Kilo\Tokens\PHP8;
|
use Aviat\Kilo\Tokens\PHP8;
|
||||||
|
use Aviat\Kilo\Type\{Point, StatusMessage};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* // Don't highlight this!
|
* // Don't highlight this!
|
||||||
@ -27,20 +29,30 @@ class Editor {
|
|||||||
*/
|
*/
|
||||||
protected Point $offset;
|
protected Point $offset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Document The document being edited
|
||||||
|
*/
|
||||||
|
protected Document $document;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var StatusMessage A disappearing status message
|
||||||
|
*/
|
||||||
|
protected StatusMessage $statusMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var TerminalSize The size of the terminal in rows and columns
|
||||||
|
*/
|
||||||
|
protected TerminalSize $terminalSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int The rendered cursor position
|
* @var int The rendered cursor position
|
||||||
*/
|
*/
|
||||||
protected int $renderX = 0;
|
protected int $renderX = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int The size of the current terminal in rows
|
* @var bool Should we stop the rendering loop?
|
||||||
*/
|
*/
|
||||||
protected int $screenRows = 0;
|
protected bool $shouldQuit = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int The size of the current terminal in columns
|
|
||||||
*/
|
|
||||||
protected int $screenCols = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int The number of times to confirm you wish to quit
|
* @var int The number of times to confirm you wish to quit
|
||||||
@ -54,29 +66,45 @@ class Editor {
|
|||||||
|
|
||||||
public bool $dirty = FALSE;
|
public bool $dirty = FALSE;
|
||||||
public string $filename = '';
|
public string $filename = '';
|
||||||
protected string $statusMsg = '';
|
|
||||||
protected int $statusMsgTime;
|
|
||||||
|
|
||||||
public ?Syntax $syntax = NULL;
|
public ?Syntax $syntax = NULL;
|
||||||
|
|
||||||
// Tokens for highlighting PHP
|
// Tokens for highlighting PHP
|
||||||
public array $tokens = [];
|
public array $tokens = [];
|
||||||
|
|
||||||
public static function new(): Editor
|
/**
|
||||||
|
* Create the Editor instance with CLI arguments
|
||||||
|
*
|
||||||
|
* @param int $argc
|
||||||
|
* @param array $argv
|
||||||
|
* @return Editor
|
||||||
|
*/
|
||||||
|
public static function new(int $argc = 0, array $argv = []): Editor
|
||||||
{
|
{
|
||||||
|
if ($argc >= 2 && ! empty($argv[1]))
|
||||||
|
{
|
||||||
|
return new self($argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
return new self();
|
return new self();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function __construct()
|
/**
|
||||||
|
* The real constructor, ladies and gentlemen
|
||||||
|
*
|
||||||
|
* @param string|null $filename
|
||||||
|
*/
|
||||||
|
private function __construct(?string $filename = NULL)
|
||||||
{
|
{
|
||||||
$this->statusMsgTime = time();
|
$this->statusMessage = StatusMessage::from('HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find');
|
||||||
$this->cursor = Point::new();
|
$this->cursor = Point::new();
|
||||||
$this->offset = Point::new();
|
$this->offset = Point::new();
|
||||||
|
$this->terminalSize = Terminal::size();
|
||||||
|
|
||||||
[$this->screenRows, $this->screenCols] = Terminal::getWindowSize();
|
if (is_string($filename))
|
||||||
|
{
|
||||||
// Remove a row for the status bar, and one for the message bar
|
$this->open($filename);
|
||||||
$this->screenRows -= 2;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __get(string $name): ?int
|
public function __get(string $name): ?int
|
||||||
@ -93,19 +121,31 @@ class Editor {
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'cursor' => $this->cursor,
|
'cursor' => $this->cursor,
|
||||||
|
'document' => $this->document,
|
||||||
'offset' => $this->offset,
|
'offset' => $this->offset,
|
||||||
'dirty' => $this->dirty,
|
'dirty' => $this->dirty,
|
||||||
'filename' => $this->filename,
|
'filename' => $this->filename,
|
||||||
'renderX' => $this->renderX,
|
'renderX' => $this->renderX,
|
||||||
'rows' => $this->rows,
|
'rows' => $this->rows,
|
||||||
'screenCols' => $this->screenCols,
|
'terminalSize' => $this->terminalSize,
|
||||||
'screenRows' => $this->screenRows,
|
'statusMessage' => $this->statusMessage,
|
||||||
'statusMsg' => $this->statusMsg,
|
|
||||||
'syntax' => $this->syntax,
|
'syntax' => $this->syntax,
|
||||||
'tokens' => $this->tokens,
|
'tokens' => $this->tokens,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the input loop
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
while ( ! $this->shouldQuit)
|
||||||
|
{
|
||||||
|
$this->refreshScreen();
|
||||||
|
$this->processKeypress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// ! Terminal
|
// ! Terminal
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -372,7 +412,7 @@ class Editor {
|
|||||||
return implode('', $lines);
|
return implode('', $lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function open(string $filename): void
|
protected function open(string $filename): void
|
||||||
{
|
{
|
||||||
// Copy filename for display
|
// Copy filename for display
|
||||||
$this->filename = $filename;
|
$this->filename = $filename;
|
||||||
@ -422,7 +462,7 @@ class Editor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->setStatusMessage('Failed to save! I/O error: %s', error_get_last()['message']);
|
$this->setStatusMessage('Failed to save! I/O error: %s', error_get_last()['message'] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -543,9 +583,9 @@ class Editor {
|
|||||||
{
|
{
|
||||||
$this->offset->y = $this->cursor->y;
|
$this->offset->y = $this->cursor->y;
|
||||||
}
|
}
|
||||||
else if ($this->cursor->y >= ($this->offset->y + $this->screenRows))
|
else if ($this->cursor->y >= ($this->offset->y + $this->terminalSize->rows))
|
||||||
{
|
{
|
||||||
$this->offset->y = $this->cursor->y - $this->screenRows + 1;
|
$this->offset->y = $this->cursor->y - $this->terminalSize->rows + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Horizontal Scrolling
|
// Horizontal Scrolling
|
||||||
@ -553,15 +593,15 @@ class Editor {
|
|||||||
{
|
{
|
||||||
$this->offset->x = $this->renderX;
|
$this->offset->x = $this->renderX;
|
||||||
}
|
}
|
||||||
else if ($this->renderX >= ($this->offset->x + $this->screenCols))
|
else if ($this->renderX >= ($this->offset->x + $this->terminalSize->cols))
|
||||||
{
|
{
|
||||||
$this->offset->x = $this->renderX - $this->screenCols + 1;
|
$this->offset->x = $this->renderX - $this->terminalSize->cols + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function drawRows(): void
|
protected function drawRows(): void
|
||||||
{
|
{
|
||||||
for ($y = 0; $y < $this->screenRows; $y++)
|
for ($y = 0; $y < $this->terminalSize->rows; $y++)
|
||||||
{
|
{
|
||||||
$filerow = $y + $this->offset->y;
|
$filerow = $y + $this->offset->y;
|
||||||
|
|
||||||
@ -582,9 +622,9 @@ class Editor {
|
|||||||
{
|
{
|
||||||
$len = 0;
|
$len = 0;
|
||||||
}
|
}
|
||||||
if ($len > $this->screenCols)
|
if ($len > $this->terminalSize->cols)
|
||||||
{
|
{
|
||||||
$len = $this->screenCols;
|
$len = $this->terminalSize->cols;
|
||||||
}
|
}
|
||||||
|
|
||||||
$chars = substr($this->rows[$rowIdx]->render, $this->offset->x, (int)$len);
|
$chars = substr($this->rows[$rowIdx]->render, $this->offset->x, (int)$len);
|
||||||
@ -639,16 +679,16 @@ class Editor {
|
|||||||
|
|
||||||
protected function drawPlaceholderRow(int $y): void
|
protected function drawPlaceholderRow(int $y): void
|
||||||
{
|
{
|
||||||
if ($this->numRows === 0 && $y === (int)($this->screenRows / 2))
|
if ($this->numRows === 0 && $y === (int)($this->terminalSize->rows / 2))
|
||||||
{
|
{
|
||||||
$welcome = sprintf('PHP Kilo editor -- version %s', KILO_VERSION);
|
$welcome = sprintf('PHP Kilo editor -- version %s', KILO_VERSION);
|
||||||
$welcomelen = strlen($welcome);
|
$welcomelen = strlen($welcome);
|
||||||
if ($welcomelen > $this->screenCols)
|
if ($welcomelen > $this->terminalSize->cols)
|
||||||
{
|
{
|
||||||
$welcomelen = $this->screenCols;
|
$welcomelen = $this->terminalSize->cols;
|
||||||
}
|
}
|
||||||
|
|
||||||
$padding = ($this->screenCols - $welcomelen) / 2;
|
$padding = ($this->terminalSize->cols - $welcomelen) / 2;
|
||||||
if ($padding > 0)
|
if ($padding > 0)
|
||||||
{
|
{
|
||||||
$this->outputBuffer .= '~';
|
$this->outputBuffer .= '~';
|
||||||
@ -678,14 +718,14 @@ class Editor {
|
|||||||
$rstatus = sprintf('%s | %d/%d', $syntaxType, $this->cursor->y + 1, $this->numRows);
|
$rstatus = sprintf('%s | %d/%d', $syntaxType, $this->cursor->y + 1, $this->numRows);
|
||||||
$len = strlen($status);
|
$len = strlen($status);
|
||||||
$rlen = strlen($rstatus);
|
$rlen = strlen($rstatus);
|
||||||
if ($len > $this->screenCols)
|
if ($len > $this->terminalSize->cols)
|
||||||
{
|
{
|
||||||
$len = $this->screenCols;
|
$len = $this->terminalSize->cols;
|
||||||
}
|
}
|
||||||
$this->outputBuffer .= substr($status, 0, $len);
|
$this->outputBuffer .= substr($status, 0, $len);
|
||||||
while ($len < $this->screenCols)
|
while ($len < $this->terminalSize->cols)
|
||||||
{
|
{
|
||||||
if ($this->screenCols - $len === $rlen)
|
if ($this->terminalSize->cols - $len === $rlen)
|
||||||
{
|
{
|
||||||
$this->outputBuffer .= substr($rstatus, 0, $rlen);
|
$this->outputBuffer .= substr($rstatus, 0, $rlen);
|
||||||
break;
|
break;
|
||||||
@ -701,19 +741,19 @@ class Editor {
|
|||||||
protected function drawMessageBar(): void
|
protected function drawMessageBar(): void
|
||||||
{
|
{
|
||||||
$this->outputBuffer .= ANSI::CLEAR_LINE;
|
$this->outputBuffer .= ANSI::CLEAR_LINE;
|
||||||
$len = strlen($this->statusMsg);
|
$len = strlen($this->statusMessage->text);
|
||||||
if ($len > $this->screenCols)
|
if ($len > $this->terminalSize->cols)
|
||||||
{
|
{
|
||||||
$len = $this->screenCols;
|
$len = $this->terminalSize->cols;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($len > 0 && (time() - $this->statusMsgTime) < 5)
|
if ($len > 0 && (time() - $this->statusMessage->time) < 5)
|
||||||
{
|
{
|
||||||
$this->outputBuffer .= substr($this->statusMsg, 0, $len);
|
$this->outputBuffer .= substr($this->statusMessage->text, 0, $len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function refreshScreen(): void
|
protected function refreshScreen(): void
|
||||||
{
|
{
|
||||||
$this->scroll();
|
$this->scroll();
|
||||||
|
|
||||||
@ -739,10 +779,7 @@ class Editor {
|
|||||||
|
|
||||||
public function setStatusMessage(string $fmt, mixed ...$args): void
|
public function setStatusMessage(string $fmt, mixed ...$args): void
|
||||||
{
|
{
|
||||||
$this->statusMsg = (count($args) > 0)
|
$this->statusMessage = StatusMessage::from($fmt, ...$args);
|
||||||
? sprintf($fmt, ...$args)
|
|
||||||
: $fmt;
|
|
||||||
$this->statusMsgTime = time();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -835,14 +872,14 @@ class Editor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyType::PAGE_UP:
|
case KeyType::PAGE_UP:
|
||||||
$y = ($y > $this->screenRows)
|
$y = ($y > $this->terminalSize->rows)
|
||||||
? $y - $this->screenRows
|
? $y - $this->terminalSize->rows
|
||||||
: 0;
|
: 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyType::PAGE_DOWN:
|
case KeyType::PAGE_DOWN:
|
||||||
$y = ($y + $this->screenRows < $this->numRows)
|
$y = ($y + $this->terminalSize->rows < $this->numRows)
|
||||||
? $y + $this->screenRows
|
? $y + $this->terminalSize->rows
|
||||||
: $this->numRows;
|
: $this->numRows;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -874,23 +911,20 @@ class Editor {
|
|||||||
$this->cursor->y = $y;
|
$this->cursor->y = $y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processKeypress(): ?string
|
protected function processKeypress(): void
|
||||||
{
|
{
|
||||||
$c = $this->readKey();
|
$c = $this->readKey();
|
||||||
|
|
||||||
if ($c === KeyCode::NULL || $c === KeyCode::EMPTY)
|
if ($c === KeyCode::NULL || $c === KeyCode::EMPTY)
|
||||||
{
|
{
|
||||||
return '';
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($c)
|
switch ($c)
|
||||||
{
|
{
|
||||||
case KeyCode::CTRL('q'):
|
case KeyCode::CTRL('q'):
|
||||||
return $this->quitAttempt();
|
$this->quitAttempt();
|
||||||
|
return;
|
||||||
case KeyType::ENTER:
|
|
||||||
$this->insertNewline();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case KeyCode::CTRL('s'):
|
case KeyCode::CTRL('s'):
|
||||||
$this->save();
|
$this->save();
|
||||||
@ -900,6 +934,10 @@ class Editor {
|
|||||||
$this->find();
|
$this->find();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case KeyType::ENTER:
|
||||||
|
$this->insertNewline();
|
||||||
|
break;
|
||||||
|
|
||||||
case KeyType::BACKSPACE:
|
case KeyType::BACKSPACE:
|
||||||
case KeyType::DEL_KEY:
|
case KeyType::DEL_KEY:
|
||||||
if ($c === KeyType::DEL_KEY)
|
if ($c === KeyType::DEL_KEY)
|
||||||
@ -931,12 +969,14 @@ class Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset quit confirmation timer on different keypress
|
// Reset quit confirmation timer on different keypress
|
||||||
$this->quitTimes = KILO_QUIT_TIMES;
|
if ($this->quitTimes < KILO_QUIT_TIMES)
|
||||||
|
{
|
||||||
return $c;
|
$this->quitTimes = KILO_QUIT_TIMES;
|
||||||
|
$this->setStatusMessage('');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function quitAttempt(): ?string
|
protected function quitAttempt(): void
|
||||||
{
|
{
|
||||||
if ($this->dirty && $this->quitTimes > 0)
|
if ($this->dirty && $this->quitTimes > 0)
|
||||||
{
|
{
|
||||||
@ -945,11 +985,12 @@ class Editor {
|
|||||||
$this->quitTimes
|
$this->quitTimes
|
||||||
);
|
);
|
||||||
$this->quitTimes--;
|
$this->quitTimes--;
|
||||||
return KeyCode::CTRL('q');
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Terminal::clear();
|
Terminal::clear();
|
||||||
return NULL;
|
|
||||||
|
$this->shouldQuit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function refreshSyntax(): void
|
protected function refreshSyntax(): void
|
||||||
|
@ -2,7 +2,20 @@
|
|||||||
|
|
||||||
namespace Aviat\Kilo;
|
namespace Aviat\Kilo;
|
||||||
|
|
||||||
|
use Aviat\Kilo\Type\TerminalSize;
|
||||||
|
|
||||||
class Terminal {
|
class Terminal {
|
||||||
|
/**
|
||||||
|
* Get the size of the current terminal window
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
* @return TerminalSize
|
||||||
|
*/
|
||||||
|
public static function size(): TerminalSize
|
||||||
|
{
|
||||||
|
return new TerminalSize(...self::getWindowSize());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the size of the current terminal window
|
* Get the size of the current terminal window
|
||||||
*
|
*
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
namespace Aviat\Kilo;
|
namespace Aviat\Kilo\Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A representation of a 2d point
|
* A representation of a 2d point
|
15
src/Type/StatusMessage.php
Normal file
15
src/Type/StatusMessage.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Aviat\Kilo\Type;
|
||||||
|
|
||||||
|
class StatusMessage {
|
||||||
|
private function __construct(
|
||||||
|
public string $text,
|
||||||
|
public int $time,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static function from(string $text, mixed ...$args): self
|
||||||
|
{
|
||||||
|
return new self(sprintf($text, ...$args), time());
|
||||||
|
}
|
||||||
|
}
|
11
src/Type/TerminalSize.php
Normal file
11
src/Type/TerminalSize.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Aviat\Kilo\Type;
|
||||||
|
|
||||||
|
class TerminalSize {
|
||||||
|
public function __construct(public int $rows, public int $cols)
|
||||||
|
{
|
||||||
|
// Remove a row for the status bar, and one for the message bar
|
||||||
|
$this->rows -= 2;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user