diff --git a/README.md b/README.md index 6a79ec5..e9f3163 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ I fully agree. This fork has lots of bugfixes and additional features: - Much better code block support (indented, backticks, tildes, differing number of them). - YAML front matter support. - Optional output of the full Markdown file, ToC inserted between `` and `` HTML comments, instead of just the ToC. You _can_ insert the ToC multiple times, although I don’t see a use case for that. + **Now also supports a single `[TOC]`, for convenience.** - Optional named anchor generation for each ToC element: `—` for GitHub, `HTML` or `{#…}` for other uses. - Optional use of `id` instead of `name` attribute in generated HTML anchors. @@ -32,7 +33,8 @@ Note _gh-toc_ works with ATX-type headings (`###`). It doesn’t try to parse fo ## Known problems -- _gh-toc_ will "brute-force replace" all text between the ToC start marker `` and the end marker `` with the new Table of Contents, _even if they are in a code block_. Avoid that for now, or don’t use the _Full MD_ option in this case and insert the ToC manually. +- ~~_gh-toc_ will "brute-force replace" all text between the ToC start marker `` and the end marker `` with the new Table of Contents, _even if they are in a code block_. Avoid that for now, or don’t use the _Full MD_ option in this case and insert the ToC manually.~~ + _Now fixed and ToC inserting/replacing is safe, even if the "ToC begin/end" comments are out of sequence or one is missing. An alert will pop up if none are found and the ToC cannot be inserted in FullMD mode._ - Selecting _FullMD_ + _Anchor: \{\#…\}_ will overwrite other than anchor definitions within the curly braces, i.e. `{#anchor .red}` → `{#new-anchor}`. Selecting _Anchor: –_ (None) doesn’t modify existing curly brace definitions. diff --git a/gh-toc.js b/gh-toc.js index f21f9e2..1fc722c 100644 --- a/gh-toc.js +++ b/gh-toc.js @@ -21,7 +21,8 @@ function tocIt(inputMD, minHeading, maxHeading, fullMD, addAnchors, useID) { var frontmatterEndExpected = false; var lastLineBlank = false; var indentedCodeEndExpected = false; - var tocTracker = {}; + var tocTracker = []; + var tocNumber = 0; for(var line = 0; line < inputMDLines.length; ++line) { //var inputMDLine = inputMDLines[line].trim(); @@ -84,6 +85,31 @@ function tocIt(inputMD, minHeading, maxHeading, fullMD, addAnchors, useID) { } } + // save position of and lines + // This tries to avoid sequence errors and single begin/end comments. + // For convenience, it now also supports a single [TOC] + var tocComment = /^$|^\[(TOC)\]$/gumis.exec(inputMDLine); + if (tocComment) { + var r = ""; + if (tocComment[1] === undefined) { + r = tocComment[2].toLowerCase(); + } + else { + r = tocComment[1].toLowerCase(); + } + if (r == "begin" || r == "toc") { + tocNumber++; + tocTracker[tocNumber] = {"begin": line, "end": -1}; + // [TOC] is single, set end line for replace + if (r == "toc") { + tocTracker[tocNumber]["end"] = line; + } + } else if (tocNumber > 0 && tocTracker[tocNumber]["end"] < 0){ + tocTracker[tocNumber]["end"] = line; + } + continue; + } + // Now find and handle ATX headings var match = /^ {0,3}(#+) (.*?)( {.*})?$/.exec(inputMDLine); // match: $1=ATX header, $2=title, $3=last {} block incl. blank before @@ -140,8 +166,6 @@ function tocIt(inputMD, minHeading, maxHeading, fullMD, addAnchors, useID) { } } - //console.log(anchorTracker); - // build ToC toc = "\n" @@ -178,21 +202,36 @@ function tocIt(inputMD, minHeading, maxHeading, fullMD, addAnchors, useID) { // // ... // - // These two lines must be exactly as shown! - // FIXME: Will also replace if "ToC begin/end" are in code blocks. + // These two lines should be exactly as shown. + // This works safely now, ignoring wrong ToC begin/end sequence & missing ones. if (fullMD) { - const re = /.*?/gmusi; - var tocReplace = re.test(inputMD); - if (tocReplace) { - outputMD = inputMD.replace(re, toc); - } else { - outputMD = ""; - alert("Cannot insert Table of Contents, missing lines\n" + var tocLines = toc.split("\n"); + var outputMDLines = inputMDLines; + var lineOffset = 0; + var changed = false; + + function insertToc(v, i, arr) { + // end=-1 signifies a ToC begin without end, + // and we can’t have a ToC end on the first line. + if (v["end"] > 0) { + toRemove = v["end"] - v["begin"] + 1; + // beware: splice modifies the array + removed = outputMDLines.splice( + v["begin"] + lineOffset, toRemove, ...tocLines); + lineOffset += -removed.length + tocLines.length; + changed = true; + } + } + + tocTracker.forEach(insertToc); + + outputMD = outputMDLines.join('\n'); + + if (!changed) { + alert("Cannot insert Table of Contents, missing one or both of\n" + "\n\n" + "These should be exactly as shown."); } - } else { - outputMD = toc + '\n'; } return outputMD;