---
title: "Markdown code block filename: three ways pandoc read it"
slug: markdown-code-block-filename
excerpt: "HTML that three pandoc readers wrote for a markdown code block filename in braces is quoted under Attribute block with a filename."
quickAnswer: "The section Attribute block with a filename holds the pandoc output for a file name set in braces. Under Words written after the language, a fence that adds a title after the word python goes through three readers. Look in Name in backticks above the fence for the run where the name sits on its own line over the block. Four more fence forms, plus one extra pass with an extension turned off, are quoted in Five more fences tried. Grep commands and their printed counts sit in Counting attributes with grep. Check Three fences in one test file for the input file plus the three pandoc commands."
keyTakeaways:
  - "The section Three fences in one test file lists the input lines and a pandoc command per reader."
  - "Under Attribute block with a filename, the HTML from the three readers is quoted."
  - "Turn to Words written after the language for the runs on a fence with a title after python."
  - "For the bold name and the colon label, see Five more fences tried."
  - "Grep counts, with a pre tag count as a control, appear in Counting attributes with grep."
publishedDate: 2026-10-21
dateModified: 2026-10-21
author: Bobby Huang
cluster: B
role: post
status: published
faq:
  - q: Which fence syntax puts a file name on a Markdown code block?
    a: "Compare your fence with the runs shown in Attribute block with a filename."
  - q: What happens to words typed after the language name?
    a: "Each reader's output for that fence is quoted in Words written after the language."
  - q: Can I check pandoc's HTML for a file name attribute?
    a: "See the numbered steps in Counting attributes with grep."
  - q: What about a colon between the language and the name?
    a: "That run, with python:app.py as the label, is quoted in Five more fences tried."
related:
  - markdown-code-blocks
  - markdown-code-block-line-numbers
  - markdown-diff-code-block
---

The phrase markdown code block filename means a name such as `app.py` that you want tied to a fenced block of code. During this test, pandoc wrote that name into its HTML as `data-filename="app.py"` when the `markdown` or the `commonmark_x` reader met an attribute block in braces. With the `commonmark` reader, the same fence came out as a `pre` element whose class was `{.python`, and the file name was missing from that line.

The runs took place on one Mac, and the first line of `pandoc --version` read `pandoc 3.8.3`. Below, I quote the HTML that pandoc gave, line breaks as it set them, and say plainly what that HTML contains. How any program draws that HTML on screen was never checked. For language tags as a topic, read [the Markdown code blocks guide](markdown-code-blocks.md); this post leaves them alone.

## Three fences in one test file

The test input lives in `codetitle.md`. An awk line count printed `19` for it, and `cat codetitle.md` printed:

````markdown
Attribute block:

```{.python filename="app.py"}
print("Jane Doe")
```

Words after the language:

```python title="app.py"
print("Jane Doe")
```

Name on the line above:

`app.py`

```python
print("Jane Doe")
```
````

Lines 1, 7 and 13 hold the three labels. Lines 3 to 5 hold the attribute block, and lines 9 to 11 hold the fence with words after `python`. Line 15 is the code span, and its plain fence sits on lines 17 to 19.

Each reader then got one command:

```sh
pandoc -f markdown -t html codetitle.md
pandoc -f commonmark -t html codetitle.md
pandoc -f commonmark_x -t html codetitle.md
```

All three returned rc 0. Pandoc's `--list-extensions` output for these readers has two extension names that matter for these results. For `markdown`, line 18 reads `+fenced_code_attributes`. Third in the `commonmark` list comes `-attributes`, while third in the list for `commonmark_x` comes `+attributes`. Both names are pandoc extensions.

## Attribute block with a filename

The first example puts a class and a key with a value inside braces:

````markdown
```{.python filename="app.py"}
print("Jane Doe")
```
````

For how the braces work, see the [post on code block line numbers](markdown-code-block-line-numbers.md); here I report just the HTML. This came back from `markdown`:

```html
<div class="sourceCode" id="cb1" data-filename="app.py"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;Jane Doe&quot;</span>)</span></code></pre></div>
```

Those two lines came out byte for byte alike from `commonmark_x`. So for `markdown` as for `commonmark_x`, the file name rides on the outer `div` as a `data-filename` attribute. For those two readers, pandoc put the word `python` in the class list of both the `pre` and the `code` tags, and two highlighting spans, `bu` and `st`, wrap parts of the code line.

Under `commonmark`, a single line came back instead:

```html
<pre class="{.python"><code>print(&quot;Jane Doe&quot;)</code></pre>
```

In that `commonmark` line there's no `div`, no `data-filename` and no span inside `code`. That `commonmark` result gives the `pre` a class that reads `{.python`, brace and all, and `app.py` does not appear anywhere on the line.

Spec lines 1840-1843 (version 0.31.2) let a fence's first info-string word name the language. Here, with `commonmark`, that first word was `{.python`, and pandoc put it on the `pre` element.

## Words written after the language

The second example drops the braces and adds a title after the language word:

````markdown
```python title="app.py"
print("Jane Doe")
```
````

One paragraph was all `markdown` gave back:

```html
<p><code>python title="app.py" print("Jane Doe")</code></p>
```

So in this `markdown` run the example produced no `pre` element. Under `markdown`, its lines became one inline `code` element that holds the words after the backticks and the code line, with one space joining the two. Inside that `markdown` span the double quotes stayed plain, while the highlighted blocks in the same output wrote them as `&quot;`.

For that fence, `commonmark` gave this block, and `commonmark_x` gave it too:

```html
<div class="sourceCode" id="cb2"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;Jane Doe&quot;</span>)</span></code></pre></div>
```

A highlighted python block came out of `commonmark` and of `commonmark_x` here. Neither `commonmark` nor `commonmark_x` kept the word `title` or the name `app.py` in that block, as an attribute or as text.

## Name in backticks above the fence

The third example sets the name in backticks on its own line, then a plain fence:

````markdown
`app.py`

```python
print("Jane Doe")
```
````

From `markdown`, pandoc gave:

```html
<p><code>app.py</code></p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;Jane Doe&quot;</span>)</span></code></pre></div>
```

Output from `commonmark` matched it, and so did output from `commonmark_x`, except that both use `cb3` where `markdown` used `cb2` in the ids and links. Under all three readers, the name came out as a `code` element inside a `p`, and the highlighted block after it holds no attribute with the file name.

## Five more fences tried

My extra script fed four more single-fence files to the three readers, then made one additional run with an extension turned off. Each file is 3 lines long by awk count, except `codetitle-bold.md`, which counts 5.

1. `codetitle-title-attr.md` swaps the key to `title`:

````markdown
```{.python title="app.py"}
print("Jane Doe")
```
````

These two lines came from `markdown`, and a run with `commonmark_x` repeated them unchanged:

```html
<div class="sourceCode" id="cb1" title="app.py"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;Jane Doe&quot;</span>)</span></code></pre></div>
```

Given `title` as the key, `markdown` wrote a plain `title` attribute with no `data-` in front, as did `commonmark_x`. The `commonmark` run gave `<pre class="{.python"><code>print(&quot;Jane Doe&quot;)</code></pre>` for this file.

2. `codetitle-noclass.md` keeps the key and drops the class:

````markdown
```{filename="app.py"}
print("Jane Doe")
```
````

Pandoc gave this under `markdown` as well as `commonmark_x`:

```html
<pre data-filename="app.py"><code>print(&quot;Jane Doe&quot;)</code></pre>
```

This time, with `commonmark`, pandoc printed:

```html
<pre class="{filename=&quot;app.py&quot;}"><code>print(&quot;Jane Doe&quot;)</code></pre>
```

Lacking a class, the fence got no `div` and no highlighting spans from `markdown` or `commonmark_x`, and the attribute sat on the `pre` itself. The `commonmark` run turned the whole brace text into the class.

3. `codetitle-bold.md` puts the name in bold above a plain fence:

````markdown
**app.py**

```python
print("Jane Doe")
```
````

Output was identical across the three readers:

```html
<p><strong>app.py</strong></p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;Jane Doe&quot;</span>)</span></code></pre></div>
```

4. `codetitle-colon.md` joins the language and the name with a colon:

````markdown
```python:app.py
print("Jane Doe")
```
````

Again the three readers matched:

```html
<div class="sourceCode" id="cb1"><pre
class="sourceCode python:app.py"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;Jane Doe&quot;</span>)</span></code></pre></div>
```

Here the `pre` class list holds `python:app.py` whole, while the `code` class list holds `python` with no `:app.py` after it, and the line still got its `bu` and `st` spans.

5. Last, I ran `codetitle-attr.md`, which holds the attribute block by itself, with the extension removed:

```sh
pandoc -f markdown-fenced_code_attributes -t html codetitle-attr.md
```

Its rc was 0, and it gave:

```html
<p><code>{.python filename="app.py"} print("Jane Doe")</code></p>
```

Once `fenced_code_attributes` was off, `markdown` gave the brace fence back as one inline `code` element, matching the shape of its output for the fence with words after `python`.

## Counting attributes with grep

These are the commands my extra script ran from the test folder for the `markdown` reader. It then ran them again for the two other readers, swapping the reader name into both the `-f` value and the file names.

```sh
pandoc -f markdown -t html codetitle.md -o codetitle-out-markdown.html
grep -c 'data-filename' codetitle-out-markdown.html
grep -c 'title=' codetitle-out-markdown.html
grep -c '<pre' codetitle-out-markdown.html
```

1. Save each reader's output to a file. All three pandoc runs returned rc 0.
2. Count lines holding `data-filename`. The count was `1` for `markdown` and for `commonmark_x`, and `0` for `commonmark`.
3. Count lines holding `title=`. Here `markdown` gave `1`, while the two other readers gave `0`. The one `markdown` match is the inline code text quoted under Words written after the language, since no `title` attribute was written for that file.
4. Count lines holding `<pre` as a control. That gave `2` under `markdown` and `3` under each of the two other readers. So the zero in step 2 came from a `commonmark` file that did contain code blocks.

A companion post runs these three readers over a [Markdown directory tree](markdown-directory-tree.md).

## Where Sashmark fits

Sashmark, a work in progress, sets out two goals here. With Round-Trip, a fix you key into the formatted copy should land in the saved bytes as that fix and nothing more. With Changes View, returning to a doc an agent kept editing should mark the stretches that got rewritten, whether git tracks the folder or not.

It is in development and has not been released. The app itself has no AI features. See the [planned features](/features), the [help pages](/help) and the [roadmap](/roadmap) for where each part stands.

More at [sashmark.com](https://www.sashmark.com/).
