From 4a38d4fd816437495ca12630df5e2423e9c93eec Mon Sep 17 00:00:00 2001 From: Eugene Kalinin Date: Thu, 15 Oct 2026 19:47:50 +0300 Subject: [PATCH] fix(toc): indent TOC entries relative to the top heading level Without a level 1 heading the first TOC entry got (level-1)*indent spaces, and with 4 or more spaces GitHub rendered the whole TOC as a code block. Entries are now indented by (level-min_level)*indent, where min_level is the smallest heading level of the document. Output for documents with a level 1 heading is unchanged. Fixes #30 --- gh-md-toc | 21 +++-- .../fix-relative-toc-indent/.openspec.yaml | 2 + .../changes/fix-relative-toc-indent/design.md | 82 +++++++++++++++++++ .../fix-relative-toc-indent/proposal.md | 60 ++++++++++++++ .../specs/remote-toc/spec.md | 41 ++++++++++ .../specs/toc-depth/spec.md | 39 +++++++++ .../specs/toc-indent/spec.md | 48 +++++++++++ .../specs/toc-numbered/spec.md | 44 ++++++++++ .../changes/fix-relative-toc-indent/tasks.md | 45 ++++++++++ tests/test directory/test_noh1.md | 11 +++ tests/tests.bats | 33 ++++++++ 11 files changed, 421 insertions(+), 5 deletions(-) create mode 100644 openspec/changes/fix-relative-toc-indent/.openspec.yaml create mode 100644 openspec/changes/fix-relative-toc-indent/design.md create mode 100644 openspec/changes/fix-relative-toc-indent/proposal.md create mode 100644 openspec/changes/fix-relative-toc-indent/specs/remote-toc/spec.md create mode 100644 openspec/changes/fix-relative-toc-indent/specs/toc-depth/spec.md create mode 100644 openspec/changes/fix-relative-toc-indent/specs/toc-indent/spec.md create mode 100644 openspec/changes/fix-relative-toc-indent/specs/toc-numbered/spec.md create mode 100644 openspec/changes/fix-relative-toc-indent/tasks.md create mode 100644 tests/test directory/test_noh1.md diff --git a/gh-md-toc b/gh-md-toc index ed53a51..e6a5cab 100755 --- a/gh-md-toc +++ b/gh-md-toc @@ -13,8 +13,9 @@ # It's a level of the current header: # substr($0, length($0), 1) # -# 3.2 Get level from 3.1 and insert corresponding number of spaces before '*': -# sprintf("%*s", (level-1)*'"$nb_spaces"', "") +# 3.2 Get level from 3.1 and insert corresponding number of spaces before '*' +# (min_level is the smallest level in the document): +# sprintf("%*s", (level-min_level)*'"$nb_spaces"', "") # # 4. Find head's text and insert it inside "* [ ... ]": # substr($0, match($0, /a>.*<\/h/)+2, RLENGTH-5) @@ -258,8 +259,18 @@ gh_toc_grab() { prefix = "" if (numbered == "list") marker = "1. " if (numbered == "outline") prefix = num " " - print sprintf("%*s", (level-1)*'"$2"', "") marker "[" prefix text "](" gh_url modified_href ")" + # keep the entry, the indent is known only after the last one + count++ + entry_level[count] = level+0 + entry[count] = marker "[" prefix text "](" gh_url modified_href ")" + if (count == 1 || level+0 < min_level) min_level = level+0 ' + # indent relative to the top level of the document, see #30 + common_awk_end=' + END { + for (i = 1; i <= count; i++) + print sprintf("%*s", (entry_level[i]-min_level)*'"$2"', "") entry[i] + }' if [ "$(uname -s)" == "OS/390" ]; then grepcmd="pcregrep -o" echoargs="" @@ -269,7 +280,7 @@ gh_toc_grab() { text = substr($0, match($0, /<\/span><\/a>[^<]*<\/h/)+11, RLENGTH-14) href = substr($0, match($0, '$href_regex')+6, RLENGTH-7) '"$common_awk_script"' - }' + }'"$common_awk_end" else grepcmd="grep -Eo" echoargs="-e" @@ -279,7 +290,7 @@ gh_toc_grab() { text = substr($0, match($0, /">.*<\/h/)+2, RLENGTH-5) href = substr($0, match($0, '$href_regex')+6, RLENGTH-7) '"$common_awk_script"' - }' + }'"$common_awk_end" fi # if closed is on the new line, then move it on the prev line diff --git a/openspec/changes/fix-relative-toc-indent/.openspec.yaml b/openspec/changes/fix-relative-toc-indent/.openspec.yaml new file mode 100644 index 0000000..a216797 --- /dev/null +++ b/openspec/changes/fix-relative-toc-indent/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-15 diff --git a/openspec/changes/fix-relative-toc-indent/design.md b/openspec/changes/fix-relative-toc-indent/design.md new file mode 100644 index 0000000..d80410a --- /dev/null +++ b/openspec/changes/fix-relative-toc-indent/design.md @@ -0,0 +1,82 @@ +# Design + +## Context + +Every input path ends in `gh_toc_grab`. Its awk script reads the level as +`substr($0, 3, 1)` and runs `common_awk_script`, which is shared by the default and the +OS/390 branches. `common_awk_script` drops entries deeper than `--depth` with `next`, +builds the `--numbered outline` number from a stack of levels, and prints each entry +right away as `sprintf("%*s", (level-1)*'"$2"', "") marker "[" prefix text "](" gh_url +modified_href ")"`, where `$2` is the indent spliced into the script by the shell. + +`common_awk_script` already uses the names `top`, `lvls`, `nums`, `prev`, `num`, `k`, +`modified_href`, `chars`, `c`, `res`, `i`, `marker` and `prefix`. `top` is the stack +pointer of the outline numbers, not the top heading level from the specs. + +Each input of several inputs goes through its own `gh_toc_grab` call (`gh_toc` is called +once per input), so one awk run sees one document. + +## Goals / Non-Goals + +**Goals:** + +- One change point that covers all inputs and both awk branches. +- The output for a document with a `#` heading stays byte-for-byte the same. + +**Non-Goals:** see proposal.md - Non-goals. + +## Decisions + +### `top` is the smallest level, not the level of the first entry + +The indent is counted from the smallest heading level among the entries. + +Alternative: count from the level of the first entry. For `###` followed by `##`, the +`##` entry would get a negative indent and would need a clamp to 0, which puts `###` and +`##` at the same indent. The smallest level never gives a negative indent and keeps the +distance between levels as it is today. + +### Keep the entries in awk and print them in `END` + +The smallest level is known only after the last heading. `common_awk_script` stores +each entry without the indent and its level in two arrays and updates the smallest +level. A new `END` block prints the entries in order with +`sprintf("%*s", (level - min_level) * '"$2"', "")` in front. + +New awk names: `count`, `entry_level`, `entry`, `min_level`. They do not clash with the +names above. + +The `END` block is a separate shell variable next to `common_awk_script`, appended to +both `awkscript` branches, so OS/390 gets the same change. + +Alternatives: + +- Run awk twice over the grep output (first pass for the smallest level). This needs the + intermediate output in a variable or a temp file and doubles the pipeline tail. +- Strip the common leading spaces after awk with `sed`. The awk output still goes + through `echo -e`, which turns `\x..` into characters, so a second text pass is harder + to keep correct than an integer subtraction in awk. + +Keeping the entries in memory is fine: a TOC has tens or hundreds of entries. + +### `--depth` and `--numbered` need no changes + +`--depth` drops entries with `next` before they are stored, so the smallest level is +taken over the entries in the TOC. `--depth` only drops levels deeper than `NUM`, so the +smallest level stays the same whenever the TOC is not empty. + +The outline numbers are built from the order of levels, not from their absolute values, +so they already start from `1.` at the top entries. + +The `--numbered list requires --indent 3 or more` check stays: nested entries still +need at least 3 spaces under the `1.` marker. + +## Risks / Trade-offs + +- [A TOC that `--insert` put into a file without a `#` heading changes on the next run] + → This is the fix itself. The proposal states it as an output change. +- [A document whose first heading is deeper than a later one, with `--indent 4` or + more, still gets a code block] → Out of scope (proposal.md - Non-goals). Handling it + would need a different indent rule than the specs define. +- [awk portability] → Arrays and `END` are POSIX awk and work in gawk, mawk and BWK awk. + `%*s` is already used today. diff --git a/openspec/changes/fix-relative-toc-indent/proposal.md b/openspec/changes/fix-relative-toc-indent/proposal.md new file mode 100644 index 0000000..8b88375 --- /dev/null +++ b/openspec/changes/fix-relative-toc-indent/proposal.md @@ -0,0 +1,60 @@ +# Proposal + +## Why + +The indent of a TOC entry is `(level - 1) * indent` spaces, counted from `#` even when the +document has no `#` heading (#30). When the first entry gets 4 or more spaces, GitHub +renders the whole TOC as a code block instead of a list. Reproduced with 0.10.0 and the +GitHub markdown API: + +- `### Foo`, `#### Bar` with the default indent: the first entry has 6 spaces, the TOC + is rendered as `
` (also with `--insert`).
+- `## Foo`, `### Bar` with `--indent 4`: the first entry has 4 spaces, the TOC is
+  rendered as `
`.
+
+The example from the issue (`## Foo`, `### Bar` with the default indent) is rendered as a
+list today, because the first entry has 3 spaces, but the cause is the same.
+
+## What Changes
+
+- Indent every TOC entry by `(level - top) * indent` spaces, where `top` is the smallest
+  heading level among the entries of the same document. For a document with a `#`
+  heading the output stays byte-for-byte the same.
+- Add a test fixture without a `#` heading and tests for a local file, stdin with
+  `--indent 4`, and stdin with `--depth`.
+
+Output change: for a document without a `#` heading, every entry moves left by
+`(top - 1) * indent` spaces. A TOC that `--insert` put into such a file changes on the
+next run.
+
+Non-goals:
+
+- `--depth`: the level stays absolute, as `toc-depth` requires.
+- `--numbered outline` numbers: they already start from `1.` at the top entries.
+- Skipped heading levels (e.g. `#` followed by `###`): the indent between entries does
+  not change.
+- A document whose first heading is deeper than a later one (e.g. `###` before `##`)
+  with `--indent 4` or more: the first entry still gets `indent` spaces.
+- Changes to README, the landing page (`site/`), or `openspec/specs/landing`.
+- A version bump.
+
+## Capabilities
+
+### New Capabilities
+
+- `toc-indent`: indentation of TOC entries relative to the top heading level of the
+  document, for every input.
+
+### Modified Capabilities
+
+- `remote-toc`: "TOC for a remote GitHub page" states `(level - 1) * indent`.
+- `toc-depth`: "TOC limited by heading level" states `(level - 1) * indent`.
+- `toc-numbered`: "Ordered list" and "Hierarchical numbers" state `(level - 1) * indent`.
+
+## Impact
+
+- `gh-md-toc`: the awk script in `gh_toc_grab` keeps the entries and prints them at the
+  end, when the top level is known. The same script is used on OS/390. Each input of
+  several inputs goes through its own `gh_toc_grab` call, so `top` is per document.
+- `tests/tests.bats` and `tests/test directory/`: one new fixture and three new tests.
+  Existing fixtures all have a `#` heading, so their assertions do not change.
diff --git a/openspec/changes/fix-relative-toc-indent/specs/remote-toc/spec.md b/openspec/changes/fix-relative-toc-indent/specs/remote-toc/spec.md
new file mode 100644
index 0000000..c17982d
--- /dev/null
+++ b/openspec/changes/fix-relative-toc-indent/specs/remote-toc/spec.md
@@ -0,0 +1,41 @@
+# Spec Delta
+
+## MODIFIED Requirements
+
+### Requirement: TOC for a remote GitHub page
+
+For a GitHub file (`/blob/`) URL or a GitHub wiki page URL, the TOC SHALL contain one
+entry per heading of the rendered document, in document order, including the first
+heading when the document starts with a heading. Each entry SHALL have the form
+`* [](#)`, indented by `(level - top) * indent` spaces, where `top`
+is the smallest heading level among the entries of the document (indent is 3 by
+default). No entry SHALL contain markup of the GitHub page around the document or
+link to anything other than a heading anchor of the document.
+
+#### Scenario: File that starts with a heading
+
+- **WHEN** the user runs `gh-md-toc https://github.com/ekalinin/sitemap.js/blob/6bc3eb12c898c1037a35a11b2eb24ababdeb3580/README.md`
+- **THEN** the output is `Table of Contents`, `=================`,
+  `* [sitemap.js](#sitemapjs)`, `   * [Installation](#installation)`,
+  `   * [Usage](#usage)`, `   * [License](#license)`, and
+  `` (ignoring empty
+  lines)
+
+#### Scenario: File with non-English headings
+
+- **WHEN** the user runs `gh-md-toc https://github.com/ekalinin/envirius/blob/f939d3b6882bfb6ecb28ef7b6e62862f934ba945/README.ru.md`
+- **THEN** the first entries are `* [envirius](#envirius)`, `   * [Идея](#идея)`,
+  `   * [Особенности](#особенности)`, `* [Установка](#установка)`
+
+#### Scenario: File that does not start with a heading
+
+- **WHEN** the user runs `gh-md-toc https://github.com/jlevy/the-art-of-command-line/blob/217da3b4fa751014ecc122fd9fede2328a7eeb3e/README-pt.md`
+- **THEN** the first entries are
+  `* [A arte da linha de comando](#a-arte-da-linha-de-comando)`, `   * [Meta](#meta)`,
+  `   * [Básico](#básico)`, `   * [Uso diário](#uso-diário)`
+
+#### Scenario: Wiki page
+
+- **WHEN** the user runs `gh-md-toc https://github.com/ekalinin/nodeenv/wiki/Who-Uses-Nodeenv`
+- **THEN** the first entries are `* [Who Uses Nodeenv?](#who-uses-nodeenv)`,
+  `   * [edx](#edx)`, `   * [OpenStack](#openstack)`
diff --git a/openspec/changes/fix-relative-toc-indent/specs/toc-depth/spec.md b/openspec/changes/fix-relative-toc-indent/specs/toc-depth/spec.md
new file mode 100644
index 0000000..2b365b8
--- /dev/null
+++ b/openspec/changes/fix-relative-toc-indent/specs/toc-depth/spec.md
@@ -0,0 +1,39 @@
+# Spec Delta
+
+## MODIFIED Requirements
+
+### Requirement: TOC limited by heading level
+
+With `--depth ` where `NUM` is greater than 0, the TOC SHALL contain entries only for
+headings whose level is less than or equal to `NUM` (`#` is level 1, `######` is level
+6). The level is absolute: it does not depend on the highest level present in the
+document. The remaining entries SHALL be the same as without `--depth`: same text, same
+anchor, same order, same indentation of `(level - top) * indent` spaces, where `top` is
+the smallest heading level among the entries of the document. This SHALL apply to every
+input: stdin, a local file, a remote URL, several inputs, and `--insert`.
+
+`--depth ` SHALL be accepted after `--indent ` (when given) and before `-`,
+`--insert`, `--no-backup`, `--hide-footer`, `--skip-header` and the inputs.
+
+#### Scenario: Local file
+
+- **WHEN** the user runs `gh-md-toc --depth 2 "tests/test directory/test_depth.md"`,
+  where the file has the headings `# Title one`, `## Section`, `### Subsection`,
+  `#### Deep`, `# Title two`
+- **THEN** the TOC entries are `* [Title one](#title-one)`, `   * [Section](#section)`,
+  `* [Title two](#title-two)`, followed by
+  ``
+
+#### Scenario: Stdin
+
+- **WHEN** the user runs
+  `cat "tests/test directory/test_depth.md" | gh-md-toc --depth 1 -`
+- **THEN** the output is `* [Title one](#title-one)` and `* [Title two](#title-two)`
+
+#### Scenario: Document without a `#` heading
+
+- **WHEN** the user runs
+  `cat "tests/test directory/test_noh1.md" | gh-md-toc --depth 2 -`, where the file has
+  the headings `## Section one`, `### Subsection`, `## Section two`
+- **THEN** the output is `* [Section one](#section-one)` and
+  `* [Section two](#section-two)`
diff --git a/openspec/changes/fix-relative-toc-indent/specs/toc-indent/spec.md b/openspec/changes/fix-relative-toc-indent/specs/toc-indent/spec.md
new file mode 100644
index 0000000..2a83fbe
--- /dev/null
+++ b/openspec/changes/fix-relative-toc-indent/specs/toc-indent/spec.md
@@ -0,0 +1,48 @@
+# Spec Delta
+
+## Purpose
+
+Keeps the TOC a markdown list when the document has no `#` heading, by indenting the
+entries relative to the top heading level of the document instead of `#`.
+
+## ADDED Requirements
+
+### Requirement: Indent relative to the top level
+
+Every TOC entry SHALL be indented by `(level - top) * indent` spaces, where `level` is
+the heading level of the entry (`#` is level 1, `######` is level 6), `top` is the
+smallest heading level among the TOC entries of the same document, and `indent` is the
+value of `--indent ` (3 by default). Entries at the `top` level SHALL have no
+indent. This SHALL apply to every input: stdin, a local file, a remote URL, several
+inputs, and `--insert`. With several inputs, each document SHALL have its own `top`.
+
+#### Scenario: Document with a `#` heading
+
+- **WHEN** the user runs `gh-md-toc "tests/test directory/test_depth.md"`
+- **THEN** the TOC entries are `* [Title one](#title-one)`, `   * [Section](#section)`,
+  `      * [Subsection](#subsection)`, `         * [Deep](#deep)`,
+  `* [Title two](#title-two)`
+
+#### Scenario: Local file without a `#` heading
+
+- **WHEN** the user runs `gh-md-toc "tests/test directory/test_noh1.md"`, where the file
+  has the headings `## Section one`, `### Subsection`, `## Section two`
+- **THEN** the TOC entries are `* [Section one](#section-one)`,
+  `   * [Subsection](#subsection)`, `* [Section two](#section-two)`, followed by
+  ``
+
+#### Scenario: Stdin with an indent of 4
+
+- **WHEN** the user runs
+  `cat "tests/test directory/test_noh1.md" | gh-md-toc --indent 4 -`
+- **THEN** the output is `* [Section one](#section-one)`,
+  `    * [Subsection](#subsection)`, `* [Section two](#section-two)`
+
+#### Scenario: Several inputs
+
+- **WHEN** the user runs
+  `gh-md-toc "tests/test directory/test_plussign.md" "tests/test directory/test_noh1.md"`
+- **THEN** the entries of `test_noh1.md` are
+  `* [Section one](tests/test directory/test_noh1.md#section-one)`,
+  `   * [Subsection](tests/test directory/test_noh1.md#subsection)`,
+  `* [Section two](tests/test directory/test_noh1.md#section-two)`
diff --git a/openspec/changes/fix-relative-toc-indent/specs/toc-numbered/spec.md b/openspec/changes/fix-relative-toc-indent/specs/toc-numbered/spec.md
new file mode 100644
index 0000000..0961ac7
--- /dev/null
+++ b/openspec/changes/fix-relative-toc-indent/specs/toc-numbered/spec.md
@@ -0,0 +1,44 @@
+# Spec Delta
+
+## MODIFIED Requirements
+
+### Requirement: Ordered list
+
+With `--numbered list`, every TOC entry SHALL have the form `1. []()`,
+with the marker `1.` for every entry, indented by `(level - top) * indent` spaces, where
+`top` is the smallest heading level among the entries of the document. The heading text
+and the link SHALL be the same as without `--numbered`.
+
+#### Scenario: Local file
+
+- **WHEN** the user runs `gh-md-toc --numbered list "tests/test directory/test_numbered.md"`,
+  where the file has the headings `# Title one`, `## Section`, `### Subsection`,
+  `## Other section`, `# Title two`, `### Skipped`, `## After skip`
+- **THEN** the TOC entries are `1. [Title one](#title-one)`, `   1. [Section](#section)`,
+  `      1. [Subsection](#subsection)`, `   1. [Other section](#other-section)`,
+  `1. [Title two](#title-two)`, `      1. [Skipped](#skipped)`,
+  `   1. [After skip](#after-skip)`, followed by
+  ``
+
+### Requirement: Hierarchical numbers
+
+With `--numbered outline`, every TOC entry SHALL have the form
+`* [ ]()`, indented by `(level - top) * indent` spaces, where
+`top` is the smallest heading level among the entries of the document. The heading text
+and the link SHALL be the same as without `--numbered`.
+
+The number SHALL follow the heading tree of the entries in the TOC. The parent of an
+entry is the nearest previous entry with a lower heading level; entries without a parent
+are top entries. The number of a top entry SHALL be its position among the top entries
+followed by a dot (`1.`, `2.`). The number of any other entry SHALL be the number of its
+parent followed by its position among the children of that parent and a dot (`2.1.`,
+`2.1.3.`). A skipped heading level SHALL NOT add a part to the number.
+
+#### Scenario: Local file
+
+- **WHEN** the user runs
+  `gh-md-toc --numbered outline "tests/test directory/test_numbered.md"`
+- **THEN** the TOC entries are `* [1. Title one](#title-one)`,
+  `   * [1.1. Section](#section)`, `      * [1.1.1. Subsection](#subsection)`,
+  `   * [1.2. Other section](#other-section)`, `* [2. Title two](#title-two)`,
+  `      * [2.1. Skipped](#skipped)`, `   * [2.2. After skip](#after-skip)`
diff --git a/openspec/changes/fix-relative-toc-indent/tasks.md b/openspec/changes/fix-relative-toc-indent/tasks.md
new file mode 100644
index 0000000..cfa1086
--- /dev/null
+++ b/openspec/changes/fix-relative-toc-indent/tasks.md
@@ -0,0 +1,45 @@
+# Tasks
+
+## 1. Setup
+
+- [x] 1.1 Create branch `fix/relative-toc-indent` from `master` and verify
+  `git branch --show-current` prints `fix/relative-toc-indent`
+
+## 2. Tests first
+
+- [x] 2.1 Add the fixture `tests/test directory/test_noh1.md` with the headings
+  `## Section one`, `### Subsection`, `## Section two`, each followed by `Blabla...` as
+  in `test_depth.md`; verify that `./gh-md-toc "tests/test directory/test_noh1.md"`
+  prints the three entries indented by 3, 6 and 3 spaces (the bug)
+- [x] 2.2 Add a test in `tests/tests.bats` for the local file, as in the spec scenario
+  "Local file without a `#` heading", including the footer line; verify it fails before
+  the fix (`bats --filter "" tests`)
+- [x] 2.3 Add a test for `--indent 4 -` with the fixture on stdin, as in the spec
+  scenario "Stdin with an indent of 4", including the number of lines; verify it fails
+  before the fix
+- [x] 2.4 Add a test for `--depth 2 -` with the fixture on stdin, as in the `toc-depth`
+  spec scenario "Document without a `#` heading", including the number of lines; verify
+  it fails before the fix
+
+## 3. Fix
+
+- [x] 3.1 `gh_toc_grab`: in `common_awk_script`, replace the final `print` with storing
+  the entry without the indent in `entry[count]`, its level in `entry_level[count]`, and
+  updating `min_level`; add a `common_awk_end` variable with an `END` block that prints
+  every entry with `sprintf("%*s", (entry_level[i] - min_level) * '"$2"', "")` in front,
+  and append it to both `awkscript` branches (default and OS/390); verify `make lint`
+  passes and the tests from 2.2 to 2.4 pass
+- [x] 3.2 Update the comment at the top of `gh-md-toc` (step 3.2,
+  `sprintf("%*s", (level-1)*'"$nb_spaces"', "")`) to the new formula; verify
+  `git diff gh-md-toc` shows only the changes from 3.1 and 3.2
+
+## 4. Verification
+
+- [x] 4.1 Run `make lint` and `make test` with `GH_TOC_TOKEN` set; verify both pass and
+  the existing assertions are not changed (`git diff tests/tests.bats` only adds lines)
+- [x] 4.2 Run the spec scenario "Several inputs" and verify the `test_noh1.md` entries
+  start with no indent
+- [x] 4.3 Run the three cases from proposal.md - Why (`###`/`####` with the default
+  indent, the same with `--insert`, `##`/`###` with `--indent 4`) and render each output
+  with the GitHub markdown API; verify each one is rendered as `
    `, not + `
    `
    diff --git a/tests/test directory/test_noh1.md b/tests/test directory/test_noh1.md
    new file mode 100644
    index 0000000..ea88b01
    --- /dev/null
    +++ b/tests/test directory/test_noh1.md	
    @@ -0,0 +1,11 @@
    +## Section one
    +
    +Blabla...
    +
    +### Subsection
    +
    +Blabla...
    +
    +## Section two
    +
    +Blabla...
    diff --git a/tests/tests.bats b/tests/tests.bats
    index 86ab465..d48d9f2 100755
    --- a/tests/tests.bats
    +++ b/tests/tests.bats
    @@ -321,3 +321,36 @@ test_help() {
     
         assert_equal "${lines[0]}"   "--numbered list requires --indent 3 or more, got '2'."
     }
    +
    +@test "TOC without level 1 heading for local file, #30" {
    +    run $BATS_TEST_DIRNAME/../gh-md-toc tests/test\ directory/test_noh1.md
    +    assert_success
    +
    +    assert_equal "${lines[2]}"   "* [Section one](#section-one)"
    +    assert_equal "${lines[3]}"   "   * [Subsection](#subsection)"
    +    assert_equal "${lines[4]}"   "* [Section two](#section-two)"
    +    assert_equal "${lines[5]}"   ""
    +}
    +
    +@test "TOC without level 1 heading and indent 4 for markdown from stdin, #30" {
    +    cat tests/test\ directory/test_noh1.md | {
    +        run $BATS_TEST_DIRNAME/../gh-md-toc --indent 4 -
    +        assert_success
    +
    +        assert_equal "${lines[0]}"   "* [Section one](#section-one)"
    +        assert_equal "${lines[1]}"   "    * [Subsection](#subsection)"
    +        assert_equal "${lines[2]}"   "* [Section two](#section-two)"
    +        assert_equal "${#lines[@]}"  "3"
    +    }
    +}
    +
    +@test "TOC without level 1 heading and depth for markdown from stdin, #30" {
    +    cat tests/test\ directory/test_noh1.md | {
    +        run $BATS_TEST_DIRNAME/../gh-md-toc --depth 2 -
    +        assert_success
    +
    +        assert_equal "${lines[0]}"   "* [Section one](#section-one)"
    +        assert_equal "${lines[1]}"   "* [Section two](#section-two)"
    +        assert_equal "${#lines[@]}"  "2"
    +    }
    +}