Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,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 @@ -353,7 +353,7 @@ public function showHelp(): 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 @@ -298,9 +298,9 @@ public function showHelp(): 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 @@ -318,7 +318,7 @@ public function showHelp(): 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
Loading