Replace inline ANSI escapes with constants and static methods
Some checks failed
Gitea - Tutorials/php-kilo/master There was a failure building this commit
Some checks failed
Gitea - Tutorials/php-kilo/master There was a failure building this commit
This commit is contained in:
parent
7acf38da9c
commit
fb5dd66bee
97
src/ANSI.php
Normal file
97
src/ANSI.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Aviat\Kilo;
|
||||
|
||||
/**
|
||||
* ANSI
|
||||
*/
|
||||
class ANSI {
|
||||
// ------------------------------------------------------------------------
|
||||
// General ANSI standard escape sequences
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Clear the terminal window
|
||||
*/
|
||||
public const CLEAR_SCREEN = "\e[2J";
|
||||
|
||||
/**
|
||||
* Clear the terminal line
|
||||
*/
|
||||
public const CLEAR_LINE = "\e[K";
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Text formatting escape sequences
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Removes text attributes, such as bold, underline, blink, inverted colors
|
||||
*/
|
||||
public const RESET_TEXT = "\e[0m";
|
||||
|
||||
public const BOLD_TEXT = "\e[1m";
|
||||
|
||||
public const UNDERLINE_TEXT = "\e[4m";
|
||||
|
||||
/**
|
||||
* Move the cursor to position 0,0 which is the top left
|
||||
*/
|
||||
public const RESET_CURSOR = "\e[H";
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// VT-series escapes, not really ANSI standard
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public const HIDE_CURSOR = "\e[?25l";
|
||||
public const SHOW_CURSOR = "\e[?25h";
|
||||
|
||||
/**
|
||||
* Generate the ascii sequence for basic text color
|
||||
*
|
||||
* @param int $color
|
||||
* @return string
|
||||
*/
|
||||
public static function color(int $color): string
|
||||
{
|
||||
return sprintf("\e[%dm", $color);
|
||||
}
|
||||
|
||||
public static function rgbColor(int $r, int $g, int $b): string
|
||||
{
|
||||
return sprintf("\e[38;2;%d;%d;%bm", $r, $g, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the cursor the specified position
|
||||
*
|
||||
* @param int $line
|
||||
* @param int $column
|
||||
* @return string
|
||||
*/
|
||||
public static function moveCursor(int $line, int $column): string
|
||||
{
|
||||
return sprintf("\e[%d;%dH", $line, $column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the specified number of lines up
|
||||
*
|
||||
* @param int $lines
|
||||
* @return string
|
||||
*/
|
||||
public static function scrollUp(int $lines): string
|
||||
{
|
||||
return sprintf("\e[%dS", $lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the specified number of lines down
|
||||
*
|
||||
* @param int $lines
|
||||
* @return string
|
||||
*/
|
||||
public static function scrollDown(int $lines): string
|
||||
{
|
||||
return sprintf("\e[%dT", $lines);
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
namespace Aviat\Kilo;
|
||||
|
||||
use Aviat\Kilo\Enum\{
|
||||
Color,
|
||||
Key,
|
||||
Highlight,
|
||||
};
|
||||
@ -111,7 +112,7 @@ class Editor {
|
||||
case "\x1bOH":
|
||||
case "\x1b[1~":
|
||||
case "\x1b[7~":
|
||||
case "\x1b[H":
|
||||
case ANSI::RESET_CURSOR:
|
||||
return Key::HOME_KEY;
|
||||
|
||||
case "\x1bOF":
|
||||
@ -363,8 +364,8 @@ class Editor {
|
||||
$handle = fopen($filename, 'rb');
|
||||
if ($handle === FALSE)
|
||||
{
|
||||
write_stdout("\x1b[2J"); // Clear the screen
|
||||
write_stdout("\x1b[H"); // Reposition cursor to top-left
|
||||
write_stdout(ANSI::CLEAR_SCREEN);
|
||||
write_stdout(ANSI::RESET_CURSOR); // Reposition cursor to top-left
|
||||
Termios::disableRawMode();
|
||||
print_r(error_get_last());
|
||||
die();
|
||||
@ -604,20 +605,20 @@ class Editor {
|
||||
$sym = (ord($c[$i]) <= 26)
|
||||
? chr(ord('@') + ord($c[$i]))
|
||||
: '?';
|
||||
$this->ab .= "\x1b[7m";
|
||||
$this->ab .= ANSI::color(Color::INVERT);
|
||||
$this->ab .= $sym;
|
||||
$this->ab .= "\x1b[m";
|
||||
$this->ab .= ANSI::RESET_TEXT;
|
||||
if ($currentColor !== -1)
|
||||
{
|
||||
$this->ab .= sprintf("\x1b%dm", $currentColor);
|
||||
$this->ab .= ANSI::color($currentColor);
|
||||
}
|
||||
}
|
||||
else if ($hl[$i] === Highlight::NORMAL)
|
||||
{
|
||||
if ($currentColor !== -1)
|
||||
{
|
||||
$this->ab .= "\x1b[0m"; // Reset background color
|
||||
$this->ab .= "\x1b[39m"; // Reset foreground color
|
||||
$this->ab .= ANSI::RESET_TEXT;
|
||||
$this->ab .= ANSI::color(Color::FG_WHITE);
|
||||
$currentColor = -1;
|
||||
}
|
||||
$this->ab .= $c[$i];
|
||||
@ -628,25 +629,25 @@ class Editor {
|
||||
if ($color !== $currentColor)
|
||||
{
|
||||
$currentColor = $color;
|
||||
$this->ab .= "\x1b[0m"; // Reset background color
|
||||
$this->ab .= sprintf("\x1b[%dm", $color);
|
||||
$this->ab .= ANSI::RESET_TEXT;
|
||||
$this->ab .= ANSI::color($color);
|
||||
}
|
||||
$this->ab .= $c[$i];
|
||||
}
|
||||
}
|
||||
|
||||
$this->ab .= "\x1b[0m";
|
||||
$this->ab .= "\x1b[39m";
|
||||
$this->ab .= ANSI::RESET_TEXT;
|
||||
$this->ab .= ANSI::color(Color::FG_WHITE);
|
||||
}
|
||||
|
||||
$this->ab .= "\x1b[K"; // Clear the current line
|
||||
$this->ab .= ANSI::CLEAR_LINE;
|
||||
$this->ab .= "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
protected function drawStatusBar(): void
|
||||
{
|
||||
$this->ab .= "\x1b[7m";
|
||||
$this->ab .= ANSI::color(Color::INVERT);
|
||||
|
||||
$statusFilename = $this->filename !== '' ? $this->filename : '[No Name]';
|
||||
$syntaxType = ($this->syntax !== NULL) ? $this->syntax->filetype : 'no ft';
|
||||
@ -671,13 +672,13 @@ class Editor {
|
||||
$this->ab .= ' ';
|
||||
$len++;
|
||||
}
|
||||
$this->ab .= "\x1b[m";
|
||||
$this->ab .= ANSI::RESET_TEXT;
|
||||
$this->ab .= "\r\n";
|
||||
}
|
||||
|
||||
protected function drawMessageBar(): void
|
||||
{
|
||||
$this->ab .= "\x1b[K";
|
||||
$this->ab .= ANSI::CLEAR_LINE;
|
||||
$len = strlen($this->statusMsg);
|
||||
if ($len > $this->screenCols)
|
||||
{
|
||||
@ -696,20 +697,20 @@ class Editor {
|
||||
|
||||
$this->ab = '';
|
||||
|
||||
$this->ab .= "\x1b[?25l"; // Hide the cursor
|
||||
$this->ab .= "\x1b[H"; // Reposition cursor to top-left
|
||||
$this->ab .= ANSI::HIDE_CURSOR;
|
||||
$this->ab .= ANSI::RESET_CURSOR;
|
||||
|
||||
$this->drawRows();
|
||||
$this->drawStatusBar();
|
||||
$this->drawMessageBar();
|
||||
|
||||
// Specify the current cursor position
|
||||
$this->ab .= sprintf("\x1b[%d;%dH",
|
||||
$this->ab .= ANSI::moveCursor(
|
||||
($this->cursorY - $this->rowOffset) + 1,
|
||||
($this->renderX - $this->colOffset) + 1
|
||||
);
|
||||
|
||||
$this->ab .= "\x1b[?25h"; // Show the cursor
|
||||
$this->ab .= ANSI::SHOW_CURSOR;
|
||||
|
||||
echo $this->ab;
|
||||
}
|
||||
@ -855,8 +856,8 @@ class Editor {
|
||||
$quit_times--;
|
||||
return '';
|
||||
}
|
||||
write_stdout("\x1b[2J"); // Clear the screen
|
||||
write_stdout("\x1b[H"); // Reposition cursor to top-left
|
||||
write_stdout(ANSI::CLEAR_SCREEN);
|
||||
write_stdout(ANSI::RESET_CURSOR);
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
|
17
src/Row.php
17
src/Row.php
@ -319,11 +319,6 @@ class Row {
|
||||
$char = $this->render[$i];
|
||||
$prevHl = ($i > 0) ? $this->hl[$i - 1] : Highlight::NORMAL;
|
||||
|
||||
if ($this->parent->syntax === NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Single-line comments
|
||||
if ($scsLen > 0 && $inString === '' && $inComment === FALSE
|
||||
&& substr($this->render, $i, $scsLen) === $scs)
|
||||
@ -384,7 +379,7 @@ class Row {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $char === '""' || $char === '\'')
|
||||
if ( $char === '"' || $char === '\'')
|
||||
{
|
||||
$inString = $char;
|
||||
$this->hl[$i] = Highlight::STRING;
|
||||
@ -441,7 +436,9 @@ class Row {
|
||||
$this->hlOpenComment = $inComment;
|
||||
if ($changed && $this->idx + 1 < $this->parent->numRows)
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->parent->rows[$this->idx + 1]->updateSyntax();
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,7 +453,9 @@ class Row {
|
||||
$this->idx < $this->parent->numRows
|
||||
))
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$tokens = $this->parent->tokens[$rowNum];
|
||||
@ -471,7 +470,9 @@ class Row {
|
||||
{
|
||||
if ($offset >= $this->rsize)
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
break;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
// A multi-line comment can end in the middle of a line...
|
||||
@ -497,7 +498,9 @@ class Row {
|
||||
$charLen = strlen($char);
|
||||
if ($charLen === 0 || $offset >= $this->rsize)
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
continue;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
$charStart = strpos($this->render, $char, $offset);
|
||||
if ($charStart === FALSE)
|
||||
@ -581,7 +584,9 @@ class Row {
|
||||
$this->hlOpenComment = $inComment;
|
||||
if ($changed && $this->idx + 1 < $this->parent->numRows)
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->parent->rows[$this->idx + 1]->updateSyntax();
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,9 +80,10 @@ class Termios {
|
||||
{
|
||||
$instance = self::getInstance();
|
||||
|
||||
write_stdout("\x1b[2J"); // Clear the screen
|
||||
write_stdout("\x1b[H"); // Reposition cursor to top-left
|
||||
echo "\n";
|
||||
// Cleanup
|
||||
write_stdout(ANSI::CLEAR_SCREEN);
|
||||
write_stdout(ANSI::RESET_CURSOR);
|
||||
write_stdout("\n"); // New line, please
|
||||
|
||||
$res = get_ffi()->tcsetattr(C::STDIN_FILENO, C::TCSAFLUSH, FFI::addr($instance->originalTermios));
|
||||
|
||||
|
@ -11,6 +11,13 @@
|
||||
#define FFI_SCOPE "terminal"
|
||||
#define FFI_LIB "libc.so.6"
|
||||
|
||||
// Nonsense for a test with a single quote
|
||||
// Ignored by PHP due to the octothorpe (#)
|
||||
#if 0
|
||||
# char* x = "String with \" escape char";
|
||||
# char y = 'q';
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//! <termios.h>
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -5,6 +5,17 @@ namespace Aviat\Kilo\Tests\Traits;
|
||||
use Aviat\Kilo\Editor;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Spatie\Snapshots\MatchesSnapshots;
|
||||
use function Aviat\Kilo\get_window_size;
|
||||
|
||||
class MockEditor extends Editor {
|
||||
public function __set(string $key, $value): void
|
||||
{
|
||||
if (property_exists($this, $key))
|
||||
{
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EditorTest extends TestCase {
|
||||
use MatchesSnapshots;
|
||||
@ -15,7 +26,10 @@ class EditorTest extends TestCase {
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->editor = Editor::new();
|
||||
// Mock the screen size, since that can vary widely
|
||||
$this->editor = MockEditor::new();
|
||||
$this->editor->__set('screenRows', 23);
|
||||
$this->editor->__set('screenCols', 80);
|
||||
}
|
||||
|
||||
public function testSanity(): void
|
||||
@ -26,14 +40,22 @@ class EditorTest extends TestCase {
|
||||
|
||||
public function test__debugInfo(): void
|
||||
{
|
||||
$state = json_encode($this->editor->__debugInfo());
|
||||
$state = json_encode($this->editor->__debugInfo(), JSON_THROW_ON_ERROR);
|
||||
$this->assertMatchesJsonSnapshot($state);
|
||||
}
|
||||
|
||||
public function testOpenPHP(): void
|
||||
{
|
||||
$this->editor->open('test.php');
|
||||
$state = json_encode($this->editor, JSON_THROW_ON_ERROR);
|
||||
|
||||
$this->assertMatchesJsonSnapshot($state);
|
||||
}
|
||||
|
||||
public function testOpen(): void
|
||||
{
|
||||
$this->editor->open('test.php');
|
||||
$state = json_encode($this->editor);
|
||||
$this->editor->open('src/ffi.h');
|
||||
$state = json_encode($this->editor, JSON_THROW_ON_ERROR);
|
||||
|
||||
$this->assertMatchesJsonSnapshot($state);
|
||||
}
|
||||
|
4397
tests/__snapshots__/EditorTest__testOpenPHP__1.json
Normal file
4397
tests/__snapshots__/EditorTest__testOpenPHP__1.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user