Table of Contents (TOC) for Free

name

Generate TOC

icon

list_alt

description

Unofficial implementation of {toc}, Table of Contents.

instructions



Adds TOC (Table of Contents) features like the official one.

This plugin has two features:
1. Inline TOC: Type "{ntoc}" (numbered) or "{btoc}" (bullet) to insert the TOC. Similar to the official {toc} feature.
2. Note-level TOC: Select "Generate TOC" from note options to add TOC at the beginning of the note. As long as it's there, a new TOC overwrites the old one whenever it is triggered again.

Settings (can be configured in "Account Settings > Plugins > Generate TOC (Gear icon)"):
- Ordered TOC Expression Name: Expression name for numbered TOC. Defaults to "ntoc".
- Unordered TOC Expression Name: Expression name for bullet TOC. Defaults to "btoc".
- Enable Note Level TOC: Set this to "false" to turn off note-level TOC feature. Defaults to true.
- Use Ordered TOC in Note Level TOC: Set this to "false" to make Nove-level TOCs unordered (bullets). Defaults to true.

Disclaimer: I haven't thoroughly tested if this successfully works in every situation. Please use it at your own risk.

Note (Nov 20th, 2024): Unfortunately, I'm no longer using Amplenote and this plugin is no longer supported. Please use all or part of this note without any permission if you are interested in further development.

setting

Ordered TOC Expression Name

setting

Unordered TOC Expression Name

setting

Enable Note Level TOC

setting

Use Ordered TOC in Note Level TOC



Updates:

Nov 20th, 2024

Fixed bugs (thanks User #123766!)

Changed the default plugin expression names ('otoc' and 'utoc' to 'ntoc' and 'btoc', respectively). If you prefer previous ones, you can change them in Plugin Settings, explained in the description.

Unfortunately, this plugin is no longer supported. If you are interested in further development, please use all or part of this note freely without any permission.

May 31st, 2024

Changed icon to "list_alt" (same as the official {toc})

May 30th, 2024

Deprecated {tocf} and added {otoc} and {utoc}. These names can be changed via the Settings.

Changed setting keys

May 26th, 2024

First release


{
_getTOCFromSections(sections, usesOrderedTOC) {
// Get the minimum level of headings (i.e. most top-level heading)
let minHeadingLevel = 3;
for (const section of sections) {
if (section.heading && section.heading.level < minHeadingLevel) {
minHeadingLevel = section.heading.level;
}
}
 
function generateTOCLine(level, text, anchor, usesOrderedTOC) {
const components = [
" ".repeat(level),
usesOrderedTOC ? "1." : "-",
" [",
text,
"](#",
anchor,
") \n\n",
];
return components.join("");
}
 
// Generate TOC
let ret = "";
let isFirstHeading = true;
for (const section of sections) {
if (section.heading) {
if (isFirstHeading && section.heading.level > minHeadingLevel) {
for (let i = 0; section.heading.level - i > minHeadingLevel; i++) {
ret += generateTOCLine(i, "_", "_", usesOrderedTOC);
}
}
isFirstHeading = false;
ret += generateTOCLine(
section.heading.level - minHeadingLevel,
section.heading.text,
section.heading.anchor,
usesOrderedTOC,
);
}
}
return ret;
},
 
_isTOCish(noteContent, usesOrderedTOC) {
const re = RegExp("^(| {4,8})" + (usesOrderedTOC ? "1\\." : "-") + " \\[.+\\]\\(#.+\\) $");
const lines = noteContent.split("\n");
for (let i = 0; i < lines.length; i++) {
if (lines[i] === "") continue;
if (lines[i] === "---") return true;
if (! re.test(lines[i])) return false;
}
return false;
},
 
noteOption: {
check(app) {
return app.settings["Enable Note Level TOC"] !== "false";
},
 
async run(app, noteUUID) {
const usesOrderedTOC = app.settings["Use Ordered TOC in Note Level TOC"] !== "false";
const noteContent = await app.getNoteContent({uuid: noteUUID});
const sections = await app.getNoteSections({uuid: noteUUID});
const toc = this._getTOCFromSections(sections, usesOrderedTOC);
if (this._isTOCish(noteContent, usesOrderedTOC)) {
app.replaceNoteContent({uuid: noteUUID}, toc, {section: {}});
} else {
app.insertNoteContent({uuid: noteUUID}, toc + "---\n\n");
}
},
},
 
async _insertTextRun(app, usesOrderedTOC) {
const sections = await app.getNoteSections({uuid: app.context.noteUUID});
const toc = this._getTOCFromSections(sections, usesOrderedTOC);
const replacedSelection = await app.context.replaceSelection("\n" + toc);
return null;
},
 
insertText: {
"Numbered": {
check(app) {
return app.settings["Ordered TOC Expression Name"] || "ntoc";
},
run(app) {
return this._insertTextRun(app, true);
},
},
 
"Bullet": {
check(app) {
return app.settings["Unordered TOC Expression Name"] || "btoc";
},
run(app) {
return this._insertTextRun(app, false);
},
},
}
}