Skip to content

Commit

Permalink
Merge branch 'main' into kodie/add-custom-help-screens
Browse files Browse the repository at this point in the history
  • Loading branch information
adhocore authored Nov 26, 2024
2 parents 9c959f6 + 2bd83f4 commit 9bcdf51
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 38 deletions.
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,41 @@ Ahc\Cli\Output\Color::style('mystyle', [
echo $color->mystyle('My text');
```

#### Built-in styles

There are a number of pre-defined built-in styles that allows you granular customization to different output conditions such as help and prompts:

- answer
- choice
- comment
- error
- help_category
- help_description_even
- help_description_odd
- help_example
- help_footer
- help_group
- help_header
- help_item_even
- help_item_odd
- help_summary
- help_text
- info
- logo
- ok
- question
- version
- warn

Overriding a built-in style works the same way as defining a new style:

```php
Ahc\Cli\Output\Color::style('error', [
'fg' => Ahc\Cli\Output\Color::RED,
'bold' => 1,
]);
```

### Cursor

Move cursor around, erase line up or down, clear screen.
Expand Down
2 changes: 1 addition & 1 deletion src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ public function showDefaultHelp(): mixed
$footer = 'Run `<command> --help` for specific help';

if ($this->logo) {
$writer->write($this->logo, true);
$writer->logo($this->logo, true);
}

$this->outputHelper()->showCommandsHelp($this->commands(), $header, $footer);
Expand Down
25 changes: 15 additions & 10 deletions src/Helper/OutputHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,13 @@ public function showCommandsHelp(array $commands, string $header = '', string $f
protected function showHelp(string $for, array $items, string $header = '', string $footer = ''): void
{
if ($header) {
$this->writer->bold($header, true);
$this->writer->help_header($header, true);
}

$this->writer->eol()->boldGreen($for . ':', true);
$this->writer->eol()->help_category($for . ':', true);

if (empty($items)) {
$this->writer->bold(' (n/a)', true);
$this->writer->help_text(' (n/a)', true);

return;
}
Expand All @@ -197,20 +197,25 @@ protected function showHelp(string $for, array $items, string $header = '', stri
$group = $lastGroup = null;

$withDefault = $for === 'Options' || $for === 'Arguments';
foreach ($this->sortItems($items, $padLen, $for) as $item) {
foreach (array_values($this->sortItems($items, $padLen, $for)) as $idx => $item) {
$name = $this->getName($item);
if ($for === 'Commands' && $lastGroup !== $group = $item->group()) {
$this->writer->boldYellow($group ?: '*', true);
$this->writer->help_group($group ?: '*', true);
$lastGroup = $group;
}
$desc = str_replace(["\r\n", "\n"], str_pad("\n", $padLen + $space + 3), $item->desc($withDefault));

$this->writer->bold(' ' . str_pad($name, $padLen + $space));
$this->writer->comment($desc, true);
if ($idx % 2 == 0) {
$this->writer->help_item_even(' ' . str_pad($name, $padLen + $space));
$this->writer->help_description_even($desc, true);
} else {
$this->writer->help_item_odd(' ' . str_pad($name, $padLen + $space));
$this->writer->help_description_odd($desc, true);
}
}

if ($footer) {
$this->writer->eol()->yellow($footer, true);
$this->writer->eol()->help_footer($footer, true);
}
}

Expand All @@ -224,7 +229,7 @@ public function showUsage(string $usage): self
$usage = str_replace('$0', $_SERVER['argv'][0] ?? '[cmd]', $usage);

if (!str_contains($usage, ' ## ')) {
$this->writer->eol()->boldGreen('Usage Examples:', true)->colors($usage)->eol();
$this->writer->eol()->help_category('Usage Examples:', true)->colors($usage)->eol();

return $this;
}
Expand All @@ -241,7 +246,7 @@ public function showUsage(string $usage): self
return str_pad('# ', $maxlen - array_shift($lines), ' ', STR_PAD_LEFT);
}, $usage);

$this->writer->eol()->boldGreen('Usage Examples:', true)->colors($usage)->eol();
$this->writer->eol()->help_category('Usage Examples:', true)->colors($usage)->eol();

return $this;
}
Expand Down
30 changes: 23 additions & 7 deletions src/IO/Interactor.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
*
* @link https://github.com/adhocore/cli
*
* @method Writer answer($text, $eol = false)
* @method Writer bgBlack($text, $eol = false)
* @method Writer bgBlue($text, $eol = false)
* @method Writer bgCyan($text, $eol = false)
Expand Down Expand Up @@ -127,6 +128,7 @@
* @method Writer boldYellowBgPurple($text, $eol = false)
* @method Writer boldYellowBgRed($text, $eol = false)
* @method Writer boldYellowBgWhite($text, $eol = false)
* @method Writer choice($text, $eol = false)
* @method Writer colors($text)
* @method Writer comment($text, $eol = false)
* @method Writer cyan($text, $eol = false)
Expand All @@ -147,7 +149,19 @@
* @method Writer greenBgRed($text, $eol = false)
* @method Writer greenBgWhite($text, $eol = false)
* @method Writer greenBgYellow($text, $eol = false)
* @method Writer help_category($text, $eol = false)
* @method Writer help_description_even($text, $eol = false)
* @method Writer help_description_odd($text, $eol = false)
* @method Writer help_example($text, $eol = false)
* @method Writer help_footer($text, $eol = false)
* @method Writer help_group($text, $eol = false)
* @method Writer help_header($text, $eol = false)
* @method Writer help_item_even($text, $eol = false)
* @method Writer help_item_odd($text, $eol = false)
* @method Writer help_summary($text, $eol = false)
* @method Writer help_text($text, $eol = false)
* @method Writer info($text, $eol = false)
* @method Writer logo($text, $eol = false)
* @method Writer ok($text, $eol = false)
* @method Writer purple($text, $eol = false)
* @method Writer purpleBgBlack($text, $eol = false)
Expand All @@ -157,6 +171,7 @@
* @method Writer purpleBgRed($text, $eol = false)
* @method Writer purpleBgWhite($text, $eol = false)
* @method Writer purpleBgYellow($text, $eol = false)
* @method Writer question($text, $eol = false)
* @method Writer red($text, $eol = false)
* @method Writer redBgBlack($text, $eol = false)
* @method Writer redBgBlue($text, $eol = false)
Expand All @@ -166,6 +181,7 @@
* @method Writer redBgWhite($text, $eol = false)
* @method Writer redBgYellow($text, $eol = false)
* @method Writer table(array $rows, array $styles = [])
* @method Writer version($text, $eol = false)
* @method Writer warn($text, $eol = false)
* @method Writer white($text, $eol = false)
* @method Writer yellow($text, $eol = false)
Expand Down Expand Up @@ -241,7 +257,7 @@ public function confirm(string $text, string $default = 'y'): bool
*/
public function choice(string $text, array $choices, $default = null, bool $case = false): mixed
{
$this->writer->yellow($text);
$this->writer->question($text);

$this->listOptions($choices, $default, false);

Expand All @@ -262,7 +278,7 @@ public function choice(string $text, array $choices, $default = null, bool $case
*/
public function choices(string $text, array $choices, $default = null, bool $case = false): mixed
{
$this->writer->yellow($text);
$this->writer->question($text);

$this->listOptions($choices, $default, true);

Expand Down Expand Up @@ -300,7 +316,7 @@ public function prompt(string $text, $default = null, ?callable $fn = null, int
$hidden = func_get_args()[4] ?? false;
$readFn = ['read', 'readHidden'][(int) $hidden];

$this->writer->yellow($text)->comment(null !== $default ? " [$default]: " : ': ');
$this->writer->question($text)->answer(null !== $default ? " [$default]: " : ': ');

try {
$input = $this->reader->{$readFn}($default, $fn);
Expand All @@ -310,7 +326,7 @@ public function prompt(string $text, $default = null, ?callable $fn = null, int
}

if ($retry > 0 && $input === '') {
$this->writer->bgRed($error, true);
$this->writer->error($error, true);

return $this->prompt($text, $default, $fn, $retry - 1, $hidden);
}
Expand Down Expand Up @@ -351,12 +367,12 @@ protected function listOptions(array $choices, $default = null, bool $multi = fa
$maxLen = max(array_map('strlen', array_keys($choices)));

foreach ($choices as $choice => $desc) {
$this->writer->eol()->cyan(str_pad(" [$choice]", $maxLen + 6))->comment($desc);
$this->writer->eol()->choice(str_pad(" [$choice]", $maxLen + 6))->answer($desc);
}

$label = $multi ? 'Choices (comma separated)' : 'Choice';

$this->writer->eol()->yellow($label);
$this->writer->eol()->question($label);

return $this->promptOptions(array_keys($choices), $default);
}
Expand All @@ -369,7 +385,7 @@ protected function promptOptions(array $choices, mixed $default): self
$options = '';

foreach ($choices as $choice) {
$style = in_array($choice, (array) $default) ? 'boldCyan' : 'cyan';
$style = in_array($choice, (array) $default) ? 'boldChoice' : 'choice';
$options .= "/<$style>$choice</end>";
}

Expand Down
8 changes: 4 additions & 4 deletions src/Input/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,9 @@ public function showDefaultHelp(): mixed
$io = $this->io();
$helper = new OutputHelper($io->writer());

$io->bold("Command {$this->_name}, version {$this->_version}", true)->eol();
$io->comment($this->_desc, true)->eol();
$io->bold('Usage: ')->yellow("{$this->_name} [OPTIONS...] [ARGUMENTS...]", true);
$io->help_header("Command {$this->_name}, version {$this->_version}", true)->eol();
$io->help_summary($this->_desc, true)->eol();
$io->help_text('Usage: ')->help_example("{$this->_name} [OPTIONS...] [ARGUMENTS...]", true);

$helper
->showArgumentsHelp($this->allArguments())
Expand All @@ -353,7 +353,7 @@ public function showDefaultHelp(): mixed
*/
public function showVersion(): mixed
{
$this->writer()->bold($this->_version, true);
$this->writer()->version($this->_version, true);

return $this->emit('_exit', 0);
}
Expand Down
45 changes: 36 additions & 9 deletions src/Output/Color.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,46 +53,68 @@ class Color
protected string $format = "\033[:mod:;:fg:;:bg:m:txt:\033[0m";

/** @var array Custom styles */
protected static array $styles = [];
protected static array $styles = [
'answer' => ['fg' => 37, 'mod' => 2],
'choice' => ['fg' => 36],
'comment' => ['fg' => 37, 'mod' => 2],
'error' => ['fg' => 31],
'help_category' => ['fg' => 32, 'mod' => 1],
'help_description_even' => ['fg' => 37, 'mod' => 2],
'help_description_odd' => ['fg' => 37, 'mod' => 2],
'help_example' => ['fg' => 33],
'help_footer' => ['fg' => 33],
'help_group' => ['fg' => 33, 'mod' => 1],
'help_header' => ['fg' => 37, 'mod' => 1],
'help_item_even' => ['fg' => 37, 'mod' => 1],
'help_item_odd' => ['fg' => 37, 'mod' => 1],
'help_summary' => ['fg' => 37, 'mod' => 2],
'help_text' => ['fg' => 37, 'mod' => 1],
'info' => ['fg' => 34],
'logo' => ['fg' => 37],
'ok' => ['fg' => 32],
'question' => ['fg' => 33],
'version' => ['fg' => 37, 'mod' => 1],
'warn' => ['fg' => 33],
];

/**
* Returns a line formatted as comment.
*/
public function comment(string $text, array $style = []): string
{
return $this->line($text, ['mod' => 2] + $style);
return $this->line($text, static::$styles['comment'] + $style);
}

/**
* Returns a line formatted as comment.
*/
public function error(string $text, array $style = []): string
{
return $this->line($text, ['fg' => static::RED] + $style);
return $this->line($text, static::$styles['error'] + $style);
}

/**
* Returns a line formatted as ok msg.
*/
public function ok(string $text, array $style = []): string
{
return $this->line($text, ['fg' => static::GREEN] + $style);
return $this->line($text, static::$styles['ok'] + $style);
}

/**
* Returns a line formatted as warning.
*/
public function warn(string $text, array $style = []): string
{
return $this->line($text, ['fg' => static::YELLOW] + $style);
return $this->line($text, static::$styles['warn'] + $style);
}

/**
* Returns a line formatted as info.
*/
public function info(string $text, array $style = []): string
{
return $this->line($text, ['fg' => static::BLUE] + $style);
return $this->line($text, static::$styles['info'] + $style);
}

/**
Expand Down Expand Up @@ -157,8 +179,9 @@ public static function style(string $name, array $style): void
throw new InvalidArgumentException('Trying to set empty or invalid style');
}

if (isset(static::$styles[$name]) || method_exists(static::class, $name)) {
throw new InvalidArgumentException('Trying to define existing style');
$invisible = (isset($style['bg']) && isset($style['fg']) && $style['bg'] === $style['fg']);
if ($invisible && method_exists(static::class, $name)) {
throw new InvalidArgumentException('Built-in styles cannot be invisible');
}

static::$styles[$name] = $style;
Expand All @@ -181,7 +204,7 @@ public function __call(string $name, array $arguments): string
[$name, $text, $style] = $this->parseCall($name, $arguments);

if (isset(static::$styles[$name])) {
return $this->line($text, $style + static::$styles[$name]);
return $this->line($text, static::$styles[$name] + $style);
}

if (defined($color = static::class . '::' . strtoupper($name))) {
Expand Down Expand Up @@ -212,6 +235,10 @@ protected function parseCall(string $name, array $arguments): array
}
}

if (isset(static::$styles[strtolower($name)])) {
$name = strtolower($name);
}

if (!preg_match_all('/([b|B|f|F]g)?([A-Z][a-z]+)([^A-Z])?/', $name, $matches)) {
return [lcfirst($name) ?: 'line', $text, $style];
}
Expand Down
Loading

0 comments on commit 9bcdf51

Please sign in to comment.