Refactor and simplify
This commit is contained in:
parent
f8894b971a
commit
6f870aa922
@ -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
117
composer.lock
generated
@ -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": "*"
|
||||
}
|
||||
}
|
||||
|
140
src/Editor.php
140
src/Editor.php
@ -135,35 +135,25 @@ 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)
|
||||
) {
|
||||
$this->syntax = $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 = 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
|
||||
}
|
||||
|
||||
return;
|
||||
// Pre-tokenize the file
|
||||
if ($this->syntax->filetype === 'PHP')
|
||||
{
|
||||
$this->tokens = PHP::getFileTokens($this->filename);
|
||||
}
|
||||
|
||||
$this->refreshSyntax();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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,25 +406,27 @@ class Editor {
|
||||
$savedHl = [];
|
||||
}
|
||||
|
||||
if ($key === "\r" || $key === "\e")
|
||||
switch ($key)
|
||||
{
|
||||
$lastMatch = -1;
|
||||
$direction = 1;
|
||||
return;
|
||||
}
|
||||
case KeyCode::ENTER:
|
||||
case KeyCode::ESCAPE:
|
||||
$lastMatch = -1;
|
||||
$direction = 1;
|
||||
return;
|
||||
|
||||
if ($key === KeyType::ARROW_RIGHT || $key === KeyType::ARROW_DOWN)
|
||||
{
|
||||
$direction = 1;
|
||||
}
|
||||
else if ($key === KeyType::ARROW_LEFT || $key === KeyType::ARROW_UP)
|
||||
{
|
||||
$direction = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$lastMatch = -1;
|
||||
$direction = 1;
|
||||
case KeyType::ARROW_DOWN:
|
||||
case KeyType::ARROW_RIGHT:
|
||||
$direction = 1;
|
||||
break;
|
||||
|
||||
case KeyType::ARROW_UP:
|
||||
case KeyType::ARROW_LEFT:
|
||||
$direction = -1;
|
||||
break;
|
||||
|
||||
default:
|
||||
$lastMatch = -1;
|
||||
$direction = 1;
|
||||
}
|
||||
|
||||
if ($lastMatch === -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();
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
}
|
@ -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";
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
15
src/Row.php
15
src/Row.php
@ -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,14 +560,12 @@ class Row {
|
||||
}
|
||||
|
||||
// Types/identifiers/keywords that don't have their own token
|
||||
if ($token['type'] === T_STRING)
|
||||
if ($token['type'] === T_STRING &&
|
||||
in_array($token['char'], $this->parent->syntax->keywords2, TRUE))
|
||||
{
|
||||
if (in_array($token['char'], $this->parent->syntax->keywords2, TRUE))
|
||||
{
|
||||
array_replace_range($this->hl, $charStart, $charLen, Highlight::KEYWORD2);
|
||||
$offset = $charEnd;
|
||||
continue;
|
||||
}
|
||||
array_replace_range($this->hl, $charStart, $charLen, Highlight::KEYWORD2);
|
||||
$offset = $charEnd;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user