diff --git a/composer.json b/composer.json index 01343823f..08660fc26 100644 --- a/composer.json +++ b/composer.json @@ -117,6 +117,7 @@ "Tempest\\Cache\\Tests\\": "src/Tempest/Cache/tests", "Tempest\\Clock\\Tests\\": "src/Tempest/Clock/tests", "Tempest\\CommandBus\\Tests\\": "src/Tempest/CommandBus/tests", + "Tempest\\Console\\Tests\\": "src/Tempest/Console/tests", "Tempest\\Container\\Tests\\": "src/Tempest/Container/tests", "Tempest\\Core\\Tests\\": "src/Tempest/Core/tests", "Tempest\\Database\\Tests\\": "src/Tempest/Database/tests", diff --git a/src/Tempest/Console/composer.json b/src/Tempest/Console/composer.json index 51ee0b396..fa17699ad 100644 --- a/src/Tempest/Console/composer.json +++ b/src/Tempest/Console/composer.json @@ -21,6 +21,11 @@ "Tempest\\Console\\": "src" } }, + "autoload-dev": { + "psr-4": { + "Tempest\\Console\\Tests\\": "tests" + } + }, "bin": [ "bin/tempest" ] diff --git a/src/Tempest/Console/src/Highlight/DynamicTokenType.php b/src/Tempest/Console/src/Highlight/DynamicTokenType.php new file mode 100644 index 000000000..59dbd7a5e --- /dev/null +++ b/src/Tempest/Console/src/Highlight/DynamicTokenType.php @@ -0,0 +1,46 @@ +style) + ->lower() + ->replace(['_', '-'], ''); + + foreach (TerminalStyle::cases() as $case) { + $normalizedCase = str($case->name) + ->lower() + ->replace(['_', '-'], ''); + + if ($normalizedCase->equals($normalizedStyle)) { + return $case; + } + } + + return TerminalStyle::RESET; + } + + public function getValue(): string + { + return ''; + } + + public function canContain(TokenType $other): bool + { + return false; + } +} diff --git a/src/Tempest/Console/src/Highlight/TempestConsoleLanguage/Injections/DynamicInjection.php b/src/Tempest/Console/src/Highlight/TempestConsoleLanguage/Injections/DynamicInjection.php new file mode 100644 index 000000000..12949c08c --- /dev/null +++ b/src/Tempest/Console/src/Highlight/TempestConsoleLanguage/Injections/DynamicInjection.php @@ -0,0 +1,60 @@ +\(?:[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) + ->replaceFirst("", $before) + ->replaceLast("", $after) + ->toString(), + subject: $matches[0], + ); + + if (preg_match($pattern, $result)) { + return $this->parse($result, $highlighter)->content; + } + + return $result; + }, + subject: $content, + ); + + return new ParsedInjection($result ?? $content); + } +} diff --git a/src/Tempest/Console/src/Highlight/TempestConsoleLanguage/TempestConsoleLanguage.php b/src/Tempest/Console/src/Highlight/TempestConsoleLanguage/TempestConsoleLanguage.php index 382cdcb73..67139e55c 100644 --- a/src/Tempest/Console/src/Highlight/TempestConsoleLanguage/TempestConsoleLanguage.php +++ b/src/Tempest/Console/src/Highlight/TempestConsoleLanguage/TempestConsoleLanguage.php @@ -5,6 +5,7 @@ namespace Tempest\Console\Highlight\TempestConsoleLanguage; use Tempest\Console\Highlight\TempestConsoleLanguage\Injections\CommentInjection; +use Tempest\Console\Highlight\TempestConsoleLanguage\Injections\DynamicInjection; use Tempest\Console\Highlight\TempestConsoleLanguage\Injections\EmphasizeInjection; use Tempest\Console\Highlight\TempestConsoleLanguage\Injections\ErrorInjection; use Tempest\Console\Highlight\TempestConsoleLanguage\Injections\H1Injection; @@ -39,6 +40,7 @@ public function getInjections(): array new H1Injection(), new H2Injection(), new SuccessInjection(), + new DynamicInjection(), ]; } diff --git a/src/Tempest/Console/src/Highlight/TempestTerminalTheme.php b/src/Tempest/Console/src/Highlight/TempestTerminalTheme.php index af6c40b5f..89c0753f2 100644 --- a/src/Tempest/Console/src/Highlight/TempestTerminalTheme.php +++ b/src/Tempest/Console/src/Highlight/TempestTerminalTheme.php @@ -16,6 +16,10 @@ public function before(TokenType $tokenType): string { + if ($tokenType instanceof DynamicTokenType) { + return $this->style($tokenType->getStyle()); + } + return match ($tokenType) { TokenTypeEnum::KEYWORD => $this->style(TerminalStyle::FG_DARK_BLUE), TokenTypeEnum::PROPERTY => $this->style(TerminalStyle::FG_DARK_GREEN), diff --git a/src/Tempest/Console/tests/TempestConsoleLanguage/Injections/DynamicInjectionTest.php b/src/Tempest/Console/tests/TempestConsoleLanguage/Injections/DynamicInjectionTest.php new file mode 100644 index 000000000..7500edb16 --- /dev/null +++ b/src/Tempest/Console/tests/TempestConsoleLanguage/Injections/DynamicInjectionTest.php @@ -0,0 +1,38 @@ +foo', "\e[96mfoo\e[0m"])] + #[TestWith(['foo', "\e[101mfoo\e[0m"])] + #[TestWith(['foo', "\e[1mfoo\e[0m"])] + #[TestWith(['foo', "\e[4mfoo\e[0m"])] + #[TestWith(['foo', "\e[0mfoo\e[0m"])] + #[TestWith(['foo', "\e[7mfoo\e[0m"])] + #[TestWith(['Tempest', "\e[46m\e[96m\e[4mTempest\e[0m\e[0m\e[0m"])] + #[TestWith(['Tempest', "\e[46m\e[96m\e[4mTempest\e[0m\e[0m\e[0m"])] + #[TestWith(['foo', "\e[96m\e[41mfoo\e[0m\e[0m"])] + #[Test] + public function language(string $content, string $expected): void + { + $highlighter = new Highlighter(new TempestTerminalTheme()); + + $this->assertSame( + $expected, + (new DynamicInjection())->parse($content, $highlighter)->content, + ); + } +} diff --git a/tests/Integration/Console/Highlight/TempestConsoleLanguage/TempestConsoleLanguageTest.php b/tests/Integration/Console/Highlight/TempestConsoleLanguage/TempestConsoleLanguageTest.php new file mode 100644 index 000000000..daa2bbe74 --- /dev/null +++ b/tests/Integration/Console/Highlight/TempestConsoleLanguage/TempestConsoleLanguageTest.php @@ -0,0 +1,38 @@ +foo', "\e[96mfoo\e[0m"])] + #[TestWith(['foo', "\e[101mfoo\e[0m"])] + #[TestWith(['foo', "\e[1mfoo\e[0m"])] + #[TestWith(['foo', "\e[4mfoo\e[0m"])] + #[TestWith(['foo', "\e[0mfoo\e[0m"])] + #[TestWith(['foo', "\e[7mfoo\e[0m"])] + #[TestWith(['Tempest', "\e[46m\e[96m\e[4mTempest\e[0m\e[0m\e[0m"])] + #[TestWith(['Tempest', "\e[46m\e[96m\e[4mTempest\e[0m\e[0m\e[0m"])] + #[TestWith(['foo', "\e[96m\e[41mfoo\e[0m\e[0m"])] + #[Test] + public function language(string $content, string $expected): void + { + $highlighter = new Highlighter(new TempestTerminalTheme()); + + $this->assertSame( + $expected, + $highlighter->parse($content, new TempestConsoleLanguage()) + ); + } +}