Aller au contenu

Ajouter un langage

phil/highlight

Highlighter::with() ajoute un langage sous son nom et ses alias, ou remplace le langage qui les avait. Les noms sont faits de lettres minuscules, de chiffres et de _ + # . -, 32 au plus ; toute autre chose lève InvalidLanguage.

Un langage de programmation : une Syntax

La plupart des langages de programmation sont une Syntax lue par Code :

use Phil\Highlight\Highlighter;
use Phil\Highlight\Language\Code;
use Phil\Highlight\Language\Quote;
use Phil\Highlight\Language\Syntax;

$lua = new Code(new Syntax(
    'lua',
    aliases: ['luau'],
    keywords: ['and', 'break', 'do', 'else', 'elseif', 'end', 'for', 'function', 'if', 'in', 'local', 'not', 'or', 'repeat', 'return', 'then', 'until', 'while'],
    constants: ['true', 'false', 'nil'],
    builtins: ['print', 'pairs', 'ipairs', 'require'],
    lineComments: ['--'],
    blockComments: ['--[[' => ']]'],
    quotes: [new Quote('[[', ']]', escape: '', multiline: true), new Quote('"'), new Quote("'")],
));

$highlighter = Highlighter::default()->with($lua);
ChampCe qu'il dit
name, aliasesLes noms par lesquels on demande le langage, en minuscules
keywords, types, constants, builtinsListes de mots ; un mot présent dans deux listes prend la première de mot-clé, constante, type, fonction intégrée
lineCommentsLes marques qui ouvrent un commentaire jusqu'à la fin de la ligne
blockCommentsChaque marque d'ouverture, jusqu'à sa marque de fermeture
quotesDes Quote, essayés dans l'ordre, les marques les plus longues d'abord : marques d'ouverture et de fermeture, caractère d'échappement (vide s'il n'y en a pas), s'il s'étend sur plusieurs lignes, s'il contient un seul caractère
stringPrefixesLes lettres ou les signes qui peuvent précéder un guillemet : rbfu en Python
variablePrefixes, specialVariablesLes sigils ($), et les caractères qui forment une variable d'un seul caractère après l'un d'eux (? dans $?)
annotationLe signe des décorateurs et des annotations : @
metaMarksLes marques lues comme méta où qu'elles se trouvent, avant les commentaires : #[, <?php
preprocessorSi un # qui ouvre une ligne ouvre une ligne du préprocesseur, prolongée par une barre oblique inverse
caseInsensitiveSi les mots sont reconnus dans n'importe quelle casse
capitalizedTypesSi un nom qui commence par une majuscule est un type
regexSi / ouvre une expression régulière là où une valeur est attendue
propertyKeysSi un texte entre guillemets suivi de : est une clé
shebangSi une première ligne qui commence par #! est méta
commentAfterSpaceSi un commentaire de ligne ne s'ouvre qu'au début d'un mot, comme # en shell
symbolsSi : devant un mot forme un symbole
macrosSi un mot suivi de ! est un appel de macro
callAfterLes signes après lesquels un mot est un appel : `
wordChars, wordSuffixesLes caractères qu'un mot peut contenir ($ en JavaScript), et ceux qui peuvent le terminer (? et ! en Ruby)
operators, punctuationLes caractères de chacun

Tout autre langage : un Language

Un langage qui n'est pas un langage de programmation implémente Language : name(), aliases() et tokenize(), qui retourne des Token dont les textes, joints, forment la source, sans jamais lever d'exception, quelle que soit la source. Les Ini et Diff fournis sont de courts modèles. Un analyseur lexical qui mélange des langages prend les autres dans son constructeur, comme Dockerfile prend le shell et Twig les langages de balisage et d'expression.

use Phil\Highlight\Language;
use Phil\Highlight\Token;
use Phil\Highlight\TokenType;

final readonly class Csv implements Language
{
    public function name(): string { return 'csv'; }

    public function aliases(): array { return ['tsv']; }

    public function tokenize(string $code): array
    {
        $tokens = [];

        foreach (preg_split('/([,\t\n])/', $code, -1, PREG_SPLIT_DELIM_CAPTURE) ?: [] as $part) {
            if ('' !== $part) {
                $tokens[] = new Token($part, str_contains(",\t\n", $part) ? TokenType::Punctuation : TokenType::String);
            }
        }

        return $tokens;
    }
}

Remplacer un langage fourni

Un langage ajouté sous un nom fourni le remplace, alias compris quand il les revendique : with(new Code(new Syntax('sql', aliases: ['postgresql'], …))) lit le SQL et le PostgreSQL à sa façon, tandis que les autres alias de SQL gardent l'analyseur lexical fourni.