diff --git a/src/Editor.php b/src/Editor.php index 8797bc5..195de29 100644 --- a/src/Editor.php +++ b/src/Editor.php @@ -135,12 +135,29 @@ class Row { { $this->hl = array_fill(0, $this->rsize, Highlight::NORMAL); - for ($i = 0; $i < $this->rsize; $i++) + $prevSep = TRUE; + + $i = 0; + + while ($i < $this->rsize) { - if (is_digit($this->render[$i])) + $char = $this->render[$i]; + $prevHl = ($i > 0) ? $this->hl[$i - 1] : Highlight::NORMAL; + + // Numbers, including decimal points + if ( + ($char === '.' && $prevHl === Highlight::NUMBER) || + (($prevSep || $prevHl === Highlight::NUMBER) && is_digit($char)) + ) { $this->hl[$i] = Highlight::NUMBER; + $i++; + $prevSep = FALSE; + continue; } + + $prevSep = is_separator($char); + $i++; } } } @@ -497,6 +514,15 @@ class Editor { static $lastMatch = -1; static $direction = 1; + static $savedHlLine = 0; + static $savedHl = []; + + if ( ! empty($savedHl)) + { + $this->rows[$savedHlLine]->hl = $savedHl; + $savedHl = []; + } + if ($key === "\r" || $key === "\x1b") { $lastMatch = -1; @@ -536,16 +562,20 @@ class Editor { $current = 0; } - $match = strpos($this->rows[$current]->render, $query); + $row = $this->rows[$current]; + + $match = strpos($row->render, $query); if ($match !== FALSE) { $lastMatch = $current; $this->cursorY = $current; - $this->cursorX = $this->rowRxToCx($this->rows[$current], $match); + $this->cursorX = $this->rowRxToCx($row, $match); $this->rowOffset = $this->numRows; + $savedHlLine = $current; + $savedHl = $row->hl; // 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; } @@ -662,7 +692,8 @@ class Editor { { if ($currentColor !== -1) { - $this->ab .= "\x1b[39m"; + $this->ab .= "\x1b[39m"; // Reset foreground color + $this->ab .= "\x1b[0m"; // Reset background color $currentColor = -1; } $this->ab .= $c[$i]; @@ -673,6 +704,7 @@ class Editor { if ($color !== $currentColor) { $currentColor = $color; + $this->ab .= "\x1b[0m"; // Reset background color $this->ab .= sprintf("\x1b[%dm", $color); } $this->ab .= $c[$i]; @@ -680,6 +712,7 @@ class Editor { } $this->ab .= "\x1b[39m"; + $this->ab .= "\x1b[0m"; } $this->ab .= "\x1b[K"; // Clear the current line diff --git a/src/functions.php b/src/functions.php index 8050c1d..4cfd0a9 100644 --- a/src/functions.php +++ b/src/functions.php @@ -161,6 +161,18 @@ function is_digit(string $char): bool return is_ascii($char) && ( $c > 0x2f && $c < 0x3a ); } +/** + * Does the one-character string contain ascii whitespace? + * + * @param string $char + * @return bool + */ +function is_space(string $char): bool +{ + $ws = [' ', "\t", "\n", "\r", "\xa", "\xb", "\xc"]; + return is_ascii($char) && in_array($char, $ws, TRUE); +} + // ---------------------------------------------------------------------------- // ! Helper functions // ---------------------------------------------------------------------------- @@ -182,6 +194,26 @@ function get_ffi(): FFI return $ffi; } +/** + * Does the one-character string contain a character that separates tokens? + * + * @param string $char + * @return bool + */ +function is_separator(string $char): bool +{ + if ( ! is_ascii($char)) + { + return FALSE; + } + + // `strpos` is used instead of `strchr`/`strstr` as we don't care about the actual match + // while `strchr` would match the C version, it also returns the match + $isSep = (strpos(',.()+-/*=~%<>[];', $char) !== FALSE); + + return is_space($char) || $char === "\0" || $isSep; +} + /** * Pull input from the stdin stream. *