Up to step 71 in tutorial
This commit is contained in:
parent
5e6e6e7c7d
commit
9184a9b90f
12
kilo
12
kilo
@ -7,14 +7,20 @@ require_once __DIR__ . '/src/constants.php';
|
|||||||
require_once __DIR__ . '/src/functions.php';
|
require_once __DIR__ . '/src/functions.php';
|
||||||
require_once __DIR__ . '/src/Editor.php';
|
require_once __DIR__ . '/src/Editor.php';
|
||||||
|
|
||||||
function main(): int
|
function main(int $argc, array $argv): int
|
||||||
{
|
{
|
||||||
global $ffi;
|
global $ffi;
|
||||||
|
|
||||||
enableRawMode();
|
enableRawMode();
|
||||||
|
|
||||||
// ob_start();
|
// ob_start();
|
||||||
$editor = new Editor($ffi);
|
$editor = Editor::new($ffi);
|
||||||
|
|
||||||
|
if ($argc >= 2)
|
||||||
|
{
|
||||||
|
$editor->open($argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Input Loop
|
// Input Loop
|
||||||
while (true)
|
while (true)
|
||||||
@ -28,4 +34,4 @@ function main(): int
|
|||||||
}
|
}
|
||||||
|
|
||||||
//! Init
|
//! Init
|
||||||
main();
|
main($argc, $argv);
|
||||||
|
117
src/Editor.php
117
src/Editor.php
@ -16,16 +16,42 @@ class Key {
|
|||||||
public const PAGE_DOWN = 'PAGE_DOWN';
|
public const PAGE_DOWN = 'PAGE_DOWN';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Row {
|
||||||
|
public string $chars;
|
||||||
|
|
||||||
|
public static function new(string $chars): self
|
||||||
|
{
|
||||||
|
return new self($chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function __construct($chars)
|
||||||
|
{
|
||||||
|
$this->chars = $chars;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Editor {
|
class Editor {
|
||||||
private FFI $ffi;
|
private FFI $ffi;
|
||||||
|
|
||||||
protected int $cursorx = 0;
|
protected int $cursorx = 0;
|
||||||
protected int $cursory = 0;
|
protected int $cursory = 0;
|
||||||
|
protected int $rowoff = 0;
|
||||||
|
protected int $coloff = 0;
|
||||||
protected int $screenRows = 0;
|
protected int $screenRows = 0;
|
||||||
protected int $screenCols = 0;
|
protected int $screenCols = 0;
|
||||||
protected string $ab = '';
|
protected string $ab = '';
|
||||||
|
|
||||||
public function __construct($ffi)
|
/**
|
||||||
|
* Array of Row objects
|
||||||
|
*/
|
||||||
|
protected array $rows = [];
|
||||||
|
|
||||||
|
public static function new(FFI $ffi): Editor
|
||||||
|
{
|
||||||
|
return new self($ffi);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function __construct($ffi)
|
||||||
{
|
{
|
||||||
$this->ffi = $ffi;
|
$this->ffi = $ffi;
|
||||||
|
|
||||||
@ -135,15 +161,63 @@ class Editor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// ! Row Operations
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
protected function appendRow(string $s): void
|
||||||
|
{
|
||||||
|
$this->rows[] = Row::new($s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// ! File I/O
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public function open(string $filename): void
|
||||||
|
{
|
||||||
|
$baseFile = basename($filename);
|
||||||
|
$basePath = str_replace($baseFile, '', $filename);
|
||||||
|
$path = (is_dir($basePath)) ? $basePath : getcwd();
|
||||||
|
|
||||||
|
$fullname = $path . '/' . $baseFile;
|
||||||
|
|
||||||
|
|
||||||
|
$handle = fopen($fullname, 'rb');
|
||||||
|
|
||||||
|
while (($line = fgets($handle)) !== FALSE)
|
||||||
|
{
|
||||||
|
$this->appendRow($line);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// ! Output
|
// ! Output
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
protected function scroll(): void
|
||||||
|
{
|
||||||
|
if ($this->cursory < $this->rowoff)
|
||||||
|
{
|
||||||
|
$this->rowoff = $this->cursory;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->cursory >= $this->rowoff + $this->screenRows)
|
||||||
|
{
|
||||||
|
$this->rowoff = $this->cursory - $this->screenRows + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected function drawRows(): void
|
protected function drawRows(): void
|
||||||
{
|
{
|
||||||
for ($y = 0; $y < $this->screenRows; $y++)
|
for ($y = 0; $y < $this->screenRows; $y++)
|
||||||
{
|
{
|
||||||
if ($y === ($this->screenRows / 3))
|
$filerow = $y + $this->rowoff;
|
||||||
|
if ($filerow >= count($this->rows))
|
||||||
|
{
|
||||||
|
if (count($this->rows) === 0 && $y === $this->screenRows / 3)
|
||||||
{
|
{
|
||||||
$welcome = sprintf('PHP Kilo editor -- version %s', KILO_VERSION);
|
$welcome = sprintf('PHP Kilo editor -- version %s', KILO_VERSION);
|
||||||
$welcomelen = strlen($welcome);
|
$welcomelen = strlen($welcome);
|
||||||
@ -164,12 +238,26 @@ class Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->ab .= substr($welcome, 0, $welcomelen);
|
$this->ab .= substr($welcome, 0, $welcomelen);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$this->ab .= '~';
|
$this->ab .= '~';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$len = strlen($this->rows[$filerow]->chars) - $this->coloff;
|
||||||
|
if ($len < 0)
|
||||||
|
{
|
||||||
|
$len = 0;
|
||||||
|
}
|
||||||
|
if ($len > $this->screenCols)
|
||||||
|
{
|
||||||
|
$len = $this->screenCols;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->ab .= substr($this->rows[$filerow]->chars, $this->coloff, $len);
|
||||||
|
}
|
||||||
|
|
||||||
$this->ab .= "\x1b[K"; // Clear the current line
|
$this->ab .= "\x1b[K"; // Clear the current line
|
||||||
if ($y < $this->screenRows - 1)
|
if ($y < $this->screenRows - 1)
|
||||||
@ -181,6 +269,8 @@ class Editor {
|
|||||||
|
|
||||||
public function refreshScreen(): void
|
public function refreshScreen(): void
|
||||||
{
|
{
|
||||||
|
$this->scroll();
|
||||||
|
|
||||||
$this->ab = '';
|
$this->ab = '';
|
||||||
|
|
||||||
$this->ab .= "\x1b[?25l"; // Hide the cursor
|
$this->ab .= "\x1b[?25l"; // Hide the cursor
|
||||||
@ -189,7 +279,7 @@ class Editor {
|
|||||||
$this->drawRows();
|
$this->drawRows();
|
||||||
|
|
||||||
// Specify the current cursor position
|
// Specify the current cursor position
|
||||||
$this->ab .= sprintf("\x1b[%d;%dH", $this->cursory + 1, $this->cursorx + 1);
|
$this->ab .= sprintf("\x1b[%d;%dH", ($this->cursory - $this->rowoff) + 1, $this->cursorx + 1);
|
||||||
|
|
||||||
$this->ab .= "\x1b[?25h"; // Show the cursor
|
$this->ab .= "\x1b[?25h"; // Show the cursor
|
||||||
|
|
||||||
@ -226,7 +316,7 @@ class Editor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Key::ARROW_DOWN:
|
case Key::ARROW_DOWN:
|
||||||
if ($this->cursory !== $this->screenRows - 1)
|
if ($this->cursory < count($this->rows))
|
||||||
{
|
{
|
||||||
$this->cursory++;
|
$this->cursory++;
|
||||||
}
|
}
|
||||||
@ -255,13 +345,7 @@ class Editor {
|
|||||||
|
|
||||||
case Key::PAGE_UP:
|
case Key::PAGE_UP:
|
||||||
case Key::PAGE_DOWN:
|
case Key::PAGE_DOWN:
|
||||||
{
|
$this->pageUpOrDown($c);
|
||||||
$times = $this->screenRows;
|
|
||||||
while ($times--)
|
|
||||||
{
|
|
||||||
$this->moveCursor($c === Key::PAGE_UP ? Key::ARROW_UP : Key::ARROW_DOWN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key::ARROW_UP:
|
case Key::ARROW_UP:
|
||||||
@ -274,4 +358,13 @@ class Editor {
|
|||||||
|
|
||||||
return $c;
|
return $c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function pageUpOrDown(string $c):void
|
||||||
|
{
|
||||||
|
$times = $this->screenRows;
|
||||||
|
while ($times--)
|
||||||
|
{
|
||||||
|
$this->moveCursor($c === Key::PAGE_UP ? Key::ARROW_UP : Key::ARROW_DOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user