233 lines
5.2 KiB
Go
233 lines
5.2 KiB
Go
package adoc
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
TITLE_PREFIX = "= "
|
|
SUBTITLE_PREFIX = "== "
|
|
SUBSUBTITLE_PREFIX = "=== "
|
|
PARAGRAPH_SEPARATOR = "\n\n"
|
|
LINE_SEPARATOR = "\n"
|
|
CODE_PREFIX = "----\n"
|
|
CODE_SUFFIX = "\n----"
|
|
LISTITEM_PREFIX = "* "
|
|
)
|
|
|
|
func New(text string) *ADoc {
|
|
doc := &ADoc{
|
|
Language: LANGUAGE_EN,
|
|
Tags: []Tag{},
|
|
}
|
|
|
|
// split up blocks
|
|
var pars []string
|
|
for _, s := range strings.Split(text, PARAGRAPH_SEPARATOR) {
|
|
if s == "" {
|
|
|
|
continue
|
|
}
|
|
|
|
pars = append(pars, s)
|
|
}
|
|
|
|
// actually, code blocks are allowed to have empty lines
|
|
var blocks []string
|
|
var inCode bool
|
|
var currentBlock string
|
|
for _, par := range pars {
|
|
switch {
|
|
case strings.HasPrefix(par, CODE_PREFIX) && strings.HasSuffix(par, CODE_SUFFIX):
|
|
blocks = append(blocks, par)
|
|
case !inCode && strings.HasPrefix(par, CODE_PREFIX):
|
|
inCode = true
|
|
currentBlock = par
|
|
case inCode && !strings.HasSuffix(par, CODE_SUFFIX):
|
|
currentBlock += PARAGRAPH_SEPARATOR + par
|
|
case inCode && strings.HasSuffix(par, CODE_SUFFIX):
|
|
currentBlock += PARAGRAPH_SEPARATOR + par
|
|
blocks = append(blocks, currentBlock)
|
|
inCode = false
|
|
currentBlock = ""
|
|
default:
|
|
blocks = append(blocks, par)
|
|
}
|
|
}
|
|
|
|
// interpret the blocks
|
|
for i, p := range blocks {
|
|
switch {
|
|
case i == 0 && strings.HasPrefix(p, TITLE_PREFIX):
|
|
ParseHeader(p, doc)
|
|
case strings.HasPrefix(p, SUBTITLE_PREFIX):
|
|
p = strings.TrimSpace(p)
|
|
s := strings.Split(p, SUBTITLE_PREFIX)
|
|
if len(s) == 1 || s[1] == "" {
|
|
|
|
continue
|
|
}
|
|
doc.Content = append(doc.Content, SubTitle(s[1]))
|
|
case strings.HasPrefix(p, SUBSUBTITLE_PREFIX):
|
|
p = strings.TrimSpace(p)
|
|
s := strings.Split(p, SUBSUBTITLE_PREFIX)
|
|
if len(s) == 1 || s[1] == "" {
|
|
|
|
continue
|
|
}
|
|
doc.Content = append(doc.Content, SubSubTitle(s[1]))
|
|
case isCodeBlock(p):
|
|
doc.Content = append(doc.Content, parseCodeBlock(p))
|
|
case strings.HasPrefix(p, LISTITEM_PREFIX):
|
|
p = strings.TrimSpace(p)
|
|
var items []ListItem
|
|
for i, ti := range strings.Split(p, LISTITEM_PREFIX) {
|
|
if i > 0 {
|
|
inline := ParseInline(strings.TrimSpace(ti))
|
|
items = append(items, ListItem(inline))
|
|
}
|
|
}
|
|
doc.Content = append(doc.Content, List(items))
|
|
|
|
default:
|
|
p = strings.TrimSpace(p)
|
|
doc.Content = append(doc.Content, Paragraph(ParseInline(p)))
|
|
}
|
|
}
|
|
|
|
return doc
|
|
}
|
|
|
|
func isCodeBlock(par string) bool {
|
|
return strings.HasPrefix(par, CODE_PREFIX) && strings.HasSuffix(par, CODE_SUFFIX)
|
|
}
|
|
|
|
func parseCodeBlock(par string) CodeBlock {
|
|
ss := strings.Split(par, "\n")
|
|
ss = ss[1 : len(ss)-1]
|
|
content := strings.Join(ss, "\n")
|
|
|
|
return CodeBlock(content)
|
|
}
|
|
|
|
func ParseHeader(text string, doc *ADoc) {
|
|
text = strings.TrimSpace(text)
|
|
lines := strings.Split(text, LINE_SEPARATOR)
|
|
for i, l := range lines {
|
|
switch {
|
|
case i == 0:
|
|
s := strings.Split(l, TITLE_PREFIX)
|
|
doc.Title = s[1]
|
|
case isDate(l):
|
|
date, _ := time.Parse("2006-01-02", l)
|
|
doc.Date = date
|
|
case strings.HasPrefix(l, ":kind:"):
|
|
s := strings.Split(l, ":")
|
|
doc.Kind = NewKind(strings.TrimSpace(s[2]))
|
|
case strings.HasPrefix(l, ":language:"):
|
|
s := strings.Split(l, ":")
|
|
doc.Language = NewLanguage(strings.TrimSpace(s[2]))
|
|
case strings.HasPrefix(l, ":tags:"):
|
|
s := strings.Split(l, ":")
|
|
t := strings.Split(s[2], ",")
|
|
for _, tag := range t {
|
|
doc.Tags = append(doc.Tags, Tag(strings.TrimSpace(tag)))
|
|
}
|
|
default:
|
|
doc.Author = l
|
|
}
|
|
}
|
|
}
|
|
|
|
func isDate(text string) bool {
|
|
if _, err := time.Parse("2006-01-02", text); err == nil {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func ParseInline(text string) []InlineElement {
|
|
var e []InlineElement
|
|
|
|
ss := strings.Split(text, "")
|
|
var buffer, curWord, prevChar string
|
|
var strong, emp, code, linkTitle bool
|
|
wordStart := true
|
|
for _, s := range ss {
|
|
switch {
|
|
case (s == "_" && wordStart) || (s == "_" && emp):
|
|
e = addElement(e, buffer+curWord, strong, emp, code)
|
|
emp = !emp
|
|
buffer = ""
|
|
curWord = ""
|
|
case s == "*":
|
|
e = addElement(e, buffer+curWord, strong, emp, code)
|
|
buffer = ""
|
|
curWord = ""
|
|
strong = !strong
|
|
case s == "`":
|
|
e = addElement(e, buffer+curWord, strong, emp, code)
|
|
code = !code
|
|
buffer = ""
|
|
curWord = ""
|
|
case s == "[" && prevChar != "":
|
|
e = addElement(e, buffer, strong, emp, code)
|
|
linkTitle = true
|
|
curWord += s
|
|
case s == "]" && linkTitle:
|
|
e = addLink(e, curWord)
|
|
buffer = ""
|
|
curWord = ""
|
|
linkTitle = false
|
|
case s == " " && !linkTitle:
|
|
buffer += curWord + " "
|
|
curWord = ""
|
|
default:
|
|
curWord += s
|
|
}
|
|
prevChar = s
|
|
wordStart = false
|
|
if prevChar == " " {
|
|
wordStart = true
|
|
}
|
|
}
|
|
if len(buffer+curWord) > 0 {
|
|
e = addElement(e, buffer+curWord, false, false, false)
|
|
}
|
|
|
|
return e
|
|
}
|
|
|
|
func addLink(ies []InlineElement, linkText string) []InlineElement {
|
|
ss := strings.Split(linkText, "[")
|
|
if len(ss) < 2 {
|
|
ss = append(ss, "ERROR")
|
|
}
|
|
|
|
return append(ies, Link{url: ss[0], title: ss[1]})
|
|
}
|
|
|
|
func addElement(ies []InlineElement, text string, strong, emp, code bool) []InlineElement {
|
|
if len(text) == 0 {
|
|
return ies
|
|
}
|
|
|
|
var ne InlineElement
|
|
switch {
|
|
case code:
|
|
ne = CodeText(text)
|
|
case strong && emp:
|
|
ne = StrongEmpText(text)
|
|
case strong && !emp:
|
|
ne = StrongText(text)
|
|
case !strong && emp:
|
|
ne = EmpText(text)
|
|
default:
|
|
ne = PlainText(text)
|
|
}
|
|
|
|
return append(ies, ne)
|
|
}
|