Tables normales dans Markdown


Les tables de démarque sont infernales:


  1. Vous ne pouvez pas écrire de texte dans des cellules plus longues que quelques mots, et encore moins une liste.
  2. Si le dialecte autorise le paragraphe 1, il n'est pas pratique de le formater.
  3. Si les cellules ne sont pas alignées, le tableau ne peut pas être lu.
  4. Il n'y a pas de prise en charge pour les tables du même type et l'automatisation, telles que la numérotation des lignes.

Il est temps d'écrire un filtre pour Pandoc qui dessine des tableaux à partir de YAML structuré, avec une numérotation des lignes, une orientation horizontale, des modèles de graphique, et en même temps de savoir comment écrire des filtres Lua.


J'écris habituellement des textes dans Markdown et les convertis au format cible à l'aide de Pandoc. Il s'agit d'un programme qui convertit les documents entre les formats, par exemple, à partir de Markdown, vous pouvez obtenir du HTML et un autre dialecte de MD et DOCX et PDF (plus de 30 formats d'entrée et plus de 50 formats de sortie). Pandoc Markdown possède de nombreuses extensions pratiques pour les liens, les notes de bas de page, les signatures et les formules.


Pandoc fonctionne comme une composition de fonctions (il aurait été écrit en Haskell): un format d'entrée spécifique → une représentation abstraite d'un document → un format de sortie spécifique. Une représentation abstraite peut être modifiée à l'aide de filtres écrits en Lua. Les filtres n'ont pas besoin de connaître le format de sortie, mais ils peuvent en tenir compte.


Notre filtre recherchera des blocs de code abstraits dans la table langage conditionnel dans une représentation abstraite, lira YAML à l'intérieur et générera des représentations abstraites de tables que Pandoc lui-même produira au format cible.


 pandoc --lua-filter table.lua input.md -o output.html 

Quelles sont les alternatives et pourquoi sont-elles pires?


  • Les tableaux HTML fonctionnent uniquement dans Markdown et sont convertis uniquement en HTML; seul le problème du formatage riche en cellules est résolu.
  • Les générateurs de tableaux nécessitent de passer d'un éditeur de texte; il n'est pas pratique de modifier le contenu des cellules qu'ils contiennent ( exemple ).
  • Les plugins éditeurs ( Emacs Org-Mode , plugins VIM ) ne sont pas universels et ne sont pas toujours disponibles.

En revanche, pandoc-crossref et tous les petits pains Pandoc fonctionnent avec le filtre des tableaux récapitulatifs. Le filtre peut également être utilisé pour générer des tables Markdown standard en spécifiant le format de sortie approprié. Des inconvénients:


  • Les cellules ne peuvent pas être fusionnées; Pandoc ne le prend pas (encore) en charge.
  • Pour les tableaux horizontaux, la stylisation doit être effectuée en utilisant le format de sortie, par exemple, via CSS.

La description du tableau comprend trois parties:


  1. Structure de la table


    Une liste ordonnée de graphiques (colonnes):


    • Au minimum, la colonne doit avoir un titre.
    • Afin de pouvoir réorganiser les colonnes sans toucher aux données, l'attribut d'enregistrement affiché dans la colonne ( id ) doit être spécifié.
    • Les colonnes spéciales n'ont pas d'ID, mais ont une description de la façon de les remplir. Vous avez d'abord besoin d'un numéro de série ( special: number ).
    • Alignement des colonnes ( align ).

    De plus, le tableau peut être vertical ou horizontal ( orientation ). Dans ce dernier cas, les graphiques seront des lignes.


  2. Propriétés de la table: identifiant des liens ( id ) et signature ( caption ). Pandoc vous permet de signer des tables, mais pas des blocs de code.


  3. Données sous la forme d'un tableau de dictionnaires YAML.



La structure peut être commune à plusieurs tables, vous pouvez donc la décrire à la fois directement avec la table et une fois dans les métadonnées (front-matter), puis faire référence au modèle nommé.


Plan de mise en œuvre:


  1. À partir des métadonnées du document, nous formons un dictionnaire de modèles.


  2. Pour chaque bloc de code avec la table classe:


    1. Nous analysons les tables YAML.
    2. Si un modèle est spécifié, nous le prenons dans le dictionnaire, sinon nous remplissons le modèle de YAML.
    3. Nous remplissons les propriétés individuelles du tableau de YAML.
    4. Nous formons des entrées de table à partir de YAML (un enregistrement est une ligne dans une table régulière ou une colonne dans une horizontale).
    5. Nous «dessinons» un tableau selon un modèle, des propriétés et des enregistrements.


Le niveau supérieur est implémenté tel qu'écrit (tout le code est disponible sur le lien à la fin de l'article):


 function Pandoc(doc) local meta_templates = doc.meta['table-templates'] if meta_templates then for name, value in pairs(meta_templates) do templates[name] = parse_template(value) end end local blocks = pandoc.walk_block(pandoc.Div(doc.blocks), { CodeBlock = create_table }) return pandoc.Pandoc(blocks, doc.meta) end 

La fonction parse_template() convertit légèrement le format des métadonnées. Pandoc représente leurs valeurs en tant MetaBlock et MetaInline . Soit des lignes simples en sont faites à l' pandoc.utils.stringify() (par exemple, l'orientation), soit des éléments visuels (par exemple, un bloc de texte dans l'en-tête de la colonne).


À propos du débogage. Il existe de nombreux exemples dans la documentation Pandoc, mais les types ne sont pas très détaillés. Pour le débogage des filtres, il est pratique d'avoir une fonction de vidage variable. Les bibliothèques sérieuses impriment trop de détails, je préfère l'une des options simples.


Fonctions de conversion des métadonnées en éléments de document
 local function to_inlines(content) if content == nil then return {} elseif type(content) == 'string' then return {pandoc.Str(content)} elseif type(content) == 'number' then return to_inlines(tostring(content)) elseif content.t == 'MetaInlines' then inlines = {} for i, item in ipairs(content) do inlines[i] = item end return inlines end end local function to_blocks(content) if (type(content) == 'table') and content.t == 'MetaBlocks' then return content else return {pandoc.Plain(to_inlines(content))} end end 

La fonction create_table() est appelée pour chaque bloc de code en triple backtics.


Nous nous intéressons uniquement aux blocs de code «dans le langage» du table :


 if not contains('table', block.classes) then return block end 

Pour analyser YAML à l'intérieur d'un bloc de code, nous créons un document composé uniquement de métadonnées YAML, analysons-le avec Pandoc et ne laissons que des métadonnées:


 local meta = pandoc.read('---\n' .. block.text .. '\n---').meta 

Ensuite, à partir de la meta lien vers un modèle ou une structure de table et les propriétés d'une table spécifique est lu.


La fonction fill_table() lit des meta sur les attributs spécifiés dans la description du graphique. Au même stade, si la colonne est marquée comme spéciale, son contenu est généré:


 local data = {} for i, serie in ipairs(template.series) do if serie.special == 'number' then data[i] = to_blocks(#datum + 1) else data[i] = to_blocks(item[serie.id]) end end 

La fonction format_table() forme le tableau de cellules résultant en fonction de l'orientation du tableau et crée un objet tableau abstrait. Il convient de noter que si les largeurs ou les en-têtes doivent être définis pour toutes les colonnes ou pour aucune, sinon Pandoc ne créera tout simplement pas de tableau.


Le script terminé peut être placé dans ~/.local/share/pandoc (le répertoire de données ~/.local/share/pandoc ) pour y accéder par son nom depuis n'importe où.


PS


À propos de la prise en compte des filtres de format de sortie. Par exemple, j'écris des spoilers dans Pandoc comme ceci:


 ::: {.spoiler title=""}  . ::: 

Il n'y a pas de spoilers dans le modèle de document Pandoc, donc le filtre doit produire des blocs bruts de la manière suivante environ. Bien sûr, le vrai code ( spoiler.lua ) doit prendre en compte le format de sortie via la variable FORMAT , et non mécaniquement: le fragment ci-dessous produit des blocs bruts en HTML, bien que le format de sortie soit markdown.


 function Div(el) if not el.attr or not contains('spoiler', el.attr.classes) then return el end local title = el.attr.attributes['title'] or '' table.insert(el.content, 1, pandoc.RawBlock('html', '<' .. 'spoiler title="' .. title .. '">', 'RawBlock')) table.insert(el.content, pandoc.RawBlock('html', '<' .. '/spoiler>', 'RawBlock')) return el.content end 

Les références


Source: https://habr.com/ru/post/fr474826/


All Articles