Move some logic to Document class

This commit is contained in:
Timothy Warren 2021-03-16 18:37:53 -04:00
parent 633094ea9f
commit 7d381d10e9
4 changed files with 201 additions and 13 deletions

View File

@ -2,6 +2,7 @@
namespace Aviat\Kilo;
use Aviat\Kilo\Tokens\PHP8;
use Aviat\Kilo\Type\Point;
/**
@ -10,16 +11,18 @@ use Aviat\Kilo\Type\Point;
* @property-read int $numRows
*/
class Document {
public ?Syntax $syntax = NULL;
public FileType $fileType;
// Tokens for highlighting PHP
public array $tokens = [];
private function __construct(
public array $rows = [],
public ?string $filename = NULL,
private bool $dirty = FALSE,
) {}
public string $filename = '',
public bool $dirty = FALSE,
) {
$this->fileType = FileType::from($this->filename);
}
public function __get(string $name): ?int
{
@ -31,21 +34,53 @@ class Document {
return NULL;
}
public static function new(): self
public static function default(): self
{
return new self();
}
public static function open(?string $filename = NULL): self
protected function rowsToString(): string
{
// @TODO move logic from Editor
return new self(filename: $filename);
$lines = array_map(fn (Row $row) => (string)$row, $this->rows);
return implode('', $lines);
}
public function save(): bool
public static function open(string $filename): ?self
{
// @TODO move logic
return false;
$handle = fopen($filename, 'rb');
if ($handle === FALSE)
{
return NULL;
}
$self = new self(filename: $filename);
while (($line = fgets($handle)) !== FALSE)
{
// Remove line endings when reading the file
$self->insertRow($self->numRows, rtrim($line), FALSE);
}
fclose($handle);
$self->selectSyntaxHighlight();
return $self;
}
public function save(): int|false
{
$contents = $this->rowsToString();
$res = file_put_contents($this->filename, $contents);
if ($res === strlen($contents))
{
$this->dirty = FALSE;
}
return $res;
}
public function insertChar(Point $at, string $c): void
@ -53,6 +88,47 @@ class Document {
}
public 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();
}
}
public function isDirty(): bool
{
return $this->dirty;
@ -67,4 +143,36 @@ class Document {
{
}
public function selectSyntaxHighlight(): void
{
if (empty($this->filename))
{
return;
}
if ($this->fileType->name === 'PHP')
{
$this->tokens = PHP8::getFileTokens($this->filename);
}
$this->refreshSyntax();
}
public 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->fileType->name !== 'PHP')
{
return;
}
$this->tokens = PHP8::getTokens($this->rowsToString());
$this->refreshSyntax();
}
}

View File

@ -9,7 +9,6 @@ use Aviat\Kilo\Type\{Point, StatusMessage};
/**
* // Don't highlight this!
* @property-read int $numRows
*/
class Editor {
use Traits\MagicProperties;

76
src/FileType.php Normal file
View File

@ -0,0 +1,76 @@
<?php declare(strict_types=1);
namespace Aviat\Kilo;
class FileType {
public static function from(?string $filename): self
{
$syntax = self::getSyntaxFromFilename((string)$filename);
return new self($syntax->filetype, $syntax);
}
private static function getSyntaxFromFilename(string $filename): Syntax
{
$ext = (string)strstr(basename($filename), '.');
return match ($ext) {
'.php', 'kilo' => Syntax::new(
'PHP',
['.php', 'kilo'],
),
'.c', '.h', '.cpp', '.cxx', '.cc', '.hpp' => 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',
],
),
'.css', '.less', '.sass', '.scss' => Syntax::new(
'CSS',
['.css', '.less', '.sass', 'scss'],
slcs: '',
),
'.js', '.jsx', '.ts', '.tsx', '.jsm', '.mjs', '.es' => 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',
],
),
'.rs' => 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',
],
),
default => Syntax::default(),
};
}
private function __construct(public string $name, public Syntax $syntax) {}
}

View File

@ -17,12 +17,17 @@ class Syntax {
string $slcs = '//',
string $mcs = '/*',
string $mce = '*/',
int $flags = 0,
int $flags = self::HIGHLIGHT_NUMBERS | self::HIGHLIGHT_STRINGS,
): self
{
return new self($name, $extList, $keywords1, $keywords2, $slcs, $mcs, $mce, $flags);
}
public static function default(): self
{
return self::new('No filetype', slcs: '', mcs: '', mce: '', flags: 0);
}
private function __construct(
/** The name of the programming language */
public string $filetype,