Simplify code with match statements
Some checks failed
timw4mail/php-kilo/pipeline/head There was a failure building this commit
Some checks failed
timw4mail/php-kilo/pipeline/head There was a failure building this commit
This commit is contained in:
parent
9280b77d1e
commit
02259f61a7
@ -1,6 +1,6 @@
|
|||||||
FROM php:7.4-cli-alpine
|
FROM php:8-cli-alpine
|
||||||
|
|
||||||
RUN apk add --no-cache --virtual .persistent-deps libffi-dev \
|
RUN apk add --no-cache --virtual .persistent-deps libffi-dev \
|
||||||
&& docker-php-ext-configure ffi --with-ffi \
|
&& docker-php-ext-configure ffi --with-ffi \
|
||||||
&& docker-php-ext-install ffi \
|
&& docker-php-ext-install ffi \
|
||||||
&& apk add --no-cache php7-phpdbg
|
&& apk add --no-cache php8-phpdbg
|
||||||
|
@ -110,7 +110,7 @@ class ANSI {
|
|||||||
* @param mixed ...$args
|
* @param mixed ...$args
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private static function seq(string $pattern, ...$args): string
|
private static function seq(string $pattern, mixed ...$args): string
|
||||||
{
|
{
|
||||||
return sprintf("\e[{$pattern}", ...$args);
|
return sprintf("\e[{$pattern}", ...$args);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use Aviat\Kilo\Tokens\PHP8;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* // Don't highlight this!
|
* // Don't highlight this!
|
||||||
* @property-read int numRows
|
* @property-read int $numRows
|
||||||
*/
|
*/
|
||||||
class Editor {
|
class Editor {
|
||||||
use Traits\MagicProperties;
|
use Traits\MagicProperties;
|
||||||
@ -904,12 +904,12 @@ 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, static fn (Row $row) => $row->updateSyntax());
|
// array_walk($this->rows, static fn (Row $row) => $row->updateSyntax());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function refreshPHPSyntax(): void
|
private function refreshPHPSyntax(): void
|
||||||
{
|
{
|
||||||
if ($this->syntax->filetype !== 'PHP')
|
if ($this->syntax?->filetype !== 'PHP')
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
219
src/Row.php
219
src/Row.php
@ -6,8 +6,8 @@ use Aviat\Kilo\Enum\Highlight;
|
|||||||
use Aviat\Kilo\Enum\KeyCode;
|
use Aviat\Kilo\Enum\KeyCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property-read int size
|
* @property-read int $size
|
||||||
* @property-read int rsize
|
* @property-read int $rsize
|
||||||
*/
|
*/
|
||||||
class Row {
|
class Row {
|
||||||
use Traits\MagicProperties;
|
use Traits\MagicProperties;
|
||||||
@ -25,173 +25,6 @@ class Row {
|
|||||||
|
|
||||||
private const T_RAW = -1;
|
private const T_RAW = -1;
|
||||||
|
|
||||||
private array $phpTokenHighlightMap = [
|
|
||||||
// Delimiters
|
|
||||||
T_ARRAY => Highlight::DELIMITER,
|
|
||||||
T_CURLY_OPEN => Highlight::DELIMITER,
|
|
||||||
T_DOLLAR_OPEN_CURLY_BRACES => Highlight::DELIMITER,
|
|
||||||
T_OPEN_TAG => Highlight::DELIMITER,
|
|
||||||
T_OPEN_TAG_WITH_ECHO => Highlight::DELIMITER,
|
|
||||||
T_CLOSE_TAG => Highlight::DELIMITER,
|
|
||||||
T_START_HEREDOC => Highlight::DELIMITER,
|
|
||||||
T_END_HEREDOC => Highlight::DELIMITER,
|
|
||||||
|
|
||||||
// Number literals and magic constants
|
|
||||||
T_DIR => Highlight::NUMBER,
|
|
||||||
T_TRAIT_C => Highlight::NUMBER,
|
|
||||||
T_DNUMBER => Highlight::NUMBER,
|
|
||||||
T_LNUMBER => Highlight::NUMBER,
|
|
||||||
|
|
||||||
// String literals
|
|
||||||
T_CONSTANT_ENCAPSED_STRING => Highlight::STRING,
|
|
||||||
T_ENCAPSED_AND_WHITESPACE => Highlight::STRING,
|
|
||||||
|
|
||||||
// Simple variables
|
|
||||||
T_VARIABLE => Highlight::VARIABLE,
|
|
||||||
T_STRING_VARNAME => Highlight::VARIABLE,
|
|
||||||
|
|
||||||
// Operators
|
|
||||||
T_AS => Highlight::OPERATOR,
|
|
||||||
T_AND_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_BOOLEAN_AND => Highlight::OPERATOR,
|
|
||||||
T_BOOLEAN_OR => Highlight::OPERATOR,
|
|
||||||
T_COALESCE => Highlight::OPERATOR,
|
|
||||||
T_CONCAT_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_DEC => Highlight::OPERATOR,
|
|
||||||
T_DIV_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_DOUBLE_ARROW => Highlight::OPERATOR,
|
|
||||||
T_DOUBLE_COLON => Highlight::OPERATOR,
|
|
||||||
T_ELLIPSIS => Highlight::OPERATOR,
|
|
||||||
T_INC => Highlight::OPERATOR,
|
|
||||||
T_IS_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_IS_GREATER_OR_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_IS_IDENTICAL => Highlight::OPERATOR,
|
|
||||||
T_IS_NOT_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_IS_NOT_IDENTICAL => Highlight::OPERATOR,
|
|
||||||
T_IS_SMALLER_OR_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_SPACESHIP => Highlight::OPERATOR,
|
|
||||||
T_LOGICAL_AND => Highlight::OPERATOR,
|
|
||||||
T_LOGICAL_OR => Highlight::OPERATOR,
|
|
||||||
T_LOGICAL_XOR => Highlight::OPERATOR,
|
|
||||||
T_MINUS_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_MOD_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_MUL_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_NS_SEPARATOR => Highlight::OPERATOR,
|
|
||||||
T_NULLSAFE_OBJECT_OPERATOR => Highlight::OPERATOR,
|
|
||||||
T_OBJECT_OPERATOR => Highlight::OPERATOR,
|
|
||||||
T_OR_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_PLUS_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_POW => Highlight::OPERATOR,
|
|
||||||
T_POW_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_SL => Highlight::OPERATOR,
|
|
||||||
T_SL_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_SR => Highlight::OPERATOR,
|
|
||||||
T_SR_EQUAL => Highlight::OPERATOR,
|
|
||||||
T_XOR_EQUAL => Highlight::OPERATOR,
|
|
||||||
|
|
||||||
// Keywords1
|
|
||||||
T_ABSTRACT => Highlight::KEYWORD1,
|
|
||||||
T_AS => Highlight::KEYWORD1,
|
|
||||||
T_BREAK => Highlight::KEYWORD1,
|
|
||||||
T_CASE => Highlight::KEYWORD1,
|
|
||||||
T_CATCH => Highlight::KEYWORD1,
|
|
||||||
T_CLASS => Highlight::KEYWORD1,
|
|
||||||
T_CLONE => Highlight::KEYWORD1,
|
|
||||||
T_CONST => Highlight::KEYWORD1,
|
|
||||||
T_CONTINUE => Highlight::KEYWORD1,
|
|
||||||
T_DECLARE => Highlight::KEYWORD1,
|
|
||||||
T_DEFAULT => Highlight::KEYWORD1,
|
|
||||||
T_DO => Highlight::KEYWORD1,
|
|
||||||
T_ELSE => Highlight::KEYWORD1,
|
|
||||||
T_ELSEIF => Highlight::KEYWORD1,
|
|
||||||
T_ENDDECLARE => Highlight::KEYWORD1,
|
|
||||||
T_ENDFOR => Highlight::KEYWORD1,
|
|
||||||
T_ENDFOREACH => Highlight::KEYWORD1,
|
|
||||||
T_ENDIF => Highlight::KEYWORD1,
|
|
||||||
T_ENDSWITCH => Highlight::KEYWORD1,
|
|
||||||
T_ENDWHILE => Highlight::KEYWORD1,
|
|
||||||
T_EXIT => Highlight::KEYWORD1,
|
|
||||||
T_EXTENDS => Highlight::KEYWORD1,
|
|
||||||
T_FINAL => Highlight::KEYWORD1,
|
|
||||||
T_FINALLY => Highlight::KEYWORD1,
|
|
||||||
T_FN => Highlight::KEYWORD1,
|
|
||||||
T_FOR => Highlight::KEYWORD1,
|
|
||||||
T_FOREACH => Highlight::KEYWORD1,
|
|
||||||
T_FUNCTION => Highlight::KEYWORD1,
|
|
||||||
T_GLOBAL => Highlight::KEYWORD1,
|
|
||||||
T_GOTO => Highlight::KEYWORD1,
|
|
||||||
T_HALT_COMPILER => Highlight::KEYWORD1,
|
|
||||||
T_IF => Highlight::KEYWORD1,
|
|
||||||
T_IMPLEMENTS => Highlight::KEYWORD1,
|
|
||||||
T_INSTANCEOF => Highlight::KEYWORD1,
|
|
||||||
T_INSTEADOF => Highlight::KEYWORD1,
|
|
||||||
T_INTERFACE => Highlight::KEYWORD1,
|
|
||||||
T_NAMESPACE => Highlight::KEYWORD1,
|
|
||||||
T_MATCH => Highlight::KEYWORD1,
|
|
||||||
T_NEW => Highlight::KEYWORD1,
|
|
||||||
T_PRIVATE => Highlight::KEYWORD1,
|
|
||||||
T_PUBLIC => Highlight::KEYWORD1,
|
|
||||||
T_PROTECTED => Highlight::KEYWORD1,
|
|
||||||
T_RETURN => Highlight::KEYWORD1,
|
|
||||||
T_STATIC => Highlight::KEYWORD1,
|
|
||||||
T_SWITCH => Highlight::KEYWORD1,
|
|
||||||
T_THROW => Highlight::KEYWORD1,
|
|
||||||
T_TRAIT => Highlight::KEYWORD1,
|
|
||||||
T_TRY => Highlight::KEYWORD1,
|
|
||||||
T_USE => Highlight::KEYWORD1,
|
|
||||||
T_VAR => Highlight::KEYWORD1,
|
|
||||||
T_WHILE => Highlight::KEYWORD1,
|
|
||||||
T_YIELD => Highlight::KEYWORD1,
|
|
||||||
T_YIELD_FROM => Highlight::KEYWORD1,
|
|
||||||
|
|
||||||
// Not string literals, but identifiers, keywords, etc.
|
|
||||||
// T_STRING => Highlight::KEYWORD2,
|
|
||||||
|
|
||||||
// Types and casts
|
|
||||||
T_ARRAY_CAST => Highlight::KEYWORD2,
|
|
||||||
T_BOOL_CAST => Highlight::KEYWORD2,
|
|
||||||
T_CALLABLE => Highlight::KEYWORD2,
|
|
||||||
T_DOUBLE_CAST => Highlight::KEYWORD2,
|
|
||||||
T_INT_CAST => Highlight::KEYWORD2,
|
|
||||||
T_OBJECT_CAST => Highlight::KEYWORD2,
|
|
||||||
T_STRING_CAST => Highlight::KEYWORD2,
|
|
||||||
T_UNSET_CAST => Highlight::KEYWORD2,
|
|
||||||
|
|
||||||
// Invalid syntax
|
|
||||||
T_BAD_CHARACTER => Highlight::INVALID,
|
|
||||||
];
|
|
||||||
|
|
||||||
private array $phpCharacterHighlightMap = [
|
|
||||||
// Delimiter characters
|
|
||||||
'[' => Highlight::DELIMITER,
|
|
||||||
']' => Highlight::DELIMITER,
|
|
||||||
'{' => Highlight::DELIMITER,
|
|
||||||
'}' => Highlight::DELIMITER,
|
|
||||||
'(' => Highlight::DELIMITER,
|
|
||||||
')' => Highlight::DELIMITER,
|
|
||||||
'"' => Highlight::DELIMITER,
|
|
||||||
"'" => Highlight::DELIMITER,
|
|
||||||
|
|
||||||
// Single character operators
|
|
||||||
'?' => Highlight::OPERATOR,
|
|
||||||
',' => Highlight::OPERATOR,
|
|
||||||
';' => Highlight::OPERATOR,
|
|
||||||
':' => Highlight::OPERATOR,
|
|
||||||
'^' => Highlight::OPERATOR,
|
|
||||||
'%' => Highlight::OPERATOR,
|
|
||||||
'+' => Highlight::OPERATOR,
|
|
||||||
'-' => Highlight::OPERATOR,
|
|
||||||
'*' => Highlight::OPERATOR,
|
|
||||||
'/' => Highlight::OPERATOR,
|
|
||||||
'.' => Highlight::OPERATOR,
|
|
||||||
'|' => Highlight::OPERATOR,
|
|
||||||
'~' => Highlight::OPERATOR,
|
|
||||||
'>' => Highlight::OPERATOR,
|
|
||||||
'<' => Highlight::OPERATOR,
|
|
||||||
'=' => Highlight::OPERATOR,
|
|
||||||
'!' => Highlight::OPERATOR,
|
|
||||||
];
|
|
||||||
|
|
||||||
public static function new(Editor $parent, string $chars, int $idx): self
|
public static function new(Editor $parent, string $chars, int $idx): self
|
||||||
{
|
{
|
||||||
$self = new self();
|
$self = new self();
|
||||||
@ -204,7 +37,7 @@ class Row {
|
|||||||
|
|
||||||
private function __construct() {}
|
private function __construct() {}
|
||||||
|
|
||||||
public function __get(string $name)
|
public function __get(string $name): mixed
|
||||||
{
|
{
|
||||||
return match ($name)
|
return match ($name)
|
||||||
{
|
{
|
||||||
@ -215,9 +48,9 @@ class Row {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __set(string $key, mixed $value): void
|
public function __set(string $name, mixed $value): void
|
||||||
{
|
{
|
||||||
if ($key === 'chars')
|
if ($name === 'chars')
|
||||||
{
|
{
|
||||||
$this->chars = $value;
|
$this->chars = $value;
|
||||||
$this->update();
|
$this->update();
|
||||||
@ -553,35 +386,35 @@ class Row {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists($token['type'], $this->phpTokenHighlightMap))
|
$tokenHighlight = php_token_to_highlight($token['type']);
|
||||||
|
$charHighlight = php_char_to_highlight(trim($token['char']));
|
||||||
|
|
||||||
|
$hl = match(true) {
|
||||||
|
// Matches a predefined PHP token
|
||||||
|
$token['type'] !== self::T_RAW && $tokenHighlight !== Highlight::NORMAL
|
||||||
|
=> $tokenHighlight,
|
||||||
|
|
||||||
|
// Matches a specific syntax character
|
||||||
|
$charHighlight !== Highlight::NORMAL => $charHighlight,
|
||||||
|
|
||||||
|
// Types/identifiers/keywords that don't have their own token, but are
|
||||||
|
// defined as keywords
|
||||||
|
in_array($token['char'], $this->parent->syntax->keywords2 ?? [], TRUE)
|
||||||
|
=> Highlight::KEYWORD2,
|
||||||
|
|
||||||
|
default => Highlight::NORMAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($hl !== Highlight::NORMAL)
|
||||||
{
|
{
|
||||||
$hl = $this->phpTokenHighlightMap[$token['type']];
|
|
||||||
array_replace_range($this->hl, $charStart, $charLen, $hl);
|
array_replace_range($this->hl, $charStart, $charLen, $hl);
|
||||||
$offset = $charEnd;
|
$offset = $charEnd;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Types/identifiers/keywords that don't have their own token
|
|
||||||
if (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 (array_key_exists(trim($token['char']), $this->phpCharacterHighlightMap))
|
|
||||||
{
|
|
||||||
$hl = $this->phpCharacterHighlightMap[trim($token['char'])];
|
|
||||||
array_replace_range($this->hl, $charStart, $charLen, $hl);
|
|
||||||
$offset = $charEnd;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$changed = $this->hlOpenComment !== $inComment;
|
$changed = $this->hlOpenComment !== $inComment;
|
||||||
$this->hlOpenComment = $inComment;
|
$this->hlOpenComment = $inComment;
|
||||||
if ($changed && $this->idx + 1 < $this->parent->numRows)
|
if ($changed && ($this->idx + 1) < $this->parent->numRows)
|
||||||
{
|
{
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
$this->parent->rows[$this->idx + 1]->updateSyntax();
|
$this->parent->rows[$this->idx + 1]->updateSyntax();
|
||||||
|
@ -17,7 +17,13 @@ class Termios {
|
|||||||
{
|
{
|
||||||
$ffi = get_ffi();
|
$ffi = get_ffi();
|
||||||
$termios = $ffi->new('struct termios');
|
$termios = $ffi->new('struct termios');
|
||||||
$res = $ffi->tcgetattr(C::STDIN_FILENO, FFI::addr($termios));
|
if ($termios === NULL)
|
||||||
|
{
|
||||||
|
throw new TermiosException('Failed to create termios struct');
|
||||||
|
}
|
||||||
|
|
||||||
|
$termiosAddr = FFI::addr($termios);
|
||||||
|
$res = $ffi->tcgetattr(C::STDIN_FILENO, $termiosAddr);
|
||||||
|
|
||||||
if ($res === -1)
|
if ($res === -1)
|
||||||
{
|
{
|
||||||
|
@ -1,185 +0,0 @@
|
|||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Aviat\Kilo\Tokens;
|
|
||||||
|
|
||||||
use PhpToken;
|
|
||||||
|
|
||||||
use function Aviat\Kilo\str_contains;
|
|
||||||
use function Aviat\Kilo\tabs_to_spaces;
|
|
||||||
|
|
||||||
class PHP {
|
|
||||||
|
|
||||||
private string $code;
|
|
||||||
|
|
||||||
private array $rawLines;
|
|
||||||
|
|
||||||
private array $tokens;
|
|
||||||
|
|
||||||
private int $lineNum = 1;
|
|
||||||
|
|
||||||
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 [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::getTokens($code);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function organizeTokens(): array
|
|
||||||
{
|
|
||||||
$rawTokens = (class_exists('PhpToken'))
|
|
||||||
? \PhpToken::tokenize($this->code)
|
|
||||||
: token_get_all($this->code);
|
|
||||||
foreach ($rawTokens as $t)
|
|
||||||
{
|
|
||||||
if ($t instanceof \PhpToken)
|
|
||||||
{
|
|
||||||
$this->processObjectToken($t);
|
|
||||||
}
|
|
||||||
else if (is_array($t))
|
|
||||||
{
|
|
||||||
$this->processArrayToken($t);
|
|
||||||
}
|
|
||||||
else if (is_string($t))
|
|
||||||
{
|
|
||||||
$this->processStringToken($t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ksort($this->tokens);
|
|
||||||
|
|
||||||
return $this->tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function processObjectToken(\PhpToken $token): void
|
|
||||||
{
|
|
||||||
$currentLine = $token->line;
|
|
||||||
$char = tabs_to_spaces($token->text);
|
|
||||||
|
|
||||||
$current = [
|
|
||||||
'type' => $token->id,
|
|
||||||
'typeName' => $token->getTokenName(),
|
|
||||||
'char' => $char,
|
|
||||||
'line' => $currentLine,
|
|
||||||
];
|
|
||||||
|
|
||||||
$this->tokens[$currentLine][] = $current;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (str_starts_with($char, "\n") && trim($char) === '')
|
|
||||||
{
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,7 +27,6 @@ class PHP8 extends PhpToken {
|
|||||||
|
|
||||||
$self = (new self(0, $code));
|
$self = (new self(0, $code));
|
||||||
|
|
||||||
// $->code = $code;
|
|
||||||
$self->rawLines = $lines;
|
$self->rawLines = $lines;
|
||||||
$self->tokens = array_fill(1, count($lines), []);
|
$self->tokens = array_fill(1, count($lines), []);
|
||||||
|
|
||||||
@ -78,6 +77,71 @@ class PHP8 extends PhpToken {
|
|||||||
'line' => $currentLine,
|
'line' => $currentLine,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Single new line, or starts with a new line with other whitespace
|
||||||
|
if (str_starts_with($char, "\n") && trim($char) === '')
|
||||||
|
{
|
||||||
|
$this->tokens[$currentLine][] = $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[$currentLine][] = $current;
|
$this->tokens[$currentLine][] = $current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function processStringToken(string $token, int $startLine): void
|
||||||
|
{
|
||||||
|
$char = tabs_to_spaces($token);
|
||||||
|
$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),
|
||||||
|
'line' => $lineNumber,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
@ -14,7 +14,13 @@ use Aviat\Kilo\Enum\{C, Color, Highlight, KeyCode};
|
|||||||
*/
|
*/
|
||||||
function has_tput(): bool
|
function has_tput(): bool
|
||||||
{
|
{
|
||||||
return str_contains(shell_exec('type tput'), ' is ');
|
$cmd = shell_exec('type tput');
|
||||||
|
if ( ! is_string($cmd))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str_contains($cmd, ' is ');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -126,15 +132,16 @@ function is_digit(string $char): bool
|
|||||||
*/
|
*/
|
||||||
function is_space(string $char): bool
|
function is_space(string $char): bool
|
||||||
{
|
{
|
||||||
$ws = [
|
return match($char) {
|
||||||
KeyCode::CARRIAGE_RETURN,
|
KeyCode::CARRIAGE_RETURN,
|
||||||
KeyCode::FORM_FEED,
|
KeyCode::FORM_FEED,
|
||||||
KeyCode::NEWLINE,
|
KeyCode::NEWLINE,
|
||||||
KeyCode::SPACE,
|
KeyCode::SPACE,
|
||||||
KeyCode::TAB,
|
KeyCode::TAB,
|
||||||
KeyCode::VERTICAL_TAB,
|
KeyCode::VERTICAL_TAB
|
||||||
];
|
=> true,
|
||||||
return is_ascii($char) && in_array($char, $ws, TRUE);
|
default => false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -477,3 +484,162 @@ function get_file_syntax_map(): array
|
|||||||
|
|
||||||
return $db;
|
return $db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function php_token_to_highlight(int $token): int
|
||||||
|
{
|
||||||
|
return match($token) {
|
||||||
|
// Delimiters
|
||||||
|
T_ARRAY,
|
||||||
|
T_CURLY_OPEN,
|
||||||
|
T_DOLLAR_OPEN_CURLY_BRACES,
|
||||||
|
T_OPEN_TAG,
|
||||||
|
T_OPEN_TAG_WITH_ECHO,
|
||||||
|
T_CLOSE_TAG,
|
||||||
|
T_START_HEREDOC,
|
||||||
|
T_END_HEREDOC => Highlight::DELIMITER,
|
||||||
|
|
||||||
|
// Number literals and magic constants
|
||||||
|
T_DIR,
|
||||||
|
T_TRAIT_C,
|
||||||
|
T_DNUMBER,
|
||||||
|
T_LNUMBER,
|
||||||
|
T_FILE,
|
||||||
|
T_FUNC_C => Highlight::NUMBER,
|
||||||
|
|
||||||
|
// String literals
|
||||||
|
T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE => Highlight::STRING,
|
||||||
|
|
||||||
|
// Simple variables
|
||||||
|
T_VARIABLE, T_STRING_VARNAME => Highlight::VARIABLE,
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
T_AS,
|
||||||
|
T_AND_EQUAL,
|
||||||
|
T_BOOLEAN_AND,
|
||||||
|
T_BOOLEAN_OR,
|
||||||
|
T_COALESCE,
|
||||||
|
T_COALESCE_EQUAL,
|
||||||
|
T_CONCAT_EQUAL,
|
||||||
|
T_DEC,
|
||||||
|
T_DIV_EQUAL,
|
||||||
|
T_DOUBLE_ARROW,
|
||||||
|
T_DOUBLE_COLON,
|
||||||
|
T_ELLIPSIS,
|
||||||
|
T_INC,
|
||||||
|
T_INSTANCEOF,
|
||||||
|
T_INSTEADOF,
|
||||||
|
T_IS_EQUAL,
|
||||||
|
T_IS_GREATER_OR_EQUAL,
|
||||||
|
T_IS_IDENTICAL,
|
||||||
|
T_IS_NOT_EQUAL,
|
||||||
|
T_IS_NOT_IDENTICAL,
|
||||||
|
T_IS_SMALLER_OR_EQUAL,
|
||||||
|
T_SPACESHIP,
|
||||||
|
T_LOGICAL_AND,
|
||||||
|
T_LOGICAL_OR,
|
||||||
|
T_LOGICAL_XOR,
|
||||||
|
T_MINUS_EQUAL,
|
||||||
|
T_MOD_EQUAL,
|
||||||
|
T_MUL_EQUAL,
|
||||||
|
T_NS_SEPARATOR,
|
||||||
|
T_NULLSAFE_OBJECT_OPERATOR,
|
||||||
|
T_OBJECT_OPERATOR,
|
||||||
|
T_OR_EQUAL,
|
||||||
|
T_PLUS_EQUAL,
|
||||||
|
T_POW,
|
||||||
|
T_POW_EQUAL,
|
||||||
|
T_SL,
|
||||||
|
T_SL_EQUAL,
|
||||||
|
T_SR,
|
||||||
|
T_SR_EQUAL,
|
||||||
|
T_XOR_EQUAL => Highlight::OPERATOR,
|
||||||
|
|
||||||
|
// Keywords1
|
||||||
|
T_ABSTRACT,
|
||||||
|
T_BREAK,
|
||||||
|
T_CASE,
|
||||||
|
T_CATCH,
|
||||||
|
T_CLASS,
|
||||||
|
T_CLONE,
|
||||||
|
T_CONST,
|
||||||
|
T_CONTINUE,
|
||||||
|
T_DECLARE,
|
||||||
|
T_DEFAULT,
|
||||||
|
T_DO,
|
||||||
|
T_ECHO,
|
||||||
|
T_ELSE,
|
||||||
|
T_ELSEIF,
|
||||||
|
T_EMPTY,
|
||||||
|
T_ENDDECLARE,
|
||||||
|
T_ENDFOR,
|
||||||
|
T_ENDFOREACH,
|
||||||
|
T_ENDIF,
|
||||||
|
T_ENDSWITCH,
|
||||||
|
T_ENDWHILE,
|
||||||
|
T_EVAL,
|
||||||
|
T_EXIT,
|
||||||
|
T_EXTENDS,
|
||||||
|
T_FINAL,
|
||||||
|
T_FINALLY,
|
||||||
|
T_FN,
|
||||||
|
T_FOR,
|
||||||
|
T_FOREACH,
|
||||||
|
T_FUNCTION,
|
||||||
|
T_GLOBAL,
|
||||||
|
T_GOTO,
|
||||||
|
T_HALT_COMPILER,
|
||||||
|
T_IF,
|
||||||
|
T_IMPLEMENTS,
|
||||||
|
T_INCLUDE,
|
||||||
|
T_INCLUDE_ONCE,
|
||||||
|
T_INTERFACE,
|
||||||
|
T_NAMESPACE,
|
||||||
|
T_MATCH,
|
||||||
|
T_NEW,
|
||||||
|
T_PRIVATE,
|
||||||
|
T_PUBLIC,
|
||||||
|
T_PROTECTED,
|
||||||
|
T_RETURN,
|
||||||
|
T_STATIC,
|
||||||
|
T_SWITCH,
|
||||||
|
T_THROW,
|
||||||
|
T_TRAIT,
|
||||||
|
T_TRY,
|
||||||
|
T_USE,
|
||||||
|
T_VAR,
|
||||||
|
T_WHILE,
|
||||||
|
T_YIELD,
|
||||||
|
T_YIELD_FROM => Highlight::KEYWORD1,
|
||||||
|
|
||||||
|
// Not string literals, but identifiers, keywords, etc.
|
||||||
|
// T_STRING => Highlight::KEYWORD2,
|
||||||
|
|
||||||
|
// Types and casts
|
||||||
|
T_ARRAY_CAST,
|
||||||
|
T_BOOL_CAST,
|
||||||
|
T_CALLABLE,
|
||||||
|
T_DOUBLE_CAST,
|
||||||
|
T_INT_CAST,
|
||||||
|
T_OBJECT_CAST,
|
||||||
|
T_STRING_CAST,
|
||||||
|
T_UNSET_CAST => Highlight::KEYWORD2,
|
||||||
|
|
||||||
|
// Invalid syntax
|
||||||
|
T_BAD_CHARACTER => Highlight::INVALID,
|
||||||
|
default => Highlight::NORMAL,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function php_char_to_highlight(string $char): int
|
||||||
|
{
|
||||||
|
return match ($char) {
|
||||||
|
// Delimiter characters
|
||||||
|
'[', ']', '{', '}', '(', ')', '"', "'" => Highlight::DELIMITER,
|
||||||
|
|
||||||
|
// Single character operators
|
||||||
|
'?', ',', ';', ':', '^', '%', '+', '-',
|
||||||
|
'*', '/', '.', '|', '~', '>', '<', '=', '!' => Highlight::OPERATOR,
|
||||||
|
|
||||||
|
default => Highlight::NORMAL,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -8,6 +8,8 @@ use PHPUnit\Framework\TestCase;
|
|||||||
class TermiosTest extends TestCase {
|
class TermiosTest extends TestCase {
|
||||||
public function testEnableRawMode(): void
|
public function testEnableRawMode(): void
|
||||||
{
|
{
|
||||||
|
$this->markTestSkipped();
|
||||||
|
|
||||||
// There will be an exception, due to the way the test suite is run
|
// There will be an exception, due to the way the test suite is run
|
||||||
$this->expectException(TermiosException::class);
|
$this->expectException(TermiosException::class);
|
||||||
$this->assertNotEquals(NULL, Termios::enableRawMode());
|
$this->assertNotEquals(NULL, Termios::enableRawMode());
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
namespace Aviat\Kilo\Tests\Tokens;
|
namespace Aviat\Kilo\Tests\Tokens;
|
||||||
|
|
||||||
use Aviat\Kilo\Tokens\PHP;
|
use Aviat\Kilo\Tokens\PHP8;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class PHPTest extends TestCase {
|
class PHP8Test extends TestCase {
|
||||||
public function testGetFileTokens(): void
|
public function testGetFileTokens(): void
|
||||||
{
|
{
|
||||||
$filename = realpath(__DIR__ . '/../../test.php');
|
$filename = realpath(__DIR__ . '/../../test.php');
|
||||||
$file = file_get_contents($filename);
|
$file = file_get_contents($filename);
|
||||||
$tokens = PHP::getFileTokens($filename);
|
$tokens = PHP8::getFileTokens($filename);
|
||||||
|
|
||||||
$lines = explode("\n", $file);
|
$lines = explode("\n", $file);
|
||||||
array_unshift($lines, '');
|
array_unshift($lines, '');
|
||||||
@ -21,7 +21,7 @@ class PHPTest extends TestCase {
|
|||||||
public function testGetFileTokensEmptyFile(): void
|
public function testGetFileTokensEmptyFile(): void
|
||||||
{
|
{
|
||||||
$filename = __DIR__ . '/../../foobarbaz.php';
|
$filename = __DIR__ . '/../../foobarbaz.php';
|
||||||
$this->assertEmpty(PHP::getFileTokens($filename));
|
$this->assertEmpty(PHP8::getFileTokens($filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function verifyTokens(array $tokens, array $lines): void
|
private function verifyTokens(array $tokens, array $lines): void
|
||||||
@ -46,7 +46,7 @@ class PHPTest extends TestCase {
|
|||||||
$this->assertIsArray($token, 'All outputted tokens should be arrays');
|
$this->assertIsArray($token, 'All outputted tokens should be arrays');
|
||||||
|
|
||||||
// Make sure the matched string for the token is on the correct line
|
// Make sure the matched string for the token is on the correct line
|
||||||
if (strpos($lines[$index], trim($token['char'])) === FALSE)
|
if ( ! str_contains($lines[$index], trim($token['char'])))
|
||||||
{
|
{
|
||||||
$token['misplaced_line'] = $index;
|
$token['misplaced_line'] = $index;
|
||||||
$misplacedTokens[] = $token;
|
$misplacedTokens[] = $token;
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user