lean4-htt/tests/elab/versoDocIndentedHeader.lean
David Thrane Christiansen 5d5642107d
fix: elaborate and render blockquotes in Verso docstrings (#13670)
This PR adds support for blockquotes to Verso docstrings, which had been
missing before. It also substantially improves the robustness of
Verso->Markdown rendering of docstrings, especially the handling of
blockquote line prefixes.
2026-05-06 23:59:11 +00:00

213 lines
5.1 KiB
Text

import Lean
set_option doc.verso true
/-!
This test checks that indentation-specific Verso syntax in docstrings is processed relative to their
indentation, rather than an absolute column 0.
-/
open Lean Elab Command in
def printVersoDocstring (name : Name) : CommandElabM Unit := do
match (← findInternalDocString? (← getEnv) name) with
| none => IO.println s!"No docstring for {name}"
| some (.inl md) => IO.println s!"Markdown:\n{md}"
| some (.inr v) => IO.println (repr v.text); IO.println (repr v.subsections)
-- Test 1: Indented header on non-first line (e.g. inside where block)
def foo := bar where
/--
# Header in where block
Content here
-/
bar := 1
/--
info: #[]
#[{ title := #[Lean.Doc.Inline.text "Header in where block"],
titleString := "Header in where block",
metadata := none,
content := #[Lean.Doc.Block.para
#[Lean.Doc.Inline.text "Content here", Lean.Doc.Inline.linebreak "\n", Lean.Doc.Inline.text " "]],
subParts := #[] }]
-/
#guard_msgs in
#eval printVersoDocstring ``foo.bar
-- Test 2: Multiple headers at indentation level
namespace Indented
/--
# First header
Some text.
## Second header
More text.
-/
def baz := 1
end Indented
/--
info: #[]
#[{ title := #[Lean.Doc.Inline.text "First header"],
titleString := "First header",
metadata := none,
content := #[Lean.Doc.Block.para #[Lean.Doc.Inline.text "Some text."]],
subParts := #[{ title := #[Lean.Doc.Inline.text "Second header"],
titleString := "Second header",
metadata := none,
content := #[Lean.Doc.Block.para
#[Lean.Doc.Inline.text "More text.", Lean.Doc.Inline.linebreak "\n",
Lean.Doc.Inline.text " "]],
subParts := #[] }] }]
-/
#guard_msgs in
#eval printVersoDocstring ``Indented.baz
-- Test 3: First-line header still works when indented
namespace FirstLine
/-- # Inline header -/
def qux := 1
/-- # Inline header
# Indented
-/
def quux := 1
end FirstLine
/--
info: #[]
#[{ title := #[Lean.Doc.Inline.text "Inline header "],
titleString := "Inline header ",
metadata := none,
content := #[],
subParts := #[] }]
-/
#guard_msgs in
#eval printVersoDocstring ``FirstLine.qux
/--
info: #[]
#[{ title := #[Lean.Doc.Inline.text "Inline header"],
titleString := "Inline header",
metadata := none,
content := #[],
subParts := #[] },
{ title := #[Lean.Doc.Inline.text "Indented"],
titleString := "Indented",
metadata := none,
content := #[],
subParts := #[] }]
-/
#guard_msgs in
#eval printVersoDocstring ``FirstLine.quux
-- Test 4: Indented code block fences
namespace CodeBlock
/--
# Code example
Some intro text.
```
example code
```
-/
def withCode := 1
end CodeBlock
/--
info: #[]
#[{ title := #[Lean.Doc.Inline.text "Code example"],
titleString := "Code example",
metadata := none,
content := #[Lean.Doc.Block.para #[Lean.Doc.Inline.text "Some intro text."], Lean.Doc.Block.code "example code\n"],
subParts := #[] }]
-/
#guard_msgs in
#eval printVersoDocstring ``CodeBlock.withCode
-- Test 5: Deeply nested indentation (column 8+)
namespace Deep
namespace Deeper
namespace Deepest
/--
# Deep header
Content at depth.
-/
def deep := 1
end Deepest
end Deeper
end Deep
/--
info: #[]
#[{ title := #[Lean.Doc.Inline.text "Deep header"],
titleString := "Deep header",
metadata := none,
content := #[Lean.Doc.Block.para
#[Lean.Doc.Inline.text "Content at depth.", Lean.Doc.Inline.linebreak "\n",
Lean.Doc.Inline.text " "]],
subParts := #[] }]
-/
#guard_msgs in
#eval printVersoDocstring ``Deep.Deeper.Deepest.deep
-- Test 6: Non-indented
/--
# Top-level header
Content.
-/
def topLevel := 1
/--
info: #[]
#[{ title := #[Lean.Doc.Inline.text "Top-level header"],
titleString := "Top-level header",
metadata := none,
content := #[Lean.Doc.Block.para #[Lean.Doc.Inline.text "Content.", Lean.Doc.Inline.linebreak "\n"]],
subParts := #[] }]
-/
#guard_msgs in
#eval printVersoDocstring ``topLevel
-- Test 7: Indented link reference
namespace Links
/--
# Header with link
See [Lean][lean-link] for more.
[lean-link]: https://lean-lang.org
-/
def withLink := 1
end Links
/--
info: #[]
#[{ title := #[Lean.Doc.Inline.text "Header with link"],
titleString := "Header with link",
metadata := none,
content := #[Lean.Doc.Block.para
#[Lean.Doc.Inline.text "See ",
Lean.Doc.Inline.link #[Lean.Doc.Inline.text "Lean"] "https://lean-lang.org",
Lean.Doc.Inline.text " for more."],
Lean.Doc.Block.concat #[]],
subParts := #[] }]
-/
#guard_msgs in
#eval printVersoDocstring ``Links.withLink
-- Test 8: Over-indented # is a syntax error (extra spaces beyond base column)
/--
@ +5:4...*
error: expected '#' (header) to start at column 2
-/
#guard_msgs (positions := true) in
def overIndented := bar where
/--
Some text.
# Not a header
-/
bar := 1