283 lines
7.2 KiB
PHP
283 lines
7.2 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of Psy Shell.
|
|
*
|
|
* (c) 2012-2023 Justin Hileman
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Psy\Output;
|
|
|
|
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
|
|
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
|
|
|
/**
|
|
* An output Theme, which controls prompt strings, formatter styles, and compact output.
|
|
*/
|
|
class Theme
|
|
{
|
|
const MODERN_THEME = []; // Defaults :)
|
|
|
|
const COMPACT_THEME = [
|
|
'compact' => true,
|
|
];
|
|
|
|
const CLASSIC_THEME = [
|
|
'compact' => true,
|
|
|
|
'prompt' => '>>> ',
|
|
'bufferPrompt' => '... ',
|
|
'replayPrompt' => '--> ',
|
|
'returnValue' => '=> ',
|
|
];
|
|
|
|
const DEFAULT_STYLES = [
|
|
'info' => ['white', 'blue', ['bold']],
|
|
'warning' => ['black', 'yellow'],
|
|
'error' => ['white', 'red', ['bold']],
|
|
'whisper' => ['gray'],
|
|
|
|
'aside' => ['blue'],
|
|
'strong' => [null, null, ['bold']],
|
|
'return' => ['cyan'],
|
|
'urgent' => ['red'],
|
|
'hidden' => ['black'],
|
|
|
|
// Visibility
|
|
'public' => [null, null, ['bold']],
|
|
'protected' => ['yellow'],
|
|
'private' => ['red'],
|
|
'global' => ['cyan', null, ['bold']],
|
|
'const' => ['cyan'],
|
|
'class' => ['blue', null, ['underscore']],
|
|
'function' => [null],
|
|
'default' => [null],
|
|
|
|
// Types
|
|
'number' => ['magenta'],
|
|
'integer' => ['magenta'],
|
|
'float' => ['yellow'],
|
|
'string' => ['green'],
|
|
'bool' => ['cyan'],
|
|
'keyword' => ['yellow'],
|
|
'comment' => ['blue'],
|
|
'code_comment' => ['gray'],
|
|
'object' => ['blue'],
|
|
'resource' => ['yellow'],
|
|
|
|
// Code-specific formatting
|
|
'inline_html' => ['cyan'],
|
|
];
|
|
|
|
const ERROR_STYLES = ['info', 'warning', 'error', 'whisper', 'class'];
|
|
|
|
private bool $compact = false;
|
|
|
|
private string $prompt = '> ';
|
|
private string $bufferPrompt = '. ';
|
|
private string $replayPrompt = '- ';
|
|
private string $returnValue = '= ';
|
|
|
|
private string $grayFallback = 'blue';
|
|
|
|
private array $styles = [];
|
|
|
|
/**
|
|
* @param string|array $config theme name or config options
|
|
*/
|
|
public function __construct($config = 'modern')
|
|
{
|
|
if (\is_string($config)) {
|
|
switch ($config) {
|
|
case 'modern':
|
|
$config = static::MODERN_THEME;
|
|
break;
|
|
|
|
case 'compact':
|
|
$config = static::COMPACT_THEME;
|
|
break;
|
|
|
|
case 'classic':
|
|
$config = static::CLASSIC_THEME;
|
|
break;
|
|
|
|
default:
|
|
\trigger_error(\sprintf('Unknown theme: %s', $config), \E_USER_NOTICE);
|
|
$config = static::MODERN_THEME;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!\is_array($config)) {
|
|
throw new \InvalidArgumentException('Invalid theme config');
|
|
}
|
|
|
|
foreach ($config as $name => $value) {
|
|
switch ($name) {
|
|
case 'compact':
|
|
$this->setCompact($value);
|
|
break;
|
|
|
|
case 'prompt':
|
|
$this->setPrompt($value);
|
|
break;
|
|
|
|
case 'bufferPrompt':
|
|
$this->setBufferPrompt($value);
|
|
break;
|
|
|
|
case 'replayPrompt':
|
|
$this->setReplayPrompt($value);
|
|
break;
|
|
|
|
case 'returnValue':
|
|
$this->setReturnValue($value);
|
|
break;
|
|
|
|
case 'grayFallback':
|
|
$this->setGrayFallback($value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
$this->setStyles($config['styles'] ?? []);
|
|
}
|
|
|
|
/**
|
|
* Enable or disable compact output.
|
|
*/
|
|
public function setCompact(bool $compact)
|
|
{
|
|
$this->compact = $compact;
|
|
}
|
|
|
|
/**
|
|
* Get whether to use compact output.
|
|
*/
|
|
public function compact(): bool
|
|
{
|
|
return $this->compact;
|
|
}
|
|
|
|
/**
|
|
* Set the prompt string.
|
|
*/
|
|
public function setPrompt(string $prompt)
|
|
{
|
|
$this->prompt = $prompt;
|
|
}
|
|
|
|
/**
|
|
* Get the prompt string.
|
|
*/
|
|
public function prompt(): string
|
|
{
|
|
return $this->prompt;
|
|
}
|
|
|
|
/**
|
|
* Set the buffer prompt string (used for multi-line input continuation).
|
|
*/
|
|
public function setBufferPrompt(string $bufferPrompt)
|
|
{
|
|
$this->bufferPrompt = $bufferPrompt;
|
|
}
|
|
|
|
/**
|
|
* Get the buffer prompt string (used for multi-line input continuation).
|
|
*/
|
|
public function bufferPrompt(): string
|
|
{
|
|
return $this->bufferPrompt;
|
|
}
|
|
|
|
/**
|
|
* Set the prompt string used when replaying history.
|
|
*/
|
|
public function setReplayPrompt(string $replayPrompt)
|
|
{
|
|
$this->replayPrompt = $replayPrompt;
|
|
}
|
|
|
|
/**
|
|
* Get the prompt string used when replaying history.
|
|
*/
|
|
public function replayPrompt(): string
|
|
{
|
|
return $this->replayPrompt;
|
|
}
|
|
|
|
/**
|
|
* Set the return value marker.
|
|
*/
|
|
public function setReturnValue(string $returnValue)
|
|
{
|
|
$this->returnValue = $returnValue;
|
|
}
|
|
|
|
/**
|
|
* Get the return value marker.
|
|
*/
|
|
public function returnValue(): string
|
|
{
|
|
return $this->returnValue;
|
|
}
|
|
|
|
/**
|
|
* Set the fallback color when "gray" is unavailable.
|
|
*/
|
|
public function setGrayFallback(string $grayFallback)
|
|
{
|
|
$this->grayFallback = $grayFallback;
|
|
}
|
|
|
|
/**
|
|
* Set the shell output formatter styles.
|
|
*
|
|
* Accepts a map from style name to [fg, bg, options], for example:
|
|
*
|
|
* [
|
|
* 'error' => ['white', 'red', ['bold']],
|
|
* 'warning' => ['black', 'yellow'],
|
|
* ]
|
|
*
|
|
* Foreground, background or options can be null, or even omitted entirely.
|
|
*/
|
|
public function setStyles(array $styles)
|
|
{
|
|
foreach (\array_keys(static::DEFAULT_STYLES) as $name) {
|
|
$this->styles[$name] = $styles[$name] ?? static::DEFAULT_STYLES[$name];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply the current output formatter styles.
|
|
*/
|
|
public function applyStyles(OutputFormatterInterface $formatter, bool $useGrayFallback)
|
|
{
|
|
foreach (\array_keys(static::DEFAULT_STYLES) as $name) {
|
|
$formatter->setStyle($name, new OutputFormatterStyle(...$this->getStyle($name, $useGrayFallback)));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply the current output formatter error styles.
|
|
*/
|
|
public function applyErrorStyles(OutputFormatterInterface $errorFormatter, bool $useGrayFallback)
|
|
{
|
|
foreach (static::ERROR_STYLES as $name) {
|
|
$errorFormatter->setStyle($name, new OutputFormatterStyle(...$this->getStyle($name, $useGrayFallback)));
|
|
}
|
|
}
|
|
|
|
private function getStyle(string $name, bool $useGrayFallback): array
|
|
{
|
|
return \array_map(function ($style) use ($useGrayFallback) {
|
|
return ($useGrayFallback && $style === 'gray') ? $this->grayFallback : $style;
|
|
}, $this->styles[$name]);
|
|
}
|
|
}
|