Prezet
Switching the Syntax Highlighter in Prezet
Published On: March 16, 2025
Estimated Reading Time: 2 minutes
#Introduction
Prezet - the package this blog uses - comes bundled with support for Torchlight out-of-the-box. I understand the decision to include this but I personally am not a fan of Torchlight. Instead I like to use self-hosted solutions and that's what I'll be covering quickly in this post.
Prezet uses CommonMark for its markdown parsing and that gives us a lot of choices when picking a syntax highlighter. I have chosen Phiki for my use but there are other options available, as you may have guessed by the name this inspired by Shiki however it isn't a fork. My reasoning for choosing Phiki is not only the self-hosting but the ability to use VS Code themes and TextMate grammar files we can extend to include other languages we might need, LSL in the case for this blog in any style we fancy.
#Installation
Assuming you already know what package you want to use, you can skip this following step. However if you're continuing with Phiki and have yet to install it, this can be done with the following;
$composer require phiki/phiki
#Configuration
From here, getting Prezet to use Phiki is going to be a bit of a long-winded path however in the long run it's going to be worth it, at least in my opinion.
Firstly we're going to want to make a ParseMarkdown
class override. Mine is located in app/Actions
to closely mimic the structure already found within the Prezet codebase, ideally maintaining that organisation already presented to us.
1<?php
2
3namespace App\Actions;
4
5use Exception;
6use League\CommonMark\Environment\Environment;
7use League\CommonMark\MarkdownConverter;
8use League\CommonMark\Output\RenderedContentInterface;
9use League\CommonMark\Exception\CommonMarkException;
10use BenBjurstrom\Prezet\Exceptions\InvalidConfigurationException;
11use BenBjurstrom\Prezet\Actions\ParseMarkdown as BaseParseMarkdown;
12use \BenBjurstrom\Prezet\Extensions\MarkdownBladeExtension;
13use Phiki\Phiki;
14
15class ParseMarkdown extends BaseParseMarkdown
16{
17 /**
18 * Override the handle function to customize extension instantiation.
19 *
20 * @param string $md
21 * @return RenderedContentInterface
22 * @throws Exception|CommonMarkException
23 */
24 public function handle(string $md): RenderedContentInterface
25 {
26 $config = config('prezet.commonmark.config');
27 if (! is_array($config)) {
28 throw new InvalidConfigurationException('prezet.commonmark.config', $config, 'is not an array');
29 }
30
31 $environment = new Environment($config);
32 $extensions = $this->getExtensions();
33
34 foreach ($extensions as $extension) {
35 if ($extension === \Phiki\CommonMark\PhikiExtension::class) {
36 $theme = config('phiki.theme');
37 $phikiEnv = \Phiki\Environment\Environment::default();
38
39 if (empty($theme)) {
40 throw new Exception("Phiki theme is not configured");
41 }
42
43 foreach (config('phiki.languages') as $language => $grammarPath) {
44 $phikiEnv->getGrammarRepository()->register($language, resource_path($grammarPath));
45 }
46
47 if (!empty(config('phiki.theme_path'))) {
48 $phikiEnv->getThemeRepository()->register($theme, resource_path(config('phiki.theme_path')));
49 }
50
51 $withGutter = config('phiki.with_gutter', false);
52 $withWrapper = config('phiki.with_wrapper', false);
53
54 $phiki = new Phiki($phikiEnv);
55 $environment->addExtension(new $extension($theme, $phiki, withGutter: $withGutter, withWrapper: $withWrapper));
56 } else {
57 $environment->addExtension(new $extension());
58 }
59 }
60
61 $converter = new MarkdownConverter($environment);
62
63 MarkdownBladeExtension::$allowBladeForNextDocument = true;
64 $result = $converter->convert($md);
65 MarkdownBladeExtension::$allowBladeForNextDocument = false;
66
67 return $result;
68 }
69}
A lot of this code is taken from the original ParseMarkdown
class that comes with Prezet, I've just added the Phiki specific code. In it you can see that Phiki has to get a specialised case as it takes an environment parameter on construct. I've also included some configuration parameters, these are in a new configuration file I've created called phiki.php
in the config
directory. This is a simple array of configuration options that are used in the ParseMarkdown
class.
1<?php
2
3return [
4 'theme' => 'github-dark',
5 'theme_path' => '',
6 'languages' => [
7 'lsl' => 'languages/lsl.json',
8 ],
9 'with_gutter' => true,
10 'with_wrapper' => true,
11];
Because I'm using a built-in theme of github-dark, the theme_path
is not populated. If you're using a custom theme you can set this to the path of the theme file.
Other options I've added are with_gutter
and with_wrapper
, with_gutter
is so far undocumented in Phiki and the documentation for line numbers is incorrect, referencing an older version at the time of writing. This adds line numbers to the code block, stylable as you see fit. with_wrapper
is a simple boolean that wraps the code block in a div with a class of phiki-wrapper
, used when trying to avoid issues with the CSS overflow
property.
Now that we've got our class override, we need to ensure it is actually used in place of the original ParseMarkdown
that comes with Prezet. We can achieve this by using Service Providers built into Laravel. This is one of those things we can also quickly create with an artisan
command.
$php artisan make:provider PrezetServiceProvider
I've named this in a generic fashion as in the future I want to add further functionality and markdown extensions, keeping this centralised means that I can come back to this and instead of creating more providers I can instead add them to this file.
1<?php
2
3namespace App\Providers;
4
5use Illuminate\Support\ServiceProvider;
6use BenBjurstrom\Prezet\Actions\ParseMarkdown as BaseParseMarkdown;
7use App\Actions\ParseMarkdown;
8
9class PrezetServiceProvider extends ServiceProvider
10{
11 public function register()
12 {
13 $this->app->singleton(BaseParseMarkdown::class, function () {
14 return new ParseMarkdown();
15 });
16 }
17}
The contents of the file is quite simple, we bind the original ParseMarkdown
to our implementation, meaning that if the original class is requested, Laravel will resolve it to our class.
When using the artisan
command to make our provider it is generally added to the providers.php
in the bootstrap
directory however it is good to ensure that it has been added.
Finally we need to add Phiki to our CommonMark extensions. This is done in the prezet.php
configuration file. This is a simple addition to the commonmark
array.
1// ... other configuration options
2
3'extensions' => [
4 // ... other extensions
5 \Phiki\CommonMark\PhikiExtension::class,
6],
7
8// ... other configuration options
#Conclusion
On the surface this is all quite simple however it is a bit of trial and error to get everything working correctly. I hope this post has been helpful to you even if you're not using Phiki, the process should be similar for other syntax highlighters.