/** * Tab binding. * * @access public * @param \Hoa\Console\Readline $self Self. * @return int */ public function _bindTab(Readline $self) { $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', $line, $words, PREG_OFFSET_CAPTURE); if (0 === $matches) { return $state; } for ($i = 0, $max = count($words[0]); $i < $max && $current > $words[0][$i][1]; ++$i) { } $word = $words[0][$i - 1]; if ('' === trim($word[0])) { return $state; } $prefix = mb_substr($word[0], 0, $current - $word[1]); $solution = $autocompleter->complete($prefix); $length = mb_strlen($prefix); if (null === $solution) { return $state; } if (is_array($solution)) { $_solution = $solution; $count = count($_solution) - 1; $cWidth = 0; $window = \Hoa\Console\Window::getSize(); $wWidth = $window['x']; $cursor = \Hoa\Console\Cursor::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) { \Hoa\Console\Window::scroll('↑', $mLines); \Hoa\Console\Cursor::move('↑', $mLines); } \Hoa\Console\Cursor::save(); \Hoa\Console\Cursor::hide(); \Hoa\Console\Cursor::move('↓ LEFT'); \Hoa\Console\Cursor::clear('↓'); foreach ($_solution as $j => $s) { echo "[0m", $s, "[0m"; if ($i++ < $mColumns) { echo ' '; } else { $i = 0; if (isset($_solution[$j + 1])) { echo "\n"; } } } \Hoa\Console\Cursor::restore(); \Hoa\Console\Cursor::show(); ++$mColumns; $read = array(STDIN); $mColumn = -1; $mLine = -1; $coord = -1; $unselect = function () use(&$mColumn, &$mLine, &$coord, &$_solution, &$cWidth) { \Hoa\Console\Cursor::save(); \Hoa\Console\Cursor::hide(); \Hoa\Console\Cursor::move('↓ LEFT'); \Hoa\Console\Cursor::move('→', $mColumn * ($cWidth + 2)); \Hoa\Console\Cursor::move('↓', $mLine); echo "[0m" . $_solution[$coord] . "[0m"; \Hoa\Console\Cursor::restore(); \Hoa\Console\Cursor::show(); return; }; $select = function () use(&$mColumn, &$mLine, &$coord, &$_solution, &$cWidth) { \Hoa\Console\Cursor::save(); \Hoa\Console\Cursor::hide(); \Hoa\Console\Cursor::move('↓ LEFT'); \Hoa\Console\Cursor::move('→', $mColumn * ($cWidth + 2)); \Hoa\Console\Cursor::move('↓', $mLine); echo "[7m" . $_solution[$coord] . "[0m"; \Hoa\Console\Cursor::restore(); \Hoa\Console\Cursor::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 = array(STDIN); continue; } switch ($char = $self->_read()) { case "[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 "[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 "[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 "[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])); \Hoa\Console\Cursor::move('←', $length); echo $solution[$coord]; \Hoa\Console\Cursor::clear('→'); echo $tail; \Hoa\Console\Cursor::move('←', mb_strlen($tail)); } default: $mColumn = -1; $mLine = -1; $coord = -1; \Hoa\Console\Cursor::save(); \Hoa\Console\Cursor::move('↓ LEFT'); \Hoa\Console\Cursor::clear('↓'); \Hoa\Console\Cursor::restore(); if ("" !== $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)); \Hoa\Console\Cursor::move('←', $length); echo $solution; \Hoa\Console\Cursor::clear('→'); echo $tail; \Hoa\Console\Cursor::move('←', mb_strlen($tail)); return $state; }