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
42 changes: 42 additions & 0 deletions docs/commonmark.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ description: "Learn how to use Phiki with The PHP League's CommonMark library."

If you're using `league/commonmark` to parse and render Markdown in your PHP project, you can easily integrate Phiki using our custom extension.

## Usage

```php
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
Expand Down Expand Up @@ -57,3 +59,43 @@ $environment
'dark' => Theme::GithubDark, // [!code ++]
])); // [!code ++]
```

### Highlighting and focusing lines

You can highlight and focus lines in your code blocks using special annotations in the code block's info string.

```md
```php {2,4-8}{3}
```

The first set of braces represents the lines to highlight, while the second set represents the lines to focus on.

In this example, line 2 will be highlighted, as well as lines 4 through 8. Line 3 will then be focused.

If you only want to focus lines, you can use an empty set of braces for the highlighted lines:

```md
```php {}{3}
```

#### Sample CSS

Phiki does not style the highlighted or focused lines by default, so you will need to add your own CSS.

You can use the following sample CSS to get started:

```css
pre.phiki code .line.highlight {
background-color: hsl(197, 88%, 94%);
}

.shiki.focus .line:not(.focus) {
transition: all 250ms;
filter: blur(2px);
}

.shiki.focus:hover .line {
transition: all 250ms;
filter: blur(0);
}
```
9 changes: 8 additions & 1 deletion src/Adapters/CommonMark/CodeBlockRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
use League\CommonMark\Node\Node;
use League\CommonMark\Renderer\ChildNodeRendererInterface;
use League\CommonMark\Renderer\NodeRendererInterface;
use Phiki\Adapters\CommonMark\Transformers\MetaTransformer;
use Phiki\Grammar\Grammar;
use Phiki\Phiki;
use Phiki\Theme\Theme;
use Phiki\Transformers\Meta;

class CodeBlockRenderer implements NodeRendererInterface
{
Expand All @@ -27,8 +29,13 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer)

$code = rtrim($node->getLiteral(), "\n");
$grammar = $this->detectGrammar($node);
$meta = new Meta(markdownInfo: $node->getInfoWords()[1] ?? null);

return $this->phiki->codeToHtml($code, $grammar, $this->theme)->withGutter($this->withGutter)->toString();
return $this->phiki->codeToHtml($code, $grammar, $this->theme)
->withGutter($this->withGutter)
->withMeta($meta)
->transformer(new MetaTransformer)
->toString();
}

protected function detectGrammar(FencedCode $node): Grammar|string
Expand Down
95 changes: 95 additions & 0 deletions src/Adapters/CommonMark/Transformers/MetaTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace Phiki\Adapters\CommonMark\Transformers;

use Phiki\Phast\Element;
use Phiki\Support\Arr;
use Phiki\Transformers\AbstractTransformer;

class MetaTransformer extends AbstractTransformer
{
protected array $highlights = [];

protected array $focuses = [];

public function preprocess(string $code): string
{
$this->parse();

return $code;
}

public function line(Element $span, array $tokens, int $index): Element
{
if (in_array($index + 1, $this->highlights, true)) {
$span->properties->get('class')->add('highlight');
}

if (in_array($index + 1, $this->focuses, true)) {
$span->properties->get('class')->add('focus');
}

return $span;
}

protected function parse(): void
{
if (! $this->meta->markdownInfo) {
return;
}

[$highlights, $focuses] = array_pad(
explode(
'}{',
rtrim(ltrim($this->meta->markdownInfo, '{'), '}'),
2
),
2,
null
);

if (! $highlights && ! $focuses) {
return;
}

$highlights = array_map(
fn(array $part) => count($part) > 1 ? $part : $part[0],
array_map(
fn(string $part) => array_map(fn(string $number) => intval($number), explode('-', trim($part))),
explode(',', $highlights)
)
);

foreach ($highlights as $part) {
if (is_array($part)) {
$this->highlights = array_merge($this->highlights, range($part[0], $part[1]));
} else {
$this->highlights[] = $part;
}
}

$this->highlights = array_unique($this->highlights);

if (! $focuses) {
return;
}

$focuses = array_map(
fn(array $part) => count($part) > 1 ? $part : $part[0],
array_map(
fn(string $part) => array_map(fn(string $number) => intval($number), explode('-', trim($part))),
explode(',', $focuses)
)
);

foreach ($focuses as $part) {
if (is_array($part)) {
$this->focuses = array_merge($this->focuses, range($part[0], $part[1]));
} else {
$this->focuses[] = $part;
}
}

$this->focuses = array_unique($this->focuses);
}
}
6 changes: 6 additions & 0 deletions src/Contracts/TransformerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Phiki\Phast\Root;
use Phiki\Token\HighlightedToken;
use Phiki\Token\Token;
use Phiki\Transformers\Meta;

interface TransformerInterface
{
Expand Down Expand Up @@ -59,4 +60,9 @@ public function token(Element $span, HighlightedToken $token, int $index, int $l
* Modify the HTML output after the AST has been converted.
*/
public function postprocess(string $html): string;

/**
* Supply the meta object to the transformer.
*/
public function withMeta(Meta $meta): void;
}
18 changes: 18 additions & 0 deletions src/Output/Html/PendingHtmlOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Phiki\Token\Token;
use Phiki\Transformers\Decorations\DecorationTransformer;
use Phiki\Transformers\Decorations\LineDecoration;
use Phiki\Transformers\Meta;
use Psr\SimpleCache\CacheInterface;
use Stringable;

Expand All @@ -34,6 +35,8 @@ class PendingHtmlOutput implements Stringable

protected int $startingLineNumber = 1;

protected Meta $meta;

/**
* @param array<string, ParsedTheme> $themes
*/
Expand Down Expand Up @@ -102,6 +105,13 @@ public function startingLine(int $lineNumber): self
return $this;
}

public function withMeta(Meta $meta): self
{
$this->meta = $meta;

return $this;
}

public function toString(): string
{
return $this->__toString();
Expand Down Expand Up @@ -153,6 +163,14 @@ public function __toString(): string
return $this->cache->get($cacheKey);
}

if (! isset($this->meta)) {
$this->meta = new Meta();
}

foreach ($this->transformers as $transformer) {
$transformer->withMeta($this->meta);
}

[$code] = $this->callTransformerMethod('preprocess', $this->code);
[$tokens] = $this->callTransformerMethod('tokens', call_user_func($this->generateTokensUsing, $code, $this->grammar));
[$highlightedTokens] = $this->callTransformerMethod('highlighted', call_user_func($this->highlightTokensUsing, $tokens, $this->themes));
Expand Down
13 changes: 13 additions & 0 deletions src/Transformers/AbstractTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@

class AbstractTransformer implements TransformerInterface
{
/**
* The meta information.
*/
protected Meta $meta;

/**
* Modify the code before it is tokenized.
*/
Expand Down Expand Up @@ -87,4 +92,12 @@ public function postprocess(string $html): string
{
return $html;
}

/**
* Store the meta object.
*/
public function withMeta(Meta $meta): void
{
$this->meta = $meta;
}
}
10 changes: 10 additions & 0 deletions src/Transformers/Meta.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Phiki\Transformers;

readonly class Meta
{
public function __construct(
public ?string $markdownInfo = null,
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<pre class="phiki language-php github-dark" data-language="php" style="background-color: #24292e;color: #e1e4e8;"><code><span class="line highlight"><span class="token" style="color: #f97583;">class</span><span class="token"> </span><span class="token" style="color: #b392f0;">A</span><span class="token"> </span><span class="token">{</span><span class="token">}</span><span class="token">
</span></span></code></pre>

This file was deleted.

19 changes: 19 additions & 0 deletions tests/Adapters/CommonMark/PhikiExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\MarkdownConverter;
use Phiki\Adapters\CommonMark\PhikiExtension;
use Phiki\Tests\Fixtures\UselessTransformer;
use Phiki\Theme\Theme;

it('registers renderers', function () {
Expand Down Expand Up @@ -47,3 +48,21 @@ class A {}

expect($generated)->toMatchSnapshot();
});

it('understands the info string', function () {
$environment = new Environment;

$environment
->addExtension(new CommonMarkCoreExtension)
->addExtension(new PhikiExtension('github-dark'));

$markdown = new MarkdownConverter($environment);

$generated = $markdown->convert(<<<'MD'
```php {0-10}
class A {}
```
MD);

expect($generated)->toMatchSnapshot();
});
Loading