forked from jshiffer/matterbridge
04567c765e
This uses our own gomatrix lib with the SendHTML function which adds HTML to formatted_body in matrix. golang-commonmark is used to convert markdown into valid HTML.
160 lines
2.8 KiB
Go
160 lines
2.8 KiB
Go
// Copyright 2015 The Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package markdown
|
|
|
|
import (
|
|
"bytes"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
type ParserBlock struct{}
|
|
|
|
type BlockRule func(*StateBlock, int, int, bool) bool
|
|
|
|
var blockRules []BlockRule
|
|
|
|
var nl = []byte{'\n'}
|
|
|
|
func normalizeNewlines(src []byte) ([]byte, int) {
|
|
if bytes.IndexByte(src, '\r') == -1 {
|
|
return src, bytes.Count(src, nl)
|
|
}
|
|
n := 0
|
|
buf := make([]byte, 0, len(src))
|
|
for i := 0; i < len(src); i++ {
|
|
switch ch := src[i]; ch {
|
|
case '\n':
|
|
n++
|
|
buf = append(buf, '\n')
|
|
case '\r':
|
|
buf = append(buf, '\n')
|
|
n++
|
|
if i < len(src)-1 && src[i+1] == '\n' {
|
|
i++
|
|
}
|
|
default:
|
|
buf = append(buf, ch)
|
|
}
|
|
}
|
|
return buf, n
|
|
}
|
|
|
|
func (b ParserBlock) Parse(src []byte, md *Markdown, env *Environment) []Token {
|
|
src, n := normalizeNewlines(src)
|
|
if len(src) == 0 || src[len(src)-1] != '\n' {
|
|
n++
|
|
}
|
|
n++
|
|
|
|
indentFound := false
|
|
start := 0
|
|
indent := 0
|
|
offset := 0
|
|
|
|
mem := make([]int, 0, n*5)
|
|
bMarks := mem[0:0:n]
|
|
eMarks := mem[n : n : n*2]
|
|
tShift := mem[n*2 : n*2 : n*3]
|
|
sCount := mem[n*3 : n*3 : n*4]
|
|
bsCount := mem[n*4 : n*4 : n*5]
|
|
|
|
_, lastRuneLen := utf8.DecodeLastRune(src)
|
|
lastRunePos := len(src) - lastRuneLen
|
|
for pos, r := range string(src) {
|
|
if !indentFound {
|
|
if runeIsSpace(r) {
|
|
indent++
|
|
if r == '\t' {
|
|
offset += 4 - offset%4
|
|
} else {
|
|
offset++
|
|
}
|
|
continue
|
|
}
|
|
indentFound = true
|
|
}
|
|
|
|
if r == '\n' || pos == lastRunePos {
|
|
if r != '\n' {
|
|
pos = len(src)
|
|
}
|
|
bMarks = append(bMarks, start)
|
|
eMarks = append(eMarks, pos)
|
|
tShift = append(tShift, indent)
|
|
sCount = append(sCount, offset)
|
|
bsCount = append(bsCount, 0)
|
|
|
|
indentFound = false
|
|
indent = 0
|
|
offset = 0
|
|
start = pos + 1
|
|
}
|
|
}
|
|
|
|
bMarks = append(bMarks, len(src))
|
|
eMarks = append(eMarks, len(src))
|
|
tShift = append(tShift, 0)
|
|
sCount = append(sCount, 0)
|
|
bsCount = append(bsCount, 0)
|
|
|
|
var s StateBlock
|
|
s.BMarks = bMarks
|
|
s.EMarks = eMarks
|
|
s.TShift = tShift
|
|
s.SCount = sCount
|
|
s.BSCount = bsCount
|
|
s.LineMax = n - 1
|
|
s.Src = string(src)
|
|
s.Md = md
|
|
s.Env = env
|
|
|
|
b.Tokenize(&s, s.Line, s.LineMax)
|
|
|
|
return s.Tokens
|
|
}
|
|
|
|
func (ParserBlock) Tokenize(s *StateBlock, startLine, endLine int) {
|
|
line := startLine
|
|
hasEmptyLines := false
|
|
maxNesting := s.Md.MaxNesting
|
|
|
|
for line < endLine {
|
|
line = s.SkipEmptyLines(line)
|
|
s.Line = line
|
|
if line >= endLine {
|
|
break
|
|
}
|
|
|
|
if s.SCount[line] < s.BlkIndent {
|
|
break
|
|
}
|
|
|
|
if s.Level >= maxNesting {
|
|
s.Line = endLine
|
|
break
|
|
}
|
|
|
|
for _, r := range blockRules {
|
|
if r(s, line, endLine, false) {
|
|
break
|
|
}
|
|
}
|
|
|
|
s.Tight = !hasEmptyLines
|
|
|
|
if s.IsLineEmpty(s.Line - 1) {
|
|
hasEmptyLines = true
|
|
}
|
|
|
|
line = s.Line
|
|
|
|
if line < endLine && s.IsLineEmpty(line) {
|
|
hasEmptyLines = true
|
|
line++
|
|
s.Line = line
|
|
}
|
|
}
|
|
}
|