Skip to content

Commit

Permalink
fix(console): handle nested style tags (#726)
Browse files Browse the repository at this point in the history
  • Loading branch information
innocenzi authored Nov 14, 2024
1 parent 0bdee91 commit 779973e
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 55 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"symfony/uid": "^7.1",
"symfony/var-dumper": "^7.1",
"symfony/var-exporter": "^7.1",
"tempest/highlight": "^2.0",
"tempest/highlight": "^2.11.2",
"vlucas/phpdotenv": "^5.6"
},
"require-dev": {
Expand Down
2 changes: 1 addition & 1 deletion src/Tempest/Console/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"tempest/core": "dev-main",
"tempest/container": "dev-main",
"tempest/debug": "dev-main",
"tempest/highlight": "^2.0",
"tempest/highlight": "^2.11.2",
"tempest/log": "dev-main",
"tempest/reflection": "dev-main",
"tempest/support": "dev-main",
Expand Down
52 changes: 51 additions & 1 deletion src/Tempest/Console/src/Highlight/DynamicTokenType.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public function __construct(
) {
}

public function getStyle(): TerminalStyle
public function getBeforeStyle(): TerminalStyle
{
$normalizedStyle = str($this->style)
->lower()
Expand All @@ -34,6 +34,56 @@ public function getStyle(): TerminalStyle
return TerminalStyle::RESET;
}

public function getAfterStyle(): TerminalStyle
{
return match ($this->getBeforeStyle()) {
// Mods
TerminalStyle::BOLD => TerminalStyle::RESET_INTENSITY,
TerminalStyle::DIM => TerminalStyle::RESET_INTENSITY,
TerminalStyle::ITALIC => TerminalStyle::RESET_ITALIC,
TerminalStyle::HIDDEN => TerminalStyle::VISIBLE,
TerminalStyle::UNDERLINE => TerminalStyle::RESET_UNDERLINE,
TerminalStyle::OVERLINE => TerminalStyle::RESET_OVERLINE,
TerminalStyle::STRIKETHROUGH => TerminalStyle::RESET_STRIKETHROUGH,
TerminalStyle::REVERSE_TEXT => TerminalStyle::RESET_REVERSE_TEXT,
// Foregrounds
TerminalStyle::FG_BLACK,
TerminalStyle::FG_DARK_RED,
TerminalStyle::FG_DARK_GREEN,
TerminalStyle::FG_DARK_YELLOW,
TerminalStyle::FG_DARK_BLUE,
TerminalStyle::FG_DARK_MAGENTA,
TerminalStyle::FG_DARK_CYAN,
TerminalStyle::FG_LIGHT_GRAY,
TerminalStyle::FG_GRAY,
TerminalStyle::FG_RED,
TerminalStyle::FG_GREEN,
TerminalStyle::FG_YELLOW,
TerminalStyle::FG_BLUE,
TerminalStyle::FG_MAGENTA,
TerminalStyle::FG_CYAN,
TerminalStyle::FG_WHITE => TerminalStyle::RESET_FOREGROUND,
// Backgrounds
TerminalStyle::BG_BLACK,
TerminalStyle::BG_DARK_RED,
TerminalStyle::BG_DARK_GREEN,
TerminalStyle::BG_DARK_YELLOW,
TerminalStyle::BG_DARK_BLUE,
TerminalStyle::BG_DARK_MAGENTA,
TerminalStyle::BG_DARK_CYAN,
TerminalStyle::BG_LIGHT_GRAY,
TerminalStyle::BG_GRAY,
TerminalStyle::BG_RED,
TerminalStyle::BG_GREEN,
TerminalStyle::BG_YELLOW,
TerminalStyle::BG_BLUE,
TerminalStyle::BG_MAGENTA,
TerminalStyle::BG_CYAN,
TerminalStyle::BG_WHITE => TerminalStyle::RESET_BACKGROUND,
default => TerminalStyle::RESET,
};
}

public function getValue(): string
{
return '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,41 +20,33 @@ public function getTokenType(): ConsoleTokenType

public function parse(string $content, Highlighter $highlighter): ParsedInjection
{
$pattern = '/(?<match>\<style=\"(?<styles>(?:[a-z-]+\s*)+)\"\>(.|\n)*\<\/style\>)/';

$result = preg_replace_callback(
pattern: $pattern,
callback: function ($matches) use ($highlighter, $pattern) {
$theme = $highlighter->getTheme();
$content = $matches['match'];
$styles = $matches['styles'];
$before = '';
$after = '';

foreach (explode(' ', $styles) as $style) {
$token = new DynamicTokenType($style);
$before .= $theme->before($token);
$after .= $theme->after($token);
}

$result = str_replace(
search: $content,
replace: str($content)
$pattern = '/(?<match>\<style=\"(?<styles>(?:[a-z-]+\s*)+)\"\>(?:(?!\<style).|\n)*?\<\/style\>)/';

do {
$content = preg_replace_callback(
subject: $content,
pattern: $pattern,
callback: function ($matches) use ($highlighter) {
$theme = $highlighter->getTheme();
$match = $matches['match'];
$styles = $matches['styles'];
$before = '';
$after = '';

foreach (explode(' ', $styles) as $style) {
$token = new DynamicTokenType($style);
$before .= $theme->before($token);
$after .= $theme->after($token);
}

return str($match)
->replaceFirst("<style=\"{$styles}\">", $before)
->replaceLast("</style>", $after)
->toString(),
subject: $matches[0],
);

if (preg_match($pattern, $result)) {
return $this->parse($result, $highlighter)->content;
->toString();
}
);
} while (preg_match($pattern, $content));

return $result;
},
subject: $content,
);

return new ParsedInjection($result ?? $content);
return new ParsedInjection($content);
}
}
8 changes: 6 additions & 2 deletions src/Tempest/Console/src/Highlight/TempestTerminalTheme.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
public function before(TokenType $tokenType): string
{
if ($tokenType instanceof DynamicTokenType) {
return $this->style($tokenType->getStyle());
return $this->style($tokenType->getBeforeStyle());
}

return match ($tokenType) {
Expand All @@ -42,6 +42,10 @@ public function before(TokenType $tokenType): string

public function after(TokenType $tokenType): string
{
if ($tokenType instanceof DynamicTokenType) {
return $this->style($tokenType->getAfterStyle());
}

return match ($tokenType) {
ConsoleTokenType::ERROR,
ConsoleTokenType::QUESTION,
Expand All @@ -57,7 +61,7 @@ private function style(TerminalStyle ...$styles): string
return implode(
'',
array_map(
fn (TerminalStyle $style) => TerminalStyle::ESC->value . $style->value,
fn (TerminalStyle $style) => TerminalStyle::ESC->value . $style->value,
$styles,
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@
*/
final class DynamicInjectionTest extends TestCase
{
#[TestWith(['<style="fg-cyan">foo</style>', "\e[96mfoo\e[0m"])]
#[TestWith(['<style="bg-red">foo</style>', "\e[101mfoo\e[0m"])]
#[TestWith(['<style="bold">foo</style>', "\e[1mfoo\e[0m"])]
#[TestWith(['<style="underline">foo</style>', "\e[4mfoo\e[0m"])]
#[TestWith(['<style="fg-cyan">foo</style>', "\e[96mfoo\e[39m"])]
#[TestWith(['<style="bg-red">foo</style>', "\e[101mfoo\e[49m"])]
#[TestWith(['<style="bold">foo</style>', "\e[1mfoo\e[22m"])]
#[TestWith(['<style="underline">foo</style>', "\e[4mfoo\e[24m"])]
#[TestWith(['<style="reset">foo</style>', "\e[0mfoo\e[0m"])]
#[TestWith(['<style="reverse-text">foo</style>', "\e[7mfoo\e[0m"])]
#[TestWith(['<style="bg-darkcyan fg-cyan underline">Tempest</style>', "\e[46m\e[96m\e[4mTempest\e[0m\e[0m\e[0m"])]
#[TestWith(['<style="bg-dark-cyan fg-cyan underline">Tempest</style>', "\e[46m\e[96m\e[4mTempest\e[0m\e[0m\e[0m"])]
#[TestWith(['<style="fg-cyan"><style="bg-dark-red">foo</style></style>', "\e[96m\e[41mfoo\e[0m\e[0m"])]
#[TestWith(['<style="reverse-text">foo</style>', "\e[7mfoo\e[27m"])]
#[TestWith(['<style="bg-darkcyan fg-cyan underline">Tempest</style>', "\e[46m\e[96m\e[4mTempest\e[49m\e[39m\e[24m"])]
#[TestWith(['<style="bg-dark-cyan fg-cyan underline">Tempest</style>', "\e[46m\e[96m\e[4mTempest\e[49m\e[39m\e[24m"])]
#[TestWith(['<style="fg-cyan"><style="bg-dark-red">foo</style></style>', "\e[96m\e[41mfoo\e[49m\e[39m"])]
#[TestWith(['<style="dim"><style="bg-dark-red fg-white">foo</style></style>', "\e[2m\e[41m\e[97mfoo\e[49m\e[39m\e[22m"])]
#[TestWith(['<style="fg-cyan">cyan</style>unstyled<style="bg-dark-red">dark red</style>', "\e[96mcyan\e[39munstyled\e[41mdark red\e[49m"])]
#[TestWith(['<style="dim"><style="fg-gray">dim-gray</style> just-gray</style>', "\e[2m\e[90mdim-gray\e[39m just-gray\e[22m"])]
#[Test]
public function language(string $content, string $expected): void
{
Expand Down
2 changes: 1 addition & 1 deletion src/Tempest/Debug/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"minimum-stability": "dev",
"require": {
"php": "^8.3",
"tempest/highlight": "^2.0",
"tempest/highlight": "^2.11.2",
"symfony/var-dumper": "^7.1"
},
"autoload": {
Expand Down
2 changes: 1 addition & 1 deletion src/Tempest/Http/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"tempest/view": "dev-main",
"tempest/mapper": "dev-main",
"tempest/container": "dev-main",
"tempest/highlight": "^2.0",
"tempest/highlight": "^2.11.2",
"laminas/laminas-diactoros": "^3.3",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0|^2.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@
*/
final class TempestConsoleLanguageTest extends TestCase
{
#[TestWith(['<style="fg-cyan">foo</style>', "\e[96mfoo\e[0m"])]
#[TestWith(['<style="bg-red">foo</style>', "\e[101mfoo\e[0m"])]
#[TestWith(['<style="bold">foo</style>', "\e[1mfoo\e[0m"])]
#[TestWith(['<style="underline">foo</style>', "\e[4mfoo\e[0m"])]
#[TestWith(['<style="fg-cyan">foo</style>', "\e[96mfoo\e[39m"])]
#[TestWith(['<style="bg-red">foo</style>', "\e[101mfoo\e[49m"])]
#[TestWith(['<style="bold">foo</style>', "\e[1mfoo\e[22m"])]
#[TestWith(['<style="underline">foo</style>', "\e[4mfoo\e[24m"])]
#[TestWith(['<style="reset">foo</style>', "\e[0mfoo\e[0m"])]
#[TestWith(['<style="reverse-text">foo</style>', "\e[7mfoo\e[0m"])]
#[TestWith(['<style="bg-darkcyan fg-cyan underline">Tempest</style>', "\e[46m\e[96m\e[4mTempest\e[0m\e[0m\e[0m"])]
#[TestWith(['<style="bg-dark-cyan fg-cyan underline">Tempest</style>', "\e[46m\e[96m\e[4mTempest\e[0m\e[0m\e[0m"])]
#[TestWith(['<style="fg-cyan"><style="bg-dark-red">foo</style></style>', "\e[96m\e[41mfoo\e[0m\e[0m"])]
#[TestWith(['<style="reverse-text">foo</style>', "\e[7mfoo\e[27m"])]
#[TestWith(['<style="bg-darkcyan fg-cyan underline">Tempest</style>', "\e[46m\e[96m\e[4mTempest\e[49m\e[39m\e[24m"])]
#[TestWith(['<style="bg-dark-cyan fg-cyan underline">Tempest</style>', "\e[46m\e[96m\e[4mTempest\e[49m\e[39m\e[24m"])]
#[TestWith(['<style="fg-cyan"><style="bg-dark-red">foo</style></style>', "\e[96m\e[41mfoo\e[49m\e[39m"])]
#[TestWith(['<style="dim"><style="bg-dark-red fg-white">foo</style></style>', "\e[2m\e[41m\e[97mfoo\e[49m\e[39m\e[22m"])]
#[TestWith(['<style="fg-cyan">cyan</style>unstyled<style="bg-dark-red">dark red</style>', "\e[96mcyan\e[39munstyled\e[41mdark red\e[49m"])]
#[TestWith(['<style="dim"><style="fg-gray">dim-gray</style> just-gray</style>', "\e[2m\e[90mdim-gray\e[39m just-gray\e[22m"])]
#[Test]
public function language(string $content, string $expected): void
{
Expand Down

0 comments on commit 779973e

Please # to comment.