Cela fait près d’un mois que l’on n’avait pas parlé de Markdown. Continuons l’aventure folle qui nous mènera à un parseur Markdown complet.
La dernière fois, nous avons réussi à diviser notre fichier en plusieurs paragraphes. Mais des paragraphes seuls ne font pas un document complet. La prochaine étape logique est d’extraire les en-têtes de notre fichier Markdown.
En-têtes
Si vous lisez ce blog, ou n’importe quel autre blog d’ailleurs, ou bien la plupart des livres, ou des documents à votre boulot, vous avez déjà vu des en-têtes.
Ils structurent un document en un plan facile à lire, nous aident à nous souvenir du contenu et nous permettent de lire un document en diagonale et de quand même avoir une idée du contenu.
En Markdown, les en-têtes utilisent deux syntaxes différentes :
- Setext
- atx
Parce que notre objectif est d’avoir un parseur qui implémente toute la syntaxe décrite sur le site de John Gruber, il nous faudra être capable d’extraire les deux types d’en-têtes.
Setext
Les en-têtes de type Setext ne supportent que deux niveaux de hiérarchie (Les titres et les sous-titres).
Les en-têtes de type Setext sont « soulignées » en utilisant des signes égal (pour des titres) et des tirets (pour des sous-titres). […] Le nombre de
=
ou de-
n’est pas important.
Voici un example de ce à quoi ressemble les en-têtes Setext :
Comprendre la syntaxe du Markdown
=================================
Les en-têtes
------------
Dans l’exemple ci-dessus, j’ai fait en sorte que la longueur des titres et des
caractères qui les soulignent correspondent pour des raisons esthétiques. Mais
comme indiqué dans les spécifications du Markdown, un seul =
ou -
suffit
pour indiquer qu’il s’agit respectivement d’un titre ou d’un sous-titre.
Donc si je récapitule tout ce que l’on a appris, et en prenant en compte ce que l’on sait pour les lignes vides et paragraphes, un en-tête Setext peut être décrit comme suit :
- Un paragraphe (c’est-à-dire du texte délimité par des lignes vides et non indenté)
- La dernière ligne du bloc de texte contient uniquement des
=
ou des-
et les espaces et tabulations en fin de ligne peuvent être ignorés.
Interpréter les en-têtes
Nous savons déjà comment traiter les paragraphes. Essayons de comprendre comment on peut interpréter la dernière ligne de l’en-tête Setext en transformant ce que l’on vient de décrire en une expression régulière.
// Soulignement de titre
/^=+[ \t]*$/
// Soulignement de sous-titre
/^-+[ \t]*$/
Afin de nous assurer que nos deux expressions régulières fonctionnent comme on veut, nous allons utiliser la même méthode que pour l’article précédent : une page web avec un peu de javascript.
Voici le code javascript dont vous aurez besoin pour tester nos deux regex. Comme d’habitude, voici un lien vers la page qui utilise ce code.
|
|
Avec ce code, on récupère nos en-têtes comme suit :
En-tête 1 : Premier titre
En-tête 2 : Sous-titre multiligne
En-tête 3 : Un autre titre
En-tête 4 : Un sous-titre simple
Mais il nous manque quelque-chose d’essentiel à la structure de notre document : les niveaux d’en-têtes.
On a réussi à interpréter nos en-têtes, mais il nous faut aussi identifier et passer au moteur de rendu leurs niveaux respectifs. Tentons de faire ça. Avec nos en-têtes Setext, nous n’avons que deux niveaux différents d’en-têtes à gérer donc ça ne devrait pas être trop compliqué.
Détecter les niveaux des en-têtes
Ajoutez le code suivant sur la page précédente, au début de la boucle forEach, ou bien, utilisez ce lien, si vous êtes pris d’une flemme passagère.
|
|
Il faut également changer le code qui affiche le résultat comme suit :
|
|
Nos modifications nous donnent le résultat suivant :
En-tête 1 (h1) : Premier titre
En-tête 2 (h2) : Sous-titre multiligne
En-tête 3 (h1) : Un autre titre
En-tête 4 (h2) : Un sous-titre simple
Parfait ! On sait maintenant détecter le niveau de notre en-tête. Continuons avec le deuxième type d’en-tête : les en-têtes atx.
atx
Les en-têtes de type atx sont les en-têtes les plus utilisés en Markdown. Ils permettent d’utiliser six niveaux différents d’en-tête. Les en-têtes atx sont à privilégier pour les documents avec une structure complexe. Par exemple, cet article utilise quatre niveaux d’en-têtes.
Ces en-têtes utilisent un à six dièses en début de ligne pour exprimer le niveau de structure. Ils sont écrits comme suit :
# En-tête de premier niveau
## En-tête de second niveau
##### En-tête de cinquième niveau et ainsi de suite
Les en-têtes peuvent également contenir un nombre arbitraire de dièses en fin de ligne. Seuls les dièses en début de ligne définissent le niveau de structure.
# En-tête de premier niveau #
##### En-tête de cinquième niveau et ainsi de suite ################
En gardant tout ça à l’esprit (j’ai à nouveau seulement paraphrasé les spécifications officielles), ainsi que ce que l’on vient de découvrir avec les en-têtes de type Setext, on peut définir les en-têtes atx comme suit :
- Une ligne unique
- Cette ligne doit commencer par un ou plusieurs dièses, et peut contenir un nombre arbitraire de dièses en fin de ligne.
Traduisons ces deux phrases en une expression régulière :
/^(#{1-6})[ ]*(.*?)[ ]+#*[ \t]*$/;
Cette expression régulière est légèrement plus compliquée que celles que nous avons utilisées par le passé. Il existe des outils tels que regex101 qui peuvent décrire le fonctionnement des expressions régulières. Vous pouvez donc vous en servir pour élucider le fonctionnement de celle ci-dessus si vous êtes curieux.
Testons cette regex. On peut utiliser le code que l’on a écrit plus tôt et le modifier avec le contenu de test suivant :
|
|
On va aussi ajuster l’expression régulière utilisée avec celle que l’on a écrit quelques lignes plus haut. Voici la boucle logique que l’on va utiliser :
|
|
On sait maintenant extraire les en-têtes de type atx. En exécutant le code ci-dessus (ou en suivant ce lien), on obtient ce qui suit :
En-tête 1 (h2) : Un titre
En-tête 2 (h1) : Un autre titre sur une ligne adjacente
En-tête 3 (h3) : Un titre avec des dièses en fin de ligne
En-tête 4 (h6) : Un autre titre mais celui-ci fini avec un #
Faire le rendu des en-têtes
La dernière étape est de faire le rendu de nos en-têtes. On va fusionner le code de l’article précédent avec le code que l’on vient d’écrire pour extraire nos en-têtes.
Notre code devrait ressembler à ça :
|
|
La fonction renderHeader
contient le code suivant :
|
|
Ce code commence à être un peu long. Dans le prochain article de cette série, nous le retravaillerons pour le simplifier. Mais au moins, il fonctionne ! Vous pourriez déjà vous en servir pour traiter un document Markdown composé d’en-têtes et de paragraphes.
Pas totalement utile pour l’instant. Pour voir le résultat, vous pouvez aller sur cette page.
Si vous êtes arrivé jusque-là, félicitations ! 🎉 Vous avez lu au moins 160 lignes de code. À partir de l’article suivant, je vais essayer de déplacer le code vers un dépôt sur GitHub pour faciliter votre lecture.