Skip to content

Commit

Permalink
Add [TOC] support
Browse files Browse the repository at this point in the history
  • Loading branch information
Moonbase59 committed Apr 5, 2024
1 parent b8b4665 commit 1ccc483
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 15 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<!-- ToC begin -->` and `<!-- ToC end -->` 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.

Expand All @@ -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 `<!-- ToC begin -->` and the end marker `<!-- ToC end -->` 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 `<!-- ToC begin -->` and the end marker `<!-- ToC end -->` 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.

Expand Down
67 changes: 53 additions & 14 deletions gh-toc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -84,6 +85,31 @@ function tocIt(inputMD, minHeading, maxHeading, fullMD, addAnchors, useID) {
}
}

// save position of <!-- ToC begin --> and <!-- ToC end --> lines
// This tries to avoid sequence errors and single begin/end comments.
// For convenience, it now also supports a single [TOC]
var tocComment = /^<!--\s*ToC (begin|end)\s*-->$|^\[(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
Expand Down Expand Up @@ -140,8 +166,6 @@ function tocIt(inputMD, minHeading, maxHeading, fullMD, addAnchors, useID) {
}
}

//console.log(anchorTracker);

// build ToC
toc =
"<!-- ToC begin -->\n"
Expand Down Expand Up @@ -178,21 +202,36 @@ function tocIt(inputMD, minHeading, maxHeading, fullMD, addAnchors, useID) {
// <!-- ToC begin -->
// ...
// <!-- ToC end -->
// 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 = /<!--\s*ToC begin\s*-->.*?<!--\s*ToC end\s*-->/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"
+ "<!-- ToC begin -->\n<!-- ToC end -->\n"
+ "These should be exactly as shown.");
}
} else {
outputMD = toc + '\n';
}

return outputMD;
Expand Down

0 comments on commit 1ccc483

Please sign in to comment.