2 Commits

Author SHA1 Message Date
ba27d840ea Styling, abbreviated commands 2024-02-27 22:07:38 +01:00
17064e7e43 Fixed line scrolling, prompt style 2024-02-27 15:03:40 +01:00
3 changed files with 41 additions and 9 deletions

View File

@ -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) {

View File

@ -13,6 +13,8 @@ class CommandHandler implements EventEmitterInterface
private array $commands = [];
private bool $allowAbbreviatedCommands = true;
public function add(string $command, callable $handler, array $signature=[]): self
{
$this->commands[$command] = [ 'handler' => $handler, 'signature' => $signature ];
@ -28,8 +30,21 @@ class CommandHandler implements EventEmitterInterface
{
$command = array_shift($line);
if ($this->allowAbbreviatedCommands) {
$candidates = array_filter(
array_keys($this->commands),
fn($c)=>str_starts_with($c,$command)
);
if (count($candidates)>2) {
$this->emit("candidates", [ $candidates, $shell ]);
return;
}
if (count($candidates)==1) {
$command = array_shift($candidates);
}
}
if (!array_key_exists($command, $this->commands)) {
$this->emit("notfound", [ $command, $shell ]);
$this->emit("command", [ $command, $line, $shell ]);
return;
}

View File

@ -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,10 @@ class Shell implements WritableStreamInterface, EventEmitterInterface
private int $promptWidth = 0;
private string $promptStyle = "36;1";
private string $inputStyle = "36";
public function __construct(?ReadableStreamInterface $input=null, ?WritableStreamInterface $output=null)
{
$this->istream = $input ?? new ReadableResourceStream(STDIN);
@ -97,8 +102,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 +129,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 +144,7 @@ class Shell implements WritableStreamInterface, EventEmitterInterface
{
$this->hidePrompt();
$this->ostream->write($data);
$this->redrawPrompt();
Loop::futureTick($this->redrawPrompt(...));
return true;
}
@ -175,10 +190,10 @@ 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";
$obuf .= "\e[{$this->inputStyle}m" . $ostr . "\e[K\e[0m\e[" . $pos . "G";
$this->ostream->write($obuf);
}