TL;DR: Read markdown text into scala data structures that you can manipulate programmatically, and write the contents back out to plain text.
While there isn't any rigorous specification for markdown, this project will target CommonMark.
This library supports parsing Markdown text into a data structure, and supports some common (but not all) markdown features:
- ✅ Headers (both atx and setext styles). Headers are a tree-like structure; each header contains the paragraphs and other markdown elements in its section, including subheaders.
- ✅ Fenced code blocks (including JSON formatting)
- ✅ Link references
- ✅ Comments (not part of CommonMark)
- ✅ Tables (not part of CommonMark)
- ✅ Paragraphs (includes all non-supported elements)
Unsupported markdown can be passed through parsing/rewriting without loss.
- ❌ Thematic breaks
- ❌ Block quotes
- ❌ Indented code bocks
- ❌ HTML blocks
- ❌ Unrecognized markdown
- ❌ Most inlined elements like links, images, bold, italics, monospaced are just treated like plain text in a Paragraph
- ❌ Ordered and unordered lists
- ❌ HTML rendering (other JVM libraries do this very well)
You can import the library into your project from maven central:
<dependency>
<groupId>com.tinfoiled</groupId>
<artifactId>markd_2.13</artifactId>
<version>0.1.0</version>
</dependency>Then you can use the API to parse and rewrite markdown text:
// Some simple markdown text in a string
val txt = {
"""English
|===
|Hello world
|# French
|Bonjour tout le monde
|""".stripMargin
}
// Parse it into a structure
val md: Header = Markd.parse(txt)
// This gives this structure: Markd is an "invisible" top level node that
// contains all the whole document, which is composed of two top-level
// headers, each containing one paragraph
val result = Markd(
Header("English", 1, List(Paragraph("Hello world"))),
Header("French", 1, List(Paragraph("Bonjour tout le monde")))
)
// And print it out again
println(md.build().toString)The output of the snippet above would be:
English
==============================================================================
Hello world
French
==============================================================================
Bonjour tout le monde
| Query | Description |
|---|---|
One.Two.Three[*] |
Find the level one header with the name One, with a subheader named Two and a third-level header Three and return those contents. |
Top |
Find and return the level one header with the title "Top" |
Top[0] |
Find and return the first child of the level one header with the title "Top" |
Top[-1] |
Find and return the last child of the level one header with the title "Top" |
"..Top\"[]" |
Find and return the level one header with the title ..Top"[]. Use escapes internally to match quotes. |
/Week .* Failures/ |
Find and return the level one header that matches the regex, such as Week 21 Failures |
Monthly..2025-02 |
Find the level one header with the title Monthly and return the first subheader named 2025-02 at any level inside |
Weekly|To Do |
Find the level one header with the title Weekly and return the To Do table that it contains. |
..|Status[12] |
Find any Status table and return the 12th table row (note that row 0 is always the column headers). |
..|Status[-1] |
Find any Status table and return the last table row. |
..|Status[0][3] |
Find any Status table and return the name of the 4th column (row 0 is the headers, and columns are zero indexed). |
..|Status[Key,rowId] |
Find any Status table and return the cell under the column Key with the row header rowId Note that this is column-first! |
..Weekly[0] |
Any header with the title Weekly and return the first element it contains. |
Weekly[code][0] |
❌ Find the top Weekly header and return the first code block it contains. |
Weekly..\json` |
Find the top Weekly header and return the first JSON code block it contains. |
Weekly[0][4] |
Find the top Weekly header, go to its first child and return that elements 5th child. |
..|/.*Status/[1] |
Find any table with a title ending with Status and return the first non-header row. |
# Build, format and run all tests
mvn spotless:apply clean verify