diff --git a/README.md b/README.md
index 7fbebba..8d8859a 100644
--- a/README.md
+++ b/README.md
@@ -100,6 +100,3 @@ If you have a suggestion or you found a bug, you can use GitHub [issues](https:/
See [CHANGELOG.md](./CHANGELOG.md)
-[^1]: No, not really, it's just a match-case statement using the file extension, defaulting to "#"
-[^2]: Not even, it's just a bunch of if-else and try-excepts statement that may prevent catastrophic damage
-[^3]: The outdated toc to be replaced is defined as the the first match of a non-greedy regex
diff --git a/USAGE.md b/USAGE.md
index 4a18b69..9687e20 100644
--- a/USAGE.md
+++ b/USAGE.md
@@ -18,33 +18,50 @@
// │ │ ├── Markdown
// │ │ ├── Beancount
// │ │ └── Perl
-// │ └──┐Wrap around comments needed
-// │ ├── CSS
-// │ ├── HTML
-// │ └── OCaml
+// │ ├──┐Wrap around comments needed
+// │ │ ├── CSS
+// │ │ ├── HTML and Quarto
+// │ │ └── OCaml
+// │ └──┐Compatibility with third-party editors
+// │ ├── Vim and Emacs
+// │ └── RStudio
+// ├── ################################################################ First section ----
+// ├── text 1
+// ├── ################################ Second section ####
+// ├── text 2
+// ├── ################################ Third section ====
// │
// └───────────────────────────────────────────────────────────────
-->
# Detailed TOC usage
-The scenarios below show different features.
+The scenarios below show different features of `toc`
+
+
- [Detailed TOC usage](#detailed-toc-usage)
- - [Read the table of contents](#read-the-table-of-contents)
- - [Embed the table of contents in the original file](#embed-the-table-of-contents-in-the-original-file)
- - [Set a custom comment character](#set-a-custom-comment-character)
- - [Show line numbers](#show-line-numbers)
- - [Process multiple files](#process-multiple-files)
- - [Exceptional file types](#exceptional-file-types)
- - [No comments needed](#no-comments-needed)
+ * [Read the table of contents](#read-the-table-of-contents)
+ * [Embed the table of contents in the original file](#embed-the-table-of-contents-in-the-original-file)
+ * [Process multiple files](#process-multiple-files)
+ * [Show line numbers](#show-line-numbers)
+ * [Set a custom comment character](#set-a-custom-comment-character)
+ * [Redirect output to another file](#redirect-output-to-another-file)
+ * [Other commands](#other-commands)
+ * [Exceptional file types](#exceptional-file-types)
+ + [No comments needed](#no-comments-needed)
- [Markdown](#markdown)
- [Beancount](#beancount)
- [Perl](#perl)
- - [Wrap around comments needed](#wrap-around-comments-needed)
+ + [Wrap around comments needed](#wrap-around-comments-needed)
- [CSS](#css)
- - [HTML](#html)
+ - [HTML and Quarto](#html-and-quarto)
- [OCaml](#ocaml)
+ + [Compatibility with third-party editors](#compatibility-with-third-party-editors)
+ - [Vim and Emacs](#vim-and-emacs)
+ - [RStudio](#rstudio)
+
+
## Read the table of contents
@@ -79,7 +96,17 @@ let Section5 = "Write //, 4 hash characters and the name of section"
If you run `toc example.js`, the program will output the following (stdout):
```js
-
+// ┌───────────────────────────────────────────────────────────────┐
+// │ Contents of example.js │
+// ├───────────────────────────────────────────────────────────────┘
+// │
+// ├──┐Main section
+// │ └──┐Nested section
+// │ └──┐Nested section
+// │ └──┐Nested section
+// │ └── Nested section
+// │
+// └───────────────────────────────────────────────────────────────
```
## Embed the table of contents in the original file
@@ -252,7 +279,7 @@ If you feel brave enough, you can run `toc *` over your entire code base, as its
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:
-```
+```js
// ┌───────────────────────────────────────────────────────────────┐
// │ Contents of example.js │
// ├───────────────────────────────────────────────────────────────┘
@@ -303,10 +330,10 @@ For Markdown files, you don't need to write comments, just organize your section
For [Beancount](https://raw.githubusercontent.com/beancount/beancount/master/examples/example.beancount) files, it's the same for Markdown, but you use `*` instead:
-```beancount
+```ini
* Options
-; text
+; comment
* Transactions
** FY2020
@@ -370,9 +397,9 @@ For CSS files, you have to wrap your `//` comments between `/*` and `*/`:
-#### HTML
+#### HTML and Quarto
-For HTML files, you have to wrap your `//` comments between ``:
+For HTML and Quarto files, you have to wrap your `//` comments between ``:
Click to view `example.html`
@@ -418,3 +445,43 @@ let () = print_endline "Hello, World!"
```
+
+
+### Compatibility with third-party editors
+
+#### Vim and Emacs
+
+If you place your Vim Modeline / Emacs mode as the first line, the toc will be appended after
+
+
+#### RStudio
+
+If you are using RStudio, you may want to end your comments with at least 4 `-`, `=` or `#`.
+This marks the comment as a foldable sections:
+
+
+ Click to view `example.R`
+
+```r
+# ################################################################ Foldable section 1 ----
+
+print("Collapse me!")
+
+# ################################ Foldable section 2 ####
+
+print("Collapse me!")
+
+# ################################ Foldable section 3 ====
+
+print("Collapse me!")
+```
+
+
+
+
+
+
+
+[^1]: No, not really, it's just a match-case statement using the file extension, defaulting to "#"
+[^2]: Not even, it's just a bunch of if-else and try-excepts statement that may prevent catastrophic damage
+[^3]: The outdated toc to be replaced is defined as the the first match of a non-greedy regex
diff --git a/tests/input/bash_vim_modeline.sh b/tests/input/bash_vim_modeline.sh
new file mode 100644
index 0000000..25fe3fd
--- /dev/null
+++ b/tests/input/bash_vim_modeline.sh
@@ -0,0 +1,9 @@
+# vim:set ts=4 sw=4 ft=sh et:
+
+# ################################################################ Nested 1
+# ################################ Nested 2
+# ################ Nested 3
+# ######## Nested 4
+# #### Nested 5
+# ## Nested 6
+echo "OK"
diff --git a/tests/input/markdown_empty_first_line.md b/tests/input/markdown_empty_first_line.md
deleted file mode 100644
index 84c8c0f..0000000
--- a/tests/input/markdown_empty_first_line.md
+++ /dev/null
@@ -1,4 +0,0 @@
-
-# Markdown with empty first line
-
-A small toc should be placed before first heading
diff --git a/tests/input/r_simple.R b/tests/input/r_simple.R
new file mode 100644
index 0000000..3f2aede
--- /dev/null
+++ b/tests/input/r_simple.R
@@ -0,0 +1,16 @@
+# ################################################################ Head 1 ####
+
+# test
+
+# ################################ Head 2 ====
+
+# test
+
+# ################ Head 3 ----
+
+# test
+
+## ################ Invalid ----
+
+
+#
\ No newline at end of file
diff --git a/tests/reference/bash_vim_modeline.sh b/tests/reference/bash_vim_modeline.sh
new file mode 100644
index 0000000..88fbabe
--- /dev/null
+++ b/tests/reference/bash_vim_modeline.sh
@@ -0,0 +1,22 @@
+# vim:set ts=4 sw=4 ft=sh et:
+
+# ┌───────────────────────────────────────────────────────────────┐
+# │ Contents of bash_vim_modeline.sh │
+# ├───────────────────────────────────────────────────────────────┘
+# │
+# ├──┐Nested 1
+# │ └──┐Nested 2
+# │ └──┐Nested 3
+# │ └──┐Nested 4
+# │ └──┐Nested 5
+# │ └── Nested 6
+# │
+# └───────────────────────────────────────────────────────────────
+
+# ################################################################ Nested 1
+# ################################ Nested 2
+# ################ Nested 3
+# ######## Nested 4
+# #### Nested 5
+# ## Nested 6
+echo "OK"
diff --git a/tests/reference/python_broken.toc.py b/tests/reference/python_broken_toc.py
similarity index 100%
rename from tests/reference/python_broken.toc.py
rename to tests/reference/python_broken_toc.py
diff --git a/tests/reference/r_simple.R b/tests/reference/r_simple.R
new file mode 100644
index 0000000..2e7404d
--- /dev/null
+++ b/tests/reference/r_simple.R
@@ -0,0 +1,26 @@
+# ┌───────────────────────────────────────────────────────────────┐
+# │ Contents of r_simple.R │
+# ├───────────────────────────────────────────────────────────────┘
+# │
+# ├──┐Head 1
+# │ └──┐Head 2
+# │ └── Head 3
+# │
+# └───────────────────────────────────────────────────────────────
+
+# ################################################################ Head 1 ####
+
+# test
+
+# ################################ Head 2 ====
+
+# test
+
+# ################ Head 3 ----
+
+# test
+
+## ################ Invalid ----
+
+
+#
\ No newline at end of file
diff --git a/tests/test_toc.py b/tests/test_toc.py
index 7a55a52..62fb89c 100644
--- a/tests/test_toc.py
+++ b/tests/test_toc.py
@@ -107,15 +107,19 @@ def test_input_files(self):
output_content, reference_content = output.read(), reference.read()
comparison = True if output_content == reference_content else False
with self.subTest(comparison=comparison):
- self.assertTrue(comparison, f'Unexpected output processing "{file.name}", please check "{self.output_dir}"')
+ self.assertTrue(comparison, f'Unexpected content (should be different) processing "{file.name}", please check "{self.output_dir}"')
+ elif output_file.is_file():
+ comparison = False
+ with self.subTest(comparison=comparison):
+ self.assertTrue(comparison, f'Unexpected output file (should be none) processing "{file.name}", please check "{self.output_dir}"')
elif reference_file.is_file():
comparison = False
with self.subTest(comparison=comparison):
- self.assertTrue(comparison, f'Unexpected empty output processing "{file.name}", please check "{self.output_dir}"')
+ self.assertTrue(comparison, f'Unexpected empty output (should be something) processing "{file.name}", please check "{self.output_dir}"')
else:
comparison = True
with self.subTest(comparison=comparison):
- self.assertTrue(comparison, f'Expected empty output processing "{file.name}"')
+ self.assertTrue(comparison, f'Expected empty output (ok) processing "{file.name}"')
# @classmethod
# def tearDownClass(cls):
diff --git a/toc/toc.py b/toc/toc.py
index 12d6017..171e9c6 100644
--- a/toc/toc.py
+++ b/toc/toc.py
@@ -59,7 +59,7 @@ def __init__(self, input: str = "", output=None, lineNumbers: bool = False, char
def set_character(self):
# automatically select the comment type from its extension, if not already set
match self.extension:
- case "c" | "carbon" | "cc" | "coffee" | "cpp" | "cs" | "css" | "d" | "dart" | "go" | "h" | "hpp" | "htm" | "html" | "hxx" | "java" | "js" | "kt" | "md" | "pas" | "php" | "pp" | "proto" | "qs" | "rs" | "scala" | "sc" | "swift" | "ts" | "typ" | "xml" | "zig":
+ case "c" | "carbon" | "cc" | "coffee" | "cpp" | "cs" | "css" | "d" | "dart" | "go" | "h" | "hpp" | "htm" | "html" | "hxx" | "java" | "js" | "kt" | "md" | "pas" | "php" | "pp" | "proto" | "qmd" | "qs" | "rs" | "scala" | "sc" | "swift" | "ts" | "typ" | "xml" | "zig":
self.character = "//"
case "ahk" | "asm" | "beancount" | "cl" | "clj" | "cljs" | "cljc" | "edn" | "fasl" | "ini" | "lisp" | "lsp" | "rkt" | "scm" | "ss":
self.character = ";"
@@ -251,7 +251,7 @@ def _toc_header(self):
match self.extension:
case "css":
_tocPrefix = "/*"
- case "html" | "md" | "xml":
+ case "html" | "xml" | "md" | "qmd" | "rmd":
_tocPrefix = ""
case "ml" | "mli" | "scpd" | "scpt":
_tocSuffix = "*)"