Initial commit
This commit is contained in:
14
lib/AutoComplete/Hinter.php
Normal file
14
lib/AutoComplete/Hinter.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace NoccyLabs\Shell\AutoComplete;
|
||||
|
||||
class Hinter implement HinterInterface
|
||||
{
|
||||
public function getHints()
|
||||
{
|
||||
}
|
||||
|
||||
public function addHinter(HinterInterface $hinter)
|
||||
{
|
||||
}
|
||||
}
|
7
lib/AutoComplete/HinterInterface.php
Normal file
7
lib/AutoComplete/HinterInterface.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace NoccyLabs\Shell\AutoComplete;
|
||||
|
||||
interface HinterInterface
|
||||
{
|
||||
}
|
136
lib/LineRead.php
Normal file
136
lib/LineRead.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace NoccyLabs\Shell;
|
||||
|
||||
class LineRead
|
||||
{
|
||||
|
||||
const STATE_IDLE = 0;
|
||||
const STATE_READ = 1;
|
||||
|
||||
protected $state = self::STATE_IDLE;
|
||||
|
||||
protected $prompt = "cmd:>";
|
||||
|
||||
protected $buffer = null;
|
||||
|
||||
protected $history = [];
|
||||
|
||||
protected $posHistory = 0;
|
||||
|
||||
protected $posCursor = 0;
|
||||
|
||||
protected $posScroll = 0;
|
||||
|
||||
protected $termWidth = 0;
|
||||
|
||||
protected $sttyOld;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
stream_set_blocking(STDIN, false);
|
||||
$this->sttyOld = exec('stty -g');
|
||||
exec('stty raw -echo isig');
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
exec('stty {$this->sttyOld}');
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
if ($this->state == self::STATE_IDLE) {
|
||||
$this->state = self::STATE_READ;
|
||||
$this->redraw();
|
||||
}
|
||||
$buffer = $this->handleInput();
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
public function erase()
|
||||
{
|
||||
fprintf(STDOUT, "\r\e[0m\e[2K");
|
||||
}
|
||||
|
||||
public function redraw()
|
||||
{
|
||||
$prompt = $this->prompt;
|
||||
$buffer = $this->buffer;
|
||||
$cursor = strlen($this->prompt) + 2 + $this->posCursor - $this->posScroll;
|
||||
fprintf(STDOUT, "\r\e[2K\e[36;1;4m%s\e[37;24m %s\e[%dG\e[0m", $prompt, $buffer, $cursor);
|
||||
}
|
||||
|
||||
protected function handleInput()
|
||||
{
|
||||
static $keyBuffer;
|
||||
$readBuf = fread(STDIN, 32);
|
||||
$keyBuffer .= $readBuf;
|
||||
$update = false;
|
||||
$returnBuffer = null;
|
||||
while (strlen($keyBuffer)>0) {
|
||||
if ($keyBuffer[0] == "\e") {
|
||||
if ($keyBuffer[1] == "[") {
|
||||
$ctrlChar = substr($keyBuffer, 0,3);
|
||||
$keyBuffer = substr($keyBuffer, 3);
|
||||
$this->parseControlCode($ctrlChar);
|
||||
}
|
||||
} else {
|
||||
$keyChar = $keyBuffer[0];
|
||||
$keyCode = ord($keyChar);
|
||||
$keyBuffer = substr($keyBuffer, 1);
|
||||
if (($keyCode >= 32) && ($keyCode < 127)) {
|
||||
$edBuffer = substr($this->buffer, 0, $this->posCursor) . $keyChar . substr($this->buffer, $this->posCursor);
|
||||
$this->posCursor++;
|
||||
$this->buffer = $edBuffer;
|
||||
$update = true;
|
||||
} elseif ($keyCode == 127) {
|
||||
if ($this->posCursor > 0) {
|
||||
$this->posCursor--;
|
||||
$edBuffer = substr($this->buffer, 0, $this->posCursor) . substr($this->buffer, $this->posCursor+1);
|
||||
$this->buffer = $edBuffer;
|
||||
$update = true;
|
||||
}
|
||||
} elseif ($keyCode == 13) {
|
||||
$returnBuffer = $this->buffer;
|
||||
$this->buffer = null;
|
||||
$this->posCursor = 0;
|
||||
printf("\n");
|
||||
$this->state = self::STATE_IDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($update) {
|
||||
$this->redraw();
|
||||
}
|
||||
return $returnBuffer;
|
||||
}
|
||||
|
||||
protected function parseControlCode($code)
|
||||
{
|
||||
switch ($code) {
|
||||
case "\e[D":
|
||||
$this->posCursor = max($this->posCursor-1,0);
|
||||
$this->redraw();
|
||||
break;
|
||||
case "\e[C":
|
||||
$this->posCursor = min($this->posCursor+1,strlen($this->buffer));
|
||||
$this->redraw();
|
||||
break;
|
||||
case "\e[H":
|
||||
$this->posCursor = 0;
|
||||
$this->redraw();
|
||||
break;
|
||||
case "\e[F":
|
||||
$this->posCursor = strlen($this->buffer);
|
||||
$this->redraw();
|
||||
break;
|
||||
case "\e[A": // up
|
||||
case "\e[B": // down
|
||||
break;
|
||||
default:
|
||||
fprintf(STDERR, "\n%s\n", substr($code,1));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
87
lib/Shell.php
Normal file
87
lib/Shell.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace NoccyLabs\Shell;
|
||||
|
||||
use NoccyLabs\Shell\LineRead;
|
||||
|
||||
abstract class Shell
|
||||
{
|
||||
public function __construct(array $config=[])
|
||||
{
|
||||
$this->configure($config);
|
||||
}
|
||||
|
||||
abstract protected function configure(array $config);
|
||||
|
||||
public function addCommand($command, callable $handler)
|
||||
{
|
||||
$this->commands[$command] = $handler;
|
||||
}
|
||||
|
||||
public function execute($command)
|
||||
{
|
||||
$buffer = str_getcsv($command, " ", "\"", "\\");
|
||||
|
||||
if (count($buffer)>0) {
|
||||
$this->executeBuffer($buffer);
|
||||
}
|
||||
}
|
||||
|
||||
protected function executeBuffer(array $buffer)
|
||||
{
|
||||
$commandName = array_shift($buffer);
|
||||
if (array_key_exists($commandName, $this->commands)) {
|
||||
$command = $this->commands[$commandName];
|
||||
call_user_func_array($command,$buffer);
|
||||
return;
|
||||
}
|
||||
$this->writeln("Bad command: ".$commandName);
|
||||
}
|
||||
|
||||
public function writeln($output)
|
||||
{
|
||||
echo "\r\e[K\e[0m".$output."\n";
|
||||
}
|
||||
|
||||
protected function onUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
public function run(callable $updateFunc=null)
|
||||
{
|
||||
$lineRead = new LineRead();
|
||||
|
||||
$this->running = true;
|
||||
|
||||
|
||||
|
||||
do {
|
||||
$buffer = $lineRead->update();
|
||||
ob_start();
|
||||
if (is_callable($updateFunc)) {
|
||||
call_user_func($updateFunc);
|
||||
}
|
||||
$this->onUpdate();
|
||||
$buf = ob_get_contents();
|
||||
ob_end_clean();
|
||||
if ($buf) {
|
||||
$lineRead->erase();
|
||||
echo rtrim($buf)."\n";
|
||||
$lineRead->redraw();
|
||||
}
|
||||
//$buffer = readline($this->getPrompt());
|
||||
if ($buffer === null) {
|
||||
usleep(10000);
|
||||
continue;
|
||||
} else {
|
||||
$this->execute($buffer);
|
||||
}
|
||||
} while ($this->running);
|
||||
|
||||
}
|
||||
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user