Fix CTRL method
This commit is contained in:
parent
afd6560db4
commit
b0ea547378
1
.gitignore
vendored
1
.gitignore
vendored
@ -169,5 +169,6 @@ tags
|
|||||||
# End of https://www.gitignore.io/api/vim,emacs,composer,jetbrains+all
|
# End of https://www.gitignore.io/api/vim,emacs,composer,jetbrains+all
|
||||||
|
|
||||||
kilo.log
|
kilo.log
|
||||||
|
kilo.exception.log
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
coverage/
|
coverage/
|
||||||
|
15
kilo
15
kilo
@ -31,6 +31,8 @@ return (static function (int $argc, array $argv): int {
|
|||||||
|
|
||||||
$editor->setStatusMessage('HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find');
|
$editor->setStatusMessage('HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find');
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
// Input Loop
|
// Input Loop
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@ -41,6 +43,19 @@ return (static function (int $argc, array $argv): int {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (\Throwable $e)
|
||||||
|
{
|
||||||
|
$msg = print_r([
|
||||||
|
'code' => $e->getCode(),
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
], TRUE);
|
||||||
|
file_put_contents('kilo.exception.log', $msg, FILE_APPEND);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
})($argc, $argv);
|
})($argc, $argv);
|
||||||
|
|
||||||
|
@ -88,42 +88,38 @@ class Editor {
|
|||||||
{
|
{
|
||||||
$c = read_stdin();
|
$c = read_stdin();
|
||||||
|
|
||||||
$simpleMap = [
|
$map = [
|
||||||
|
// Unambiguous mappings
|
||||||
KeyCode::ARROW_DOWN => KeyType::ARROW_DOWN,
|
KeyCode::ARROW_DOWN => KeyType::ARROW_DOWN,
|
||||||
KeyCode::ARROW_LEFT => KeyType::ARROW_LEFT,
|
KeyCode::ARROW_LEFT => KeyType::ARROW_LEFT,
|
||||||
KeyCode::ARROW_RIGHT => KeyType::ARROW_RIGHT,
|
KeyCode::ARROW_RIGHT => KeyType::ARROW_RIGHT,
|
||||||
KeyCode::ARROW_UP => KeyType::ARROW_UP,
|
KeyCode::ARROW_UP => KeyType::ARROW_UP,
|
||||||
KeyCode::BACKSPACE => KeyType::BACKSPACE,
|
|
||||||
KeyCode::DEL_KEY => KeyType::DEL_KEY,
|
KeyCode::DEL_KEY => KeyType::DEL_KEY,
|
||||||
KeyCode::ENTER => KeyType::ENTER,
|
KeyCode::ENTER => KeyType::ENTER,
|
||||||
KeyCode::ESCAPE => KeyType::ESCAPE,
|
KeyCode::ESCAPE => KeyType::ESCAPE,
|
||||||
KeyCode::PAGE_DOWN => KeyType::PAGE_DOWN,
|
KeyCode::PAGE_DOWN => KeyType::PAGE_DOWN,
|
||||||
KeyCode::PAGE_UP => KeyType::PAGE_UP,
|
KeyCode::PAGE_UP => KeyType::PAGE_UP,
|
||||||
];
|
|
||||||
|
|
||||||
$multiMap = [
|
// Backspace
|
||||||
|
KeyCode::CTRL('h') => KeyType::BACKSPACE,
|
||||||
|
KeyCode::BACKSPACE => KeyType::BACKSPACE,
|
||||||
|
|
||||||
|
// Home Key
|
||||||
"\eOH" => KeyType::HOME_KEY,
|
"\eOH" => KeyType::HOME_KEY,
|
||||||
"\e[1~" => KeyType::HOME_KEY,
|
"\e[1~" => KeyType::HOME_KEY,
|
||||||
"\e[7~" => KeyType::HOME_KEY,
|
"\e[7~" => KeyType::HOME_KEY,
|
||||||
ANSI::RESET_CURSOR => KeyType::HOME_KEY,
|
ANSI::RESET_CURSOR => KeyType::HOME_KEY,
|
||||||
|
|
||||||
|
// End Key
|
||||||
"\eOF" => KeyType::END_KEY,
|
"\eOF" => KeyType::END_KEY,
|
||||||
"\e[4~" => KeyType::END_KEY,
|
"\e[4~" => KeyType::END_KEY,
|
||||||
"\e[8~" => KeyType::END_KEY,
|
"\e[8~" => KeyType::END_KEY,
|
||||||
"\e[F" => KeyType::END_KEY,
|
"\e[F" => KeyType::END_KEY,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (array_key_exists($c, $simpleMap))
|
return (array_key_exists($c, $map))
|
||||||
{
|
? $map[$c]
|
||||||
return $simpleMap[$c];
|
: $c;
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists($c, $multiMap))
|
|
||||||
{
|
|
||||||
return $multiMap[$c];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function selectSyntaxHighlight(): void
|
protected function selectSyntaxHighlight(): void
|
||||||
@ -448,18 +444,20 @@ class Editor {
|
|||||||
$current = 0;
|
$current = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$match = strpos($this->rows[$current]->render, $query);
|
$row =& $this->rows[$current];
|
||||||
|
|
||||||
|
$match = strpos($row->render, $query);
|
||||||
if ($match !== FALSE)
|
if ($match !== FALSE)
|
||||||
{
|
{
|
||||||
$lastMatch = $current;
|
$lastMatch = $current;
|
||||||
$this->cursorY = $current;
|
$this->cursorY = $current;
|
||||||
$this->cursorX = $this->rowRxToCx($this->rows[$current], $match);
|
$this->cursorX = $this->rowRxToCx($row, $match);
|
||||||
$this->rowOffset = $this->numRows;
|
$this->rowOffset = $this->numRows;
|
||||||
|
|
||||||
$savedHlLine = $current;
|
$savedHlLine = $current;
|
||||||
$savedHl = $this->rows[$current]->hl;
|
$savedHl = $row->hl;
|
||||||
// Update the highlight array of the relevant row with the 'MATCH' type
|
// Update the highlight array of the relevant row with the 'MATCH' type
|
||||||
array_replace_range($this->rows[$current]->hl, $match, strlen($query), Highlight::MATCH);
|
array_replace_range($row->hl, $match, strlen($query), Highlight::MATCH);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -710,6 +708,7 @@ class Editor {
|
|||||||
$this->refreshScreen();
|
$this->refreshScreen();
|
||||||
|
|
||||||
$c = $this->readKey();
|
$c = $this->readKey();
|
||||||
|
$isModifier = in_array($c, $modifiers, TRUE);
|
||||||
|
|
||||||
if ($c === KeyType::ESCAPE || ($c === KeyType::ENTER && $buffer !== ''))
|
if ($c === KeyType::ESCAPE || ($c === KeyType::ENTER && $buffer !== ''))
|
||||||
{
|
{
|
||||||
@ -721,11 +720,11 @@ class Editor {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($c === KeyType::DEL_KEY || $c === KeyType::BACKSPACE || KeyType::CTRL('h'))
|
if ($c === KeyType::DEL_KEY || $c === KeyType::BACKSPACE)
|
||||||
{
|
{
|
||||||
$buffer = substr($buffer, 0, -1);
|
$buffer = substr($buffer, 0, -1);
|
||||||
}
|
}
|
||||||
else if (is_ascii($c) && ( ! is_ctrl($c)) && ! in_array($c, $modifiers, TRUE))
|
else if (is_ascii($c) && ( ! (is_ctrl($c) || $isModifier)))
|
||||||
{
|
{
|
||||||
$buffer .= $c;
|
$buffer .= $c;
|
||||||
}
|
}
|
||||||
@ -811,7 +810,7 @@ class Editor {
|
|||||||
$this->insertNewline();
|
$this->insertNewline();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyType::CTRL('q'):
|
case KeyCode::CTRL('q'):
|
||||||
if ($this->dirty > 0 && $quit_times > 0)
|
if ($this->dirty > 0 && $quit_times > 0)
|
||||||
{
|
{
|
||||||
$this->setStatusMessage('WARNING!!! File has unsaved changes.' .
|
$this->setStatusMessage('WARNING!!! File has unsaved changes.' .
|
||||||
@ -824,7 +823,7 @@ class Editor {
|
|||||||
return NULL;
|
return NULL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyType::CTRL('s'):
|
case KeyCode::CTRL('s'):
|
||||||
$this->save();
|
$this->save();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -839,12 +838,11 @@ class Editor {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyType::CTRL('f'):
|
case KeyCode::CTRL('f'):
|
||||||
$this->find();
|
$this->find();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyType::BACKSPACE:
|
case KeyType::BACKSPACE:
|
||||||
case KeyType::CTRL('h'):
|
|
||||||
case KeyType::DEL_KEY:
|
case KeyType::DEL_KEY:
|
||||||
if ($c === KeyType::DEL_KEY)
|
if ($c === KeyType::DEL_KEY)
|
||||||
{
|
{
|
||||||
@ -865,7 +863,7 @@ class Editor {
|
|||||||
$this->moveCursor($c);
|
$this->moveCursor($c);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyType::CTRL('l'):
|
case KeyCode::CTRL('l'):
|
||||||
case KeyType::ESCAPE:
|
case KeyType::ESCAPE:
|
||||||
// Do nothing
|
// Do nothing
|
||||||
break;
|
break;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace Aviat\Kilo\Enum;
|
namespace Aviat\Kilo\Enum;
|
||||||
|
|
||||||
use Aviat\Kilo\Traits;
|
use Aviat\Kilo\Traits;
|
||||||
|
use function Aviat\Kilo\ctrl_key;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 'Raw' input from stdin
|
* 'Raw' input from stdin
|
||||||
@ -28,4 +29,28 @@ class KeyCode {
|
|||||||
public const SPACE = ' ';
|
public const SPACE = ' ';
|
||||||
public const TAB = "\t";
|
public const TAB = "\t";
|
||||||
public const VERTICAL_TAB = "\v";
|
public const VERTICAL_TAB = "\v";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
// So, 0x60 < $ord < 0x7b
|
||||||
|
if ($ord > 0x60 && $ord < 0x7b)
|
||||||
|
{
|
||||||
|
return chr(ctrl_key($char));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid input, not an ascii letter
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
namespace Aviat\Kilo\Enum;
|
namespace Aviat\Kilo\Enum;
|
||||||
|
|
||||||
use Aviat\Kilo\Traits;
|
use Aviat\Kilo\Traits;
|
||||||
use function Aviat\Kilo\ctrl_key;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants representing various control keys
|
* Constants representing various control keys
|
||||||
@ -23,28 +22,4 @@ class KeyType {
|
|||||||
public const HOME_KEY = 'HOME';
|
public const HOME_KEY = 'HOME';
|
||||||
public const PAGE_DOWN = 'PAGE_DOWN';
|
public const PAGE_DOWN = 'PAGE_DOWN';
|
||||||
public const PAGE_UP = 'PAGE_UP';
|
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
|
|
||||||
// So, 0x60 < $ord < 0x7b
|
|
||||||
if ($ord > 0x60 && $ord < 0x7b)
|
|
||||||
{
|
|
||||||
return chr(ctrl_key($char));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid input, not an ascii letter
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
namespace Aviat\Kilo\Tests\Enum;
|
namespace Aviat\Kilo\Tests\Enum;
|
||||||
|
|
||||||
use Aviat\Kilo\Enum\KeyType;
|
use function Aviat\Kilo\ctrl_key;
|
||||||
|
|
||||||
|
use Aviat\Kilo\Enum\KeyCode;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class KeyTypeTest extends TestCase {
|
class KeyCodeTest extends TestCase {
|
||||||
public function testSanityCheck(): void
|
public function testSanityCheck(): void
|
||||||
{
|
{
|
||||||
for ($i = 1; $i < 27; $i++)
|
for ($i = 1; $i < 27; $i++)
|
||||||
@ -14,21 +16,22 @@ class KeyTypeTest extends TestCase {
|
|||||||
$ord = $i;
|
$ord = $i;
|
||||||
$expected = chr($ord);
|
$expected = chr($ord);
|
||||||
|
|
||||||
$actual = KeyType::CTRL($char);
|
$actual = KeyCode::CTRL($char);
|
||||||
|
|
||||||
$this->assertEquals($expected, $actual, "CTRL + '{$char}' should return chr($ord)");
|
$this->assertEquals(ctrl_key($char), $ord, "chr(ctrl_key) !== CTRL");
|
||||||
|
$this->assertEquals($expected, $actual, "CTRL+'{$char}' should return chr($ord)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullOnInvalidChar(): void
|
public function testNullOnInvalidChar(): void
|
||||||
{
|
{
|
||||||
$this->assertNull(KeyType::CTRL("\t"));
|
$this->assertNull(KeyCode::CTRL("\t"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSameOutputOnUpperOrLower(): void
|
public function testSameOutputOnUpperOrLower(): void
|
||||||
{
|
{
|
||||||
$lower = KeyType::CTRL('v');
|
$lower = KeyCode::CTRL('v');
|
||||||
$upper = KeyType::CTRL('V');
|
$upper = KeyCode::CTRL('V');
|
||||||
|
|
||||||
$this->assertEquals($lower, $upper);
|
$this->assertEquals($lower, $upper);
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user