Refactor and simplify

This commit is contained in:
Timothy Warren 2020-02-05 14:50:31 -05:00
parent f8894b971a
commit 6f870aa922
11 changed files with 196 additions and 191 deletions

View File

@ -1,6 +1,6 @@
# PHP Kilo
[![Build Status](https://jenkins.timshomepage.net/buildStatus/icon?job=Gitea+-+Tutorials%2Fphp-kilo%2Fmaster)](https://jenkins.timshomepage.net/job/Gitea%20-%20Tutorials/job/php-kilo/job/master/)
[![Build Status](https://jenkins.timshomepage.net/buildStatus/icon?job=timw4mail%2Fphp-kilo%2Fmaster)](https://jenkins.timshomepage.net/job/timw4mail/job/php-kilo/job/master/)
A reimplementation of the [Kilo](https://viewsourcecode.org/snaptoken/kilo/index.html) tutorial in PHP. Requires PHP 7.4,
due to requiring the `FFI` extension.

117
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f11505a6676b236651e7784cfcd62ec8",
"content-hash": "6fa43d16a15a27c27d5070fe477fd13f",
"packages": [],
"packages-dev": [
{
@ -65,16 +65,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.9.3",
"version": "1.9.5",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea"
"reference": "b2c28789e80a97badd14145fda39b545d83ca3ef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea",
"reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef",
"reference": "b2c28789e80a97badd14145fda39b545d83ca3ef",
"shasum": ""
},
"require": {
@ -109,7 +109,7 @@
"object",
"object graph"
],
"time": "2019-08-09T12:45:53+00:00"
"time": "2020-01-17T21:11:47+00:00"
},
{
"name": "phar-io/manifest",
@ -267,16 +267,16 @@
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "4.3.2",
"version": "4.3.4",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e"
"reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c",
"reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c",
"shasum": ""
},
"require": {
@ -288,6 +288,7 @@
"require-dev": {
"doctrine/instantiator": "^1.0.5",
"mockery/mockery": "^1.0",
"phpdocumentor/type-resolver": "0.4.*",
"phpunit/phpunit": "^6.4"
},
"type": "library",
@ -314,7 +315,7 @@
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"time": "2019-09-12T14:27:41+00:00"
"time": "2019-12-28T18:55:12+00:00"
},
{
"name": "phpdocumentor/type-resolver",
@ -365,33 +366,33 @@
},
{
"name": "phpspec/prophecy",
"version": "1.9.0",
"version": "v1.10.2",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203"
"reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203",
"reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9",
"reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
"sebastian/comparator": "^1.1|^2.0|^3.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
"sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0"
},
"require-dev": {
"phpspec/phpspec": "^2.5|^3.2",
"phpspec/phpspec": "^2.5 || ^3.2",
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.8.x-dev"
"dev-master": "1.10.x-dev"
}
},
"autoload": {
@ -424,7 +425,7 @@
"spy",
"stub"
],
"time": "2019-10-03T11:07:50+00:00"
"time": "2020-01-20T15:57:02+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -680,16 +681,16 @@
},
{
"name": "phpunit/phpunit",
"version": "8.4.3",
"version": "8.5.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "67f9e35bffc0dd52d55d565ddbe4230454fd6a4e"
"reference": "018b6ac3c8ab20916db85fa91bf6465acb64d1e0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/67f9e35bffc0dd52d55d565ddbe4230454fd6a4e",
"reference": "67f9e35bffc0dd52d55d565ddbe4230454fd6a4e",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/018b6ac3c8ab20916db85fa91bf6465acb64d1e0",
"reference": "018b6ac3c8ab20916db85fa91bf6465acb64d1e0",
"shasum": ""
},
"require": {
@ -733,7 +734,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "8.4-dev"
"dev-master": "8.5-dev"
}
},
"autoload": {
@ -759,7 +760,7 @@
"testing",
"xunit"
],
"time": "2019-11-06T09:42:23+00:00"
"time": "2020-01-08T08:49:49+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@ -1378,23 +1379,23 @@
},
{
"name": "spatie/phpunit-snapshot-assertions",
"version": "2.2.0",
"version": "2.2.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/phpunit-snapshot-assertions.git",
"reference": "7da647e383d5ba960b384a45e8bd59c4211b366d"
"reference": "cc6769ab92a41d1d58d72f228e15d82d180f0b44"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/phpunit-snapshot-assertions/zipball/7da647e383d5ba960b384a45e8bd59c4211b366d",
"reference": "7da647e383d5ba960b384a45e8bd59c4211b366d",
"url": "https://api.github.com/repos/spatie/phpunit-snapshot-assertions/zipball/cc6769ab92a41d1d58d72f228e15d82d180f0b44",
"reference": "cc6769ab92a41d1d58d72f228e15d82d180f0b44",
"shasum": ""
},
"require": {
"ext-dom": "*",
"php": "^7.2",
"phpunit/phpunit": "^8.0",
"symfony/yaml": "^4.0"
"symfony/yaml": "^4.0|^5.0"
},
"type": "library",
"autoload": {
@ -1424,20 +1425,20 @@
"spatie",
"testing"
],
"time": "2019-10-23T15:00:34+00:00"
"time": "2019-11-21T23:15:29+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.12.0",
"version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "550ebaac289296ce228a706d0867afc34687e3f4"
"reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4",
"reference": "550ebaac289296ce228a706d0867afc34687e3f4",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"shasum": ""
},
"require": {
@ -1449,7 +1450,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.12-dev"
"dev-master": "1.13-dev"
}
},
"autoload": {
@ -1482,31 +1483,31 @@
"polyfill",
"portable"
],
"time": "2019-08-06T08:03:45+00:00"
"time": "2019-11-27T13:56:44+00:00"
},
{
"name": "symfony/yaml",
"version": "v4.3.8",
"version": "v5.0.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "324cf4b19c345465fad14f3602050519e09e361d"
"reference": "69b44e3b8f90949aee2eb3aa9b86ceeb01cbf62a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/324cf4b19c345465fad14f3602050519e09e361d",
"reference": "324cf4b19c345465fad14f3602050519e09e361d",
"url": "https://api.github.com/repos/symfony/yaml/zipball/69b44e3b8f90949aee2eb3aa9b86ceeb01cbf62a",
"reference": "69b44e3b8f90949aee2eb3aa9b86ceeb01cbf62a",
"shasum": ""
},
"require": {
"php": "^7.1.3",
"php": "^7.2.5",
"symfony/polyfill-ctype": "~1.8"
},
"conflict": {
"symfony/console": "<3.4"
"symfony/console": "<4.4"
},
"require-dev": {
"symfony/console": "~3.4|~4.0"
"symfony/console": "^4.4|^5.0"
},
"suggest": {
"symfony/console": "For validating YAML files using the lint command"
@ -1514,7 +1515,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.3-dev"
"dev-master": "5.0-dev"
}
},
"autoload": {
@ -1541,7 +1542,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2019-10-30T12:58:49+00:00"
"time": "2020-01-21T11:12:28+00:00"
},
{
"name": "theseer/tokenizer",
@ -1585,31 +1586,29 @@
},
{
"name": "webmozart/assert",
"version": "1.5.0",
"version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
"reference": "88e6d84706d09a236046d686bbea96f07b3a34f4"
"reference": "573381c0a64f155a0d9a23f4b0c797194805b925"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4",
"reference": "88e6d84706d09a236046d686bbea96f07b3a34f4",
"url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925",
"reference": "573381c0a64f155a0d9a23f4b0c797194805b925",
"shasum": ""
},
"require": {
"php": "^5.3.3 || ^7.0",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"vimeo/psalm": "<3.6.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.36 || ^7.5.13"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
}
},
"autoload": {
"psr-4": {
"Webmozart\\Assert\\": "src/"
@ -1631,7 +1630,7 @@
"check",
"validate"
],
"time": "2019-08-24T08:43:50+00:00"
"time": "2019-11-24T13:36:37+00:00"
}
],
"aliases": [],
@ -1642,5 +1641,7 @@
"platform": {
"ext-ffi": "*"
},
"platform-dev": []
"platform-dev": {
"ext-json": "*"
}
}

View File

@ -135,16 +135,13 @@ class Editor {
}
// In PHP, `strchr` and `strstr` are the same function
$ext = (string)strstr($this->filename, '.');
$ext = (string)strstr(basename($this->filename), '.');
foreach (get_file_syntax_map() as $syntax)
{
foreach ($syntax->filematch as $searchExt)
{
$is_ext = (strpos($searchExt, '.') === 0);
if (
($is_ext && ( ! strcmp($ext, $searchExt))) ||
(( ! $is_ext) && strpos($this->filename, $searchExt) !== FALSE)
in_array($ext, $syntax->filematch, TRUE) ||
in_array(basename($this->filename), $syntax->filematch, TRUE)
) {
$this->syntax = $syntax;
@ -154,19 +151,12 @@ class Editor {
$this->tokens = PHP::getFileTokens($this->filename);
}
// Update the syntax highlighting for all the rows of the file
for ($i = 0; $i < $this->numRows; $i++)
{
// @codeCoverageIgnoreStart
$this->rows[$i]->updateSyntax();
// @codeCoverageIgnoreEnd
}
$this->refreshSyntax();
return;
}
}
}
}
// ------------------------------------------------------------------------
// ! Row Operations
@ -177,7 +167,7 @@ class Editor {
$rx = 0;
for ($i = 0; $i < $cx; $i++)
{
if ($row->chars[$i] === "\t")
if ($row->chars[$i] === KeyCode::TAB)
{
$rx += (KILO_TAB_STOP - 1) - ($rx % KILO_TAB_STOP);
}
@ -192,7 +182,7 @@ class Editor {
$cur_rx = 0;
for ($cx = 0; $cx < $row->size; $cx++)
{
if ($row->chars[$cx] === "\t")
if ($row->chars[$cx] === KeyCode::TAB)
{
$cur_rx += (KILO_TAB_STOP - 1) - ($cur_rx % KILO_TAB_STOP);
}
@ -231,12 +221,6 @@ class Editor {
ksort($this->rows);
// Update row indexes
for ($i = 0; $i < $this->numRows; $i++)
{
$this->rows[$i]->idx = $i;
}
$this->rows[$at]->update();
$this->dirty++;
@ -358,15 +342,11 @@ class Editor {
$this->selectSyntaxHighlight();
// #TODO gracefully handle issues with loading a file
$handle = fopen($filename, 'rb');
if ($handle === FALSE)
{
write_stdout(ANSI::CLEAR_SCREEN);
write_stdout(ANSI::RESET_CURSOR); // Reposition cursor to top-left
Termios::disableRawMode();
print_r(error_get_last());
die();
$this->setStatusMessage('Failed to open file: %s', $filename);
return;
}
while (($line = fgets($handle)) !== FALSE)
@ -426,23 +406,25 @@ class Editor {
$savedHl = [];
}
if ($key === "\r" || $key === "\e")
switch ($key)
{
case KeyCode::ENTER:
case KeyCode::ESCAPE:
$lastMatch = -1;
$direction = 1;
return;
}
if ($key === KeyType::ARROW_RIGHT || $key === KeyType::ARROW_DOWN)
{
case KeyType::ARROW_DOWN:
case KeyType::ARROW_RIGHT:
$direction = 1;
}
else if ($key === KeyType::ARROW_LEFT || $key === KeyType::ARROW_UP)
{
break;
case KeyType::ARROW_UP:
case KeyType::ARROW_LEFT:
$direction = -1;
}
else
{
break;
default:
$lastMatch = -1;
$direction = 1;
}
@ -452,11 +434,6 @@ class Editor {
$direction = 1;
}
if (empty($query))
{
return;
}
$current = $lastMatch;
for ($i = 0; $i < $this->numRows; $i++)
@ -598,7 +575,7 @@ class Editor {
for ($i = 0; $i < $len; $i++)
{
// Handle 'non-printable' characters
if (is_cntrl($c[$i]))
if (is_ctrl($c[$i]))
{
$sym = (ord($c[$i]) <= 26)
? chr(ord('@') + ord($c[$i]))
@ -736,7 +713,7 @@ class Editor {
$c = $this->readKey();
if ($c === KeyType::ESCAPE)
if ($c === KeyType::ESCAPE || ($c === KeyType::ENTER && $buffer !== ''))
{
$this->setStatusMessage('');
if ($callback !== NULL)
@ -746,21 +723,11 @@ class Editor {
return '';
}
if ($c === KeyType::ENTER && $buffer !== '')
{
$this->setStatusMessage('');
if ($callback !== NULL)
{
$callback($buffer, $c);
}
return $buffer;
}
if ($c === KeyType::DEL_KEY || $c === KeyType::BACKSPACE || $c === chr(ctrl_key('h')))
if ($c === KeyType::DEL_KEY || $c === KeyType::BACKSPACE || KeyType::CTRL('h'))
{
$buffer = substr($buffer, 0, -1);
}
else if (is_ascii($c) && ( ! is_cntrl($c)) && ! in_array($c, $modifiers, TRUE))
else if (is_ascii($c) && ( ! is_ctrl($c)) && ! in_array($c, $modifiers, TRUE))
{
$buffer .= $c;
}
@ -835,7 +802,7 @@ class Editor {
$c = $this->readKey();
if ($c === "\0" || $c === '')
if ($c === KeyCode::NULL || $c === KeyCode::EMPTY)
{
return '';
}
@ -846,7 +813,7 @@ class Editor {
$this->insertNewline();
break;
case chr(ctrl_key('q')):
case KeyType::CTRL('q'):
if ($this->dirty > 0 && $quit_times > 0)
{
$this->setStatusMessage('WARNING!!! File has unsaved changes.' .
@ -859,7 +826,7 @@ class Editor {
return NULL;
break;
case chr(ctrl_key('s')):
case KeyType::CTRL('s'):
$this->save();
break;
@ -874,12 +841,12 @@ class Editor {
}
break;
case chr(ctrl_key('f')):
case KeyType::CTRL('f'):
$this->find();
break;
case KeyType::BACKSPACE:
case chr(ctrl_key('h')):
case KeyType::CTRL('h'):
case KeyType::DEL_KEY:
if ($c === KeyType::DEL_KEY)
{
@ -900,7 +867,7 @@ class Editor {
$this->moveCursor($c);
break;
case chr(ctrl_key('l')):
case KeyType::CTRL('l'):
case KeyType::ESCAPE:
// Do nothing
break;
@ -915,7 +882,7 @@ class Editor {
return $c;
}
private function pageUpOrDown(string $c): void
public function pageUpOrDown(string $c): void
{
if ($c === KeyType::PAGE_UP)
{
@ -937,6 +904,12 @@ class Editor {
}
}
protected function refreshSyntax(): void
{
// Update the syntax highlighting for all the rows of the file
array_walk($this->rows, fn (Row $row) => $row->updateSyntax());
}
private function refreshPHPSyntax(): void
{
if ($this->syntax->filetype !== 'PHP')
@ -945,9 +918,6 @@ class Editor {
}
$this->tokens = PHP::getTokens($this->rowsToString());
for ($i = 0; $i < $this->numRows; $i++)
{
$this->rows[$i]->update();
}
$this->refreshSyntax();
}
}

View File

@ -1,12 +0,0 @@
<?php declare(strict_types=1);
namespace Aviat\Kilo\Enum;
use Aviat\Kilo\Traits;
class Event {
use Traits\ConstList;
public const INPUT_KEY = 'INPUT_KEY';
public const QUIT_ATTEMPT = 'QUIT_ATTEMPT';
}

View File

@ -4,6 +4,9 @@ namespace Aviat\Kilo\Enum;
use Aviat\Kilo\Traits;
/**
* 'Raw' input from stdin
*/
class KeyCode {
use Traits\ConstList;
@ -12,9 +15,17 @@ class KeyCode {
public const ARROW_RIGHT = "\e[C";
public const ARROW_UP = "\e[A";
public const BACKSPACE = "\x7f";
public const CARRIAGE_RETURN = "\r";
public const DEL_KEY = "\e[3~";
public const EMPTY = '';
public const ENTER = "\r";
public const ESCAPE = "\e";
public const FORM_FEED = "\f";
public const NEWLINE = "\n";
public const NULL = "\0";
public const PAGE_DOWN = "\e[6~";
public const PAGE_UP = "\e[5~";
public const SPACE = ' ';
public const TAB = "\t";
public const VERTICAL_TAB = "\v";
}

View File

@ -3,7 +3,11 @@
namespace Aviat\Kilo\Enum;
use Aviat\Kilo\Traits;
use function Aviat\Kilo\ctrl_key;
/**
* Constants representing various control keys
*/
class KeyType {
use Traits\ConstList;
@ -19,4 +23,27 @@ class KeyType {
public const HOME_KEY = 'HOME';
public const PAGE_DOWN = 'PAGE_DOWN';
public const PAGE_UP = 'PAGE_UP';
/**
* Returns the ascii character for the specified
* ctrl + letter combo
*
* @param string $char
* @return string
*/
public static function CTRL(string $char): ?string
{
$char = strtolower($char);
$ord = ord($char);
// a = 0x61
// z = 0x7a
if ($ord >= 0x61 && $ord <= 0x7a)
{
return chr(ctrl_key($char));
}
// Invalid input, not an ascii letter
return NULL;
}
}

View File

@ -2,9 +2,18 @@
namespace Aviat\Kilo;
use Aviat\Kilo\Enum\Event as EventEnum;
class Event {
use Traits\ConstList;
// ------------------------------------------------------------------------
// Valid Events
// ------------------------------------------------------------------------
public const INPUT_KEY = 'INPUT_KEY';
public const PAGE_CHANGE = 'PAGE_CHANGE';
public const MOVE_CURSOR = 'MOVE_CURSOR';
public const QUIT_ATTEMPT = 'QUIT_ATTEMPT';
// Mapping of events to handlers
private static $subscribeMap = [];
public static function fire(string $eventName, $value): void
@ -20,7 +29,7 @@ class Event {
}
}
public static function bind(string $eventName, callable $fn): void
public static function on(string $eventName, callable $fn): void
{
static::validateEvent($eventName);
@ -37,11 +46,11 @@ class Event {
private static function validateEvent(string $eventName): void
{
$validEvents = EventEnum::getConstList();
$validEvents = self::getConstList();
if ( ! array_key_exists($eventName, $validEvents))
{
throw new \InvalidArgumentException("Invalid event '{$eventName}'. Event const must exist in Aviat\\Kilo\\Enum\\Event.");
throw new \InvalidArgumentException("Invalid event '{$eventName}'. Event const must exist in Aviat\\Kilo\\Event.");
}
}
}

View File

@ -3,6 +3,7 @@
namespace Aviat\Kilo;
use Aviat\Kilo\Enum\Highlight;
use Aviat\Kilo\Enum\KeyCode;
/**
* @property-read int size
@ -413,7 +414,7 @@ class Row {
$klen = strlen($k);
$nextCharOffset = $i + $klen;
$isEndOfLine = $nextCharOffset >= $this->rsize;
$nextChar = ($isEndOfLine) ? "\0" : $this->render[$nextCharOffset];
$nextChar = ($isEndOfLine) ? KeyCode::NULL : $this->render[$nextCharOffset];
if (substr($this->render, $i, $klen) === $k && is_separator($nextChar))
{
@ -559,16 +560,14 @@ class Row {
}
// Types/identifiers/keywords that don't have their own token
if ($token['type'] === T_STRING)
{
if (in_array($token['char'], $this->parent->syntax->keywords2, TRUE))
if ($token['type'] === T_STRING &&
in_array($token['char'], $this->parent->syntax->keywords2, TRUE))
{
array_replace_range($this->hl, $charStart, $charLen, Highlight::KEYWORD2);
$offset = $charEnd;
continue;
}
}
}
// Highlight raw characters
if (($token['type'] === self::T_RAW) && array_key_exists(trim($token['char']), $this->phpCharacterHighlightMap))

View File

@ -4,11 +4,7 @@ namespace Aviat\Kilo;
use FFI;
use Aviat\Kilo\Enum\{
C,
Color,
Highlight,
};
use Aviat\Kilo\Enum\{C, Color, Highlight, KeyCode};
/**
* See if tput exists for fallback terminal size detection
@ -18,7 +14,7 @@ use Aviat\Kilo\Enum\{
*/
function has_tput(): bool
{
return (int)shell_exec('type tput') === 0;
return str_contains(shell_exec('type tput'), ' is ');
}
// ----------------------------------------------------------------------------
@ -104,7 +100,7 @@ function is_ascii(string $single_char): bool
* @param string $char
* @return bool
*/
function is_cntrl(string $char): bool
function is_ctrl(string $char): bool
{
$c = ord($char);
return is_ascii($char) && ( $c === 0x7f || $c < 0x20 );
@ -130,7 +126,14 @@ function is_digit(string $char): bool
*/
function is_space(string $char): bool
{
$ws = [' ', "\t", "\n", "\r", "\v", "\f"];
$ws = [
KeyCode::CARRIAGE_RETURN,
KeyCode::FORM_FEED,
KeyCode::NEWLINE,
KeyCode::SPACE,
KeyCode::TAB,
KeyCode::VERTICAL_TAB,
];
return is_ascii($char) && in_array($char, $ws, TRUE);
}
@ -168,11 +171,9 @@ function is_separator(string $char): bool
return FALSE;
}
// `strpos` is used instead of `strchr`/`strstr` as we don't care about the actual match
// while `strchr` would match the C version, it also returns the match
$isSep = (strpos(',.()+-/*=~%<>[];', $char) !== FALSE);
$isSep = str_contains(',.()+-/*=~%<>[];', $char);
return is_space($char) || $char === "\0" || $isSep;
return is_space($char) || $char === KeyCode::NULL || $isSep;
}
/**
@ -287,7 +288,7 @@ function syntax_to_color(int $hl): int
*/
function tabs_to_spaces(string $str, ?int $number = KILO_TAB_STOP): string
{
return str_replace("\t", str_repeat(' ', $number), $str);
return str_replace(KeyCode::TAB, str_repeat(KeyCode::SPACE, $number), $str);
}
/**

View File

@ -3,7 +3,6 @@
namespace Aviat\Kilo\Tests;
use Aviat\Kilo\Event;
use Aviat\Kilo\Enum\Event as EventType;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
@ -11,7 +10,7 @@ class EventTest extends TestCase {
public function testRequiresValidEvent(): void
{
$this->expectException(InvalidArgumentException::class);
Event::bind('badEventName', fn () => null);
Event::on('badEventName', fn () => null);
$this->expectException(InvalidArgumentException::class);
Event::fire('badEventName', []);
@ -22,7 +21,7 @@ class EventTest extends TestCase {
$fn = static function($value = false) {
static::assertTrue($value);
};
Event::bind(EventType::INPUT_KEY, $fn);
Event::fire(EventType::INPUT_KEY, TRUE);
Event::on(Event::INPUT_KEY, $fn);
Event::fire(Event::INPUT_KEY, TRUE);
}
}

View File

@ -11,7 +11,7 @@ use function Aviat\Kilo\ctrl_key;
use function Aviat\Kilo\get_file_syntax_map;
use function Aviat\Kilo\get_window_size;
use function Aviat\Kilo\is_ascii;
use function Aviat\Kilo\is_cntrl;
use function Aviat\Kilo\is_ctrl;
use function Aviat\Kilo\is_digit;
use function Aviat\Kilo\is_separator;
use function Aviat\Kilo\is_space;
@ -42,22 +42,22 @@ class FunctionTest extends TestCase {
$this->assertEquals(0x01, ctrl_key('a'));
}
public function test_is_cntrl(): void
public function test_is_ctrl(): void
{
for ($i = 0x0; $i < 0x20; $i++)
{
$char = chr($i);
$this->assertTrue(is_cntrl($char), 'Should be a control character');
$this->assertTrue(is_ctrl($char), 'Should be a control character');
}
for ($n = 0x20; $n < 0x7f; $n++)
{
$char = chr($n);
$this->assertFalse(is_cntrl($char), 'Should not be a control character');
$this->assertFalse(is_ctrl($char), 'Should not be a control character');
}
// Escape, code 7f, is an outlier
$this->assertTrue(is_cntrl(chr(0x7f)));
$this->assertTrue(is_ctrl(chr(0x7f)));
}
public function test_is_space(): void