forked from jshiffer/matterbridge
286 lines
5.0 KiB
Go
286 lines
5.0 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 "strconv"
|
||
|
|
||
|
var listTerminatedBy []BlockRule
|
||
|
|
||
|
func skipBulletListMarker(s *StateBlock, startLine int) int {
|
||
|
pos := s.BMarks[startLine] + s.TShift[startLine]
|
||
|
max := s.EMarks[startLine]
|
||
|
src := s.Src
|
||
|
|
||
|
if pos >= max {
|
||
|
return -1
|
||
|
}
|
||
|
|
||
|
marker := src[pos]
|
||
|
if marker != '*' && marker != '-' && marker != '+' {
|
||
|
return -1
|
||
|
}
|
||
|
pos++
|
||
|
|
||
|
if pos < max && !byteIsSpace(src[pos]) {
|
||
|
return -1
|
||
|
}
|
||
|
|
||
|
return pos
|
||
|
}
|
||
|
|
||
|
func skipOrderedListMarker(s *StateBlock, startLine int) int {
|
||
|
start := s.BMarks[startLine] + s.TShift[startLine]
|
||
|
pos := start
|
||
|
max := s.EMarks[startLine]
|
||
|
|
||
|
if pos+1 >= max {
|
||
|
return -1
|
||
|
}
|
||
|
|
||
|
src := s.Src
|
||
|
ch := src[pos]
|
||
|
if ch < '0' || ch > '9' {
|
||
|
return -1
|
||
|
}
|
||
|
pos++
|
||
|
|
||
|
for {
|
||
|
if pos >= max {
|
||
|
return -1
|
||
|
}
|
||
|
|
||
|
ch = src[pos]
|
||
|
pos++
|
||
|
|
||
|
if ch >= '0' && ch <= '9' {
|
||
|
if pos-start >= 10 {
|
||
|
return -1
|
||
|
}
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if ch == ')' || ch == '.' {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
return -1
|
||
|
}
|
||
|
|
||
|
if pos < max && !byteIsSpace(src[pos]) {
|
||
|
return -1
|
||
|
}
|
||
|
|
||
|
return pos
|
||
|
}
|
||
|
|
||
|
func markParagraphsTight(s *StateBlock, idx int) {
|
||
|
level := s.Level + 2
|
||
|
tokens := s.Tokens
|
||
|
|
||
|
for i := idx + 2; i < len(tokens)-2; i++ {
|
||
|
if tokens[i].Level() == level {
|
||
|
if tok, ok := tokens[i].(*ParagraphOpen); ok {
|
||
|
tok.Hidden = true
|
||
|
i += 2
|
||
|
tokens[i].(*ParagraphClose).Hidden = true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func ruleList(s *StateBlock, startLine, endLine int, silent bool) bool {
|
||
|
isTerminatingParagraph := false
|
||
|
tight := true
|
||
|
|
||
|
if s.SCount[startLine]-s.BlkIndent >= 4 {
|
||
|
return false
|
||
|
}
|
||
|
src := s.Src
|
||
|
|
||
|
if silent && s.ParentType == ptParagraph {
|
||
|
if s.TShift[startLine] >= s.BlkIndent {
|
||
|
isTerminatingParagraph = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var start int
|
||
|
var markerValue int
|
||
|
|
||
|
isOrdered := false
|
||
|
posAfterMarker := skipOrderedListMarker(s, startLine)
|
||
|
if posAfterMarker > 0 {
|
||
|
isOrdered = true
|
||
|
start = s.BMarks[startLine] + s.TShift[startLine]
|
||
|
markerValue, _ = strconv.Atoi(src[start : posAfterMarker-1])
|
||
|
|
||
|
if isTerminatingParagraph && markerValue != 1 {
|
||
|
return false
|
||
|
}
|
||
|
} else {
|
||
|
posAfterMarker = skipBulletListMarker(s, startLine)
|
||
|
if posAfterMarker < 0 {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if isTerminatingParagraph {
|
||
|
if s.SkipSpaces(posAfterMarker) >= s.EMarks[startLine] {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
markerChar := src[posAfterMarker-1]
|
||
|
|
||
|
if silent {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
tokenIdx := len(s.Tokens)
|
||
|
|
||
|
var listMap *[2]int
|
||
|
if isOrdered {
|
||
|
tok := &OrderedListOpen{
|
||
|
Order: markerValue,
|
||
|
Map: [2]int{startLine, 0},
|
||
|
}
|
||
|
s.PushOpeningToken(tok)
|
||
|
listMap = &tok.Map
|
||
|
} else {
|
||
|
tok := &BulletListOpen{
|
||
|
Map: [2]int{startLine, 0},
|
||
|
}
|
||
|
s.PushOpeningToken(tok)
|
||
|
listMap = &tok.Map
|
||
|
}
|
||
|
|
||
|
nextLine := startLine
|
||
|
prevEmptyEnd := false
|
||
|
|
||
|
oldParentType := s.ParentType
|
||
|
s.ParentType = ptList
|
||
|
|
||
|
var pos int
|
||
|
var contentStart int
|
||
|
|
||
|
outer:
|
||
|
for nextLine < endLine {
|
||
|
pos = posAfterMarker
|
||
|
max := s.EMarks[nextLine]
|
||
|
|
||
|
initial := s.SCount[nextLine] + posAfterMarker - (s.BMarks[startLine] + s.TShift[startLine])
|
||
|
offset := initial
|
||
|
|
||
|
loop:
|
||
|
for pos < max {
|
||
|
switch src[pos] {
|
||
|
case '\t':
|
||
|
offset += 4 - (offset+s.BSCount[nextLine])%4
|
||
|
case ' ':
|
||
|
offset++
|
||
|
default:
|
||
|
break loop
|
||
|
}
|
||
|
pos++
|
||
|
}
|
||
|
|
||
|
contentStart = pos
|
||
|
|
||
|
indentAfterMarker := 1
|
||
|
if contentStart < max {
|
||
|
if iam := offset - initial; iam <= 4 {
|
||
|
indentAfterMarker = iam
|
||
|
}
|
||
|
}
|
||
|
|
||
|
indent := initial + indentAfterMarker
|
||
|
|
||
|
tok := &ListItemOpen{
|
||
|
Map: [2]int{startLine, 0},
|
||
|
}
|
||
|
s.PushOpeningToken(tok)
|
||
|
itemMap := &tok.Map
|
||
|
|
||
|
oldIndent := s.BlkIndent
|
||
|
oldTight := s.Tight
|
||
|
oldTShift := s.TShift[startLine]
|
||
|
oldLIndent := s.SCount[startLine]
|
||
|
s.BlkIndent = indent
|
||
|
s.Tight = true
|
||
|
s.TShift[startLine] = contentStart - s.BMarks[startLine]
|
||
|
s.SCount[startLine] = offset
|
||
|
|
||
|
if contentStart >= max && s.IsLineEmpty(startLine+1) {
|
||
|
s.Line = min(s.Line+2, endLine)
|
||
|
} else {
|
||
|
s.Md.Block.Tokenize(s, startLine, endLine)
|
||
|
}
|
||
|
|
||
|
if !s.Tight || prevEmptyEnd {
|
||
|
tight = false
|
||
|
}
|
||
|
|
||
|
prevEmptyEnd = s.Line-startLine > 1 && s.IsLineEmpty(s.Line-1)
|
||
|
|
||
|
s.BlkIndent = oldIndent
|
||
|
s.TShift[startLine] = oldTShift
|
||
|
s.SCount[startLine] = oldLIndent
|
||
|
s.Tight = oldTight
|
||
|
|
||
|
s.PushClosingToken(&ListItemClose{})
|
||
|
|
||
|
startLine = s.Line
|
||
|
nextLine = startLine
|
||
|
(*itemMap)[1] = nextLine
|
||
|
|
||
|
if nextLine >= endLine {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
contentStart = s.BMarks[startLine]
|
||
|
|
||
|
if s.SCount[nextLine] < s.BlkIndent {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
for _, r := range listTerminatedBy {
|
||
|
if r(s, nextLine, endLine, true) {
|
||
|
break outer
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if isOrdered {
|
||
|
posAfterMarker = skipOrderedListMarker(s, nextLine)
|
||
|
if posAfterMarker < 0 {
|
||
|
break
|
||
|
}
|
||
|
} else {
|
||
|
posAfterMarker = skipBulletListMarker(s, nextLine)
|
||
|
if posAfterMarker < 0 {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if markerChar != src[posAfterMarker-1] {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if isOrdered {
|
||
|
s.PushClosingToken(&OrderedListClose{})
|
||
|
} else {
|
||
|
s.PushClosingToken(&BulletListClose{})
|
||
|
}
|
||
|
|
||
|
(*listMap)[1] = nextLine
|
||
|
s.Line = nextLine
|
||
|
s.ParentType = oldParentType
|
||
|
|
||
|
if tight {
|
||
|
markParagraphsTight(s, tokenIdx)
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|