ok
Direktori : /proc/thread-self/root/home2/selectio/www/3-idiots/vendor/psy/psysh/src/Readline/Hoa/ |
Current File : //proc/thread-self/root/home2/selectio/www/3-idiots/vendor/psy/psysh/src/Readline/Hoa/Readline.php |
<?php /** * Hoa * * * @license * * New BSD License * * Copyright © 2007-2017, Hoa community. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the Hoa nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ namespace Psy\Readline\Hoa; /** * Class \Hoa\Console\Readline. * * Read, edit, bind… a line from the input. */ class Readline { /** * State: continue to read. */ const STATE_CONTINUE = 1; /** * State: stop to read. */ const STATE_BREAK = 2; /** * State: no output the current buffer. */ const STATE_NO_ECHO = 4; /** * Current editing line. */ protected $_line = null; /** * Current editing line seek. */ protected $_lineCurrent = 0; /** * Current editing line length. */ protected $_lineLength = 0; /** * Current buffer (most of the time, a char). */ protected $_buffer = null; /** * Mapping. */ protected $_mapping = []; /** * History. */ protected $_history = []; /** * History current position. */ protected $_historyCurrent = 0; /** * History size. */ protected $_historySize = 0; /** * Prefix. */ protected $_prefix = null; /** * Autocompleter. */ protected $_autocompleter = null; /** * Initialize the readline editor. */ public function __construct() { if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) { return; } $this->_mapping["\033[A"] = [$this, '_bindArrowUp']; $this->_mapping["\033[B"] = [$this, '_bindArrowDown']; $this->_mapping["\033[C"] = [$this, '_bindArrowRight']; $this->_mapping["\033[D"] = [$this, '_bindArrowLeft']; $this->_mapping["\001"] = [$this, '_bindControlA']; $this->_mapping["\002"] = [$this, '_bindControlB']; $this->_mapping["\005"] = [$this, '_bindControlE']; $this->_mapping["\006"] = [$this, '_bindControlF']; $this->_mapping["\010"] = $this->_mapping["\177"] = [$this, '_bindBackspace']; $this->_mapping["\027"] = [$this, '_bindControlW']; $this->_mapping["\n"] = [$this, '_bindNewline']; $this->_mapping["\t"] = [$this, '_bindTab']; return; } /** * Read a line from the input. */ public function readLine(string $prefix = null) { $input = Console::getInput(); if (true === $input->eof()) { return false; } $direct = Console::isDirect($input->getStream()->getStream()); $output = Console::getOutput(); if (false === $direct || \defined('PHP_WINDOWS_VERSION_PLATFORM')) { $out = $input->readLine(); if (false === $out) { return false; } $out = \substr($out, 0, -1); if (true === $direct) { $output->writeAll($prefix); } else { $output->writeAll($prefix.$out."\n"); } return $out; } $this->resetLine(); $this->setPrefix($prefix); $read = [$input->getStream()->getStream()]; $write = $except = []; $output->writeAll($prefix); while (true) { @\stream_select($read, $write, $except, 30, 0); if (empty($read)) { $read = [$input->getStream()->getStream()]; continue; } $char = $this->_read(); $this->_buffer = $char; $return = $this->_readLine($char); if (0 === ($return & self::STATE_NO_ECHO)) { $output->writeAll($this->_buffer); } if (0 !== ($return & self::STATE_BREAK)) { break; } } return $this->getLine(); } /** * Readline core. */ public function _readLine(string $char) { if (isset($this->_mapping[$char]) && \is_callable($this->_mapping[$char])) { $mapping = $this->_mapping[$char]; return $mapping($this); } if (isset($this->_mapping[$char])) { $this->_buffer = $this->_mapping[$char]; } elseif (false === Ustring::isCharPrintable($char)) { ConsoleCursor::bip(); return static::STATE_CONTINUE | static::STATE_NO_ECHO; } if ($this->getLineLength() === $this->getLineCurrent()) { $this->appendLine($this->_buffer); return static::STATE_CONTINUE; } $this->insertLine($this->_buffer); $tail = \mb_substr( $this->getLine(), $this->getLineCurrent() - 1 ); $this->_buffer = "\033[K".$tail.\str_repeat( "\033[D", \mb_strlen($tail) - 1 ); return static::STATE_CONTINUE; } /** * Add mappings. */ public function addMappings(array $mappings) { foreach ($mappings as $key => $mapping) { $this->addMapping($key, $mapping); } } /** * Add a mapping. * Supported key: * • \e[… for \033[…; * • \C-… for Ctrl-…; * • abc for a simple mapping. * A mapping is a callable that has only one parameter of type * Hoa\Console\Readline and that returns a self::STATE_* constant. */ public function addMapping(string $key, $mapping) { if ('\e[' === \substr($key, 0, 3)) { $this->_mapping["\033[".\substr($key, 3)] = $mapping; } elseif ('\C-' === \substr($key, 0, 3)) { $_key = \ord(\strtolower(\substr($key, 3))) - 96; $this->_mapping[\chr($_key)] = $mapping; } else { $this->_mapping[$key] = $mapping; } } /** * Add an entry in the history. */ public function addHistory(string $line = null) { if (empty($line)) { return; } $this->_history[] = $line; $this->_historyCurrent = $this->_historySize++; } /** * Clear history. */ public function clearHistory() { unset($this->_history); $this->_history = []; $this->_historyCurrent = 0; $this->_historySize = 1; } /** * Get an entry in the history. */ public function getHistory(int $i = null) { if (null === $i) { $i = $this->_historyCurrent; } if (!isset($this->_history[$i])) { return null; } return $this->_history[$i]; } /** * Go backward in the history. */ public function previousHistory() { if (0 >= $this->_historyCurrent) { return $this->getHistory(0); } return $this->getHistory($this->_historyCurrent--); } /** * Go forward in the history. */ public function nextHistory() { if ($this->_historyCurrent + 1 >= $this->_historySize) { return $this->getLine(); } return $this->getHistory(++$this->_historyCurrent); } /** * Get current line. */ public function getLine() { return $this->_line; } /** * Append to current line. */ public function appendLine(string $append) { $this->_line .= $append; $this->_lineLength = \mb_strlen($this->_line); $this->_lineCurrent = $this->_lineLength; } /** * Insert into current line at the current seek. */ public function insertLine(string $insert) { if ($this->_lineLength === $this->_lineCurrent) { return $this->appendLine($insert); } $this->_line = \mb_substr($this->_line, 0, $this->_lineCurrent). $insert. \mb_substr($this->_line, $this->_lineCurrent); $this->_lineLength = \mb_strlen($this->_line); $this->_lineCurrent += \mb_strlen($insert); return; } /** * Reset current line. */ protected function resetLine() { $this->_line = null; $this->_lineCurrent = 0; $this->_lineLength = 0; } /** * Get current line seek. */ public function getLineCurrent(): int { return $this->_lineCurrent; } /** * Get current line length. * * @return int */ public function getLineLength(): int { return $this->_lineLength; } /** * Set prefix. */ public function setPrefix(string $prefix) { $this->_prefix = $prefix; } /** * Get prefix. */ public function getPrefix() { return $this->_prefix; } /** * Get buffer. Not for user. */ public function getBuffer() { return $this->_buffer; } /** * Set an autocompleter. */ public function setAutocompleter(Autocompleter $autocompleter) { $old = $this->_autocompleter; $this->_autocompleter = $autocompleter; return $old; } /** * Get the autocompleter. * * @return ?Autocompleter */ public function getAutocompleter() { return $this->_autocompleter; } /** * Read on input. Not for user. */ public function _read(int $length = 512): string { return Console::getInput()->read($length); } /** * Set current line. Not for user. */ public function setLine(string $line) { $this->_line = $line; $this->_lineLength = \mb_strlen($this->_line ?: ''); $this->_lineCurrent = $this->_lineLength; } /** * Set current line seek. Not for user. */ public function setLineCurrent(int $current) { $this->_lineCurrent = $current; } /** * Set line length. Not for user. */ public function setLineLength(int $length) { $this->_lineLength = $length; } /** * Set buffer. Not for user. */ public function setBuffer(string $buffer) { $this->_buffer = $buffer; } /** * Up arrow binding. * Go backward in the history. */ public function _bindArrowUp(self $self): int { if (0 === (static::STATE_CONTINUE & static::STATE_NO_ECHO)) { ConsoleCursor::clear('↔'); Console::getOutput()->writeAll($self->getPrefix()); } $buffer = $self->previousHistory() ?? ''; $self->setBuffer($buffer); $self->setLine($buffer); return static::STATE_CONTINUE; } /** * Down arrow binding. * Go forward in the history. */ public function _bindArrowDown(self $self): int { if (0 === (static::STATE_CONTINUE & static::STATE_NO_ECHO)) { ConsoleCursor::clear('↔'); Console::getOutput()->writeAll($self->getPrefix()); } $self->setBuffer($buffer = $self->nextHistory()); $self->setLine($buffer); return static::STATE_CONTINUE; } /** * Right arrow binding. * Move cursor to the right. */ public function _bindArrowRight(self $self): int { if ($self->getLineLength() > $self->getLineCurrent()) { if (0 === (static::STATE_CONTINUE & static::STATE_NO_ECHO)) { ConsoleCursor::move('→'); } $self->setLineCurrent($self->getLineCurrent() + 1); } $self->setBuffer(''); return static::STATE_CONTINUE; } /** * Left arrow binding. * Move cursor to the left. */ public function _bindArrowLeft(self $self): int { if (0 < $self->getLineCurrent()) { if (0 === (static::STATE_CONTINUE & static::STATE_NO_ECHO)) { ConsoleCursor::move('←'); } $self->setLineCurrent($self->getLineCurrent() - 1); } $self->setBuffer(''); return static::STATE_CONTINUE; } /** * Backspace and Control-H binding. * Delete the first character at the right of the cursor. */ public function _bindBackspace(self $self): int { $buffer = ''; if (0 < $self->getLineCurrent()) { if (0 === (static::STATE_CONTINUE & static::STATE_NO_ECHO)) { ConsoleCursor::move('←'); ConsoleCursor::clear('→'); } if ($self->getLineLength() === $current = $self->getLineCurrent()) { $self->setLine(\mb_substr($self->getLine(), 0, -1)); } else { $line = $self->getLine(); $current = $self->getLineCurrent(); $tail = \mb_substr($line, $current); $buffer = $tail.\str_repeat("\033[D", \mb_strlen($tail)); $self->setLine(\mb_substr($line, 0, $current - 1).$tail); $self->setLineCurrent($current - 1); } } $self->setBuffer($buffer); return static::STATE_CONTINUE; } /** * Control-A binding. * Move cursor to beginning of line. */ public function _bindControlA(self $self): int { for ($i = $self->getLineCurrent() - 1; 0 <= $i; --$i) { $self->_bindArrowLeft($self); } return static::STATE_CONTINUE; } /** * Control-B binding. * Move cursor backward one word. */ public function _bindControlB(self $self): int { $current = $self->getLineCurrent(); if (0 === $current) { return static::STATE_CONTINUE; } $words = \preg_split( '#\b#u', $self->getLine(), -1, \PREG_SPLIT_OFFSET_CAPTURE | \PREG_SPLIT_NO_EMPTY ); for ( $i = 0, $max = \count($words) - 1; $i < $max && $words[$i + 1][1] < $current; ++$i ) { } for ($j = $words[$i][1] + 1; $current >= $j; ++$j) { $self->_bindArrowLeft($self); } return static::STATE_CONTINUE; } /** * Control-E binding. * Move cursor to end of line. */ public function _bindControlE(self $self): int { for ( $i = $self->getLineCurrent(), $max = $self->getLineLength(); $i < $max; ++$i ) { $self->_bindArrowRight($self); } return static::STATE_CONTINUE; } /** * Control-F binding. * Move cursor forward one word. */ public function _bindControlF(self $self): int { $current = $self->getLineCurrent(); if ($self->getLineLength() === $current) { return static::STATE_CONTINUE; } $words = \preg_split( '#\b#u', $self->getLine(), -1, \PREG_SPLIT_OFFSET_CAPTURE | \PREG_SPLIT_NO_EMPTY ); for ( $i = 0, $max = \count($words) - 1; $i < $max && $words[$i][1] < $current; ++$i ) { } if (!isset($words[$i + 1])) { $words[$i + 1] = [1 => $self->getLineLength()]; } for ($j = $words[$i + 1][1]; $j > $current; --$j) { $self->_bindArrowRight($self); } return static::STATE_CONTINUE; } /** * Control-W binding. * Delete first backward word. */ public function _bindControlW(self $self): int { $current = $self->getLineCurrent(); if (0 === $current) { return static::STATE_CONTINUE; } $words = \preg_split( '#\b#u', $self->getLine(), -1, \PREG_SPLIT_OFFSET_CAPTURE | \PREG_SPLIT_NO_EMPTY ); for ( $i = 0, $max = \count($words) - 1; $i < $max && $words[$i + 1][1] < $current; ++$i ) { } for ($j = $words[$i][1] + 1; $current >= $j; ++$j) { $self->_bindBackspace($self); } return static::STATE_CONTINUE; } /** * Newline binding. */ public function _bindNewline(self $self): int { $self->addHistory($self->getLine()); return static::STATE_BREAK; } /** * Tab binding. */ public function _bindTab(self $self): int { $output = Console::getOutput(); $autocompleter = $self->getAutocompleter(); $state = static::STATE_CONTINUE | static::STATE_NO_ECHO; if (null === $autocompleter) { return $state; } $current = $self->getLineCurrent(); $line = $self->getLine(); if (0 === $current) { return $state; } $matches = \preg_match_all( '#'.$autocompleter->getWordDefinition().'$#u', \mb_substr($line, 0, $current), $words ); if (0 === $matches) { return $state; } $word = $words[0][0]; if ('' === \trim($word)) { return $state; } $solution = $autocompleter->complete($word); $length = \mb_strlen($word); if (null === $solution) { return $state; } if (\is_array($solution)) { $_solution = $solution; $count = \count($_solution) - 1; $cWidth = 0; $window = ConsoleWindow::getSize(); $wWidth = $window['x']; $cursor = ConsoleCursor::getPosition(); \array_walk($_solution, function (&$value) use (&$cWidth) { $handle = \mb_strlen($value); if ($handle > $cWidth) { $cWidth = $handle; } return; }); \array_walk($_solution, function (&$value) use (&$cWidth) { $handle = \mb_strlen($value); if ($handle >= $cWidth) { return; } $value .= \str_repeat(' ', $cWidth - $handle); return; }); $mColumns = (int) \floor($wWidth / ($cWidth + 2)); $mLines = (int) \ceil(($count + 1) / $mColumns); --$mColumns; $i = 0; if (0 > $window['y'] - $cursor['y'] - $mLines) { ConsoleWindow::scroll('↑', $mLines); ConsoleCursor::move('↑', $mLines); } ConsoleCursor::save(); ConsoleCursor::hide(); ConsoleCursor::move('↓ LEFT'); ConsoleCursor::clear('↓'); foreach ($_solution as $j => $s) { $output->writeAll("\033[0m".$s."\033[0m"); if ($i++ < $mColumns) { $output->writeAll(' '); } else { $i = 0; if (isset($_solution[$j + 1])) { $output->writeAll("\n"); } } } ConsoleCursor::restore(); ConsoleCursor::show(); ++$mColumns; $input = Console::getInput(); $read = [$input->getStream()->getStream()]; $write = $except = []; $mColumn = -1; $mLine = -1; $coord = -1; $unselect = function () use ( &$mColumn, &$mLine, &$coord, &$_solution, &$cWidth, $output ) { ConsoleCursor::save(); ConsoleCursor::hide(); ConsoleCursor::move('↓ LEFT'); ConsoleCursor::move('→', $mColumn * ($cWidth + 2)); ConsoleCursor::move('↓', $mLine); $output->writeAll("\033[0m".$_solution[$coord]."\033[0m"); ConsoleCursor::restore(); ConsoleCursor::show(); return; }; $select = function () use ( &$mColumn, &$mLine, &$coord, &$_solution, &$cWidth, $output ) { ConsoleCursor::save(); ConsoleCursor::hide(); ConsoleCursor::move('↓ LEFT'); ConsoleCursor::move('→', $mColumn * ($cWidth + 2)); ConsoleCursor::move('↓', $mLine); $output->writeAll("\033[7m".$_solution[$coord]."\033[0m"); ConsoleCursor::restore(); ConsoleCursor::show(); return; }; $init = function () use ( &$mColumn, &$mLine, &$coord, &$select ) { $mColumn = 0; $mLine = 0; $coord = 0; $select(); return; }; while (true) { @\stream_select($read, $write, $except, 30, 0); if (empty($read)) { $read = [$input->getStream()->getStream()]; continue; } switch ($char = $self->_read()) { case "\033[A": if (-1 === $mColumn && -1 === $mLine) { $init(); break; } $unselect(); $coord = \max(0, $coord - $mColumns); $mLine = (int) \floor($coord / $mColumns); $mColumn = $coord % $mColumns; $select(); break; case "\033[B": if (-1 === $mColumn && -1 === $mLine) { $init(); break; } $unselect(); $coord = \min($count, $coord + $mColumns); $mLine = (int) \floor($coord / $mColumns); $mColumn = $coord % $mColumns; $select(); break; case "\t": case "\033[C": if (-1 === $mColumn && -1 === $mLine) { $init(); break; } $unselect(); $coord = \min($count, $coord + 1); $mLine = (int) \floor($coord / $mColumns); $mColumn = $coord % $mColumns; $select(); break; case "\033[D": if (-1 === $mColumn && -1 === $mLine) { $init(); break; } $unselect(); $coord = \max(0, $coord - 1); $mLine = (int) \floor($coord / $mColumns); $mColumn = $coord % $mColumns; $select(); break; case "\n": if (-1 !== $mColumn && -1 !== $mLine) { $tail = \mb_substr($line, $current); $current -= $length; $self->setLine( \mb_substr($line, 0, $current). $solution[$coord]. $tail ); $self->setLineCurrent( $current + \mb_strlen($solution[$coord]) ); ConsoleCursor::move('←', $length); $output->writeAll($solution[$coord]); ConsoleCursor::clear('→'); $output->writeAll($tail); ConsoleCursor::move('←', \mb_strlen($tail)); } // no break default: $mColumn = -1; $mLine = -1; $coord = -1; ConsoleCursor::save(); ConsoleCursor::move('↓ LEFT'); ConsoleCursor::clear('↓'); ConsoleCursor::restore(); if ("\033" !== $char && "\n" !== $char) { $self->setBuffer($char); return $self->_readLine($char); } break 2; } } return $state; } $tail = \mb_substr($line, $current); $current -= $length; $self->setLine( \mb_substr($line, 0, $current). $solution. $tail ); $self->setLineCurrent( $current + \mb_strlen($solution) ); ConsoleCursor::move('←', $length); $output->writeAll($solution); ConsoleCursor::clear('→'); $output->writeAll($tail); ConsoleCursor::move('←', \mb_strlen($tail)); return $state; } } /* * Advanced interaction. */ Console::advancedInteraction();