diff --git a/README.md b/README.md index c36f424..d8ee71a 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,16 @@ $environment ->addExtension(new PhikiExtension('github-dark', withWrapper: true)); ``` +#### Inline code highlighting + +If you wish to highlight inline code snippets in your Markdown content, you can specify a grammar after the opening backtick. + +```md +`{php}echo "Hello, world!"` +``` + +This will produce a highlighted `` element using the configured theme and the chosen grammar, specified inside of the `{}` characters. + ### Laravel If you're using Laravel's `Str::markdown()` or `str()->markdown()` methods, you can use the same CommonMark extension by passing it through to the method. diff --git a/meta/sample.php b/meta/sample.php index 7e5bacd..bd83fb8 100644 --- a/meta/sample.php +++ b/meta/sample.php @@ -2,6 +2,10 @@ set_time_limit(2); +use League\CommonMark\Environment\Environment as EnvironmentEnvironment; +use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; +use League\CommonMark\MarkdownConverter; +use Phiki\CommonMark\PhikiExtension; use Phiki\Environment\Environment; use Phiki\Grammar\DefaultGrammars; use Phiki\Phiki; @@ -17,6 +21,7 @@ $grammar = $_GET['grammar'] ?? 'php'; $withGutter = ($_GET['gutter'] ?? false) === 'on'; +$markdown = $_GET['markdown'] ?? null; $environment = Environment::default()->enableStrictMode(); /** @var \Phiki\Grammar\GrammarRepository $repository */ $repository = $environment->getGrammarRepository(); @@ -60,6 +65,14 @@ $tokenDiff = array_diff_multidimensional($tokens, $vscodeTextmateOutput, false); +$converter = new MarkdownConverter( + (new EnvironmentEnvironment()) + ->addExtension(new CommonMarkCoreExtension) + ->addExtension(new PhikiExtension('github-dark')) +); + +$generatedMarkdown = $markdown ? $converter->convert($markdown)->getContent() : null; + ?> @@ -154,6 +167,18 @@ class="flex items-center gap-x-4"> + +
+
+

markdown:

+
+ + +
+
+ +
+
diff --git a/src/CommonMark/InlineCodeRenderer.php b/src/CommonMark/InlineCodeRenderer.php new file mode 100644 index 0000000..22bb107 --- /dev/null +++ b/src/CommonMark/InlineCodeRenderer.php @@ -0,0 +1,34 @@ +getLiteral(), $match, PREG_UNMATCHED_AS_NULL) !== 1) { + return $internal->render($node, $childRenderer); + } + + return $this->phiki->codeToHtml($match[2], $match[1], $this->theme, inline: true); + } +} diff --git a/src/CommonMark/PhikiExtension.php b/src/CommonMark/PhikiExtension.php index a4edcd9..8bf1c1b 100644 --- a/src/CommonMark/PhikiExtension.php +++ b/src/CommonMark/PhikiExtension.php @@ -4,6 +4,7 @@ use League\CommonMark\Environment\EnvironmentBuilderInterface; use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode; +use League\CommonMark\Extension\CommonMark\Node\Inline\Code; use League\CommonMark\Extension\ExtensionInterface; use Phiki\Phiki; use Phiki\Theme\Theme; @@ -24,6 +25,7 @@ public function __construct( public function register(EnvironmentBuilderInterface $environment): void { $environment - ->addRenderer(FencedCode::class, new CodeBlockRenderer($this->theme, $this->phiki, $this->withGutter, $this->withWrapper), 10); + ->addRenderer(FencedCode::class, new CodeBlockRenderer($this->theme, $this->phiki, $this->withGutter, $this->withWrapper), 10) + ->addRenderer(Code::class, new InlineCodeRenderer($this->theme, $this->phiki), 10); } } diff --git a/src/Generators/HtmlGenerator.php b/src/Generators/HtmlGenerator.php index ae7fd48..c2d076f 100644 --- a/src/Generators/HtmlGenerator.php +++ b/src/Generators/HtmlGenerator.php @@ -16,11 +16,20 @@ public function __construct( protected array $themes, protected bool $withGutter = false, protected bool $withWrapper = false, + protected bool $inline = false, ) {} public function generate(array $tokens): string { - return $this->withWrapper ? $this->buildWrapper($tokens) : $this->buildPre($tokens); + if ($this->inline) { + return $this->buildCode($tokens); + } + + if ($this->withWrapper) { + return $this->buildWrapper($tokens); + } + + return $this->buildPre($tokens); } private function buildWrapper($tokens): string @@ -81,7 +90,38 @@ private function buildCode(array $tokens): string $output[] = $this->buildLine($line, $i); } - return ''.implode($output).''; + if (! $this->inline) { + return '' . implode($output) . ''; + } + + $codeClasses = array_filter([ + 'phiki-inline', + $this->grammarName ? "language-$this->grammarName" : null, + $this->getDefaultTheme()->name, + count($this->themes) > 1 ? 'phiki-themes' : null, + ]); + + foreach ($this->themes as $theme) { + if ($theme !== $this->getDefaultTheme()) { + $codeClasses[] = $theme->name; + } + } + + $codeStyles = [$this->getDefaultTheme()->base()->toStyleString()]; + + foreach ($this->themes as $id => $theme) { + if ($id !== $this->getDefaultThemeId()) { + $codeStyles[] = $theme->base()->toCssVarString($id); + } + } + + return sprintf( + '%s', + implode(' ', $codeClasses), + $this->grammarName ? " data-language=\"$this->grammarName\"" : null, + implode(';', $codeStyles), + implode('', $output), + ); } private function buildLine(array $line, int $index): string diff --git a/src/Phiki.php b/src/Phiki.php index 9f7045c..5f1562d 100644 --- a/src/Phiki.php +++ b/src/Phiki.php @@ -58,7 +58,7 @@ public function codeToHighlightedTokens(string $code, string|Grammar $grammar, s * @param bool $withGutter Include a gutter in the generated HTML. The gutter typically contains line numbers and helps provide context for the code. * @param bool $withWrapper Wrap the generated HTML in an additional `
` so that it can be styled with CSS. Useful for avoiding overflow issues. */ - public function codeToHtml(string $code, string|Grammar $grammar, string|array|Theme $theme, bool $withGutter = false, bool $withWrapper = false): string + public function codeToHtml(string $code, string|Grammar $grammar, string|array|Theme $theme, bool $withGutter = false, bool $withWrapper = false, bool $inline = false): string { $tokens = $this->codeToHighlightedTokens($code, $grammar, $theme); $generator = new HtmlGenerator( @@ -69,6 +69,7 @@ public function codeToHtml(string $code, string|Grammar $grammar, string|array|T $this->wrapThemes($theme), $withGutter, $withWrapper, + $inline, ); return $generator->generate($tokens); diff --git "a/tests/.pest/snapshots/Unit/CommonMark/PhikiExtensionTest/_CommonMark___Extension__\342\206\222_it_highlights_inline_code_when_grammar_is_present.snap" "b/tests/.pest/snapshots/Unit/CommonMark/PhikiExtensionTest/_CommonMark___Extension__\342\206\222_it_highlights_inline_code_when_grammar_is_present.snap" new file mode 100644 index 0000000..a6eab8a --- /dev/null +++ "b/tests/.pest/snapshots/Unit/CommonMark/PhikiExtensionTest/_CommonMark___Extension__\342\206\222_it_highlights_inline_code_when_grammar_is_present.snap" @@ -0,0 +1,2 @@ +

echo "Hello, world!"; +

diff --git a/tests/Unit/CommonMark/PhikiExtensionTest.php b/tests/Unit/CommonMark/PhikiExtensionTest.php index d467f53..4e26712 100644 --- a/tests/Unit/CommonMark/PhikiExtensionTest.php +++ b/tests/Unit/CommonMark/PhikiExtensionTest.php @@ -45,4 +45,19 @@ class A {} expect($generated) ->toContain('data-language="php"'); }); + + it('highlights inline code when grammar is present', function () { + $environment = new Environment; + + $environment + ->addExtension(new CommonMarkCoreExtension) + ->addExtension(new PhikiExtension('github-dark')); + + $markdown = new MarkdownConverter($environment); + $generated = $markdown->convert(<<<'MD' + `{php}echo "Hello, world!";` + MD)->getContent(); + + expect($generated)->toMatchSnapshot(); + }); });