This commit is contained in:
parent
5329378b93
commit
99685f3fcb
11
kilo
11
kilo
@ -6,7 +6,7 @@ namespace Aviat\Kilo;
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
// Log notices/errors/warnings to file
|
||||
set_exception_handler(static function (\Throwable $e) {
|
||||
set_exception_handler(static function (mixed $e) {
|
||||
$msg = print_r([
|
||||
'code' => $e->getCode(),
|
||||
'message' => $e->getMessage(),
|
||||
@ -31,7 +31,14 @@ return (static function (int $argc, array $argv): int {
|
||||
}
|
||||
|
||||
// Input Loop
|
||||
do { $editor->refreshScreen();} while ($editor->processKeypress() !== NULL);
|
||||
while (true)
|
||||
{
|
||||
$editor->refreshScreen();
|
||||
if ($editor->processKeypress() === NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
})($argc, $argv);
|
||||
|
12
src/ANSI.php
12
src/ANSI.php
@ -53,7 +53,7 @@ class ANSI {
|
||||
*/
|
||||
public static function color(int $color): string
|
||||
{
|
||||
return self::seq('%dm', $color);
|
||||
return self::escapeSequence('%dm', $color);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,7 +66,7 @@ class ANSI {
|
||||
*/
|
||||
public static function rgbColor(int $r, int $g, int $b): string
|
||||
{
|
||||
return self::seq('38;2;%d;%d;%dm', $r, $g, $b);
|
||||
return self::escapeSequence('38;2;%d;%d;%dm', $r, $g, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,7 +80,7 @@ class ANSI {
|
||||
{
|
||||
// The terminal has a 1-based coordinate system,
|
||||
// add one to each to allow 0-based coordinate system input
|
||||
return self::seq('%d;%dH', $line + 1, $column + 1);
|
||||
return self::escapeSequence('%d;%dH', $line + 1, $column + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,7 +91,7 @@ class ANSI {
|
||||
*/
|
||||
public static function scrollUp(int $lines): string
|
||||
{
|
||||
return self::seq('%dS', $lines);
|
||||
return self::escapeSequence('%dS', $lines);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,7 +102,7 @@ class ANSI {
|
||||
*/
|
||||
public static function scrollDown(int $lines): string
|
||||
{
|
||||
return self::seq('%dT', $lines);
|
||||
return self::escapeSequence('%dT', $lines);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,7 +112,7 @@ class ANSI {
|
||||
* @param mixed ...$args
|
||||
* @return string
|
||||
*/
|
||||
private static function seq(string $pattern, mixed ...$args): string
|
||||
private static function escapeSequence(string $pattern, mixed ...$args): string
|
||||
{
|
||||
return sprintf("\e[{$pattern}", ...$args);
|
||||
}
|
||||
|
@ -18,14 +18,14 @@ class Editor {
|
||||
private string $outputBuffer = '';
|
||||
|
||||
/**
|
||||
* @var Position The 0-based location of the cursor in the current viewport
|
||||
* @var Point The 0-based location of the cursor in the current viewport
|
||||
*/
|
||||
protected Position $cursor;
|
||||
protected Point $cursor;
|
||||
|
||||
/**
|
||||
* @var Position The scroll offset of the file in the current viewport
|
||||
* @var Point The scroll offset of the file in the current viewport
|
||||
*/
|
||||
protected Position $offset;
|
||||
protected Point $offset;
|
||||
|
||||
/**
|
||||
* @var int The rendered cursor position
|
||||
@ -42,6 +42,11 @@ class Editor {
|
||||
*/
|
||||
protected int $screenCols = 0;
|
||||
|
||||
/**
|
||||
* @var int The number of times to confirm you wish to quit
|
||||
*/
|
||||
protected int $quitTimes = KILO_QUIT_TIMES;
|
||||
|
||||
/**
|
||||
* Array of Row objects
|
||||
*/
|
||||
@ -65,8 +70,8 @@ class Editor {
|
||||
private function __construct()
|
||||
{
|
||||
$this->statusMsgTime = time();
|
||||
$this->cursor = Position::new();
|
||||
$this->offset = Position::new();
|
||||
$this->cursor = Point::new();
|
||||
$this->offset = Point::new();
|
||||
|
||||
[$this->screenRows, $this->screenCols] = Terminal::getWindowSize();
|
||||
|
||||
@ -507,10 +512,8 @@ class Editor {
|
||||
|
||||
protected function find(): void
|
||||
{
|
||||
$savedCx = $this->cursor->x;
|
||||
$savedCy = $this->cursor->y;
|
||||
$savedColOff = $this->offset->x;
|
||||
$savedRowOff = $this->offset->y;
|
||||
$savedCursor = Point::from($this->cursor);
|
||||
$savedOffset = Point::from($this->offset);
|
||||
|
||||
$query = $this->prompt('Search: %s (Use ESC/Arrows/Enter)', [$this, 'findCallback']);
|
||||
|
||||
@ -518,10 +521,8 @@ class Editor {
|
||||
// restore original cursor and scroll locations
|
||||
if ($query === '')
|
||||
{
|
||||
$this->cursor->x = $savedCx;
|
||||
$this->cursor->y = $savedCy;
|
||||
$this->offset->x = $savedColOff;
|
||||
$this->offset->y = $savedRowOff;
|
||||
$this->cursor = Point::from($savedCursor);
|
||||
$this->offset = Point::from($savedOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@ -564,11 +565,12 @@ class Editor {
|
||||
{
|
||||
$filerow = $y + $this->offset->y;
|
||||
|
||||
$this->outputBuffer .= ANSI::CLEAR_LINE;
|
||||
|
||||
($filerow >= $this->numRows)
|
||||
? $this->drawPlaceholderRow($y)
|
||||
: $this->drawRow($filerow);
|
||||
|
||||
$this->outputBuffer .= ANSI::CLEAR_LINE;
|
||||
$this->outputBuffer .= "\r\n";
|
||||
}
|
||||
}
|
||||
@ -851,7 +853,7 @@ class Editor {
|
||||
case KeyType::END_KEY:
|
||||
if ($y < $this->numRows)
|
||||
{
|
||||
$x = $this->rows[$y]->size - 1;
|
||||
$x = $this->rows[$y]->size;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -859,13 +861,13 @@ class Editor {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
if ($x > $row->size)
|
||||
// Snap cursor to the end of a row when moving
|
||||
// from a longer row to a shorter one
|
||||
$row = $this->rows[$y];
|
||||
$rowLen = ($row !== NULL) ? $row->size : 0;
|
||||
if ($x > $rowLen)
|
||||
{
|
||||
$x = $row->size;
|
||||
}
|
||||
if ($y > $this->screenRows)
|
||||
{
|
||||
$y = $this->screenRows;
|
||||
$x = $rowLen;
|
||||
}
|
||||
|
||||
$this->cursor->x = $x;
|
||||
@ -874,8 +876,6 @@ class Editor {
|
||||
|
||||
public function processKeypress(): ?string
|
||||
{
|
||||
static $quit_times = KILO_QUIT_TIMES;
|
||||
|
||||
$c = $this->readKey();
|
||||
|
||||
if ($c === KeyCode::NULL || $c === KeyCode::EMPTY)
|
||||
@ -885,21 +885,13 @@ class Editor {
|
||||
|
||||
switch ($c)
|
||||
{
|
||||
case KeyCode::CTRL('q'):
|
||||
return $this->quitAttempt();
|
||||
|
||||
case KeyType::ENTER:
|
||||
$this->insertNewline();
|
||||
break;
|
||||
|
||||
case KeyCode::CTRL('q'):
|
||||
if ($this->dirty > 0 && $quit_times > 0)
|
||||
{
|
||||
$this->setStatusMessage('WARNING!!! File has unsaved changes.' .
|
||||
'Press Ctrl-Q %d more times to quit.', $quit_times);
|
||||
$quit_times--;
|
||||
return '';
|
||||
}
|
||||
Terminal::clear();
|
||||
return NULL;
|
||||
|
||||
case KeyCode::CTRL('s'):
|
||||
$this->save();
|
||||
break;
|
||||
@ -938,15 +930,32 @@ class Editor {
|
||||
break;
|
||||
}
|
||||
|
||||
$quit_times = KILO_QUIT_TIMES;
|
||||
// Reset quit confirmation timer on different keypress
|
||||
$this->quitTimes = KILO_QUIT_TIMES;
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
protected function quitAttempt(): ?string
|
||||
{
|
||||
if ($this->dirty > 0 && $this->quitTimes > 0)
|
||||
{
|
||||
$this->setStatusMessage(
|
||||
'WARNING!!! File has unsaved changes. Press Ctrl-Q %d more times to quit.',
|
||||
$this->quitTimes
|
||||
);
|
||||
$this->quitTimes--;
|
||||
return KeyCode::CTRL('q');
|
||||
}
|
||||
|
||||
Terminal::clear();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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->update());
|
||||
}
|
||||
|
||||
private function refreshPHPSyntax(): void
|
||||
|
33
src/Point.php
Normal file
33
src/Point.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Aviat\Kilo;
|
||||
|
||||
/**
|
||||
* A representation of a 2d point
|
||||
*/
|
||||
final class Point {
|
||||
private function __construct(public int $x, public int $y) {}
|
||||
|
||||
/**
|
||||
* Create a new Point from coordinates
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @return Point
|
||||
*/
|
||||
public static function new(int $x = 0, int $y = 0): Point
|
||||
{
|
||||
return new Point($x, $y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Point from another position
|
||||
*
|
||||
* @param Point $pos
|
||||
* @return Point
|
||||
*/
|
||||
public static function from(Point $pos): Point
|
||||
{
|
||||
return Point::new($pos->x, $pos->y);
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Aviat\Kilo;
|
||||
|
||||
class Position {
|
||||
private function __construct(public int $x, public int $y) {}
|
||||
|
||||
public static function new(int $x = 0, int $y = 0): self
|
||||
{
|
||||
return new Position($x, $y);
|
||||
}
|
||||
}
|
4
test.php
4
test.php
@ -16,6 +16,10 @@ abstract class Foo implements Ifoo {
|
||||
protected function doNothing(): void {}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public function __construct(public string $foo, public string $bar) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Docblock comment
|
||||
*/
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user