Yet another Syntax Highlighter in PHP meant to be as extensible and easy to use as it only can, but with performance in mind.
You can try it live with the most recent version on https://keylighter.kadet.net/.
Name "KeyLighter" is inspired by Key Light concept in photography and cinematography.
The key light is the first and usually most important light a photographer, cinematographer, lighting cameraman, or other scene composer will use in a lighting setup. The purpose of the key light is to highlight the form and dimension of the subject.
KeyLighter is supposed to do the same thing - for code.
$ composer require kadet/keylighter
To use KeyLighter you just need PHP 7.3 or later, no special extensions required.
It's possible to install KeyLighter as a global composer library
$ composer global require kadet/keylighter
Then you can use builtin simple cli highlighting app:
$ keylighter [command = highlight] [-l|--language [LANGUAGE]] [-f|--format [FORMAT]] [-d|--debug [DEBUG]] [--] <path>...
If you want pipe into KeyLighter just specify php://stdin
as path. You can
use list
command to see all available commands, and --help
argument for
detailed help. You don't have to specify highlight
command explicitly.
You're using PowerShell on Windows? Cool! KeyLighter comes with integrated PowerShell module that makes CLI usage even better. Just import module (For example in profile), and you're ready to go.
PS> Import-Module "${env:APPDATA}\Composer\vendor\kadet\keylighter\bin\KeyLighter.psd1"
To use autocompletion features you will need PowerShell v5 (Comes with windows 10) or install TabExpansion++ Module.
use Kadet\Highlighter\Language;
echo \Kadet\Highlighter\highlight($source, new Language\Php(), $formatter);
// or
echo \Kadet\Highlighter\KeyLighter::get()->highlight($source, new Language\Php(), $formatter);
// or
$keylighter = new \Kadet\Highlighter\KeyLighter([options]);
echo $keylighter->highlight($source, new Language\Php(), $formatter);
You can find all available languages here and formatters here.
KeyLighter was originally designed as CLI highlighter for my own usage, but then I decided that it should be able to generate any possible output, currently supported:
\Kadet\Highlighter\Formatter\CliFormatter
It can even be styled, default styles are stored in Styles\Cli\Default.php
,
but you can just pass additional argument into a constructor:
new \Kadet\Highlighter\Formatter\CliFormatter([
'string' => ['color' => 'green'],
'keyword' => ['color' => 'yellow'],
...
])
\Kadet\Highlighter\Formatter\HtmlFormatter
Every token is placed inside it's own span
and classes are prefixed so it can
be easily styled with css and should not interfere with any of your existing
classes
<span class="kl-variable">$maxOption</span>
pre > span.kl-variable { color: #F7750D; }
It's easy to write your own formatters. Documentation coming soon.
Some tokens are valid in some contexts, some are not. This library is context sensitive, and you can define when they are valid.
In this case, context mean just "inside of other token", for example lets assume
that string
token is defined as everything from " to the next " and keyword
is defined as 'sit' substring.
↓ string:start ↓ keyword:start
"Lorem ipsum dolor sit amtet"
keyword:end ↑ ↑ string:end
Token tree:
Token.name Token.pos
------------------------------
string:start 0
keyword:start 21
keyword:end 23
string:end 30
So as you can see keyword
is inside of string
, and therefore is not valid
and should be deleted. You can define tokens valid only in some context, or
invalid in other.
Oh, and token names cascade, it means that string.single
is string
, but
string
is necessarily not string.single
.
It's possible to easily extend KeyLighter
with new languages, more detailed
documentation coming soon.
For example XML definition looks like this:
class Xml extends GreedyLanguage
{
private const IDENTIFIER = '(?P<namespace>[\w\.-]+:)?(?P<name>[\w\.-]+)';
/**
* Tokenization rules
*/
public function setupRules()
{
$this->rules->addMany([
'tag.open' => [
new OpenRule(new RegexMatcher('/(<[\w\.-]+)[:\/>:\s]/')),
new CloseRule(new SubStringMatcher('>'), ['context' => ['!string', '!comment']])
],
'tag.close' => new Rule(new RegexMatcher('/(<\/' . self::IDENTIFIER . '>)/')),
'symbol.tag' => new Rule(new RegexMatcher('/<\\/?' . self::IDENTIFIER . '/', [
'name' => Token::NAME,
'namespace' => '$.namespace'
]), ['context' => ['tag', '!string']]),
'symbol.attribute' => new Rule(new RegexMatcher('/' . self::IDENTIFIER . '=/', [
'name' => Token::NAME,
'namespace' => '$.namespace'
]), ['context' => ['tag', '!string']]),
'constant.entity' => new Rule(new RegexMatcher('/(&(?:\#\d+|[a-z])+;)/si')),
'comment' => new Rule(new CommentMatcher(null, [['<!--', '-->']])),
'string' => CommonFeatures::strings(['single' => '\'', 'double' => '"'], ['context' => ['tag']]),
]);
}
/** {@inheritdoc} */
public function getIdentifier()
{
return 'xml';
}
public static function getMetadata()
{
return [
'name' => ['xml'],
'mime' => ['application/xml', 'text/xml'],
'extension' => ['*.xml']
];
}
}
I will try to write as many definitions as I only can, but any PRs are welcome.
Many languages can be used simultaneously, css or js inside html, sql in
php and so on. KeyLighter can handle and highlight embedded languages
without any problem.
Even though it wasn't supposed to be fastest code highlighter in PHP it is still quite fast, more than 2x faster than GeSHi.
KeyLighter uses phpunit
for testing:
$ ./vendor/bin/phpunit
There are still few things to do, you can find all on trello.
See CONTRIBUTING.md for details.