diff --git a/examples/commands.php b/examples/commands.php index 5e36a3c..b69ed8c 100644 --- a/examples/commands.php +++ b/examples/commands.php @@ -10,9 +10,11 @@ $shell = new NoccyLabs\React\Shell\Shell(); $commands = new CommandHandler(); $commands->add('help', function ($args, $shell) { $shell->write("This could be usage help :)\n"); + $shell->write("Exit by pressing ^C\n"); }); -$commands->on('notfound', function ($command, $shell) { - $shell->write("Command not found: {$command}. Try help\n"); +$commands->on('command', function ($command, $args, $shell) { + $shell->write("Bad command '{$command}', try help.\n"); + $shell->write("Arguments passed: ".json_encode($args)."\n"); }); $shell->on('prompt', function ($shell) { diff --git a/src/CommandHandler.php b/src/CommandHandler.php index 46e58c8..c0cc527 100644 --- a/src/CommandHandler.php +++ b/src/CommandHandler.php @@ -29,11 +29,11 @@ class CommandHandler implements EventEmitterInterface $command = array_shift($line); if (!array_key_exists($command, $this->commands)) { - $this->emit("notfound", [ $command, $shell ]); + $this->emit("command", [ $command, $line, $shell ]); return; } Loop::futureTick(fn() => call_user_func($this->commands[$command]['handler'], $line, $shell)); } -} \ No newline at end of file +} diff --git a/src/Shell.php b/src/Shell.php index 849b95e..7b2f171 100644 --- a/src/Shell.php +++ b/src/Shell.php @@ -4,6 +4,7 @@ namespace NoccyLabs\React\Shell; use Evenement\EventEmitterInterface; use Evenement\EventEmitterTrait; +use PgSql\Lob; use React\EventLoop\Loop; use React\Stream\ReadableResourceStream; use React\Stream\ReadableStreamInterface; @@ -34,6 +35,8 @@ class Shell implements WritableStreamInterface, EventEmitterInterface private int $promptWidth = 0; + private string $promptStyle = "35;1"; + public function __construct(?ReadableStreamInterface $input=null, ?WritableStreamInterface $output=null) { $this->istream = $input ?? new ReadableResourceStream(STDIN); @@ -97,8 +100,11 @@ class Shell implements WritableStreamInterface, EventEmitterInterface break; case "\e[H": // home + $this->cursorPos = 0; + break; case "\e[F": // end - break; + $this->cursorPos = mb_strlen($this->buffer); + break; case "\e[D": // left if ($this->cursorPos < 1) return; @@ -121,6 +127,13 @@ class Shell implements WritableStreamInterface, EventEmitterInterface return; } } + if ($this->cursorPos < $this->scrollOffset) { + $this->scrollOffset = $this->cursorPos; + } + $availWidth = $this->termWidth - $this->promptWidth; + if ($this->cursorPos - $this->scrollOffset >= $availWidth) { + $this->scrollOffset = abs($availWidth - $this->cursorPos) + 2; + } $this->updatePrompt(); } @@ -129,7 +142,7 @@ class Shell implements WritableStreamInterface, EventEmitterInterface { $this->hidePrompt(); $this->ostream->write($data); - $this->redrawPrompt(); + Loop::futureTick($this->redrawPrompt(...)); return true; } @@ -175,7 +188,7 @@ class Shell implements WritableStreamInterface, EventEmitterInterface $pos = $this->promptWidth + $this->cursorPos - $this->scrollOffset + 1; - $obuf = "\r" . "\e[1m" . $this->prompt . "\e[22m"; + $obuf = "\r" . "\e[{$this->promptStyle}m" . $this->prompt . "\e[0m"; $ostr = mb_substr($this->buffer, $this->scrollOffset) . " "; $ostr = mb_substr($ostr, 0, $this->termWidth - $this->promptWidth); $obuf .= $ostr . "\e[K\e[" . $pos . "G";