mirror of
https://github.com/russross/blackfriday.git
synced 2024-03-22 13:40:34 +08:00
working on inline parsing
This commit is contained in:
parent
90f07150c4
commit
91dd5cc40f
272
markdown.go
272
markdown.go
|
@ -399,11 +399,80 @@ func char_emphasis(ob *bytes.Buffer, rndr *render, data []byte, offset int) int
|
||||||
|
|
||||||
func char_codespan(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
func char_codespan(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
data = data[offset:]
|
data = data[offset:]
|
||||||
|
|
||||||
|
nb := 0
|
||||||
|
|
||||||
|
// counting the number of backticks in the delimiter
|
||||||
|
for nb < len(data) && data[nb] == '`' {
|
||||||
|
nb++
|
||||||
|
}
|
||||||
|
|
||||||
|
// finding the next delimiter
|
||||||
|
i, end := 0, 0
|
||||||
|
for end = nb; end < len(data) && i < nb; end++ {
|
||||||
|
if data[end] == '`' {
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i < nb && end >= len(data) {
|
||||||
|
return 0 // no matching delimiter
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim outside whitespace
|
||||||
|
f_begin := nb
|
||||||
|
for f_begin < end && (data[f_begin] == ' ' || data[f_begin] == '\t') {
|
||||||
|
f_begin++
|
||||||
|
}
|
||||||
|
|
||||||
|
f_end := end - nb
|
||||||
|
for f_end > nb && (data[f_end-1] == ' ' || data[f_end-1] == '\t') {
|
||||||
|
f_end--
|
||||||
|
}
|
||||||
|
|
||||||
|
// real code span
|
||||||
|
if rndr.mk.codespan == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if f_begin < f_end {
|
||||||
|
if rndr.mk.codespan(ob, data[f_end:f_end], rndr.mk.opaque) == 0 {
|
||||||
|
end = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if rndr.mk.codespan(ob, nil, rndr.mk.opaque) == 0 {
|
||||||
|
end = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return end
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// '\n' preceded by two spaces
|
||||||
|
func char_linebreak(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
|
if offset < 2 || data[offset-1] != ' ' || data[offset-2] != ' ' {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove trailing spaces from ob and render
|
||||||
|
ob_bytes := ob.Bytes()
|
||||||
|
end := len(ob_bytes)
|
||||||
|
for end > 0 && ob_bytes[end-1] == ' ' {
|
||||||
|
end--
|
||||||
|
}
|
||||||
|
ob.Truncate(end)
|
||||||
|
|
||||||
|
if rndr.mk.linebreak == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if rndr.mk.linebreak(ob, rndr.mk.opaque) > 0 {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func char_linebreak(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
|
||||||
data = data[offset:]
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,19 +481,79 @@ func char_link(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// '<' when tags or autolinks are allowed
|
||||||
func char_langle_tag(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
func char_langle_tag(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
data = data[offset:]
|
data = data[offset:]
|
||||||
|
altype := MKDA_NOT_AUTOLINK
|
||||||
|
end := tag_length(data, &altype)
|
||||||
|
ret := 0
|
||||||
|
|
||||||
|
if end > 2 {
|
||||||
|
switch {
|
||||||
|
case rndr.mk.autolink != nil && altype != MKDA_NOT_AUTOLINK:
|
||||||
|
u_link := bytes.NewBuffer(nil)
|
||||||
|
unscape_text(u_link, data[1:end-2])
|
||||||
|
ret = rndr.mk.autolink(ob, u_link.Bytes(), altype, rndr.mk.opaque)
|
||||||
|
case rndr.mk.raw_html_tag != nil:
|
||||||
|
ret = rndr.mk.raw_html_tag(ob, data[:end], rndr.mk.opaque)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
return end
|
||||||
|
}
|
||||||
|
|
||||||
|
// '\\' backslash escape
|
||||||
|
var escape_chars = []byte("\\`*_{}[]()#+-.!:|&<>")
|
||||||
|
|
||||||
func char_escape(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
func char_escape(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
data = data[offset:]
|
data = data[offset:]
|
||||||
|
|
||||||
|
if len(data) > 1 {
|
||||||
|
if bytes.IndexByte(escape_chars, data[1]) < 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rndr.mk.normal_text != nil {
|
||||||
|
rndr.mk.normal_text(ob, data[1:2], rndr.mk.opaque)
|
||||||
|
} else {
|
||||||
|
ob.WriteByte(data[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// '&' escaped when it doesn't belong to an entity
|
||||||
|
// valid entities are assumed to be anything matching &#?[A-Za-z0-9]+;
|
||||||
func char_entity(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
func char_entity(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
data = data[offset:]
|
data = data[offset:]
|
||||||
return 0
|
|
||||||
|
end := 1
|
||||||
|
|
||||||
|
if end < len(data) && data[end] == '#' {
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
|
||||||
|
for end < len(data) && (unicode.IsDigit(int(data[end])) || unicode.IsLetter(int(data[end]))) {
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
|
||||||
|
if end < len(data) && data[end] == ';' {
|
||||||
|
end++ // real entity
|
||||||
|
} else {
|
||||||
|
return 0 // lone '&'
|
||||||
|
}
|
||||||
|
|
||||||
|
if rndr.mk.entity != nil {
|
||||||
|
rndr.mk.entity(ob, data[:end], rndr.mk.opaque)
|
||||||
|
} else {
|
||||||
|
ob.Write(data[:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
return end
|
||||||
}
|
}
|
||||||
|
|
||||||
func char_autolink(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
func char_autolink(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
|
@ -443,6 +572,122 @@ func ispunct(c int) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return the length of the given tag, or 0 is it's not valid
|
||||||
|
func tag_length(data []byte, autolink *int) int {
|
||||||
|
var i, j int
|
||||||
|
|
||||||
|
// a valid tag can't be shorter than 3 chars
|
||||||
|
if len(data) < 3 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// begins with a '<' optionally followed by '/', followed by letter or number
|
||||||
|
if data[0] != '<' {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if data[1] == '/' {
|
||||||
|
i = 2
|
||||||
|
} else {
|
||||||
|
i = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if !unicode.IsDigit(int(data[i])) && !unicode.IsLetter(int(data[i])) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// scheme test
|
||||||
|
*autolink = MKDA_NOT_AUTOLINK
|
||||||
|
|
||||||
|
// try to find the beggining of an URI
|
||||||
|
for i < len(data) && ((unicode.IsLetter(int(data[i])) || unicode.IsDigit(int(data[i]))) || data[i] == '.' || data[i] == '+' || data[i] == '-') {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > 1 && data[i] == '@' {
|
||||||
|
if j = is_mail_autolink(data[i:]); j != 0 {
|
||||||
|
*autolink = MKDA_EMAIL
|
||||||
|
return i + j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > 2 && data[i] == ':' {
|
||||||
|
*autolink = MKDA_NORMAL
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// complete autolink test: no whitespace or ' or "
|
||||||
|
switch {
|
||||||
|
case i >= len(data):
|
||||||
|
*autolink = MKDA_NOT_AUTOLINK
|
||||||
|
case *autolink != 0:
|
||||||
|
j = i
|
||||||
|
|
||||||
|
for i < len(data) {
|
||||||
|
if data[i] == '\\' {
|
||||||
|
i += 2
|
||||||
|
} else {
|
||||||
|
if data[i] == '>' || data[i] == '\'' || data[i] == '"' || unicode.IsSpace(int(data[i])) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if i >= len(data) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if i > j && data[i] == '>' {
|
||||||
|
return i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// one of the forbidden chars has been found
|
||||||
|
*autolink = MKDA_NOT_AUTOLINK
|
||||||
|
}
|
||||||
|
|
||||||
|
// looking for sometinhg looking like a tag end
|
||||||
|
for i < len(data) && data[i] != '>' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i >= len(data) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for the address part of a mail autolink and '>'
|
||||||
|
// this is less strict than the original markdown e-mail address matching
|
||||||
|
func is_mail_autolink(data []byte) int {
|
||||||
|
nb := 0
|
||||||
|
|
||||||
|
// address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@'
|
||||||
|
for i := 0; i < len(data); i++ {
|
||||||
|
if unicode.IsLetter(int(data[i])) || unicode.IsDigit(int(data[i])) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch data[i] {
|
||||||
|
case '@':
|
||||||
|
nb++
|
||||||
|
|
||||||
|
case '-', '.', '_':
|
||||||
|
break
|
||||||
|
|
||||||
|
case '>':
|
||||||
|
if nb == 1 {
|
||||||
|
return i + 1
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// look for the next emph char, skipping other constructs
|
// look for the next emph char, skipping other constructs
|
||||||
func find_emph_char(data []byte, c byte) int {
|
func find_emph_char(data []byte, c byte) int {
|
||||||
i := 1
|
i := 1
|
||||||
|
@ -1694,6 +1939,27 @@ func attr_escape(ob *bytes.Buffer, src []byte) {
|
||||||
ob.WriteString(html.EscapeString(string(src)))
|
ob.WriteString(html.EscapeString(string(src)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unscape_text(ob *bytes.Buffer, src []byte) {
|
||||||
|
i := 0
|
||||||
|
for i < len(src) {
|
||||||
|
org := i
|
||||||
|
for i < len(src) && src[i] != '\\' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > org {
|
||||||
|
ob.Write(src[org:i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if i+1 >= len(src) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
ob.WriteByte(src[i+1])
|
||||||
|
i += 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func rndr_header(ob *bytes.Buffer, text []byte, level int, opaque interface{}) {
|
func rndr_header(ob *bytes.Buffer, text []byte, level int, opaque interface{}) {
|
||||||
options := opaque.(*html_renderopts)
|
options := opaque.(*html_renderopts)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user