This was actually written in May of 2022, but I had just started a new position and never got around to publishing. Same goes for the JS and canvas
rendition of the Malevich painting
One of the great benefits of open-source work and the surrounding communities is the opportunity to learn from people who are better at things than you. GitHub user @pamtbaau, a moderator and very active user of the Grav discourse forum, gave me some good suggestions after my pull request.
This post covers responding to and implementing those suggestions. (Don’t forget
to update the documentation: There will a couple of changes to make in
README.md
after each change to the plugin logic, not covered in the post.
Similarly, don’t forget to update the CHANGELOG
and the plugin metadata.)
Use shortcode with parameters
Back in the second post in this series, I elected to give each language its own shortcode because I liked the brevity of the approach. @pamtbaau’s reasons for using different approach are:
- Only load shortcode for requested language(s), instead of 349. It saves a bit of performance.
- No pollution of the shortcodes namespace with 349 extra names.
- No need to search for 349 languages in Markdown while they are not used anyway.
Yep. (I didn’t experience any performance issues, but that’s immaterial. Good
points all around.) I’ll use a hybrid of my original sketch, and @pamtbaau’s
suggested syntax, preferring my hl
shortcode with their code
parameter
(instead of my valid, but probably too opaque, @
) in the inline form,
yielding:
- Self-closing, inline form, with generic
hl
tag, lang given as aBBCode
fragment, andcode
syntax to specify the code text:
[hl=js code="console.log('hey')" /]
- Paired shortcodes, with generic
hl
tag and lang given asBBCode
fragment:[hl=python] def greet(name): print(f"Hi, {name}!") [/hl]
This has the benefit of simplifying the code, too. In
HighlightPhpShortcode.php
, the render
method stays as-is. Updates to the
init
method are pretty straightforward:
- remove the loop and the array of available languages;
- remove the resulting unecessary instantiation of the
Highlighter
class; - update the dynamic
$k
variable to the statichl
tag; - update the means of identifying the language
from$sc->getName();
to$sc->getBbCode();
; and - in the inline case, assign the
$code
variable using$sc->getParameter('code');
.
With the above, and using a ternary instead of the if
block, we end up with the following:
public function init()
{
$rawHandlers = $this->shortcode->getRawHandlers();
$rawHandlers->add('hl', function (ShortcodeInterface $sc) {
$lang = $sc->getBbCode();
$content = $sc->getContent();
$isInline = is_null($content);
$code = $isInline ? $sc->getParameter('code') : $content;
$code = trim($code);
return $this->render($lang, $code, $isInline);
});
}
Use a fixed location for custom styles
I don’t see a real added value for a customisable location.
Now that it’s been pointed out, neither do I!
My suggestion would be to use folder:
/user/data/highlight-php/
. I prefer the/user/data
folder to not pollute the user folder. Drop the folder property from the config.
Good point. What’s more, /user/data
is the recommended location for plugins to store data. I missed that, and took my cue from the shortcode-core documentation. Whoops.I submitted pull requests on the grav-plugin-shortcode-core and grav-plugin-file-content repositories to reflect this. There’s even a dedicated stream for that folder, user-data://
; I’ll use that.
The changes here are in the highlight-php.php
and highlight-php.yaml
files.
In the latter, it’s just a matter of deleting a line.
enabled: true
theme: default
-custom_styles: highlight-php-styles
In the PHP, there’s scarcely more to it: one deletion, and updating the path in
onPluginsInitialized
:
-$customStylesDirName = $this->config->get('plugins.highlight-php.custom_styles');
$locator = Grav::instance()['locator'];
-$userCustomDirPath = $locator->findResource('user://') . '/' . 'custom' . '/' . $customStylesDirName;
+$userCustomDirPath = $locator->findResource('user-data://') . '/' . 'highlight-php';
if (!($locator->findResource($userCustomDirPath))) {
Folder::create($userCustomDirPath);
}
There’s more related to this change to come in the new handling for custom styles in the next section.
Add a property in a page to enable/disable the plugin
I presume a page using snippets will be the exceptions instead of the rule. Test for it during
onPageInitialized
.
In my case, I’d just as soon enable it globally and be done with it. Per-page configuration is a good idea, though, so I built it.
This necessitated some different event subscriptions and a careful review of the
Grav lifecycle. I also elected to add a new blueprint to the plugin
(plugin://blueprints/default.yaml
) that extends the default to allow for
toggling the plugin for a given page via the admin panel. I added a new section
to the bottom of the default ‘Options’ tab, along with some explanatory text; I
elected not to allow for per-page syntax highlighting options. (I.e., in my
plugin, the user selects a syntax highlighting colour scheme for the site, and
can only enable/disable syntax highlighting per page.)
Use two config properties for styles
…
style
for build-in styles andcustomStyle
for a custom style. Add aNone
value to both.
- User can now
- Select a build-in style or None
- Select a custom style or None
- Select both build-in and custom style to override the build-in style.
- Load each style (if value !== ‘None’) as separate asset with customStyle as last.
This was the biggest change, requiring edits throughout the plugin.
blueprints.yaml
and highlight.php
both get substantial changes. Here’s a
high-level summary of the changes.If you’ve made it this far and are making your own plugin, I assume you’ll be able to use git and/or GitHub well enough to look at the details yourself. Here’s a direct link to the commit where this took place.
blueprints.yaml
Rejigged fields to handle the displaying the builtin and custom themes as
options in dropdown fields. Each dropdown uses a data-options@
function call;
this function in turn necessitated some refactoring of highlight-php.php
. Also
added a new field to handle the site-wide default asset loading strategy for the
plugin.
highlight-php.php
Here’s the meat of the revision. Added some abstraction to facilitate the two different directory listings in the dropdown fields. Some additional logic was required to handle the four possibilities with per-page settings enabled:
- plugin loads assets site-wide by default, with no per-page configuration set;
- plugin loads assets site-wide by default, with a per-page configuration set;
- plugin loads assets per-page by default, with no per-page configuration set; and
- plugin loads assets per-page by default, with a per-page configuration set.
Wrapping up
That’s a final wrap on this plugin (aside from vendor updates or bugfixes, although none have been reported). I’m grateful to the user who suggested the improvements described here. For me, the experience of developing and releasing this plugin has been a treat: taking part in a globally distributed, like-minded community is fun. I’ve learned some things along the way, and I gather from the handful of stars on the GitHub repository that this is useful to at least a couple of other people. That’s nice, too.
Next steps for me:
- add a quick “series” plugin to my site.
- revisit the Malevich artwork series (with p5.js).
- implement notes.