ext.convertHelpDocs = {markdownDir, htmlDir -> fileTree(markdownDir).getFiles().parallelStream().forEach { File markdownPath -> markdownToHtml(markdownPath.path, "${htmlDir}/${markdownPath.name.replaceAll("\\.md\$", ".html")}") } } static markdownToHtml(markdownPath, htmlPath) { def text = new File(markdownPath).text text = convertHeaders(text) text = convertOrderedLists(text) text = convertUnorderedLists(text) text = convertInlineTags(text) text = addStylesToTags(text) text = insertIndex(text, generateIndex(text)) text = removeWhitespace(text) new File(htmlPath).text = "Help${text}" } static getStyles() { return "body {padding: 0 6px; background-color: #f4f4f4; color: #000;}" + "a {color: #225682}" + "a:visited {color: #644280}" + "li {margin: 4px 0; padding: 1px;}" + "p {text-align: left;}" + "p.wrap{word-wrap: break-word;}" + ".toc {border: 1px solid; display: inline-block; padding: 12px 20px 12px 0; margin: 12px 0;}" + ".toc > h3 {text-align: center; margin: 0;}" + "@media (prefers-color-scheme: dark) {" + "body { background-color: #333; color: #c8c8c8; }" + "a {color: #a0c1de}" + "a:visited {color: #d9bce1}" + "}" } static generateIndex(html) { def entries = html.split("\n").collect( { line -> def matches = line =~ "

(.+)

" if (matches.size() > 0 && matches[0].size() > 2) { return "${matches[0][2]}" } else { return null } }).findAll { it != null } return "

Contents

" + "
    ${entries.collect { "
  1. ${it}
  2. " }.join("\n")}
" + "
" } static insertIndex(html, index) { return html.replaceFirst(" if (line.startsWith("#")) { def headerNumber = 0 for (int i = 0; i < line.length(); i++) { if (line[i] != '#') { headerNumber = i break } } def header = line.replaceAll("^#+", "").trim() def anchor = header.toLowerCase().replaceAll("[^\\d\\p{L}]+", "-").replaceAll("[\\-]+\$", "") return "${header}" } else { return line } } return html.join("\n") } static convertOrderedLists(markdown) { def html = markdown.split("\n").collect { line -> if (line.matches("^\\d+\\..*")) { return "
  • ${line.replaceAll("^\\d+\\.\\s*", "")}
  • " } else { return line } } return html.join("\n").replaceAll("(?\n)
  • ", "
    1. ").replaceAll("
    2. (?!\n
    ") } static convertUnorderedLists(markdown) { boolean inList = false boolean inNestedList = false def html = "" markdown.split("\n").each { line -> def convertedLine = "" def innerLi = line.replaceAll("^\\s*-\\s*", "") if (line.matches("^-.*")) { if (!inList) { convertedLine += "
  • " inNestedList = false } convertedLine += "
  • ${innerLi}
  • " } else if (line.matches("^\\s+-.*")) { if (!inNestedList) { if (html.endsWith("")) { html = html.substring(0, html.length() - 5) } else if (html.endsWith("\n")) { html = html.substring(0, html.length() - 6) } convertedLine += "" } if (inList) { inList = false convertedLine += "" } convertedLine += line } html += convertedLine + "\n" } return html } static convertInlineTags(markdown) { return markdown .replaceAll("\n([^\n<]+?)(\n|\$)", "

    \$1

    ") .replaceAll("_([^_]+)_", "\$1") .replaceAll("[*]{2}(.+?)[*]{2}", "\$1") .replaceAll("\\[([^]]+)\\]\\(([^)]+)\\)", "\$1") .replaceAll("href=\"([^\"]+)-\"", "href=\"\$1\"") .replaceAll("href=\"([^\"]+?)--([^\"]+?)\"", "href=\"\$1-\$2\"") } static addStylesToTags(html) { return html.replaceAll("

    ([^<]+?googlequicksearchbox[^<]+?)

    ", "

    \$1

    ") } static removeWhitespace(html) { return html.replaceAll("\\s+", " ").replaceAll("/> <", "/><") }