Markin is a Swift library for parsing a Markdown-like text format.
The library is small, supports Swift Package Manager, and does not have any third party dependencies.
The parser generates a tree of elements. The tree is Codable
compliant.
The tree can also be transformed back to Markin format. This means that Markin documents can be parsed, manipulated programmatically by modifying/removing/replacing/adding tree elements, and written back to file.
The library has HTML rendering built-in, but external rendering of the element tree is entirely feasible.
Markin is available under the MIT License. See the LICENSE file in the repository for details.
Add the Markin package as a dependency in the Package.swift
file of your Swift Package Manager package. The example below specifies a dependency to the latest version of the Markin package on the master branch. You probably want to use a version tag from a release instead.
.package(url: "https://github.com/apparata/Markin.git", .branch("master"))
Simply instantiate a parser and call the parse()
method with a string in Markin format as a parameter. The parse()
method returns a DocumentElement
object, which is the root object of the element tree.
let exampleMarkin = """
# This is a Header
This is a paragraph.
"""
do {
let parser = MarkinParser()
let document = try parser.parse(exampleMarkin)
} catch {
print(error)
}
let document = try parser.parse(exampleMarkin)
let html = document.formatAsHTML()
let document = try parser.parse(exampleMarkin)
let markin = document.formatAsMarkin()
let document = try parser.parse(exampleMarkin)
let jsonData = try JSONEncoder().encode(document)
let document = try JSONDecoder().decode(DocumentElement.self, from: jsonData)
The format is based on Markdown, but I have plans to extend it. The core syntax will stay the same, however.
Headers are available in 6 hierarchical levels:
# This is the largest header
## This is the second largest header
### This is the third largest header
#### This is the fourth largest header
##### This is the fifth largest header
####### This is the sixth largest header.
A place holder for where the table of contents should be rendered is written like this on its own separate line:
%TOC
A text paragraph consists of consecutive lines of text, terminated by a blank line.
This is the first sentence of the paragraph. This is the second sentence.
This is the third sentence, also in the paragraph.
This is a second paragraph.
A block quote is formatted as text paragraphs, but each line is prefixed with a > in the following manner:
> This is the first line of the block quote.
> This is the second line of the block quote.
>
> This is the first line of the second paragraph
> of the block quote.
Unordered list:
- First list entry
- Second list entry
- First nested list entry
- Second nested list entry
- Third list entry
Ordered lists:
1. First list entry
1. Second list entry
1. First nested list entry
1. Second nested list entry
1. Third list entry
A horizontal divider line is written as three dashes on a separate line:
---
Code can be written in code blocks. The language can be specified after the opening ``` sequence.
```Swift
let a = 7
```
Bold text is achieved by using the marker * as follows:
The word *bold* is bold in this sentence.
Currently, bold and italic text can't be nested.
Italic text is achieved by using the marker _ as follows:
The word _italic_ is in italics in this sentence.
Currently, bold and italic text can't be nested.
Code can be written inline using single backticks ` like this:
This is text that has `inline code` in it.
Links can be written on the form [caption](url) like this:
This is text that has a [link](https://google.com) in it.
Images can be written on the form ![caption](url) like this:
This is text that has an image ![with alt text](mypicture.jpg) in it.
MarkinElement
DocumentElement
BlockElement
BlockQuoteElement
CodeBlockElement
HeaderElement
HorizontalRuleElement
ListElement
ParagraphElement
TableOfContentsElement
InlineElement
BoldElement
CodeElement
ImageElement
ItalicElement
LinkElement
TextElement
You can try the experimental SwiftUI renderer by adding a MarkinView
to your view.
struct ContentView: View {
let document: DocumentElement
var body: some View {
MarkinView(document: document)
}
}