|
|
@ -4,6 +4,7 @@ import (
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
"unicode"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/rwxrob/scan"
|
|
|
|
"github.com/rwxrob/scan"
|
|
|
|
"github.com/rwxrob/term"
|
|
|
|
"github.com/rwxrob/term"
|
|
|
@ -67,109 +68,113 @@ func (s *Block) String() string { return string(s.V) }
|
|
|
|
func Blocks(in string) []*Block {
|
|
|
|
func Blocks(in string) []*Block {
|
|
|
|
|
|
|
|
|
|
|
|
var blocks []*Block
|
|
|
|
var blocks []*Block
|
|
|
|
verbpre := regexp.MustCompile(` {4,}`)
|
|
|
|
|
|
|
|
s := scan.R{Buf: []byte(to.Dedented(in))}
|
|
|
|
s := scan.R{Buf: []byte(to.Dedented(in))}
|
|
|
|
|
|
|
|
//s.Trace++
|
|
|
|
|
|
|
|
|
|
|
|
MAIN:
|
|
|
|
|
|
|
|
for s.Scan() {
|
|
|
|
for s.Scan() {
|
|
|
|
|
|
|
|
|
|
|
|
if s.Rune == '*' { // bulleted list
|
|
|
|
// bulleted list
|
|
|
|
if !s.Peek(" ") {
|
|
|
|
if s.Peek("* ") {
|
|
|
|
goto PARA
|
|
|
|
var beg, end int
|
|
|
|
}
|
|
|
|
beg = s.Pos - 1
|
|
|
|
m := s.Pos - 1
|
|
|
|
|
|
|
|
for s.Scan() {
|
|
|
|
for s.Scan() {
|
|
|
|
if s.Peek("\n\n") {
|
|
|
|
if s.Peek("\n\n") {
|
|
|
|
blocks = append(blocks, &Block{Bulleted, s.Buf[m:s.Pos]})
|
|
|
|
end = s.Pos - 1
|
|
|
|
s.Pos += 2
|
|
|
|
s.Pos++
|
|
|
|
continue MAIN
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if s.Rune == '1' { // numbered list
|
|
|
|
blocks = append(blocks, &Block{Bulleted, s.Buf[beg:end]})
|
|
|
|
if !s.Peek(". ") {
|
|
|
|
continue
|
|
|
|
goto PARA
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m := s.Pos - 1
|
|
|
|
|
|
|
|
|
|
|
|
// numbered list
|
|
|
|
|
|
|
|
if s.Peek("1. ") {
|
|
|
|
|
|
|
|
var beg, end int
|
|
|
|
|
|
|
|
beg = s.Pos - 1
|
|
|
|
|
|
|
|
|
|
|
|
for s.Scan() {
|
|
|
|
for s.Scan() {
|
|
|
|
if s.Peek("\n\n") {
|
|
|
|
if s.Peek("\n\n") {
|
|
|
|
blocks = append(blocks, &Block{Numbered, s.Buf[m:s.Pos]})
|
|
|
|
end = s.Pos - 1
|
|
|
|
s.Pos += 2
|
|
|
|
s.Pos++
|
|
|
|
continue MAIN
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if s.Rune == ' ' { // verbatim
|
|
|
|
blocks = append(blocks, &Block{Numbered, s.Buf[beg:end]})
|
|
|
|
s.Pos -= 1
|
|
|
|
|
|
|
|
ln := s.Match(verbpre)
|
|
|
|
|
|
|
|
s.Pos++
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ln < 0 {
|
|
|
|
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pre := s.Buf[s.Pos-1 : s.Pos+ln-1]
|
|
|
|
|
|
|
|
s.Pos += len(pre) - 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var block []byte
|
|
|
|
// verbatim
|
|
|
|
for s.Scan() {
|
|
|
|
if ln := s.Match(begVerbatim); ln >= 4 {
|
|
|
|
|
|
|
|
s.Pos--
|
|
|
|
if s.Rune == '\n' {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// add in indented lines
|
|
|
|
var beg, end int
|
|
|
|
if s.Peek(string(pre)) {
|
|
|
|
beg = s.Pos
|
|
|
|
block = append(block, '\n')
|
|
|
|
|
|
|
|
s.Pos += len(pre)
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// end of the block
|
|
|
|
for s.Scan() {
|
|
|
|
blocks = append(blocks, &Block{Verbatim, block})
|
|
|
|
|
|
|
|
continue MAIN
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
block = append(block, []byte(string(s.Rune))...)
|
|
|
|
if s.Peek("\n\n") {
|
|
|
|
|
|
|
|
s.Pos++
|
|
|
|
|
|
|
|
end = s.Pos - 2
|
|
|
|
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if s.Rune == '\n' || s.Rune == '\r' || s.Rune == '\t' {
|
|
|
|
dedented := to.Dedented(string(s.Buf[beg:end]))
|
|
|
|
|
|
|
|
blocks = append(blocks, &Block{Verbatim, []byte(dedented)})
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PARA:
|
|
|
|
// paragraph (default)
|
|
|
|
{
|
|
|
|
if !unicode.IsSpace(s.Rune) {
|
|
|
|
var block []byte
|
|
|
|
|
|
|
|
block = append(block, []byte(string(s.Rune))...)
|
|
|
|
buf := []byte(string(s.Rune))
|
|
|
|
|
|
|
|
|
|
|
|
for s.Scan() {
|
|
|
|
for s.Scan() {
|
|
|
|
|
|
|
|
|
|
|
|
if s.Peek("\n\n") {
|
|
|
|
if s.Peek("\n\n") {
|
|
|
|
block = append(block, []byte(string(s.Rune))...)
|
|
|
|
s.Pos++
|
|
|
|
blocks = append(blocks, &Block{Paragraph, block})
|
|
|
|
break
|
|
|
|
s.Scan()
|
|
|
|
|
|
|
|
s.Scan()
|
|
|
|
|
|
|
|
continue MAIN
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if s.Rune == '\n' || s.Rune == '\r' {
|
|
|
|
if ln := s.Match(ws); ln > 0 {
|
|
|
|
block = append(block, ' ')
|
|
|
|
buf = append(buf, ' ')
|
|
|
|
|
|
|
|
s.Pos += ln - 1
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
block = append(block, []byte(string(s.Rune))...)
|
|
|
|
buf = append(buf, []byte(string(s.Rune))...)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(block) > 0 {
|
|
|
|
|
|
|
|
blocks = append(blocks, &Block{Paragraph, block})
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // PARA
|
|
|
|
if len(buf) > 0 {
|
|
|
|
|
|
|
|
blocks = append(blocks, &Block{Paragraph, buf})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return blocks
|
|
|
|
return blocks
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// don't expose these until mark has own package
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var begVerbatim = regexp.MustCompile(`^ {4,}`)
|
|
|
|
|
|
|
|
var ws = regexp.MustCompile(`^[\s\r\n]+`)
|
|
|
|
|
|
|
|
var begUnder = regexp.MustCompile(`^<\p{L}`)
|
|
|
|
|
|
|
|
var endUnder = regexp.MustCompile(`^\p{L}>`)
|
|
|
|
|
|
|
|
var begBoldItalic = regexp.MustCompile(`^\*{3}\p{L}`)
|
|
|
|
|
|
|
|
var endBoldItalic = regexp.MustCompile(`^\p{L}\*{3}`)
|
|
|
|
|
|
|
|
var begBold = regexp.MustCompile(`^\*{2}\p{L}`)
|
|
|
|
|
|
|
|
var endBold = regexp.MustCompile(`^\p{L}\*{2}`)
|
|
|
|
|
|
|
|
var begItalic = regexp.MustCompile(`^\*\p{L}`)
|
|
|
|
|
|
|
|
var endItalic = regexp.MustCompile(`^\p{L}\*`)
|
|
|
|
|
|
|
|
|
|
|
|
// Emph renders BonzaiMark emphasis spans specifically for
|
|
|
|
// Emph renders BonzaiMark emphasis spans specifically for
|
|
|
|
// VT100-compatible terminals (which almost all are today):
|
|
|
|
// VT100-compatible terminals (which almost all are today):
|
|
|
|
//
|
|
|
|
//
|
|
|
@ -187,13 +192,15 @@ func Emph[T string | []byte | []rune](buf T) string {
|
|
|
|
for s.Scan() {
|
|
|
|
for s.Scan() {
|
|
|
|
|
|
|
|
|
|
|
|
// <under>
|
|
|
|
// <under>
|
|
|
|
if s.Rune == '<' {
|
|
|
|
if s.Match(begUnder) > 0 {
|
|
|
|
nbuf = append(nbuf, '<')
|
|
|
|
nbuf = append(nbuf, '<')
|
|
|
|
nbuf = append(nbuf, []rune(term.Under)...)
|
|
|
|
nbuf = append(nbuf, []rune(term.Under)...)
|
|
|
|
for s.Scan() {
|
|
|
|
for s.Scan() {
|
|
|
|
if s.Rune == '>' {
|
|
|
|
if s.Match(endUnder) > 0 {
|
|
|
|
|
|
|
|
nbuf = append(nbuf, s.Rune)
|
|
|
|
nbuf = append(nbuf, []rune(term.Reset)...)
|
|
|
|
nbuf = append(nbuf, []rune(term.Reset)...)
|
|
|
|
nbuf = append(nbuf, '>')
|
|
|
|
nbuf = append(nbuf, '>')
|
|
|
|
|
|
|
|
s.Pos++
|
|
|
|
break
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nbuf = append(nbuf, s.Rune)
|
|
|
|
nbuf = append(nbuf, s.Rune)
|
|
|
@ -202,13 +209,15 @@ func Emph[T string | []byte | []rune](buf T) string {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***BoldItalic***
|
|
|
|
// ***BoldItalic***
|
|
|
|
if s.Rune == '*' && s.Peek("**") {
|
|
|
|
if s.Match(begBoldItalic) > 0 {
|
|
|
|
s.Pos += 2
|
|
|
|
s.Scan()
|
|
|
|
|
|
|
|
s.Scan()
|
|
|
|
nbuf = append(nbuf, []rune(term.BoldItalic)...)
|
|
|
|
nbuf = append(nbuf, []rune(term.BoldItalic)...)
|
|
|
|
for s.Scan() {
|
|
|
|
for s.Scan() {
|
|
|
|
if s.Rune == '*' && s.Peek("**") {
|
|
|
|
if s.Match(endBoldItalic) > 0 {
|
|
|
|
s.Pos += 2
|
|
|
|
nbuf = append(nbuf, s.Rune)
|
|
|
|
nbuf = append(nbuf, []rune(term.Reset)...)
|
|
|
|
nbuf = append(nbuf, []rune(term.Reset)...)
|
|
|
|
|
|
|
|
s.Pos += 3
|
|
|
|
break
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nbuf = append(nbuf, s.Rune)
|
|
|
|
nbuf = append(nbuf, s.Rune)
|
|
|
@ -217,12 +226,13 @@ func Emph[T string | []byte | []rune](buf T) string {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// **Bold**
|
|
|
|
// **Bold**
|
|
|
|
if s.Rune == '*' && s.Peek("*") {
|
|
|
|
if s.Match(begBold) > 0 {
|
|
|
|
s.Pos++
|
|
|
|
s.Pos += 1
|
|
|
|
nbuf = append(nbuf, []rune(term.Bold)...)
|
|
|
|
nbuf = append(nbuf, []rune(term.Bold)...)
|
|
|
|
for s.Scan() {
|
|
|
|
for s.Scan() {
|
|
|
|
if s.Rune == '*' && s.Peek("*") {
|
|
|
|
if s.Match(endBold) > 0 {
|
|
|
|
s.Pos++
|
|
|
|
nbuf = append(nbuf, s.Rune)
|
|
|
|
|
|
|
|
s.Pos += 2
|
|
|
|
nbuf = append(nbuf, []rune(term.Reset)...)
|
|
|
|
nbuf = append(nbuf, []rune(term.Reset)...)
|
|
|
|
break
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -232,11 +242,13 @@ func Emph[T string | []byte | []rune](buf T) string {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// *Italic*
|
|
|
|
// *Italic*
|
|
|
|
if s.Rune == '*' {
|
|
|
|
if s.Match(begItalic) > 0 {
|
|
|
|
nbuf = append(nbuf, []rune(term.Italic)...)
|
|
|
|
nbuf = append(nbuf, []rune(term.Italic)...)
|
|
|
|
for s.Scan() {
|
|
|
|
for s.Scan() {
|
|
|
|
if s.Rune == '*' {
|
|
|
|
if s.Match(endItalic) > 0 {
|
|
|
|
|
|
|
|
nbuf = append(nbuf, s.Rune)
|
|
|
|
nbuf = append(nbuf, []rune(term.Reset)...)
|
|
|
|
nbuf = append(nbuf, []rune(term.Reset)...)
|
|
|
|
|
|
|
|
s.Pos++
|
|
|
|
break
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nbuf = append(nbuf, s.Rune)
|
|
|
|
nbuf = append(nbuf, s.Rune)
|
|
|
|