Complete chapter 5

This commit is contained in:
Timothy Warren 2019-10-22 16:16:28 -04:00
parent 1522544fc9
commit 98a3b8b691
3 changed files with 126 additions and 33 deletions

View File

@ -7,4 +7,6 @@ due to requiring the `FFI` extension.
* The `editor` prefix has been removed from all the relevant functions, instead they are methods on the `Editor` class.
* Enums are faked with class constants
* Properties that must be manually updated in the C version (like counts/string length) are implemented with magic methods,
so they are essentially calculated on read.
* Generally, if a function exists in PHP, with the same name as the C function, the PHP version will be used.

View File

@ -27,7 +27,7 @@ class Key {
public const ARROW_RIGHT = 'ARROW_RIGHT';
public const ARROW_UP = 'ARROW_UP';
public const BACKSPACE = 'BACKSPACE';
public const DEL_KEY = 'DEL';
public const DEL_KEY = 'DELETE';
public const END_KEY = 'END';
public const ENTER = 'ENTER';
public const ESCAPE = 'ESCAPE';
@ -70,6 +70,27 @@ class Row {
return NULL;
}
}
public function update(): void
{
$idx = 0;
for ($i = 0; $i < $this->size; $i++)
{
if ($this->chars[$i] === "\t")
{
$this->render[$idx++] = ' ';
while ($idx % KILO_TAB_STOP !== 0)
{
$this->render[$idx++] = ' ';
}
}
else
{
$this->render[$idx++] = $this->chars[$i];
}
}
}
}
/**
@ -233,32 +254,29 @@ class Editor {
return $rx;
}
protected function updateRow(Row $row): void
protected function insertRow(int $at, string $s): void
{
$idx = 0;
for ($i = 0; $i < $row->size; $i++)
if ($at < 0 || $at > $this->numRows)
{
if ($row->chars[$i] === "\t")
{
$row->render[$idx++] = ' ';
while ($idx % KILO_TAB_STOP !== 0)
{
$row->render[$idx++] = ' ';
}
}
else
{
$row->render[$idx++] = $row->chars[$i];
}
return;
}
}
protected function appendRow(string $s): void
{
$at = $this->numRows;
$this->rows[$at] = Row::new($s);
$this->updateRow($this->rows[$at]);
$row = Row::new($s);
if ($at === $this->numRows)
{
$this->rows[] = $row;
}
else
{
$this->rows = [
...array_slice($this->rows, 0, $at),
$row,
...array_slice($this->rows, $at),
];
}
$this->rows[$at]->update();
$this->dirty++;
}
@ -288,15 +306,14 @@ class Editor {
// Safely insert into arbitrary position in the existing string
$row->chars = substr($row->chars, 0, $at) . $c . substr($row->chars, $at);
$this->updateRow($row);
$row->update();
$this->dirty++;
}
protected function rowAppendString(Row $row, string $s): void
{
$row->chars .= $s;
$this->updateRow($row);
$row->update();
$this->dirty++;
}
@ -308,7 +325,7 @@ class Editor {
}
$row->chars = substr_replace($row->chars, '', $at, 1);
$this->updateRow($row);
$row->update();
$this->dirty++;
}
@ -320,12 +337,34 @@ class Editor {
{
if ($this->cursorY === $this->numRows)
{
$this->appendRow('');
$this->insertRow($this->numRows, '');
}
$this->rowInsertChar($this->rows[$this->cursorY], $this->cursorX, $c);
$this->cursorX++;
}
protected function insertNewline(): void
{
if ($this->cursorX === 0)
{
$this->insertRow($this->cursorY, '');
}
else
{
$row = $this->rows[$this->cursorY];
// Add a new row, with the contents from the cursor to the end of the line
$this->insertRow($this->cursorY + 1, substr($row->chars, $this->cursorX));
// Update the (now previous) row
$row->chars = substr($row->chars, 0, $this->cursorX);
$row->update();
}
$this->cursorY++;
$this->cursorX = 0;
}
protected function deleteChar(): void
{
if ($this->cursorY === $this->numRows || ($this->cursorX === 0 && $this->cursorY === 0))
@ -377,11 +416,17 @@ class Editor {
// #TODO gracefully handle issues with loading a file
$handle = fopen($fullname, 'rb');
if ($handle === FALSE)
{
disableRawMode();
print_r(error_clear_last());
die();
}
while (($line = fgets($handle)) !== FALSE)
{
// Remove line endings when reading the file
$this->appendRow(rtrim($line, "\n\r\0"));
$this->insertRow($this->numRows, rtrim($line));
}
fclose($handle);
@ -393,7 +438,12 @@ class Editor {
{
if ($this->filename === '')
{
return;
$this->filename = $this->prompt('Save as: %s');
if ($this->filename === '')
{
$this->setStatusMessage('Save aborted');
return;
}
}
$contents = $this->rowsToString();
@ -577,6 +627,40 @@ class Editor {
// ! Input
// ------------------------------------------------------------------------
protected function prompt(string $prompt): string
{
$buffer = '';
while (TRUE)
{
$this->setStatusMessage($prompt, $buffer);
$this->refreshScreen();
$c = $this->readKey();
$cord = ord($c);
if ($c === Key::ESCAPE)
{
$this->setStatusMessage('');
return '';
}
if ($c === Key::ENTER && $buffer !== '')
{
$this->setStatusMessage('');
return $buffer;
}
if ($c === Key::DEL_KEY || $c === Key::BACKSPACE || $c === chr(ctrl_key('h')))
{
$buffer = substr($buffer, 0, -1);
}
else if ($cord < 128 && $this->ffi->iscntrl($cord) === 0)
{
$buffer .= $c;
}
}
}
protected function moveCursor(string $key): void
{
$row = ($this->cursorY >= $this->numRows)
@ -648,7 +732,7 @@ class Editor {
switch ($c)
{
case Key::ENTER:
// TODO
$this->insertNewline();
break;
case chr(ctrl_key('q')):

View File

@ -57,8 +57,6 @@ function read_stdin(int $len = 128): string
{
$handle = fopen('php://stdin', 'rb');
$input = fread($handle, $len);
$input = rtrim($input);
fclose($handle);
return $input;
@ -87,7 +85,16 @@ function read_stdout(int $len = 128): string
return $input;
}
/**
* Do bit twiddling to convert a letter into
* its Ctrl-letter equivalent
*
* @param string $char
* @return int
*/
function ctrl_key(string $char): int
{
// b1,100,001 (a) & b0,011,111 = b0,000,001
// b1,100,010 (b) & b0,011,111 = b0,000,010
return ord($char) & 0x1f;
}