Tempest is still a work in progress. Visit our GitHub or Discord

Adding tokens

Some people or projects might want more fine-grained control over how specific words are coloured. A common example are null, true, and false in json files. By default, tempest/highlight will treat those value as normal text, and won't apply any special highlighting to them:

{
    "null-property": null,
    "value-property": "value"
}

However, it's super trivial to add your own, extended styling on these kinds of tokens. Start by adding a custom language, let's call it ExtendedJsonLanguage:

use Tempest\Highlight\Languages\Json\JsonLanguage;

class ExtendedJsonLanguage extends JsonLanguage
{
    public function getPatterns(): array
    {
        return [
            ...parent::getPatterns(),
        ];
    }
}

Next, let's add a pattern that matches null:

use Tempest\Highlight\IsPattern;
use Tempest\Highlight\Pattern;
use Tempest\Highlight\Tokens\DynamicTokenType;
use Tempest\Highlight\Tokens\TokenType;

final readonly class JsonNullPattern implements Pattern
{
    use IsPattern;

    public function getPattern(): string
    {
        return '\: (?<match>null)';
    }

    public function getTokenType(): TokenType
    {
        return new DynamicTokenType('hl-null');
    }
}

Note how we return a DynamicTokenType from the getTokenType() method. The value passed into this object will be used as the classname for this token.

Next, let's add this pattern in our newly created ExtendedJsonLanguage:

class ExtendedJsonLanguage extends JsonLanguage
{
    public function getPatterns(): array
    {
        return [
            ...parent::getPatterns(),
            {*new JsonNullPattern(),*}
        ];
    }
}

Finally, register ExtendedJsonLanguage into the highlighter:

$highlighter->addLanguage(new ExtendedJsonLanguage());

Note that, because we extended JsonLanguage, this language will target all code blocks tagged as json. You could provide a different name, if you want to make a distinction between the default implementation and yours (this is what's happening on this page):

class ExtendedJsonLanguage extends JsonLanguage
{
    public function getName(): string
    {
        return 'json_extended';
    }

    // …
}

There we have it!

{
    "null-property": null,
    "value-property": "value"
}

You can add as many patterns as you like, you can even make your own TokenType implementation if you don't want to rely on DynamicTokenType:

enum ExtendedTokenType: string implements TokenType
{
    case VALUE_NULL = 'null';
    case VALUE_TRUE = 'true';
    case VALUE_FALSE = 'false';

    public function getValue(): string
    {
        return $this->value;
    }

    public function canContain(TokenType $other): bool
    {
        return false;
    }
}