The scenarios below show different features of toc
Let's say you want to structure your javascript file "example.js".
Single-line comments in this language start with //
.
You open your file and add these comments where you need them:
#!/usr/bin/env node
// ################################################################ Main section
let Section1 = "Write //, 64 hash characters and the name of section"
// ################################ Nested section
let Section2 = "Write //, 32 hash characters and the name of section"
// ################ Nested section
let Section3 = "Write //, 16 hash characters and the name of section"
// ######## Nested section
let Section4 = "Write //, 8 hash characters and the name of section"
// #### Nested section
let Section5 = "Write //, 4 hash characters and the name of section"
If you run toc example.js
, the program will output the following (stdout):
// ┌───────────────────────────────────────────────────────────────┐
// │ Contents of example.js │
// ├───────────────────────────────────────────────────────────────┘
// │
// ├──┐Main section
// │ └──┐Nested section
// │ └──┐Nested section
// │ └──┐Nested section
// │ └── Nested section
// │
// └───────────────────────────────────────────────────────────────
If you want to output the toc we just saw to the file, you should run toc -f example.js
and get (stderr):
Adding toc to file example.js
By opening "example.js", the file content will be:
Click to view the original `example.js` with toc
#!/usr/bin/env node
// ┌───────────────────────────────────────────────────────────────┐
// │ Contents of example.js │
// ├───────────────────────────────────────────────────────────────┘
// │
// ├──┐Main section
// │ └──┐Nested section
// │ └──┐Nested section
// │ └──┐Nested section
// │ └── Nested section
// │
// └───────────────────────────────────────────────────────────────
// ################################################################ Main section
let Section1 = "Write //, 64 hash characters and the name of section"
// ################################ Nested section
let Section2 = "Write //, 32 hash characters and the name of section"
// ################ Nested section
let Section3 = "Write //, 16 hash characters and the name of section"
// ######## Nested section
let Section4 = "Write //, 8 hash characters and the name of section"
// #### Nested section
let Section5 = "Write //, 4 hash characters and the name of section"
Notice how toc
recognized the shebang for node
and added the table of contents after it.
If you run again toc -f example.js
, it recognizes that there is no need to update the toc, as no changes have been made.
However, if you add new sections to the file and run again toc -f example.js
, it will update the file accordingly:
Updating toc in example.js
Click to view the modified `example.js` with toc
#!/usr/bin/env node
// ┌───────────────────────────────────────────────────────────────┐
// │ Contents of example.js │
// ├───────────────────────────────────────────────────────────────┘
// │
// ├──┐Main section
// │ ├──┐New section
// │ │ └── Also New section
// │ ├──┐New section
// │ │ └── Also New section
// │ └──┐Nested section
// │ └──┐Nested section
// │ └──┐Nested section
// │ └── Nested section
// │
// └───────────────────────────────────────────────────────────────
// ################################################################ Main section
let Section_1 = "Write //, 64 hash characters and the name of section"
// ################################ New section
let Section_1_1 = "Write //, 32 hash characters and the name of section"
// ################ Also New section
let Section_1_1_1 = "Write //, 32 hash characters and the name of section"
// ################################ New section
let Section_1_1 = "Write //, 32 hash characters and the name of section"
// ################ Also New section
let Section_1_1_1 = "Write //, 32 hash characters and the name of section"
// ################################ Nested section
let Section_1_2 = "Write //, 32 hash characters and the name of section"
// ################ Nested section
let Section_1_2_3 = "Write //, 16 hash characters and the name of section"
// ######## Nested section
let Section_1_2_4 = "Write //, 8 hash characters and the name of section"
// #### Nested section
let Section_1_2_5 = "Write //, 4 hash characters and the name of section"
You can alternatively write the files you want to keep updated in a simple glob list:
# ignore-this-file.txt
README.md
USAGE.md
CHANGELOG.md
toc/cli.py
toc/toc.py
# glob expansion
tests/test*.py
To keep these files up-to-date, you just need to run toc -l -f files.txt
:
Skipping unchanged toc in "README.md"
Updating toc in "USAGE.md"
Adding toc to "CHANGELOG.md"
Updating toc in "toc/cli.py"
Updating toc in "toc/toc.py"
Skipping unchanged toc in "tests/test_cli.py"
SSkipping unchanged toc in "tests/test_toc.py"
Note that more than one list can be passed in a single command, lines starting with "#" are ignored, and there is support for glob expansion.
If you feel brave enough, you can run toc *
over your entire code base, as its AI1 will:
- skip directories
- skip non-text files
- skip non-existing files
- skip non-readable files
- skip non-writable files
- skip files that don't have suitable "section" comments
- skip files whose toc is already up-to-date
- only update files whose toc can be added or updated safely2
- preserve shebangs, markdown frontmatters and other declarations of edited files
For very long files, it may come in handy to run toc -n example.js
to see the line number of each section, similar to the page numbers in the table of contents of a book:
// ┌───────────────────────────────────────────────────────────────┐
// │ Contents of example.js │
// ├───────────────────────────────────────────────────────────────┘
// │
// ├──┐Main section 19
// │ ├──┐New section 23
// │ │ └── Also New section 27
// │ ├──┐New section 31
// │ │ └── Also New section 35
// │ └──┐Nested section 39
// │ └──┐Nested section 43
// │ └──┐Nested section 47
// │ └── Nested section 51
// │
// └───────────────────────────────────────────────────────────────
If you want to only see headings up to a certain level, you can limit them with toc -d 2 example.js
:
// ┌───────────────────────────────────────────────────────────────┐
// │ Contents of example.js │
// ├───────────────────────────────────────────────────────────────┘
// │
// ├──┐Main section
// │ ├── New section
// │ ├── New section
// │ └── Nested section
// │
// └───────────────────────────────────────────────────────────────
But how could toc
recognize that //
is the proper comment character for that file?
Well, thanks to AI3 toc
supports most programming and markup languages, from COBOL to Carbon.
In case it doesn't work as expected, you can force the behavior by running toc -c "//" example.xyz
.
You can also force toc to consider an arbitrary file extension by running toc -e "html" new-file-format.html6
.
toc
can read stdin by using -
as an argument.
You may want to set an extension as well: curl -s https://github.com/AlphaJack/toc | toc -e html -
Result:
<!--
// ┌───────────────────────────────────────────────────────────────┐
// │ Contents of stdin.html │
// ├───────────────────────────────────────────────────────────────┘
// │
// ├──┐Search code, repositories, users, issues, pull requests...
// │ └──┐Use saved searches to filter your results more quickly
// │ └── License
// ├──┐AlphaJack/toc
// │ ├──┐About
// │ │ ├── Topics
// │ │ ├── Resources
// │ │ ├── License
// │ │ ├── Stars
// │ │ ├── Watchers
// │ │ └── Forks
// │ ├── Languages
// │ └──┐Footer
// │ └── Footer navigation
// │
// └───────────────────────────────────────────────────────────────
-->
For testing purposes, you can run toc -o output.py input.py
to choose a different output file.
Note that this option does nothing if the toc in "input.py" is already up-to-date.
The -o
flag is incompatible with the -l
one.
You can run toc -h
for usage info and toc -v
to read the current version
For these file types, you don't need to write comments, as toc
can leverage the language syntax to build the table of contents
For AsciiDoc files, just organize your sections with one or more =
:
= Document Title (Level 0)
== Level 1 Section Title
= Level 0 Section Title (Part)
== Level 1 Section Title
=== Level 2 Section Title
==== Level 3 Section Title
===== Level 4 Section Title
====== Level 5 Section Title
For Beancount files, just organize your sections with one or more *
:
* Options
; comment
* Transactions
** FY2020
2020-04-20 * "Food"
Assets:Bank -20.00 EUR
Expenses:Groceries
For HTML files, just use regular headings tags such as <h1>
, <h2>
and so on:
<!doctype html>
<html>
<h1>Title</h1>
<h2>Subtitle</h2>
</html>
For LaTeX files, just use regular headings, from chapter
to subparagraph
:
\chapter{My chapter}
\section{First section of the chapter}
\subsection{First child of the section}
\section{Second section of the chapter}
For man
pages such as groff and mandoc, just use regular headings indicators .\TH
, .\SH
and .\Ss
:
.TH MAN 1 "2023-09-23" "2.12.0" "Manual pager utils"
.SH NAME
man \- an interface to the system reference manuals
.SH SYNOPSIS
.\" The general command line
For Markdown files, just organize your sections with one or more #
:
# Title
## Section
For Perl files, use the default Pod heading style:
=pod
=encoding utf8
=head1 Title
=head2 Section
=for comment
Text
=cut
For reStructuredText files, just use the conventional heading format:
*******
Chapter
*******
=======
Section
=======
----------
Subsection
----------
For Typst files, just organize your sections with one or more =
(like for AsciiDoc):
= Introduction
= Background
== History
== State of the Art
= Analysis
== Setup
These languages do not support single-line comments, and thus every comment should be wrapped by a multi-line comment separator
For CSS files, you have to wrap your //
comments between /*
and */
:
/*
// ################################################################ Landscape touchscreen
*/
@media (orientation: landscape) and (hover: none) and (pointer: coarse) {
/*
// ################################ Element selectors
*/
body {background-color: blue;}
} /* end Landscape touchscreen */
For OCaml files, you have to wrap your *
comments between (*
and *)
:
(*
* ################################################################ Unique section
*)
let () = print_endline "Hello, World!"
If you place your Vim Modeline / Emacs mode as the first line, the toc will be appended after:
// vim: noai:ts=4:sw=4
#include <stdlib.h>
void main() { exit(0);}
If you are using RStudio, you may want to end your comments with at least 4 -
, =
or #
.
This marks the comment as a foldable section:
# ################################################################ Foldable section 1 ####
print("Collapse me!")
# ################################ Foldable section 2 ####
print("Collapse me!")