Skip to content

Commit f13b227

Browse files
authored
ensure qmd always writes spans (#58)
1 parent c866e81 commit f13b227

File tree

6 files changed

+91
-13
lines changed

6 files changed

+91
-13
lines changed

crates/quarto-markdown-pandoc/src/writers/html.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,12 @@ fn write_block<T: std::io::Write>(block: &Block, buf: &mut T) -> std::io::Result
468468
write_blocks(&note.content, buf)?;
469469
writeln!(buf, "</div>")?;
470470
}
471+
Block::CaptionBlock(caption) => {
472+
// Caption blocks are rendered as divs with caption class
473+
write!(buf, "<div class=\"caption\">")?;
474+
write_inlines(&caption.content, buf)?;
475+
writeln!(buf, "</div>")?;
476+
}
471477
}
472478
Ok(())
473479
}

crates/quarto-markdown-pandoc/src/writers/qmd.rs

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -910,20 +910,14 @@ fn write_quoted(
910910
Ok(())
911911
}
912912
fn write_span(span: &crate::pandoc::Span, buf: &mut dyn std::io::Write) -> std::io::Result<()> {
913-
// Spans with attributes use bracket syntax: [content]{#id .class key=value}
914-
if !is_empty_attr(&span.attr) {
915-
write!(buf, "[")?;
916-
for inline in &span.content {
917-
write_inline(inline, buf)?;
918-
}
919-
write!(buf, "]")?;
920-
write_attr(&span.attr, buf)?;
921-
} else {
922-
// Spans without attributes just output their content
923-
for inline in &span.content {
924-
write_inline(inline, buf)?;
925-
}
913+
// Spans always use bracket syntax: [content]{#id .class key=value}
914+
// Even empty attributes should be written as [content]{} for proper roundtripping
915+
write!(buf, "[")?;
916+
for inline in &span.content {
917+
write_inline(inline, buf)?;
926918
}
919+
write!(buf, "]")?;
920+
write_attr(&span.attr, buf)?;
927921
Ok(())
928922
}
929923

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[hello]{}

docs/_quarto.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ website:
1010
- about.qmd
1111
- href: syntax/index.qmd
1212
text: Syntax
13+
- href: writers/index.qmd
14+
text: Writers
1315

1416
format:
1517
html:

docs/writers/index.qmd

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
title: "Writers"
3+
---
4+
5+
This section documents the output format writers available in `quarto-markdown`.
6+
7+
Writers convert the parsed AST (Abstract Syntax Tree) representation of a document into various output formats.
8+
9+
## Available Writers
10+
11+
- [QMD Writer](qmd.qmd) - Write documents back to Quarto Markdown format

docs/writers/qmd.qmd

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
title: "QMD Writer"
3+
---
4+
5+
The QMD writer converts the parsed AST representation back into Quarto Markdown (`.qmd`) format. This writer is designed to support roundtripping: parsing a document and writing it back should preserve the original structure and meaning.
6+
7+
## Purpose
8+
9+
The QMD writer is used for:
10+
11+
- **Roundtripping**: Converting documents through the parse → AST → write pipeline while preserving content
12+
- **Format normalization**: Standardizing markdown formatting across documents
13+
- **Document transformation**: Making programmatic changes to documents via the AST
14+
15+
## Differences from Pandoc
16+
17+
While `quarto-markdown` aims to be largely compatible with Pandoc, the QMD writer intentionally differs from Pandoc's markdown writer in certain cases to support better roundtripping and preserve explicit markup.
18+
19+
### Span Elements with Empty Attributes
20+
21+
**Pandoc behavior**: When a span has empty attributes (`["", [], []]`), Pandoc's markdown writer omits the brackets and braces, outputting only the content.
22+
23+
```markdown
24+
Input: [hello]{}
25+
Pandoc: hello
26+
```
27+
28+
**quarto-markdown behavior**: The QMD writer preserves the explicit span syntax, even when attributes are empty.
29+
30+
```markdown
31+
Input: [hello]{}
32+
quarto-markdown: [hello]{}
33+
```
34+
35+
**Rationale**: Preserving the explicit span markup supports proper roundtripping. If a document author wrote `[content]{}`, they did so intentionally, and this information should be preserved through the parse-write cycle. This also maintains the distinction between:
36+
37+
- Text that was marked up as a span: `[hello]{}`
38+
- Plain text that happens to have no special formatting: `hello`
39+
40+
## Usage
41+
42+
The QMD writer can be invoked using the `quarto-markdown-pandoc` binary:
43+
44+
```bash
45+
# Read from stdin, write QMD to stdout
46+
echo "**bold text**" | quarto-markdown-pandoc -t qmd
47+
48+
# Read from file, write QMD to stdout
49+
quarto-markdown-pandoc -i input.qmd -t qmd
50+
51+
# Roundtrip through JSON
52+
quarto-markdown-pandoc -i input.qmd -t json | \
53+
quarto-markdown-pandoc -f json -t qmd
54+
```
55+
56+
## Formatting Conventions
57+
58+
The QMD writer follows these formatting conventions:
59+
60+
- **Emphasis**: Uses `*` for emphasis and `**` for strong emphasis
61+
- **Line breaks**: Soft breaks become newlines (differs from Pandoc which uses spaces)
62+
- **Smart quotes**: Unicode right single quotation marks (') are converted back to ASCII apostrophes (')
63+
- **Escaping**: Special markdown characters (`\`, `>`, `#`) are escaped as needed
64+
- **Attributes**: Written in the format `{#id .class key="value"}`

0 commit comments

Comments
 (0)