In progress refactoring of Editor->Document
Some checks failed
timw4mail/php-kilo/pipeline/head There was a failure building this commit
Some checks failed
timw4mail/php-kilo/pipeline/head There was a failure building this commit
This commit is contained in:
parent
7d381d10e9
commit
d0aea78ac3
35
composer.lock
generated
35
composer.lock
generated
@ -528,16 +528,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan",
|
"name": "phpstan/phpstan",
|
||||||
"version": "0.12.80",
|
"version": "0.12.81",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan.git",
|
"url": "https://github.com/phpstan/phpstan.git",
|
||||||
"reference": "c6a1b17f22ecf708d434d6bee05092647ec7e686"
|
"reference": "0dd5b0ebeff568f7000022ea5f04aa86ad3124b8"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6a1b17f22ecf708d434d6bee05092647ec7e686",
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/0dd5b0ebeff568f7000022ea5f04aa86ad3124b8",
|
||||||
"reference": "c6a1b17f22ecf708d434d6bee05092647ec7e686",
|
"reference": "0dd5b0ebeff568f7000022ea5f04aa86ad3124b8",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -568,7 +568,7 @@
|
|||||||
"description": "PHPStan - PHP Static Analysis Tool",
|
"description": "PHPStan - PHP Static Analysis Tool",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan/tree/0.12.80"
|
"source": "https://github.com/phpstan/phpstan/tree/0.12.81"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -584,7 +584,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-02-28T20:22:43+00:00"
|
"time": "2021-03-08T22:03:02+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
@ -2994,30 +2994,35 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webmozart/assert",
|
"name": "webmozart/assert",
|
||||||
"version": "1.9.1",
|
"version": "1.10.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/webmozarts/assert.git",
|
"url": "https://github.com/webmozarts/assert.git",
|
||||||
"reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389"
|
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389",
|
"url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25",
|
||||||
"reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389",
|
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^5.3.3 || ^7.0 || ^8.0",
|
"php": "^7.2 || ^8.0",
|
||||||
"symfony/polyfill-ctype": "^1.8"
|
"symfony/polyfill-ctype": "^1.8"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"phpstan/phpstan": "<0.12.20",
|
"phpstan/phpstan": "<0.12.20",
|
||||||
"vimeo/psalm": "<3.9.1"
|
"vimeo/psalm": "<4.6.1 || 4.6.2"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^4.8.36 || ^7.5.13"
|
"phpunit/phpunit": "^8.5.13"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.10-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Webmozart\\Assert\\": "src/"
|
"Webmozart\\Assert\\": "src/"
|
||||||
@ -3041,9 +3046,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/webmozarts/assert/issues",
|
"issues": "https://github.com/webmozarts/assert/issues",
|
||||||
"source": "https://github.com/webmozarts/assert/tree/1.9.1"
|
"source": "https://github.com/webmozarts/assert/tree/1.10.0"
|
||||||
},
|
},
|
||||||
"time": "2020-07-08T17:02:28+00:00"
|
"time": "2021-03-09T10:59:23+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
|
@ -83,9 +83,13 @@ class Document {
|
|||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function insertChar(Point $at, string $c): void
|
public function insert(Point $at, string $c): void
|
||||||
{
|
{
|
||||||
|
if ($at->y === $this->numRows)
|
||||||
|
{
|
||||||
|
$this->insertRow($this->numRows, '');
|
||||||
|
}
|
||||||
|
$this->rows[$at->y]->insertChar($at->x, $c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function insertRow(int $at, string $s, bool $updateSyntax = TRUE): void
|
public function insertRow(int $at, string $s, bool $updateSyntax = TRUE): void
|
||||||
@ -139,9 +143,32 @@ class Document {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function insertNewline(Point $at): void
|
protected function insertNewline(): void
|
||||||
{
|
{
|
||||||
|
// @TODO attempt smart indentation on newline?
|
||||||
|
|
||||||
|
if ($this->cursor->x === 0)
|
||||||
|
{
|
||||||
|
$this->insertRow($this->cursor->y, '');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$row = $this->rows[$this->cursor->y];
|
||||||
|
$chars = $row->chars;
|
||||||
|
$newChars = substr($chars, 0, $this->cursor->x);
|
||||||
|
|
||||||
|
// Truncate the previous row
|
||||||
|
$row->chars = $newChars;
|
||||||
|
|
||||||
|
// Add a new row, with the contents from the cursor to the end of the line
|
||||||
|
$this->insertRow($this->cursor->y + 1, substr($chars, $this->cursor->x));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cursor->y++;
|
||||||
|
$this->cursor->x = 0;
|
||||||
|
|
||||||
|
// Re-tokenize the file
|
||||||
|
$this->refreshPHPSyntax();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function selectSyntaxHighlight(): void
|
public function selectSyntaxHighlight(): void
|
||||||
|
258
src/Editor.php
258
src/Editor.php
@ -4,15 +4,12 @@ namespace Aviat\Kilo;
|
|||||||
|
|
||||||
use Aviat\Kilo\Type\TerminalSize;
|
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\Type\{Point, StatusMessage};
|
use Aviat\Kilo\Type\{Point, StatusMessage};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* // Don't highlight this!
|
* // Don't highlight this!
|
||||||
*/
|
*/
|
||||||
class Editor {
|
class Editor {
|
||||||
use Traits\MagicProperties;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string The screen buffer
|
* @var string The screen buffer
|
||||||
*/
|
*/
|
||||||
@ -58,19 +55,6 @@ class Editor {
|
|||||||
*/
|
*/
|
||||||
protected int $quitTimes = KILO_QUIT_TIMES;
|
protected int $quitTimes = KILO_QUIT_TIMES;
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of Row objects
|
|
||||||
*/
|
|
||||||
public array $rows = [];
|
|
||||||
|
|
||||||
public bool $dirty = FALSE;
|
|
||||||
public string $filename = '';
|
|
||||||
|
|
||||||
public ?Syntax $syntax = NULL;
|
|
||||||
|
|
||||||
// Tokens for highlighting PHP
|
|
||||||
public array $tokens = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the Editor instance with CLI arguments
|
* Create the Editor instance with CLI arguments
|
||||||
*
|
*
|
||||||
@ -99,22 +83,21 @@ class Editor {
|
|||||||
$this->cursor = Point::new();
|
$this->cursor = Point::new();
|
||||||
$this->offset = Point::new();
|
$this->offset = Point::new();
|
||||||
$this->terminalSize = Terminal::size();
|
$this->terminalSize = Terminal::size();
|
||||||
$this->document = Document::new();
|
$this->document = Document::default();
|
||||||
|
|
||||||
if (is_string($filename))
|
if (is_string($filename))
|
||||||
{
|
{
|
||||||
$this->open($filename);
|
$maybeDocument = Document::open($filename);
|
||||||
}
|
if ($maybeDocument === NULL)
|
||||||
}
|
|
||||||
|
|
||||||
public function __get(string $name): ?int
|
|
||||||
{
|
{
|
||||||
if ($name === 'numRows')
|
$this->document = Document::default();
|
||||||
{
|
$this->setStatusMessage("ERR: Could not open file: {}", $filename);
|
||||||
return count($this->rows);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->document = $maybeDocument;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __debugInfo(): array
|
public function __debugInfo(): array
|
||||||
@ -123,14 +106,9 @@ class Editor {
|
|||||||
'cursor' => $this->cursor,
|
'cursor' => $this->cursor,
|
||||||
'document' => $this->document,
|
'document' => $this->document,
|
||||||
'offset' => $this->offset,
|
'offset' => $this->offset,
|
||||||
'dirty' => $this->dirty,
|
|
||||||
'filename' => $this->filename,
|
|
||||||
'renderX' => $this->renderX,
|
'renderX' => $this->renderX,
|
||||||
'rows' => $this->rows,
|
|
||||||
'terminalSize' => $this->terminalSize,
|
'terminalSize' => $this->terminalSize,
|
||||||
'statusMessage' => $this->statusMessage,
|
'statusMessage' => $this->statusMessage,
|
||||||
'syntax' => $this->syntax,
|
|
||||||
'tokens' => $this->tokens,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,38 +159,6 @@ 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(basename($this->filename), '.');
|
|
||||||
|
|
||||||
foreach (get_file_syntax_map() as $syntax)
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
in_array($ext, $syntax->filematch, TRUE) ||
|
|
||||||
in_array(basename($this->filename), $syntax->filematch, TRUE)
|
|
||||||
) {
|
|
||||||
$this->syntax = $syntax;
|
|
||||||
|
|
||||||
// Pre-tokenize the file
|
|
||||||
if ($this->syntax->filetype === 'PHP')
|
|
||||||
{
|
|
||||||
$this->tokens = PHP8::getFileTokens($this->filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->refreshSyntax();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// ! Row Operations
|
// ! Row Operations
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -266,62 +212,22 @@ class Editor {
|
|||||||
return $cx;
|
return $cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function insertRow(int $at, string $s, bool $updateSyntax = TRUE): void
|
|
||||||
{
|
|
||||||
if ($at > $this->numRows)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$row = Row::new($this, $s, $at);
|
|
||||||
|
|
||||||
if ($at === $this->numRows)
|
|
||||||
{
|
|
||||||
$this->rows[] = $row;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->rows = [
|
|
||||||
...array_slice($this->rows, 0, $at),
|
|
||||||
$row,
|
|
||||||
...array_slice($this->rows, $at),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Update indexes of each row so that correct highlighting is done
|
|
||||||
for ($idx = $at; $idx < $this->numRows; $idx++)
|
|
||||||
{
|
|
||||||
$this->rows[$idx]->idx = $idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ksort($this->rows);
|
|
||||||
|
|
||||||
$this->rows[$at]->update();
|
|
||||||
|
|
||||||
$this->dirty = true;
|
|
||||||
|
|
||||||
// Re-tokenize the file
|
|
||||||
if ($updateSyntax)
|
|
||||||
{
|
|
||||||
$this->refreshPHPSyntax();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function deleteRow(int $at): void
|
protected function deleteRow(int $at): void
|
||||||
{
|
{
|
||||||
if ($at < 0 || $at >= $this->numRows)
|
if ($at < 0 || $at >= $this->document->numRows)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the row
|
// Remove the row
|
||||||
unset($this->rows[$at]);
|
unset($this->document->rows[$at]);
|
||||||
|
|
||||||
// Re-index the array of rows
|
// Re-index the array of rows
|
||||||
$this->rows = array_values($this->rows);
|
$this->document->rows = array_values($this->document->rows);
|
||||||
for ($i = $at; $i < $this->numRows; $i++)
|
for ($i = $at; $i < $this->document->numRows; $i++)
|
||||||
{
|
{
|
||||||
$this->rows[$i]->idx = $i;
|
$this->document->rows[$i]->idx = $i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-tokenize the file
|
// Re-tokenize the file
|
||||||
@ -336,16 +242,8 @@ class Editor {
|
|||||||
|
|
||||||
protected function insertChar(string $c): void
|
protected function insertChar(string $c): void
|
||||||
{
|
{
|
||||||
if ($this->cursor->y === $this->numRows)
|
$this->document->insert($this->cursor, $c);
|
||||||
{
|
$this->moveCursor(KeyType::ARROW_RIGHT);
|
||||||
$this->insertRow($this->numRows, '');
|
|
||||||
}
|
|
||||||
$this->rows[$this->cursor->y]->insertChar($this->cursor->x, $c);
|
|
||||||
|
|
||||||
// Re-tokenize the file
|
|
||||||
$this->refreshPHPSyntax();
|
|
||||||
|
|
||||||
$this->cursor->x++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function insertNewline(): void
|
protected function insertNewline(): void
|
||||||
@ -354,11 +252,11 @@ class Editor {
|
|||||||
|
|
||||||
if ($this->cursor->x === 0)
|
if ($this->cursor->x === 0)
|
||||||
{
|
{
|
||||||
$this->insertRow($this->cursor->y, '');
|
$this->document->insert(Point::new(0, $this->cursor->y), '');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$row = $this->rows[$this->cursor->y];
|
$row = $this->document->rows[$this->cursor->y];
|
||||||
$chars = $row->chars;
|
$chars = $row->chars;
|
||||||
$newChars = substr($chars, 0, $this->cursor->x);
|
$newChars = substr($chars, 0, $this->cursor->x);
|
||||||
|
|
||||||
@ -366,24 +264,25 @@ class Editor {
|
|||||||
$row->chars = $newChars;
|
$row->chars = $newChars;
|
||||||
|
|
||||||
// Add a new row, with the contents from the cursor to the end of the line
|
// Add a new row, with the contents from the cursor to the end of the line
|
||||||
$this->insertRow($this->cursor->y + 1, substr($chars, $this->cursor->x));
|
$this->document->insert(Point::new(0, $this->cursor->y + 1), substr($chars, $this->cursor->x));
|
||||||
|
// $this->insertRow($this->cursor->y + 1, substr($chars, $this->cursor->x));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->cursor->y++;
|
$this->cursor->y++;
|
||||||
$this->cursor->x = 0;
|
$this->cursor->x = 0;
|
||||||
|
|
||||||
// Re-tokenize the file
|
// Re-tokenize the file
|
||||||
$this->refreshPHPSyntax();
|
// $this->refreshPHPSyntax();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function deleteChar(): void
|
protected function deleteChar(): void
|
||||||
{
|
{
|
||||||
if ($this->cursor->y === $this->numRows || ($this->cursor->x === 0 && $this->cursor->y === 0))
|
if ($this->cursor->y === $this->document->numRows || ($this->cursor->x === 0 && $this->cursor->y === 0))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$row = $this->rows[$this->cursor->y];
|
$row = $this->document->rows[$this->cursor->y];
|
||||||
if ($this->cursor->x > 0)
|
if ($this->cursor->x > 0)
|
||||||
{
|
{
|
||||||
$row->deleteChar($this->cursor->x - 1);
|
$row->deleteChar($this->cursor->x - 1);
|
||||||
@ -391,8 +290,8 @@ class Editor {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$this->cursor->x = $this->rows[$this->cursor->y - 1]->size;
|
$this->cursor->x = $this->document->rows[$this->cursor->y - 1]->size;
|
||||||
$this->rows[$this->cursor->y -1]->appendString($row->chars);
|
$this->document->rows[$this->cursor->y -1]->appendString($row->chars);
|
||||||
$this->deleteRow($this->cursor->y);
|
$this->deleteRow($this->cursor->y);
|
||||||
$this->cursor->y--;
|
$this->cursor->y--;
|
||||||
}
|
}
|
||||||
@ -407,39 +306,14 @@ class Editor {
|
|||||||
|
|
||||||
protected function rowsToString(): string
|
protected function rowsToString(): string
|
||||||
{
|
{
|
||||||
$lines = array_map(fn (Row $row) => (string)$row, $this->rows);
|
$lines = array_map(fn (Row $row) => (string)$row, $this->document->rows);
|
||||||
|
|
||||||
return implode('', $lines);
|
return implode('', $lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function open(string $filename): void
|
|
||||||
{
|
|
||||||
// Copy filename for display
|
|
||||||
$this->filename = $filename;
|
|
||||||
|
|
||||||
$this->selectSyntaxHighlight();
|
|
||||||
|
|
||||||
$handle = fopen($filename, 'rb');
|
|
||||||
if ($handle === FALSE)
|
|
||||||
{
|
|
||||||
$this->setStatusMessage('Failed to open file: %s', $filename);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (($line = fgets($handle)) !== FALSE)
|
|
||||||
{
|
|
||||||
// Remove line endings when reading the file
|
|
||||||
$this->insertRow($this->numRows, rtrim($line), FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose($handle);
|
|
||||||
|
|
||||||
$this->dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function save(): void
|
protected function save(): void
|
||||||
{
|
{
|
||||||
if ($this->filename === '')
|
if ($this->document->filename === '')
|
||||||
{
|
{
|
||||||
$newFilename = $this->prompt('Save as: %s');
|
$newFilename = $this->prompt('Save as: %s');
|
||||||
if ($newFilename === '')
|
if ($newFilename === '')
|
||||||
@ -448,17 +322,14 @@ class Editor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->filename = $newFilename;
|
$this->document->filename = $newFilename;
|
||||||
$this->selectSyntaxHighlight();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$contents = $this->rowsToString();
|
$res = $this->document->save();
|
||||||
|
|
||||||
$res = file_put_contents($this->filename, $contents);
|
if ($res !== FALSE)
|
||||||
if ($res === strlen($contents))
|
|
||||||
{
|
{
|
||||||
$this->setStatusMessage('%d bytes written to disk', strlen($contents));
|
$this->setStatusMessage('%d bytes written to disk', $res);
|
||||||
$this->dirty = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,7 +350,7 @@ class Editor {
|
|||||||
|
|
||||||
if ( ! empty($savedHl))
|
if ( ! empty($savedHl))
|
||||||
{
|
{
|
||||||
$this->rows[$savedHlLine]->hl = $savedHl;
|
$this->document->rows[$savedHlLine]->hl = $savedHl;
|
||||||
$savedHl = [];
|
$savedHl = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,19 +389,19 @@ class Editor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($i = 0; $i < $this->numRows; $i++)
|
for ($i = 0; $i < $this->document->numRows; $i++)
|
||||||
{
|
{
|
||||||
$current += $direction;
|
$current += $direction;
|
||||||
if ($current === -1)
|
if ($current === -1)
|
||||||
{
|
{
|
||||||
$current = $this->numRows - 1;
|
$current = $this->document->numRows - 1;
|
||||||
}
|
}
|
||||||
else if ($current === $this->numRows)
|
else if ($current === $this->document->numRows)
|
||||||
{
|
{
|
||||||
$current = 0;
|
$current = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$row =& $this->rows[$current];
|
$row =& $this->document->rows[$current];
|
||||||
|
|
||||||
$match = strpos($row->render, $query);
|
$match = strpos($row->render, $query);
|
||||||
if ($match !== FALSE)
|
if ($match !== FALSE)
|
||||||
@ -538,7 +409,7 @@ class Editor {
|
|||||||
$lastMatch = $current;
|
$lastMatch = $current;
|
||||||
$this->cursor->y = (int)$current;
|
$this->cursor->y = (int)$current;
|
||||||
$this->cursor->x = $this->rowRxToCx($row, $match);
|
$this->cursor->x = $this->rowRxToCx($row, $match);
|
||||||
$this->offset->y = $this->numRows;
|
$this->offset->y = $this->document->numRows;
|
||||||
|
|
||||||
$savedHlLine = $current;
|
$savedHlLine = $current;
|
||||||
$savedHl = $row->hl;
|
$savedHl = $row->hl;
|
||||||
@ -573,9 +444,9 @@ class Editor {
|
|||||||
protected function scroll(): void
|
protected function scroll(): void
|
||||||
{
|
{
|
||||||
$this->renderX = 0;
|
$this->renderX = 0;
|
||||||
if ($this->cursor->y < $this->numRows)
|
if ($this->cursor->y < $this->document->numRows)
|
||||||
{
|
{
|
||||||
$this->renderX = $this->rowCxToRx($this->rows[$this->cursor->y], $this->cursor->x);
|
$this->renderX = $this->rowCxToRx($this->document->rows[$this->cursor->y], $this->cursor->x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vertical Scrolling
|
// Vertical Scrolling
|
||||||
@ -607,7 +478,7 @@ class Editor {
|
|||||||
|
|
||||||
$this->outputBuffer .= ANSI::CLEAR_LINE;
|
$this->outputBuffer .= ANSI::CLEAR_LINE;
|
||||||
|
|
||||||
($filerow >= $this->numRows)
|
($filerow >= $this->document->numRows)
|
||||||
? $this->drawPlaceholderRow($y)
|
? $this->drawPlaceholderRow($y)
|
||||||
: $this->drawRow($filerow);
|
: $this->drawRow($filerow);
|
||||||
|
|
||||||
@ -617,7 +488,7 @@ class Editor {
|
|||||||
|
|
||||||
protected function drawRow(int $rowIdx): void
|
protected function drawRow(int $rowIdx): void
|
||||||
{
|
{
|
||||||
$len = $this->rows[$rowIdx]->rsize - $this->offset->x;
|
$len = $this->document->rows[$rowIdx]->rsize - $this->offset->x;
|
||||||
if ($len < 0)
|
if ($len < 0)
|
||||||
{
|
{
|
||||||
$len = 0;
|
$len = 0;
|
||||||
@ -627,8 +498,8 @@ class Editor {
|
|||||||
$len = $this->terminalSize->cols;
|
$len = $this->terminalSize->cols;
|
||||||
}
|
}
|
||||||
|
|
||||||
$chars = substr($this->rows[$rowIdx]->render, $this->offset->x, (int)$len);
|
$chars = substr($this->document->rows[$rowIdx]->render, $this->offset->x, (int)$len);
|
||||||
$hl = array_slice($this->rows[$rowIdx]->hl, $this->offset->x, (int)$len);
|
$hl = array_slice($this->document->rows[$rowIdx]->hl, $this->offset->x, (int)$len);
|
||||||
|
|
||||||
$currentColor = -1;
|
$currentColor = -1;
|
||||||
|
|
||||||
@ -679,7 +550,7 @@ class Editor {
|
|||||||
|
|
||||||
protected function drawPlaceholderRow(int $y): void
|
protected function drawPlaceholderRow(int $y): void
|
||||||
{
|
{
|
||||||
if ($this->numRows === 0 && $y === (int)($this->terminalSize->rows / 2))
|
if ($this->document->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);
|
||||||
@ -714,8 +585,8 @@ class Editor {
|
|||||||
$statusFilename = $this->filename !== '' ? $this->filename : '[No Name]';
|
$statusFilename = $this->filename !== '' ? $this->filename : '[No Name]';
|
||||||
$syntaxType = ($this->syntax !== NULL) ? $this->syntax->filetype : 'no ft';
|
$syntaxType = ($this->syntax !== NULL) ? $this->syntax->filetype : 'no ft';
|
||||||
$isDirty = $this->dirty ? '(modified)' : '';
|
$isDirty = $this->dirty ? '(modified)' : '';
|
||||||
$status = sprintf('%.20s - %d lines %s', $statusFilename, $this->numRows, $isDirty);
|
$status = sprintf('%.20s - %d lines %s', $statusFilename, $this->document->numRows, $isDirty);
|
||||||
$rstatus = sprintf('%s | %d/%d', $syntaxType, $this->cursor->y + 1, $this->numRows);
|
$rstatus = sprintf('%s | %d/%d', $syntaxType, $this->cursor->y + 1, $this->document->numRows);
|
||||||
$len = strlen($status);
|
$len = strlen($status);
|
||||||
$rlen = strlen($rstatus);
|
$rlen = strlen($rstatus);
|
||||||
if ($len > $this->terminalSize->cols)
|
if ($len > $this->terminalSize->cols)
|
||||||
@ -828,7 +699,7 @@ class Editor {
|
|||||||
{
|
{
|
||||||
$x = $this->cursor->x;
|
$x = $this->cursor->x;
|
||||||
$y = $this->cursor->y;
|
$y = $this->cursor->y;
|
||||||
$row = $this->rows[$y];
|
$row = $this->document->rows[$y];
|
||||||
|
|
||||||
switch ($key)
|
switch ($key)
|
||||||
{
|
{
|
||||||
@ -841,7 +712,7 @@ class Editor {
|
|||||||
{
|
{
|
||||||
// Beginning of a line, go to end of previous line
|
// Beginning of a line, go to end of previous line
|
||||||
$y--;
|
$y--;
|
||||||
$x = $this->rows[$y]->size - 1;
|
$x = $this->document->rows[$y]->size - 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -865,7 +736,7 @@ class Editor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyType::ARROW_DOWN:
|
case KeyType::ARROW_DOWN:
|
||||||
if ($y < $this->numRows)
|
if ($y < $this->document->numRows)
|
||||||
{
|
{
|
||||||
$y++;
|
$y++;
|
||||||
}
|
}
|
||||||
@ -878,9 +749,9 @@ class Editor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyType::PAGE_DOWN:
|
case KeyType::PAGE_DOWN:
|
||||||
$y = ($y + $this->terminalSize->rows < $this->numRows)
|
$y = ($y + $this->terminalSize->rows < $this->document->numRows)
|
||||||
? $y + $this->terminalSize->rows
|
? $y + $this->terminalSize->rows
|
||||||
: $this->numRows;
|
: $this->document->numRows;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyType::HOME_KEY:
|
case KeyType::HOME_KEY:
|
||||||
@ -888,9 +759,9 @@ class Editor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyType::END_KEY:
|
case KeyType::END_KEY:
|
||||||
if ($y < $this->numRows)
|
if ($y < $this->document->numRows)
|
||||||
{
|
{
|
||||||
$x = $this->rows[$y]->size;
|
$x = $this->document->rows[$y]->size;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -900,7 +771,7 @@ class Editor {
|
|||||||
|
|
||||||
// Snap cursor to the end of a row when moving
|
// Snap cursor to the end of a row when moving
|
||||||
// from a longer row to a shorter one
|
// from a longer row to a shorter one
|
||||||
$row = $this->rows[$y];
|
$row = $this->document->rows[$y];
|
||||||
$rowLen = ($row !== NULL) ? $row->size : 0;
|
$rowLen = ($row !== NULL) ? $row->size : 0;
|
||||||
if ($x > $rowLen)
|
if ($x > $rowLen)
|
||||||
{
|
{
|
||||||
@ -978,7 +849,7 @@ class Editor {
|
|||||||
|
|
||||||
protected function quitAttempt(): void
|
protected function quitAttempt(): void
|
||||||
{
|
{
|
||||||
if ($this->dirty && $this->quitTimes > 0)
|
if ($this->document->dirty && $this->quitTimes > 0)
|
||||||
{
|
{
|
||||||
$this->setStatusMessage(
|
$this->setStatusMessage(
|
||||||
'WARNING!!! File has unsaved changes. Press Ctrl-Q %d more times to quit.',
|
'WARNING!!! File has unsaved changes. Press Ctrl-Q %d more times to quit.',
|
||||||
@ -992,21 +863,4 @@ class Editor {
|
|||||||
|
|
||||||
$this->shouldQuit = true;
|
$this->shouldQuit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function refreshSyntax(): void
|
|
||||||
{
|
|
||||||
// Update the syntax highlighting for all the rows of the file
|
|
||||||
array_walk($this->rows, static fn (Row $row) => $row->update());
|
|
||||||
}
|
|
||||||
|
|
||||||
private function refreshPHPSyntax(): void
|
|
||||||
{
|
|
||||||
if ($this->syntax?->filetype !== 'PHP')
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->tokens = PHP8::getTokens($this->rowsToString());
|
|
||||||
$this->refreshSyntax();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
42
src/Row.php
42
src/Row.php
@ -21,12 +21,12 @@ class Row {
|
|||||||
public int $idx;
|
public int $idx;
|
||||||
|
|
||||||
// This feels dirty...
|
// This feels dirty...
|
||||||
private Editor $parent;
|
private Document $parent;
|
||||||
private bool $hlOpenComment = FALSE;
|
private bool $hlOpenComment = FALSE;
|
||||||
|
|
||||||
private const T_RAW = -1;
|
private const T_RAW = -1;
|
||||||
|
|
||||||
public static function new(Editor $parent, string $chars, int $idx): self
|
public static function new(Document $parent, string $chars, int $idx): self
|
||||||
{
|
{
|
||||||
$self = new self();
|
$self = new self();
|
||||||
$self->chars = $chars;
|
$self->chars = $chars;
|
||||||
@ -113,38 +113,38 @@ class Row {
|
|||||||
$this->parent->dirty = true;
|
$this->parent->dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function highlight(): void
|
||||||
|
{
|
||||||
|
// $this->update();
|
||||||
|
$this->highlightGeneral();
|
||||||
|
}
|
||||||
|
|
||||||
public function update(): void
|
public function update(): void
|
||||||
{
|
{
|
||||||
$this->render = tabs_to_spaces($this->chars);
|
$this->render = tabs_to_spaces($this->chars);
|
||||||
|
$this->highlight();
|
||||||
$this->updateSyntax();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// ! Syntax Highlighting
|
// ! Syntax Highlighting
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
public function updateSyntax(): void
|
public function highlightGeneral(): void
|
||||||
{
|
{
|
||||||
$this->hl = array_fill(0, $this->rsize, Highlight::NORMAL);
|
$this->hl = array_fill(0, $this->rsize, Highlight::NORMAL);
|
||||||
|
|
||||||
if ($this->parent->syntax === NULL)
|
if ($this->parent->fileType->name === 'PHP')
|
||||||
{
|
{
|
||||||
|
$this->highlightPHP();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->parent->syntax->filetype === 'PHP')
|
$keywords1 = $this->parent->fileType->syntax->keywords1;
|
||||||
{
|
$keywords2 = $this->parent->fileType->syntax->keywords2;
|
||||||
$this->updateSyntaxPHP();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$keywords1 = $this->parent->syntax->keywords1;
|
$scs = $this->parent->fileType->syntax->singleLineCommentStart;
|
||||||
$keywords2 = $this->parent->syntax->keywords2;
|
$mcs = $this->parent->fileType->syntax->multiLineCommentStart;
|
||||||
|
$mce = $this->parent->fileType->syntax->multiLineCommentEnd;
|
||||||
$scs = $this->parent->syntax->singleLineCommentStart;
|
|
||||||
$mcs = $this->parent->syntax->multiLineCommentStart;
|
|
||||||
$mce = $this->parent->syntax->multiLineCommentEnd;
|
|
||||||
|
|
||||||
$scsLen = strlen($scs);
|
$scsLen = strlen($scs);
|
||||||
$mcsLen = strlen($mcs);
|
$mcsLen = strlen($mcs);
|
||||||
@ -198,7 +198,7 @@ class Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// String/Char literals
|
// String/Char literals
|
||||||
if ($this->parent->syntax->flags & Syntax::HIGHLIGHT_STRINGS)
|
if ($this->parent->fileType->syntax->flags & Syntax::HIGHLIGHT_STRINGS)
|
||||||
{
|
{
|
||||||
if ($inString !== '')
|
if ($inString !== '')
|
||||||
{
|
{
|
||||||
@ -231,7 +231,7 @@ class Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Numbers, including decimal points
|
// Numbers, including decimal points
|
||||||
if ($this->parent->syntax->flags & Syntax::HIGHLIGHT_NUMBERS)
|
if ($this->parent->fileType->syntax->flags & Syntax::HIGHLIGHT_NUMBERS)
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
($char === '.' && $prevHl === Highlight::NUMBER) ||
|
($char === '.' && $prevHl === Highlight::NUMBER) ||
|
||||||
@ -284,7 +284,7 @@ class Row {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function updateSyntaxPHP():void
|
protected function highlightPHP(): void
|
||||||
{
|
{
|
||||||
$rowNum = $this->idx + 1;
|
$rowNum = $this->idx + 1;
|
||||||
|
|
||||||
@ -402,7 +402,7 @@ class Row {
|
|||||||
|
|
||||||
// Types/identifiers/keywords that don't have their own token, but are
|
// Types/identifiers/keywords that don't have their own token, but are
|
||||||
// defined as keywords
|
// defined as keywords
|
||||||
in_array($token['char'], $this->parent->syntax->keywords2 ?? [], TRUE)
|
in_array($token['char'], $this->parent->fileType->syntax->keywords2, TRUE)
|
||||||
=> Highlight::KEYWORD2,
|
=> Highlight::KEYWORD2,
|
||||||
|
|
||||||
default => Highlight::NORMAL,
|
default => Highlight::NORMAL,
|
||||||
|
@ -187,97 +187,3 @@ function tabs_to_spaces(string $str, int $number = KILO_TAB_STOP): string
|
|||||||
{
|
{
|
||||||
return str_replace(KeyCode::TAB, str_repeat(KeyCode::SPACE, $number), $str);
|
return str_replace(KeyCode::TAB, str_repeat(KeyCode::SPACE, $number), $str);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate/Get the syntax highlighting objects
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
function get_file_syntax_map(): array
|
|
||||||
{
|
|
||||||
static $db = [];
|
|
||||||
|
|
||||||
if (count($db) === 0)
|
|
||||||
{
|
|
||||||
$db = [
|
|
||||||
Syntax::new(
|
|
||||||
'C',
|
|
||||||
['.c', '.h', '.cpp'],
|
|
||||||
[
|
|
||||||
'continue', 'typedef', 'switch', 'return', 'static', 'while', 'break', 'struct',
|
|
||||||
'union', 'class', 'else', 'enum', 'for', 'case', 'if',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'#include', 'unsigned', '#define', '#ifndef', 'double', 'signed', '#endif',
|
|
||||||
'#ifdef', 'float', '#error', '#undef', 'long', 'char', 'int', 'void', '#if',
|
|
||||||
],
|
|
||||||
'//',
|
|
||||||
'/*',
|
|
||||||
'*/',
|
|
||||||
Syntax::HIGHLIGHT_NUMBERS | Syntax::HIGHLIGHT_STRINGS,
|
|
||||||
),
|
|
||||||
Syntax::new(
|
|
||||||
'CSS',
|
|
||||||
['.css', '.less', '.sass', 'scss'],
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
'',
|
|
||||||
'/*',
|
|
||||||
'*/',
|
|
||||||
Syntax::HIGHLIGHT_NUMBERS | Syntax::HIGHLIGHT_STRINGS,
|
|
||||||
),
|
|
||||||
Syntax::new(
|
|
||||||
'JavaScript',
|
|
||||||
['.js', '.jsx', '.ts', '.tsx', '.jsm', '.mjs', '.es'],
|
|
||||||
[
|
|
||||||
'instanceof', 'continue', 'debugger', 'function', 'default', 'extends',
|
|
||||||
'finally', 'delete', 'export', 'import', 'return', 'switch', 'typeof',
|
|
||||||
'break', 'catch', 'class', 'const', 'super', 'throw', 'while', 'yield',
|
|
||||||
'case', 'else', 'this', 'void', 'with', 'from', 'for', 'new', 'try',
|
|
||||||
'var', 'do', 'if', 'in', 'as',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'=>', 'Number', 'String', 'Object', 'Math', 'JSON', 'Boolean',
|
|
||||||
],
|
|
||||||
'//',
|
|
||||||
'/*',
|
|
||||||
'*/',
|
|
||||||
Syntax::HIGHLIGHT_NUMBERS | Syntax::HIGHLIGHT_STRINGS,
|
|
||||||
),
|
|
||||||
Syntax::new(
|
|
||||||
'PHP',
|
|
||||||
['.php', 'kilo'],
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
'//',
|
|
||||||
'/*',
|
|
||||||
'*/',
|
|
||||||
Syntax::HIGHLIGHT_NUMBERS | Syntax::HIGHLIGHT_STRINGS,
|
|
||||||
),
|
|
||||||
Syntax::new(
|
|
||||||
'Rust',
|
|
||||||
['.rs'],
|
|
||||||
[
|
|
||||||
'continue', 'return', 'static', 'struct', 'unsafe', 'break', 'const', 'crate',
|
|
||||||
'extern', 'match', 'super', 'trait', 'where', 'else', 'enum', 'false', 'impl',
|
|
||||||
'loop', 'move', 'self', 'type', 'while', 'for', 'let', 'mod', 'pub', 'ref', 'true',
|
|
||||||
'use', 'mut', 'as', 'fn', 'if', 'in',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'DoubleEndedIterator', 'ExactSizeIterator', 'IntoIterator', 'PartialOrd', 'PartialEq',
|
|
||||||
'Iterator', 'ToString', 'Default', 'ToOwned', 'Extend', 'FnOnce', 'Option', 'String',
|
|
||||||
'AsMut', 'AsRef', 'Clone', 'Debug', 'FnMut', 'Sized', 'Unpin', 'array', 'isize',
|
|
||||||
'usize', '&str', 'Copy', 'Drop', 'From', 'Into', 'None', 'Self', 'Send', 'Some',
|
|
||||||
'Sync', 'bool', 'char', 'i128', 'u128', 'Box', 'Err', 'Ord', 'Vec', 'dyn', 'f32',
|
|
||||||
'f64', 'i16', 'i32', 'i64', 'str', 'u16', 'u32', 'u64', 'Eq', 'Fn', 'Ok', 'i8', 'u8',
|
|
||||||
],
|
|
||||||
'//',
|
|
||||||
'/*',
|
|
||||||
'*/',
|
|
||||||
Syntax::HIGHLIGHT_NUMBERS | Syntax::HIGHLIGHT_STRINGS,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $db;
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user