This commit is contained in:
parent
1e4735ee38
commit
7cd7d1baa5
@ -15,8 +15,8 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"phpunit/phpunit": "^8",
|
"phpunit/phpunit": "^9.5.0",
|
||||||
"spatie/phpunit-snapshot-assertions": "^2.2.0"
|
"spatie/phpunit-snapshot-assertions": "^4.2.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"coverage": "phpdbg -qrr -- vendor/bin/phpunit -c phpunit.xml tests",
|
"coverage": "phpdbg -qrr -- vendor/bin/phpunit -c phpunit.xml tests",
|
||||||
|
2072
composer.lock
generated
2072
composer.lock
generated
File diff suppressed because it is too large
Load Diff
43
kilo
43
kilo
@ -6,16 +6,16 @@ namespace Aviat\Kilo;
|
|||||||
require_once __DIR__ . '/vendor/autoload.php';
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
// Log notices/errors/warnings to file
|
// Log notices/errors/warnings to file
|
||||||
set_error_handler(static function (int $no, $str, $file, $line) {
|
set_exception_handler(static function (\Throwable $e) {
|
||||||
$msg = print_r([
|
$msg = print_r([
|
||||||
'errno' => $no,
|
'code' => $e->getCode(),
|
||||||
'message' => $str,
|
'message' => $e->getMessage(),
|
||||||
'file' => $file,
|
'file' => $e->getFile(),
|
||||||
'line' => $line,
|
'line' => $e->getLine(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
], TRUE);
|
], TRUE);
|
||||||
file_put_contents('kilo.log', $msg, FILE_APPEND);
|
file_put_contents('kilo.log', $msg, FILE_APPEND);
|
||||||
|
});
|
||||||
}, -1);
|
|
||||||
|
|
||||||
// ! Init with an IIFE
|
// ! Init with an IIFE
|
||||||
return (static function (int $argc, array $argv): int {
|
return (static function (int $argc, array $argv): int {
|
||||||
@ -23,38 +23,15 @@ return (static function (int $argc, array $argv): int {
|
|||||||
register_shutdown_function([Termios::class, 'disableRawMode']);
|
register_shutdown_function([Termios::class, 'disableRawMode']);
|
||||||
|
|
||||||
$editor = Editor::new();
|
$editor = Editor::new();
|
||||||
|
$editor->setStatusMessage('HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find');
|
||||||
|
|
||||||
if ($argc >= 2)
|
if ($argc >= 2)
|
||||||
{
|
{
|
||||||
$editor->open($argv[1]);
|
$editor->open($argv[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$editor->setStatusMessage('HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find');
|
// Input Loop
|
||||||
|
do { $editor->refreshScreen();} while ($editor->processKeypress() !== NULL);
|
||||||
try
|
|
||||||
{
|
|
||||||
// Input Loop
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
$editor->refreshScreen();
|
|
||||||
$char = $editor->processKeypress();
|
|
||||||
if ($char === NULL)
|
|
||||||
{
|
|
||||||
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);
|
||||||
|
41
phpunit.xml
41
phpunit.xml
@ -1,24 +1,21 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<phpunit
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" colors="true" stopOnFailure="false" beStrictAboutTestsThatDoNotTestAnything="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||||
colors="true"
|
<coverage>
|
||||||
stopOnFailure="false"
|
<include>
|
||||||
beStrictAboutTestsThatDoNotTestAnything="true"
|
<directory suffix=".php">src</directory>
|
||||||
>
|
</include>
|
||||||
<filter>
|
<exclude>
|
||||||
<whitelist>
|
<file>src/constants.php</file>
|
||||||
<directory suffix=".php">src</directory>
|
</exclude>
|
||||||
<exclude>
|
<report>
|
||||||
<file>src/constants.php</file>
|
<html outputDirectory="coverage"/>
|
||||||
</exclude>
|
<text outputFile="php://stdout" showUncoveredFiles="true"/>
|
||||||
</whitelist>
|
</report>
|
||||||
</filter>
|
</coverage>
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="PHPKilo">
|
<testsuite name="PHPKilo">
|
||||||
<directory phpVersion="7.4.0" phpVersionOperator=">=">tests</directory>
|
<directory phpVersion="7.4.0" phpVersionOperator=">=">tests</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
<logging>
|
<logging/>
|
||||||
<log type="coverage-html" target="coverage"/>
|
|
||||||
<log type="coverage-text" target="php://stdout" showUncoveredFiles="true" />
|
|
||||||
</logging>
|
|
||||||
</phpunit>
|
</phpunit>
|
@ -52,7 +52,7 @@ class Editor {
|
|||||||
$this->screenRows -= 2;
|
$this->screenRows -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __get(string $name)
|
public function __get(string $name): ?int
|
||||||
{
|
{
|
||||||
if ($name === 'numRows')
|
if ($name === 'numRows')
|
||||||
{
|
{
|
||||||
@ -96,7 +96,6 @@ class Editor {
|
|||||||
KeyCode::ARROW_UP => KeyType::ARROW_UP,
|
KeyCode::ARROW_UP => KeyType::ARROW_UP,
|
||||||
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::PAGE_DOWN => KeyType::PAGE_DOWN,
|
KeyCode::PAGE_DOWN => KeyType::PAGE_DOWN,
|
||||||
KeyCode::PAGE_UP => KeyType::PAGE_UP,
|
KeyCode::PAGE_UP => KeyType::PAGE_UP,
|
||||||
|
|
||||||
@ -104,6 +103,10 @@ class Editor {
|
|||||||
KeyCode::CTRL('h') => KeyType::BACKSPACE,
|
KeyCode::CTRL('h') => KeyType::BACKSPACE,
|
||||||
KeyCode::BACKSPACE => KeyType::BACKSPACE,
|
KeyCode::BACKSPACE => KeyType::BACKSPACE,
|
||||||
|
|
||||||
|
// Escape
|
||||||
|
KeyCode::CTRL('l') => KeyType::ESCAPE,
|
||||||
|
KeyCode::ESCAPE => KeyType::ESCAPE,
|
||||||
|
|
||||||
// Home Key
|
// Home Key
|
||||||
"\eOH" => KeyType::HOME_KEY,
|
"\eOH" => KeyType::HOME_KEY,
|
||||||
"\e[1~" => KeyType::HOME_KEY,
|
"\e[1~" => KeyType::HOME_KEY,
|
||||||
@ -271,6 +274,8 @@ class Editor {
|
|||||||
|
|
||||||
protected function insertNewline(): void
|
protected function insertNewline(): void
|
||||||
{
|
{
|
||||||
|
// @TODO attempt smart indentation on newline?
|
||||||
|
|
||||||
if ($this->cursorX === 0)
|
if ($this->cursorX === 0)
|
||||||
{
|
{
|
||||||
$this->insertRow($this->cursorY, '');
|
$this->insertRow($this->cursorY, '');
|
||||||
@ -432,6 +437,11 @@ class Editor {
|
|||||||
|
|
||||||
$current = $lastMatch;
|
$current = $lastMatch;
|
||||||
|
|
||||||
|
if (empty($query))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for ($i = 0; $i < $this->numRows; $i++)
|
for ($i = 0; $i < $this->numRows; $i++)
|
||||||
{
|
{
|
||||||
$current += $direction;
|
$current += $direction;
|
||||||
@ -717,7 +727,7 @@ class Editor {
|
|||||||
{
|
{
|
||||||
$callback($buffer, $c);
|
$callback($buffer, $c);
|
||||||
}
|
}
|
||||||
return '';
|
return ($c === KeyType::ENTER) ? $buffer : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($c === KeyType::DEL_KEY || $c === KeyType::BACKSPACE)
|
if ($c === KeyType::DEL_KEY || $c === KeyType::BACKSPACE)
|
||||||
@ -786,7 +796,7 @@ class Editor {
|
|||||||
$row = ($this->cursorY >= $this->numRows)
|
$row = ($this->cursorY >= $this->numRows)
|
||||||
? NULL
|
? NULL
|
||||||
: $this->rows[$this->cursorY];
|
: $this->rows[$this->cursorY];
|
||||||
$rowlen = $row ? $row->size : 0;
|
$rowlen = $row->size ?? 0;
|
||||||
if ($this->cursorX > $rowlen)
|
if ($this->cursorX > $rowlen)
|
||||||
{
|
{
|
||||||
$this->cursorX = $rowlen;
|
$this->cursorX = $rowlen;
|
||||||
@ -903,7 +913,7 @@ class Editor {
|
|||||||
protected function refreshSyntax(): void
|
protected function refreshSyntax(): void
|
||||||
{
|
{
|
||||||
// Update the syntax highlighting for all the rows of the file
|
// Update the syntax highlighting for all the rows of the file
|
||||||
array_walk($this->rows, fn (Row $row) => $row->updateSyntax());
|
array_walk($this->rows, static fn (Row $row) => $row->updateSyntax());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function refreshPHPSyntax(): void
|
private function refreshPHPSyntax(): void
|
||||||
|
@ -495,7 +495,7 @@ class Row {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$char = $token['char'];
|
$char = $token['char'] ?? '';
|
||||||
$charLen = strlen($char);
|
$charLen = strlen($char);
|
||||||
if ($charLen === 0 || $offset >= $this->rsize)
|
if ($charLen === 0 || $offset >= $this->rsize)
|
||||||
{
|
{
|
||||||
|
@ -7,156 +7,166 @@ use function Aviat\Kilo\tabs_to_spaces;
|
|||||||
|
|
||||||
class PHP {
|
class PHP {
|
||||||
|
|
||||||
private string $code;
|
private string $code;
|
||||||
|
|
||||||
private array $rawLines;
|
private array $rawLines;
|
||||||
|
|
||||||
private array $tokens;
|
private array $tokens;
|
||||||
|
|
||||||
private int $lineNum = 1;
|
private int $lineNum = 1;
|
||||||
|
|
||||||
private function __construct(string $code)
|
private function __construct(string $code)
|
||||||
{
|
|
||||||
$lines = explode("\n", $code);
|
|
||||||
array_unshift($lines, '');
|
|
||||||
unset($lines[0]);
|
|
||||||
|
|
||||||
$this->code = $code;
|
|
||||||
$this->rawLines = $lines;
|
|
||||||
$this->tokens = array_fill(1, count($lines), []);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use 'token_get_all' to get the tokens for a file,
|
|
||||||
* organized by row number
|
|
||||||
*
|
|
||||||
* @param string $code
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function getTokens(string $code): array
|
|
||||||
{
|
|
||||||
return (new self($code))->organizeTokens();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return tokens for the current $filename, organized
|
|
||||||
* by row number
|
|
||||||
*
|
|
||||||
* @param string $filename
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function getFileTokens(string $filename): array
|
|
||||||
{
|
|
||||||
$code = @file_get_contents($filename);
|
|
||||||
|
|
||||||
if ($code === FALSE)
|
|
||||||
{
|
{
|
||||||
return [];
|
$lines = explode("\n", $code);
|
||||||
|
array_unshift($lines, '');
|
||||||
|
unset($lines[0]);
|
||||||
|
|
||||||
|
$this->code = $code;
|
||||||
|
$this->rawLines = $lines;
|
||||||
|
$this->tokens = array_fill(1, count($lines), []);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::getTokens($code);
|
/**
|
||||||
}
|
* Use 'token_get_all' to get the tokens for a file,
|
||||||
|
* organized by row number
|
||||||
protected function organizeTokens(): array
|
*
|
||||||
{
|
* @param string $code
|
||||||
$rawTokens = token_get_all($this->code);
|
* @return array
|
||||||
foreach ($rawTokens as $t)
|
*/
|
||||||
|
public static function getTokens(string $code): array
|
||||||
{
|
{
|
||||||
if (is_array($t))
|
if (class_exists('PhpToken'))
|
||||||
{
|
{
|
||||||
$this->processArrayToken($t);
|
return \PhpToken::tokenize($code);
|
||||||
}
|
}
|
||||||
else if (is_string($t))
|
|
||||||
|
return (new self($code))->organizeTokens();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return tokens for the current $filename, organized
|
||||||
|
* by row number
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function getFileTokens(string $filename): array
|
||||||
|
{
|
||||||
|
$code = @file_get_contents($filename);
|
||||||
|
|
||||||
|
if ($code === FALSE)
|
||||||
{
|
{
|
||||||
$this->processStringToken($t);
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return self::getTokens($code);
|
||||||
}
|
}
|
||||||
|
|
||||||
ksort($this->tokens);
|
protected function organizeTokens(): array
|
||||||
|
|
||||||
return $this->tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function processArrayToken(array $token): void
|
|
||||||
{
|
|
||||||
[$type, $rawChar, $currentLine] = $token;
|
|
||||||
$char = tabs_to_spaces($rawChar);
|
|
||||||
|
|
||||||
$this->lineNum = $currentLine;
|
|
||||||
|
|
||||||
$current = [
|
|
||||||
'type' => $type,
|
|
||||||
'typeName' => token_name($type),
|
|
||||||
'char' => $char,
|
|
||||||
'line' => $currentLine,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Single new line, or starts with a new line with other whitespace
|
|
||||||
if (strpos($char, "\n") === 0 && trim($char) === '')
|
|
||||||
{
|
{
|
||||||
$this->tokens[$this->lineNum][] = $current;
|
$rawTokens = token_get_all($this->code);
|
||||||
$this->lineNum++;
|
foreach ($rawTokens as $t)
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only return the first line of a multi-line token for this line array
|
|
||||||
if (str_contains($char, "\n"))
|
|
||||||
{
|
|
||||||
$chars = explode("\n", $char);
|
|
||||||
$current['original'] = [
|
|
||||||
'string' => $char,
|
|
||||||
'lines' => $chars,
|
|
||||||
];
|
|
||||||
$current['char'] = array_shift($chars);
|
|
||||||
|
|
||||||
// Add new lines for additional newline characters
|
|
||||||
$nextLine = $currentLine;
|
|
||||||
foreach ($chars as $char)
|
|
||||||
{
|
{
|
||||||
$nextLine++;
|
if (is_array($t))
|
||||||
|
|
||||||
if ( ! empty($char))
|
|
||||||
{
|
{
|
||||||
$this->processStringToken($char, $nextLine);
|
$this->processArrayToken($t);
|
||||||
|
}
|
||||||
|
else if (is_string($t))
|
||||||
|
{
|
||||||
|
$this->processStringToken($t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ksort($this->tokens);
|
||||||
|
|
||||||
|
return $this->tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->tokens[$this->lineNum][] = $current;
|
protected function processObjectToken(\PhpToken $token)
|
||||||
}
|
|
||||||
|
|
||||||
protected function processStringToken(string $token, ?int $startLine = NULL): void
|
|
||||||
{
|
|
||||||
$char = tabs_to_spaces($token);
|
|
||||||
|
|
||||||
$startLine = $startLine ?? $this->lineNum;
|
|
||||||
$lineNumber = $this->findCorrectLine($char, $startLine) ?? $startLine;
|
|
||||||
|
|
||||||
// Simple characters, usually delimiters or single character operators
|
|
||||||
$this->tokens[$lineNumber][] = [
|
|
||||||
'type' => -1,
|
|
||||||
'typeName' => 'RAW',
|
|
||||||
'char' => tabs_to_spaces($token),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function findCorrectLine(string $search, int $rowOffset, int $searchLength = 5): ?int
|
|
||||||
{
|
|
||||||
$end = $rowOffset + $searchLength;
|
|
||||||
if ($end > count($this->rawLines))
|
|
||||||
{
|
{
|
||||||
$end = count($this->rawLines);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($i = $rowOffset; $i < $end; $i++)
|
protected function processArrayToken(array $token): void
|
||||||
{
|
{
|
||||||
if (str_contains($this->rawLines[$i], $search))
|
[$type, $rawChar, $currentLine] = $token;
|
||||||
|
$char = tabs_to_spaces($rawChar);
|
||||||
|
|
||||||
|
$this->lineNum = $currentLine;
|
||||||
|
|
||||||
|
$current = [
|
||||||
|
'type' => $type,
|
||||||
|
'typeName' => token_name($type),
|
||||||
|
'char' => $char,
|
||||||
|
'line' => $currentLine,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Single new line, or starts with a new line with other whitespace
|
||||||
|
if (strpos($char, "\n") === 0 && trim($char) === '')
|
||||||
{
|
{
|
||||||
return $i;
|
$this->tokens[$this->lineNum][] = $current;
|
||||||
|
$this->lineNum++;
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only return the first line of a multi-line token for this line array
|
||||||
|
if (str_contains($char, "\n"))
|
||||||
|
{
|
||||||
|
$chars = explode("\n", $char);
|
||||||
|
$current['original'] = [
|
||||||
|
'string' => $char,
|
||||||
|
'lines' => $chars,
|
||||||
|
];
|
||||||
|
$current['char'] = array_shift($chars);
|
||||||
|
|
||||||
|
// Add new lines for additional newline characters
|
||||||
|
$nextLine = $currentLine;
|
||||||
|
foreach ($chars as $char)
|
||||||
|
{
|
||||||
|
$nextLine++;
|
||||||
|
|
||||||
|
if ( ! empty($char))
|
||||||
|
{
|
||||||
|
$this->processStringToken($char, $nextLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->tokens[$this->lineNum][] = $current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function processStringToken(string $token, ?int $startLine = NULL): void
|
||||||
|
{
|
||||||
|
$char = tabs_to_spaces($token);
|
||||||
|
|
||||||
|
$startLine = $startLine ?? $this->lineNum;
|
||||||
|
$lineNumber = $this->findCorrectLine($char, $startLine) ?? $startLine;
|
||||||
|
|
||||||
|
// Simple characters, usually delimiters or single character operators
|
||||||
|
$this->tokens[$lineNumber][] = [
|
||||||
|
'type' => -1,
|
||||||
|
'typeName' => 'RAW',
|
||||||
|
'char' => tabs_to_spaces($token),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findCorrectLine(string $search, int $rowOffset, int $searchLength = 5): ?int
|
||||||
|
{
|
||||||
|
$end = $rowOffset + $searchLength;
|
||||||
|
if ($end > count($this->rawLines))
|
||||||
|
{
|
||||||
|
$end = count($this->rawLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = $rowOffset; $i < $end; $i++)
|
||||||
|
{
|
||||||
|
if (str_contains($this->rawLines[$i], $search))
|
||||||
|
{
|
||||||
|
return $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -283,7 +283,7 @@ function syntax_to_color(int $hl): int
|
|||||||
* Replace tabs with the specified number of spaces.
|
* Replace tabs with the specified number of spaces.
|
||||||
*
|
*
|
||||||
* @param string $str
|
* @param string $str
|
||||||
* @param int? $number
|
* @param int|null $number
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
function tabs_to_spaces(string $str, ?int $number = KILO_TAB_STOP): string
|
function tabs_to_spaces(string $str, ?int $number = KILO_TAB_STOP): string
|
||||||
|
9
test.php
9
test.php
@ -8,9 +8,10 @@ abstract class Foo implements Ifoo {
|
|||||||
* @param float $b
|
* @param float $b
|
||||||
* @param array $c
|
* @param array $c
|
||||||
* @param callable $d
|
* @param callable $d
|
||||||
|
* @param string $e
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
abstract public function bar(int $a, float $b, array $c, callable $d): string;
|
abstract public function bar(int $a, float $b, array $c, callable $d, string $e): string;
|
||||||
|
|
||||||
protected function doNothing(): void {}
|
protected function doNothing(): void {}
|
||||||
}
|
}
|
||||||
@ -41,6 +42,8 @@ class FooBar extends Foo implements Ifoo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$square = fn (int $x) => $x ** 2;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Multi-line comment
|
* Multi-line comment
|
||||||
*/
|
*/
|
||||||
@ -76,7 +79,11 @@ $template = <<<'TEMPLATE'
|
|||||||
TEMPLATE;
|
TEMPLATE;
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>HTML</title>
|
||||||
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1><?= $_SERVER['HTTP_HOST'] ?></h1>
|
<h1><?= $_SERVER['HTTP_HOST'] ?></h1>
|
||||||
</body>
|
</body>
|
||||||
|
@ -18,7 +18,7 @@ class KeyCodeTest extends TestCase {
|
|||||||
|
|
||||||
$actual = KeyCode::CTRL($char);
|
$actual = KeyCode::CTRL($char);
|
||||||
|
|
||||||
$this->assertEquals(ctrl_key($char), $ord, "chr(ctrl_key) !== CTRL");
|
$this->assertEquals(ctrl_key($char), $ord, 'chr(ctrl_key) !== CTRL');
|
||||||
$this->assertEquals($expected, $actual, "CTRL+'{$char}' should return chr($ord)");
|
$this->assertEquals($expected, $actual, "CTRL+'{$char}' should return chr($ord)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user