diff --git a/Source/Chrome/Text.php b/Source/Chrome/Text.php index 3fad00b..69aabe5 100644 --- a/Source/Chrome/Text.php +++ b/Source/Chrome/Text.php @@ -360,7 +360,7 @@ public static function underline(string $text, string $pattern = '*'): string foreach ($text as $key => &$value) { $i = -1; $max = strlen($value); - while ($value{++$i} == ' ' && $i < $max); + while ($value[++$i] == ' ' && $i < $max); $underline = str_repeat(' ', $i) . diff --git a/Source/Cursor.php b/Source/Cursor.php index b44f187..6cce9fe 100644 --- a/Source/Cursor.php +++ b/Source/Cursor.php @@ -79,7 +79,7 @@ public static function move(string $steps, int $repeat = 1) $output->writeAll( str_replace( '%p1%d', - $repeat, + (string) $repeat, $tput->get('parm_up_cursor') ) ); @@ -98,7 +98,7 @@ public static function move(string $steps, int $repeat = 1) $output->writeAll( str_replace( '%p1%d', - $repeat, + (string) $repeat, $tput->get('parm_right_cursor') ) ); @@ -117,7 +117,7 @@ public static function move(string $steps, int $repeat = 1) $output->writeAll( str_replace( '%p1%d', - $repeat, + (string) $repeat, $tput->get('parm_down_cursor') ) ); @@ -136,7 +136,7 @@ public static function move(string $steps, int $repeat = 1) $output->writeAll( str_replace( '%p1%d', - $repeat, + (string) $repeat, $tput->get('parm_left_cursor') ) ); @@ -269,7 +269,7 @@ public static function clear(string $parts = 'all'): void case 'a': case 'all': case '↕': - $output->writeAll($tput->get('clear_screen')); + $output->writeAll($tput->get('clear_screen') ?? ''); static::moveTo(1, 1); break; @@ -284,21 +284,21 @@ public static function clear(string $parts = 'all'): void case 'r': case 'right': case '→': - $output->writeAll($tput->get('clr_eol')); + $output->writeAll($tput->get('clr_eol') ?? ''); break; case 'd': case 'down': case '↓': - $output->writeAll($tput->get('clr_eos')); + $output->writeAll($tput->get('clr_eos') ?? ''); break; case 'l': case 'left': case '←': - $output->writeAll($tput->get('clr_bol')); + $output->writeAll($tput->get('clr_bol') ?? ''); break; @@ -317,7 +317,7 @@ public static function clear(string $parts = 'all'): void public static function hide(): void { Console::getOutput()->writeAll( - Console::getTput()->get('cursor_invisible') + Console::getTput()->get('cursor_invisible') ?? '' ); } @@ -327,7 +327,7 @@ public static function hide(): void public static function show(): void { Console::getOutput()->writeAll( - Console::getTput()->get('cursor_visible') + Console::getTput()->get('cursor_visible') ?? '' ); } @@ -685,7 +685,7 @@ public static function setStyle(string $style, bool $blink = true) public static function bip(): void { Console::getOutput()->writeAll( - Console::getTput()->get('bell') + Console::getTput()->get('bell') ?? '' ); } } diff --git a/Source/Readline/Readline.php b/Source/Readline/Readline.php index 0f37ab7..ae5e838 100644 --- a/Source/Readline/Readline.php +++ b/Source/Readline/Readline.php @@ -114,7 +114,15 @@ class Readline */ protected $_autocompleter = null; + /** + * @var float + */ + protected $selectTimeout = 30; + /** + * @var callable + */ + protected $frameCallback; /** * Initialize the readline editor. @@ -142,10 +150,28 @@ public function __construct() return; } + + /** + * @param float $timeout + */ + public function setSelectTimeout(float $timeout): void + { + $this->selectTimeout = $timeout; + } + + /** + * @param callable $callback + */ + public function setFrameCallback(callable $callback): void + { + $this->frameCallback = $callback; + + } + /** * Read a line from the input. */ - public function readLine(string $prefix = null): ?string + public function readLine(string $prefix = ''): ?string { $input = Console::getInput(); @@ -180,11 +206,22 @@ public function readLine(string $prefix = null): ?string $output->writeAll($prefix); while (true) { - @stream_select($read, $write, $except, 30, 0); - + $select = @stream_select($read, $write, $except, + (int) $this->selectTimeout, + (int) (($this->selectTimeout - (int) $this->selectTimeout) * 1e6) + ); + if ($select === false) { // if select() is interrupted by signal + $read = []; + } if (empty($read)) { $read = [$input->getStream()->getStream()]; + if ($this->frameCallback !== null) { + if (call_user_func($this->frameCallback, $this)) { + return null; + } + } + continue; } @@ -192,6 +229,12 @@ public function readLine(string $prefix = null): ?string $this->_buffer = $char; $return = $this->_readLine($char); + if ($this->frameCallback !== null) { + if ($ret = call_user_func($this->frameCallback, $this)) { + return null; + } + } + if (0 === ($return & self::STATE_NO_ECHO)) { $output->writeAll($this->_buffer); } @@ -207,7 +250,7 @@ public function readLine(string $prefix = null): ?string /** * Readline core. */ - public function _readLine(string $char): ?string + public function _readLine(string $char) { if (isset($this->_mapping[$char]) && is_callable($this->_mapping[$char])) { @@ -236,9 +279,9 @@ public function _readLine(string $char): ?string $this->getLineCurrent() - 1 ); $this->_buffer = "\033[K" . $tail . str_repeat( - "\033[D", - mb_strlen($tail) - 1 - ); + "\033[D", + mb_strlen($tail) - 1 + ); return static::STATE_CONTINUE; } @@ -279,7 +322,7 @@ public function addMapping(string $key, $mapping): void */ public function addHistory(string $line = null) { - if (empty($line)) { + if ($line === null || $line === '') { return; } @@ -366,8 +409,8 @@ public function insertLine(string $insert) } $this->_line = mb_substr($this->_line, 0, $this->_lineCurrent) . - $insert . - mb_substr($this->_line, $this->_lineCurrent); + $insert . + mb_substr($this->_line, $this->_lineCurrent); $this->_lineLength = mb_strlen($this->_line); $this->_lineCurrent += mb_strlen($insert); @@ -429,7 +472,7 @@ public function getBuffer(): ?string /** * Set an autocompleter. */ - public function setAutocompleter(Autocompleter $autocompleter): ?Autocompleter + public function setAutocompleter(Autocompleter\Autocompleter $autocompleter): ?Autocompleter\Autocompleter { $old = $this->_autocompleter; $this->_autocompleter = $autocompleter; @@ -440,7 +483,7 @@ public function setAutocompleter(Autocompleter $autocompleter): ?Autocompleter /** * Get the autocompleter. */ - public function getAutocompleter(): Autocompleter + public function getAutocompleter(): ?Autocompleter { return $this->_autocompleter; } @@ -497,7 +540,7 @@ public function _bindArrowUp(self $self): int Console\Cursor::clear('↔'); Console::getOutput()->writeAll($self->getPrefix()); } - $self->setBuffer($buffer = $self->previousHistory()); + $self->setBuffer($buffer = (string) $self->previousHistory()); $self->setLine($buffer); return static::STATE_CONTINUE; @@ -515,7 +558,7 @@ public function _bindArrowDown(self $self): int Console::getOutput()->writeAll($self->getPrefix()); } - $self->setBuffer($buffer = $self->nextHistory()); + $self->setBuffer($buffer = (string) $self->nextHistory()); $self->setLine($buffer); return static::STATE_CONTINUE; @@ -535,7 +578,7 @@ public function _bindArrowRight(self $self): int $self->setLineCurrent($self->getLineCurrent() + 1); } - $self->setBuffer(null); + $self->setBuffer(''); return static::STATE_CONTINUE; } @@ -554,7 +597,7 @@ public function _bindArrowLeft(self $self): int $self->setLineCurrent($self->getLineCurrent() - 1); } - $self->setBuffer(null); + $self->setBuffer(''); return static::STATE_CONTINUE; } @@ -565,7 +608,7 @@ public function _bindArrowLeft(self $self): int */ public function _bindBackspace(self $self): int { - $buffer = null; + $buffer = ''; if (0 < $self->getLineCurrent()) { if (0 === (static::STATE_CONTINUE & static::STATE_NO_ECHO)) { @@ -757,16 +800,18 @@ public function _bindTab(self $self): int ); if (0 === $matches) { - return $state; + $word = ''; + } + else { + $word = $words[0][0]; } - $word = $words[0][0]; - - if ('' === trim($word)) { + /*if ('' === trim($word)) { return $state; - } + }*/ $solution = $autocompleter->complete($word); + $length = mb_strlen($word); if (null === $solution) { @@ -893,7 +938,10 @@ public function _bindTab(self $self): int }; while (true) { - @stream_select($read, $write, $except, 30, 0); + @stream_select($read, $write, $except, + (int) $this->selectTimeout, + (int) (($this->selectTimeout - (int) $this->selectTimeout) * 1e6) + ); if (empty($read)) { $read = [$input->getStream()->getStream()]; @@ -983,7 +1031,7 @@ public function _bindTab(self $self): int Console\Cursor::move('←', mb_strlen($tail)); } - // no break + // no break default: $mColumn = -1; $mLine = -1;