Complete step 164
This commit is contained in:
parent
79d9a6def3
commit
5566441dc3
1
kilo
1
kilo
@ -12,6 +12,7 @@ define('KILO_QUIT_TIMES', 3);
|
|||||||
|
|
||||||
// Set up autoloading
|
// Set up autoloading
|
||||||
require_once __DIR__ . '/src/functions.php';
|
require_once __DIR__ . '/src/functions.php';
|
||||||
|
require_once __DIR__ . '/src/hldb.php';
|
||||||
spl_autoload_register(static function ($class) {
|
spl_autoload_register(static function ($class) {
|
||||||
$parts = explode('\\', $class);
|
$parts = explode('\\', $class);
|
||||||
$file = __DIR__ . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . $parts[1] . '.php';
|
$file = __DIR__ . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . $parts[1] . '.php';
|
||||||
|
@ -23,11 +23,13 @@ class Editor {
|
|||||||
*/
|
*/
|
||||||
protected array $rows = [];
|
protected array $rows = [];
|
||||||
|
|
||||||
protected int $dirty = 0;
|
public int $dirty = 0;
|
||||||
protected string $filename = '';
|
protected string $filename = '';
|
||||||
protected string $statusMsg = '';
|
protected string $statusMsg = '';
|
||||||
protected int $statusMsgTime;
|
protected int $statusMsgTime;
|
||||||
|
|
||||||
|
public ?Syntax $syntax;
|
||||||
|
|
||||||
public static function new(): Editor
|
public static function new(): Editor
|
||||||
{
|
{
|
||||||
return new self();
|
return new self();
|
||||||
@ -97,6 +99,42 @@ class Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function selectSyntaxHighlight(): void
|
||||||
|
{
|
||||||
|
$this->syntax = NULL;
|
||||||
|
if (empty($this->filename))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In PHP, `strchr` and `strstr` are the same function
|
||||||
|
$ext = (string)strstr($this->filename, '.');
|
||||||
|
|
||||||
|
foreach (get_hldb() as $syntax)
|
||||||
|
{
|
||||||
|
$i = 0;
|
||||||
|
while ($syntax->filematch[$i])
|
||||||
|
{
|
||||||
|
$is_ext = ($syntax->filematch[$i][0] === '.');
|
||||||
|
if (
|
||||||
|
($is_ext && ( ! strcmp($ext, $syntax->filematch[$i]))) ||
|
||||||
|
(( ! $is_ext) && strpos($this->filename, $syntax->filematch{$i}) !== FALSE)
|
||||||
|
) {
|
||||||
|
$this->syntax = $syntax;
|
||||||
|
|
||||||
|
for ($i = 0; $i < $this->numRows; $i++)
|
||||||
|
{
|
||||||
|
$this->rows[$i]->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// ! Row Operations
|
// ! Row Operations
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -143,7 +181,7 @@ class Editor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$row = Row::new($s);
|
$row = Row::new($this, $s);
|
||||||
|
|
||||||
if ($at === $this->numRows)
|
if ($at === $this->numRows)
|
||||||
{
|
{
|
||||||
@ -179,38 +217,6 @@ class Editor {
|
|||||||
$this->dirty++;
|
$this->dirty++;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function rowInsertChar(Row $row, int $at, string $c): void
|
|
||||||
{
|
|
||||||
if ($at < 0 || $at > $row->size)
|
|
||||||
{
|
|
||||||
$at = $row->size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safely insert into arbitrary position in the existing string
|
|
||||||
$row->chars = substr($row->chars, 0, $at) . $c . substr($row->chars, $at);
|
|
||||||
$row->update();
|
|
||||||
$this->dirty++;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function rowAppendString(Row $row, string $s): void
|
|
||||||
{
|
|
||||||
$row->chars .= $s;
|
|
||||||
$row->update();
|
|
||||||
$this->dirty++;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function rowDeleteChar(Row $row, int $at): void
|
|
||||||
{
|
|
||||||
if ($at < 0 || $at >= $row->size)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$row->chars = substr_replace($row->chars, '', $at, 1);
|
|
||||||
$row->update();
|
|
||||||
$this->dirty++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// ! Editor Operations
|
// ! Editor Operations
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -221,7 +227,7 @@ class Editor {
|
|||||||
{
|
{
|
||||||
$this->insertRow($this->numRows, '');
|
$this->insertRow($this->numRows, '');
|
||||||
}
|
}
|
||||||
$this->rowInsertChar($this->rows[$this->cursorY], $this->cursorX, $c);
|
$this->rows[$this->cursorY]->insertChar($this->cursorX, $c);
|
||||||
$this->cursorX++;
|
$this->cursorX++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,13 +263,13 @@ class Editor {
|
|||||||
$row = $this->rows[$this->cursorY];
|
$row = $this->rows[$this->cursorY];
|
||||||
if ($this->cursorX > 0)
|
if ($this->cursorX > 0)
|
||||||
{
|
{
|
||||||
$this->rowDeleteChar($row, $this->cursorX - 1);
|
$row->deleteChar($this->cursorX - 1);
|
||||||
$this->cursorX--;
|
$this->cursorX--;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$this->cursorX = $this->rows[$this->cursorY - 1]->size;
|
$this->cursorX = $this->rows[$this->cursorY - 1]->size;
|
||||||
$this->rowAppendString($this->rows[$this->cursorY -1], $row->chars);
|
$this->rows[$this->cursorY -1]->appendString($row->chars);
|
||||||
$this->deleteRow($this->cursorY);
|
$this->deleteRow($this->cursorY);
|
||||||
$this->cursorY--;
|
$this->cursorY--;
|
||||||
}
|
}
|
||||||
@ -289,12 +295,7 @@ class Editor {
|
|||||||
// Copy filename for display
|
// Copy filename for display
|
||||||
$this->filename = $filename;
|
$this->filename = $filename;
|
||||||
|
|
||||||
// Determine the full path to the file
|
$this->selectSyntaxHighlight();
|
||||||
/* $baseFile = basename($filename);
|
|
||||||
$basePath = str_replace($baseFile, '', $filename);
|
|
||||||
$path = (is_dir($basePath)) ? $basePath : getcwd();
|
|
||||||
|
|
||||||
$fullname = $path . '/' . $baseFile; */
|
|
||||||
|
|
||||||
// #TODO gracefully handle issues with loading a file
|
// #TODO gracefully handle issues with loading a file
|
||||||
$handle = fopen($filename, 'rb');
|
$handle = fopen($filename, 'rb');
|
||||||
@ -302,7 +303,7 @@ class Editor {
|
|||||||
{
|
{
|
||||||
write_stdout("\x1b[2J"); // Clear the screen
|
write_stdout("\x1b[2J"); // Clear the screen
|
||||||
write_stdout("\x1b[H"); // Reposition cursor to top-left
|
write_stdout("\x1b[H"); // Reposition cursor to top-left
|
||||||
disableRawMode();
|
disable_raw_mode();
|
||||||
print_r(error_get_last());
|
print_r(error_get_last());
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
@ -330,6 +331,7 @@ class Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->filename = $newFilename;
|
$this->filename = $newFilename;
|
||||||
|
$this->selectSyntaxHighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
$contents = $this->rowsToString();
|
$contents = $this->rowsToString();
|
||||||
@ -565,9 +567,10 @@ class Editor {
|
|||||||
$this->ab .= "\x1b[7m";
|
$this->ab .= "\x1b[7m";
|
||||||
|
|
||||||
$statusFilename = $this->filename !== '' ? $this->filename : '[No Name]';
|
$statusFilename = $this->filename !== '' ? $this->filename : '[No Name]';
|
||||||
|
$syntaxType = ($this->syntax !== NULL) ? $this->syntax->filetype : 'no ft';
|
||||||
$isDirty = ($this->dirty > 0) ? '(modified)' : '';
|
$isDirty = ($this->dirty > 0) ? '(modified)' : '';
|
||||||
$status = sprintf('%.20s - %d lines %s', $statusFilename, $this->numRows, $isDirty);
|
$status = sprintf('%.20s - %d lines %s', $statusFilename, $this->numRows, $isDirty);
|
||||||
$rstatus = sprintf('%d/%d', $this->cursorY + 1, $this->numRows);
|
$rstatus = sprintf('%s | %d/%d', $syntaxType, $this->cursorY + 1, $this->numRows);
|
||||||
$len = strlen($status);
|
$len = strlen($status);
|
||||||
$rlen = strlen($rstatus);
|
$rlen = strlen($rstatus);
|
||||||
if ($len > $this->screenCols)
|
if ($len > $this->screenCols)
|
||||||
|
77
src/Row.php
77
src/Row.php
@ -12,17 +12,21 @@ class Row {
|
|||||||
public string $chars = '';
|
public string $chars = '';
|
||||||
public string $render = '';
|
public string $render = '';
|
||||||
|
|
||||||
|
// This feels dirty...
|
||||||
|
private Editor $parent;
|
||||||
|
|
||||||
public array $hl = [];
|
public array $hl = [];
|
||||||
|
|
||||||
public static function new(string $chars): self
|
public static function new(Editor $parent, string $chars): self
|
||||||
{
|
{
|
||||||
return new self($chars);
|
$self = new self();
|
||||||
|
$self->chars = $chars;
|
||||||
|
$self->parent = $parent;
|
||||||
|
|
||||||
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function __construct($chars)
|
private function __construct() {}
|
||||||
{
|
|
||||||
$this->chars = $chars;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __get(string $name)
|
public function __get(string $name)
|
||||||
{
|
{
|
||||||
@ -44,6 +48,41 @@ class Row {
|
|||||||
return $this->chars . "\n";
|
return $this->chars . "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function insertChar(int $at, string $c): void
|
||||||
|
{
|
||||||
|
if ($at < 0 || $at > $this->size)
|
||||||
|
{
|
||||||
|
$at = $this->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safely insert into arbitrary position in the existing string
|
||||||
|
$this->chars = substr($this->chars, 0, $at) . $c . substr($this->chars, $at);
|
||||||
|
$this->update();
|
||||||
|
|
||||||
|
$this->parent->dirty++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function appendString(string $s): void
|
||||||
|
{
|
||||||
|
$this->chars .= $s;
|
||||||
|
$this->update();
|
||||||
|
|
||||||
|
$this->parent->dirty++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteChar(int $at): void
|
||||||
|
{
|
||||||
|
if ($at < 0 || $at >= $this->size)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->chars = substr_replace($this->chars, '', $at, 1);
|
||||||
|
$this->update();
|
||||||
|
|
||||||
|
$this->parent->dirty++;
|
||||||
|
}
|
||||||
|
|
||||||
public function update(): void
|
public function update(): void
|
||||||
{
|
{
|
||||||
$idx = 0;
|
$idx = 0;
|
||||||
@ -84,16 +123,24 @@ class Row {
|
|||||||
$char = $this->render[$i];
|
$char = $this->render[$i];
|
||||||
$prevHl = ($i > 0) ? $this->hl[$i - 1] : Highlight::NORMAL;
|
$prevHl = ($i > 0) ? $this->hl[$i - 1] : Highlight::NORMAL;
|
||||||
|
|
||||||
// Numbers, including decimal points
|
if ($this->parent->syntax === NULL)
|
||||||
if (
|
|
||||||
($char === '.' && $prevHl === Highlight::NUMBER) ||
|
|
||||||
(($prevSep || $prevHl === Highlight::NUMBER) && is_digit($char))
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
$this->hl[$i] = Highlight::NUMBER;
|
return;
|
||||||
$i++;
|
}
|
||||||
$prevSep = FALSE;
|
|
||||||
continue;
|
if ($this->parent->syntax->flags & Syntax::HIGHLIGHT_NUMBERS)
|
||||||
|
{
|
||||||
|
// Numbers, including decimal points
|
||||||
|
if (
|
||||||
|
($char === '.' && $prevHl === Highlight::NUMBER) ||
|
||||||
|
(($prevSep || $prevHl === Highlight::NUMBER) && is_digit($char))
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$this->hl[$i] = Highlight::NUMBER;
|
||||||
|
$i++;
|
||||||
|
$prevSep = FALSE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$prevSep = is_separator($char);
|
$prevSep = is_separator($char);
|
||||||
|
21
src/hldb.php
Normal file
21
src/hldb.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Kilo;
|
||||||
|
|
||||||
|
function get_hldb(): array
|
||||||
|
{
|
||||||
|
static $db = [];
|
||||||
|
|
||||||
|
if (count($db) === 0)
|
||||||
|
{
|
||||||
|
$db = [
|
||||||
|
Syntax::new(
|
||||||
|
'c',
|
||||||
|
['.c', '.h', '.cpp'],
|
||||||
|
Syntax::HIGHLIGHT_NUMBERS,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $db;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user