From edf273e6043a64857ddd75458397c65d219f5ccf Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 6 Sep 2025 09:47:55 +0200 Subject: [PATCH] feat: improve directives parsing --- script.js | 122 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 97 insertions(+), 25 deletions(-) diff --git a/script.js b/script.js index e12b2d5..db6fc4f 100644 --- a/script.js +++ b/script.js @@ -747,32 +747,104 @@ function parseRst(text) { return `
`; }); - // Custom directives (.. directive::) - html = html.replace(/^\.\. ([^:]+)::\s*(.*)$/gim, '
'); - - // Directive options (:option: value) - html = html.replace(/^ :([^:]+):\s*(.*)$/gim, (match, key, value) => { - // Remplacer les liens par des placeholders dans les options - const processedValue = value.replace(/`([^<]+) <([^>]+)>`_/g, (match, text, url) => { - return createLinkPlaceholder(text, url); - }); - return `
${key}: ${processedValue}
`; - }); - - // Special handling for big_button directive - html = html.replace(/
]*>/g, '
'); - - // Process directive content blocks (indented text after directive) - html = html.replace(/(
' + processedContent + '
'; + // Process directives with their options and content - regex corrigée pour s'arrêter aux lignes non indentées + html = html.replace(/\.\. ([^:]+)::([\s\S]*?)(?=\n\S|\.\.|$)/g, (match, directive, block) => { + // Gestion spéciale pour big_button + const isBigButton = directive === 'big_button'; + const className = isBigButton ? 'rst-big-button' : `rst-directive rst-directive-${directive}`; + + let result = `
`; + + if (block && block.trim()) { + const lines = block.split('\n'); + let options = ''; + let content = ''; + let inContentSection = false; + + // Séparer d'abord les lignes d'options et de contenu + let optionLines = []; + let contentLines = []; + let isInOptions = true; + + for (const line of lines) { + if (!line.trim()) { + // Ligne vide - si on est en section contenu, l'ajouter + if (!isInOptions) { + contentLines.push(line); + } + continue; + } + + // Vérifier si c'est une option (:key: value) + const optionMatch = line.match(/^[ \t]*:([^:]+):\s*(.*)$/); + + if (optionMatch && isInOptions) { + // C'est une option et on est toujours dans la section options + optionLines.push(line); + } else { + // C'est du contenu - marquer qu'on est sorti de la section options + isInOptions = false; + contentLines.push(line); + } + } + + // Traiter les options + for (const line of optionLines) { + const optionMatch = line.match(/^[ \t]*:([^:]+):\s*(.*)$/); + if (optionMatch) { + const [, key, value] = optionMatch; + const processedValue = value.replace(/`([^<]+) <([^>]+)>`_/g, (match, text, url) => { + return createLinkPlaceholder(text, url); + }); + options += `
${key}: ${processedValue}
`; + } + } + + // Traiter le contenu - trouver l'indentation minimale du contenu uniquement + if (contentLines.length > 0) { + // Trouver l'indentation minimale parmi les lignes de contenu non vides + let minContentIndent = null; + for (const line of contentLines) { + if (line.trim()) { + const indentMatch = line.match(/^([ \t]*)/); + if (indentMatch) { + const indent = indentMatch[1]; + if (minContentIndent === null || indent.length < minContentIndent.length) { + minContentIndent = indent; + } + } + } + } + + // Construire le contenu en préservant l'indentation relative + for (const line of contentLines) { + if (!line.trim()) { + content += '\n'; + } else { + let contentLine = line; + if (minContentIndent && line.startsWith(minContentIndent)) { + contentLine = line.substring(minContentIndent.length); + } else { + // Fallback : enlever toute indentation de début + contentLine = line.replace(/^[ \t]+/, ''); + } + content += contentLine + '\n'; + } + } + } + + result += options; + + if (content.trim()) { + const processedContent = content.trim().replace(/`([^<]+) <([^>]+)>`_/g, (match, text, url) => { + return createLinkPlaceholder(text, url); + }); + result += `
${processedContent}
`; + } } - return directive + '
'; + + result += '
'; + return result; }); // Bold