Go full PHP with composer and phpunit

This commit is contained in:
Timothy Warren 2019-11-08 16:27:08 -05:00
parent c4f9d67980
commit 5787fe3822
17 changed files with 1899 additions and 269 deletions

26
composer.json Normal file
View File

@ -0,0 +1,26 @@
{
"autoload": {
"files": [
"src/constants.php",
"src/functions.php"
],
"psr-4": {
"Aviat\\Kilo\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Aviat\\Kilo\\Tests\\": "tests/"
}
},
"require-dev": {
"phpunit/phpunit": "^8"
},
"scripts": {
"coverage": "phpdbg -qrr -- vendor/bin/phpunit",
"test": "vendor/bin/phpunit"
},
"require": {
"ext-ffi": "*"
}
}

1535
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

18
kilo
View File

@ -1,10 +1,9 @@
#!/usr/bin/php
<?php declare(strict_types=1);
namespace Kilo;
namespace Aviat\Kilo;
require_once __DIR__ . '/src/functions.php';
require_once __DIR__ . '/src/hldb.php';
require_once __DIR__ . '/vendor/autoload.php';
// Log notices/errors/warnings to file
set_error_handler(static function (int $no, $str, $file, $line, $context) {
@ -19,19 +18,10 @@ set_error_handler(static function (int $no, $str, $file, $line, $context) {
}, -1);
// Set up autoloading
spl_autoload_register(static function ($class) {
$parts = explode('\\', $class);
$file = __DIR__ . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . $parts[1] . '.php';
if (file_exists($file))
{
require_once $file;
}
});
// ! Init with an IIFE
return (static function (int $argc, array $argv): int {
enable_raw_mode();
Termios::enableRawMode();
register_shutdown_function([Termios::class, 'disableRawMode']);
$editor = Editor::new();

View File

@ -1,6 +1,11 @@
<?php declare(strict_types=1);
namespace Kilo;
namespace Aviat\Kilo;
use Aviat\Kilo\Enum\{
Key,
Highlight,
};
/**
* // Don't highlight this!
@ -348,7 +353,7 @@ class Editor {
{
write_stdout("\x1b[2J"); // Clear the screen
write_stdout("\x1b[H"); // Reposition cursor to top-left
disable_raw_mode();
Termios::disableRawMode();
print_r(error_get_last());
die();
}

View File

@ -1,6 +1,6 @@
<?php declare(strict_types=1);
namespace Kilo;
namespace Aviat\Kilo\Enum;
/**
* Just a namespace for C language constants

View File

@ -1,6 +1,6 @@
<?php declare(strict_types=1);
namespace Kilo;
namespace Aviat\Kilo\Enum;
/**
* ANSI Color escape sequences

View File

@ -1,6 +1,6 @@
<?php declare(strict_types=1);
namespace Kilo;
namespace Aviat\Kilo\Enum;
class Highlight {
public const NORMAL = 0;

View File

@ -1,6 +1,6 @@
<?php declare(strict_types=1);
namespace Kilo;
namespace Aviat\Kilo\Enum;
use ReflectionClass;

View File

@ -1,6 +1,6 @@
<?php declare(strict_types=1);
namespace Kilo;
namespace Aviat\Kilo;
trait MagicProperties {
abstract public function __get(string $name);

View File

@ -1,6 +1,8 @@
<?php declare(strict_types=1);
namespace Kilo;
namespace Aviat\Kilo;
use Aviat\Kilo\Enum\Highlight;
/**
* @property-read int size
@ -269,6 +271,11 @@ class Row {
{
$this->hl = array_fill(0, $this->rsize, Highlight::NORMAL);
if ( ! isset($this->parent->syntax))
{
return;
}
if ($this->parent->syntax->filetype === 'PHP')
{
$this->updateSyntaxPHP();
@ -508,7 +515,7 @@ class Row {
if (in_array($token['type'], [T_DOC_COMMENT, T_COMMENT], TRUE))
{
// Single line comments
if (strpos($char, '//') !== FALSE)
if (strpos($char, '//') !== FALSE || strpos($char, '#') !== FALSE)
{
array_replace_range($this->hl, $charStart, $charLen, Highlight::COMMENT);
break;

View File

@ -1,6 +1,6 @@
<?php declare(strict_types=1);
namespace Kilo;
namespace Aviat\Kilo;
class Syntax {
public const HIGHLIGHT_NUMBERS = (1 << 0);

95
src/Termios.php Normal file
View File

@ -0,0 +1,95 @@
<?php declare(strict_types=1);
namespace Aviat\Kilo;
use FFI;
use FFI\CData;
use Aviat\Kilo\Enum\C;
/**
* An implicit singleton wrapper around terminal settings to simplify enabling/disabling raw mode
*/
class Termios {
private CData $originalTermios;
private function __construct()
{
$ffi = get_ffi();
$termios = $ffi->new('struct termios');
$res = $ffi->tcgetattr(C::STDIN_FILENO, FFI::addr($termios));
if ($res === -1)
{
die('Failed to get terminal settings');
}
$this->originalTermios = $termios;
// Make sure to restore normal mode on exit/die/crash
register_shutdown_function([$this, '__destruct']);
}
public function __destruct()
{
self::disableRawMode();
}
public static function enableRawMode(): void
{
static $run = FALSE;
// Don't run this more than once!
if ($run === TRUE)
{
return;
}
$run = TRUE;
$instance = self::getInstance();
// So, the only thing that seems to really matter here is that c_oflag is 0...
$termios = $instance->originalTermios;
$termios->c_iflag = 0; //$termios->c_iflag & ~(C::BRKINT | C::ICRNL | C::INPCK | C::ISTRIP | C::IXON);
$termios->c_oflag = 0; // $termios->c_oflag && ~(C::OPOST);
$termios->c_cflag |= (C::CS8);
$termios->c_lflag = $termios->c_lflag & ~( C::ECHO | C::ICANON | C::IEXTEN | C::ISIG);
$termios->c_cc[C::VMIN] = 0;
$termios->c_cc[C::VTIME] = 1;
// Turn on raw mode
$res = get_ffi()->tcsetattr(C::STDIN_FILENO, C::TCSAFLUSH, FFI::addr($termios));
if ($res === -1)
{
die('Failed to set raw mode');
}
}
public static function disableRawMode(): void
{
$instance = self::getInstance();
write_stdout("\x1b[2J"); // Clear the screen
write_stdout("\x1b[H"); // Reposition cursor to top-left
echo "\n";
$res = get_ffi()->tcsetattr(C::STDIN_FILENO, C::TCSAFLUSH, FFI::addr($instance->originalTermios));
if ($res === -1)
{
die('Failed to restore terminal settings');
}
}
private static function getInstance(): self
{
static $instance;
if ($instance === NULL)
{
$instance = new self();
}
return $instance;
}
}

View File

@ -1,10 +1,10 @@
<?php declare(strict_types=1);
namespace Kilo;
namespace Aviat\Kilo;
// -----------------------------------------------------------------------------
// ! App Constants
// -----------------------------------------------------------------------------
define('KILO_VERSION', '0.1.0');
define('KILO_VERSION', '0.2.0');
define('KILO_TAB_STOP', 4);
define('KILO_QUIT_TIMES', 3);

View File

@ -1,66 +1,19 @@
<?php declare(strict_types=1);
namespace Kilo;
namespace Aviat\Kilo;
use FFI;
require_once __DIR__ . '/constants.php';
/** @var FFI\CData $original_termios */
$original_termios = get_ffi()->new('struct termios');
use Aviat\Kilo\Enum\{
C,
Color,
Highlight,
};
// ----------------------------------------------------------------------------
// ! Raw mode / Terminal size
// ! Terminal size
// ----------------------------------------------------------------------------
function enable_raw_mode(): void
{
global $original_termios;
// Make sure to restore normal mode on exit/die/crash
register_shutdown_function('Kilo\disable_raw_mode');
$ffi = get_ffi();
// Populate the original terminal settings
$res = $ffi->tcgetattr(C::STDIN_FILENO, FFI::addr($original_termios));
if ($res === -1)
{
die('tcgetattr');
}
// So, the only thing that seems to really matter here is that c_oflag is 0...
$termios = clone $original_termios;
$termios->c_iflag = 0; //$termios->c_iflag & ~(C::BRKINT | C::ICRNL | C::INPCK | C::ISTRIP | C::IXON);
$termios->c_oflag = 0; // $termios->c_oflag && ~(C::OPOST);
$termios->c_cflag |= (C::CS8);
$termios->c_lflag = $termios->c_lflag & ~( C::ECHO | C::ICANON | C::IEXTEN | C::ISIG);
$termios->c_cc[C::VMIN] = 0;
$termios->c_cc[C::VTIME] = 1;
// Turn on raw mode
$res = $ffi->tcsetattr(C::STDIN_FILENO, C::TCSAFLUSH, FFI::addr($termios));
if ($res === -1)
{
die('tcsetattr');
}
}
function disable_raw_mode(): void
{
global $original_termios;
$ffi = get_ffi();
$res = $ffi->tcsetattr(C::STDIN_FILENO, C::TCSAFLUSH, FFI::addr($original_termios));
echo "\n";
if ($res === -1)
{
die('tcsetattr');
}
}
/**
* @TODO fix
*/
@ -314,9 +267,16 @@ function syntax_to_color(int $hl): int
: Color::FG_WHITE;
}
function tabs_to_spaces(string $str): string
/**
* Replace tabs with the specified number of spaces.
*
* @param string $str
* @param int? $number
* @return string
*/
function tabs_to_spaces(string $str, ?int $number = KILO_TAB_STOP): string
{
return str_replace("\t", str_repeat(' ', KILO_TAB_STOP), $str);
return str_replace("\t", str_repeat(' ', $number), $str);
}
/**
@ -410,3 +370,192 @@ function get_php_tokens(string $code): array
return $tokens;
}
/**
* Generate/Get the syntax highlighting objects
*
* @return array
*/
function get_hldb(): 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'],
[
'?php', '$this', '__halt_compiler', 'abstract', 'and', 'array', 'as', 'break',
'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare',
'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor',
'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends',
'final', 'finally', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements',
'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list',
'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once',
'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor',
'yield', 'yield from', '__CLASS__', '__DIR__', '__FILE__', '__FUNCTION__', '__LINE__',
'__METHOD__', '__NAMESPACE__', '__TRAIT__',
],
[
'int', 'float', 'bool', 'string', 'true', 'TRUE', 'false', 'FALSE', 'null', 'NULL',
'void', 'iterable', 'object', 'strict_types'
],
'//',
'/*',
'*/',
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',
'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;
}

View File

@ -1,187 +0,0 @@
<?php declare(strict_types=1);
namespace Kilo;
function get_hldb(): 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'],
[
'?php', '$this', '__halt_compiler', 'abstract', 'and', 'array', 'as', 'break',
'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare',
'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor',
'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends',
'final', 'finally', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements',
'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list',
'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once',
'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor',
'yield', 'yield from', '__CLASS__', '__DIR__', '__FILE__', '__FUNCTION__', '__LINE__',
'__METHOD__', '__NAMESPACE__', '__TRAIT__',
],
[
'int', 'float', 'bool', 'string', 'true', 'TRUE', 'false', 'FALSE', 'null', 'NULL',
'void', 'iterable', 'object', 'strict_types'
],
'//',
'/*',
'*/',
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',
'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;
}

View File

@ -46,6 +46,7 @@ class FooBar extends Foo implements Ifoo {
*/
$foobar = new FooBar();
// C++ style comment
$x = 3;
# Perl-style comment

9
tests/FunctionTest.php Normal file
View File

@ -0,0 +1,9 @@
<?php declare(strict_types=1);
namespace Aviat\Kilo\Tests;
use PHPUnit\Framework\TestCase;
class FunctionTest extends TestCase {
}