mirror of
https://github.com/yuin/goldmark
synced 2025-03-04 23:04:52 +00:00
commit
615d5706c6
21 changed files with 50 additions and 44 deletions
|
|
@ -436,7 +436,7 @@ func Walk(n Node, walker Walker) error {
|
||||||
}
|
}
|
||||||
if status != WalkSkipChildren {
|
if status != WalkSkipChildren {
|
||||||
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
|
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
|
||||||
if err := Walk(c, walker); err != nil {
|
if err = Walk(c, walker); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/yuin/goldmark"
|
. "github.com/yuin/goldmark"
|
||||||
"github.com/yuin/goldmark/testutil"
|
|
||||||
"github.com/yuin/goldmark/renderer/html"
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
|
"github.com/yuin/goldmark/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type commonmarkSpecTestCase struct {
|
type commonmarkSpecTestCase struct {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
"github.com/yuin/goldmark/testutil"
|
|
||||||
"github.com/yuin/goldmark/renderer/html"
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
|
"github.com/yuin/goldmark/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDefinitionList(t *testing.T) {
|
func TestDefinitionList(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
"github.com/yuin/goldmark/testutil"
|
|
||||||
"github.com/yuin/goldmark/renderer/html"
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
|
"github.com/yuin/goldmark/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFootnote(t *testing.T) {
|
func TestFootnote(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
"github.com/yuin/goldmark/testutil"
|
|
||||||
"github.com/yuin/goldmark/renderer/html"
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
|
"github.com/yuin/goldmark/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStrikethrough(t *testing.T) {
|
func TestStrikethrough(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -84,10 +84,10 @@ func (b *tableParagraphTransformer) parseRow(segment text.Segment, alignments []
|
||||||
closure = len(line[pos:])
|
closure = len(line[pos:])
|
||||||
}
|
}
|
||||||
node := ast.NewTableCell()
|
node := ast.NewTableCell()
|
||||||
segment := text.NewSegment(segment.Start+pos, segment.Start+pos+closure)
|
seg := text.NewSegment(segment.Start+pos, segment.Start+pos+closure)
|
||||||
segment = segment.TrimLeftSpace(source)
|
seg = seg.TrimLeftSpace(source)
|
||||||
segment = segment.TrimRightSpace(source)
|
seg = seg.TrimRightSpace(source)
|
||||||
node.Lines().Append(segment)
|
node.Lines().Append(seg)
|
||||||
node.Alignment = alignment
|
node.Alignment = alignment
|
||||||
row.AppendChild(row, node)
|
row.AppendChild(row, node)
|
||||||
pos += closure + 1
|
pos += closure + 1
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
"github.com/yuin/goldmark/testutil"
|
|
||||||
"github.com/yuin/goldmark/renderer/html"
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
|
"github.com/yuin/goldmark/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTable(t *testing.T) {
|
func TestTable(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
"github.com/yuin/goldmark/testutil"
|
|
||||||
"github.com/yuin/goldmark/renderer/html"
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
|
"github.com/yuin/goldmark/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTaskList(t *testing.T) {
|
func TestTaskList(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
"github.com/yuin/goldmark/testutil"
|
|
||||||
"github.com/yuin/goldmark/renderer/html"
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
|
"github.com/yuin/goldmark/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTypographer(t *testing.T) {
|
func TestTypographer(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@ package fuzz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
"github.com/yuin/goldmark/extension"
|
"github.com/yuin/goldmark/extension"
|
||||||
"github.com/yuin/goldmark/renderer/html"
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Fuzz runs automated fuzzing against goldmark.
|
||||||
func Fuzz(data []byte) int {
|
func Fuzz(data []byte) int {
|
||||||
markdown := goldmark.New(
|
markdown := goldmark.New(
|
||||||
goldmark.WithRendererOptions(
|
goldmark.WithRendererOptions(
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/yuin/goldmark"
|
. "github.com/yuin/goldmark"
|
||||||
"github.com/yuin/goldmark/testutil"
|
|
||||||
"github.com/yuin/goldmark/parser"
|
"github.com/yuin/goldmark/parser"
|
||||||
|
"github.com/yuin/goldmark/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAttributeAndAutoHeadingID(t *testing.T) {
|
func TestAttributeAndAutoHeadingID(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/yuin/goldmark/text"
|
"github.com/yuin/goldmark/text"
|
||||||
"github.com/yuin/goldmark/util"
|
"github.com/yuin/goldmark/util"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var attrNameID = []byte("id")
|
var attrNameID = []byte("id")
|
||||||
|
|
@ -104,7 +106,7 @@ func parseAttribute(reader text.Reader) (Attribute, bool) {
|
||||||
}
|
}
|
||||||
i := 0
|
i := 0
|
||||||
for ; i < len(line); i++ {
|
for ; i < len(line); i++ {
|
||||||
c := line[i]
|
c = line[i]
|
||||||
if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||||
(c >= '0' && c <= '9') ||
|
(c >= '0' && c <= '9') ||
|
||||||
c == '_' || c == ':' || c == '.' || c == '-') {
|
c == '_' || c == ':' || c == '.' || c == '-') {
|
||||||
|
|
@ -125,7 +127,6 @@ func parseAttribute(reader text.Reader) (Attribute, bool) {
|
||||||
return Attribute{}, false
|
return Attribute{}, false
|
||||||
}
|
}
|
||||||
return Attribute{Name: name, Value: value}, true
|
return Attribute{Name: name, Value: value}, true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAttributeValue(reader text.Reader) (interface{}, bool) {
|
func parseAttributeValue(reader text.Reader) (interface{}, bool) {
|
||||||
|
|
@ -153,7 +154,6 @@ func parseAttributeValue(reader text.Reader) (interface{}, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
return value, true
|
return value, true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAttributeArray(reader text.Reader) ([]interface{}, bool) {
|
func parseAttributeArray(reader text.Reader) ([]interface{}, bool) {
|
||||||
|
|
@ -228,7 +228,7 @@ func parseAttributeString(reader text.Reader) ([]byte, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func scanAttributeDecimal(reader text.Reader, w *bytes.Buffer) {
|
func scanAttributeDecimal(reader text.Reader, w io.ByteWriter) {
|
||||||
for {
|
for {
|
||||||
c := reader.Peek()
|
c := reader.Peek()
|
||||||
if util.IsNumeric(c) {
|
if util.IsNumeric(c) {
|
||||||
|
|
|
||||||
|
|
@ -108,19 +108,19 @@ func (b *atxHeadingParser) Open(parent ast.Node, reader text.Reader, pc Context)
|
||||||
start--
|
start--
|
||||||
closureClose := -1
|
closureClose := -1
|
||||||
closureOpen := -1
|
closureOpen := -1
|
||||||
for i := start; i < stop; {
|
for j := start; j < stop; {
|
||||||
c := line[i]
|
c := line[j]
|
||||||
if util.IsEscapedPunctuation(line, i) {
|
if util.IsEscapedPunctuation(line, j) {
|
||||||
i += 2
|
j += 2
|
||||||
} else if util.IsSpace(c) && i < stop-1 && line[i+1] == '#' {
|
} else if util.IsSpace(c) && j < stop-1 && line[j+1] == '#' {
|
||||||
closureOpen = i + 1
|
closureOpen = j + 1
|
||||||
j := i + 1
|
k := j + 1
|
||||||
for ; j < stop && line[j] == '#'; j++ {
|
for ; k < stop && line[k] == '#'; k++ {
|
||||||
}
|
}
|
||||||
closureClose = j
|
closureClose = k
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
i++
|
j++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if closureClose > 0 {
|
if closureClose > 0 {
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ func (s *codeSpanParser) Parse(parent ast.Node, block text.Reader, pc Context) a
|
||||||
}
|
}
|
||||||
closure := i - oldi
|
closure := i - oldi
|
||||||
if closure == opener && (i+1 >= len(line) || line[i+1] != '`') {
|
if closure == opener && (i+1 >= len(line) || line[i+1] != '`') {
|
||||||
segment := segment.WithStop(segment.Start + i - closure)
|
segment = segment.WithStop(segment.Start + i - closure)
|
||||||
if !segment.IsEmpty() {
|
if !segment.IsEmpty() {
|
||||||
node.AppendChild(node, ast.NewRawTextSegment(segment))
|
node.AppendChild(node, ast.NewRawTextSegment(segment))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,12 @@ package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/yuin/goldmark/ast"
|
"github.com/yuin/goldmark/ast"
|
||||||
"github.com/yuin/goldmark/text"
|
"github.com/yuin/goldmark/text"
|
||||||
"github.com/yuin/goldmark/util"
|
"github.com/yuin/goldmark/util"
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var allowedBlockTags = map[string]bool{
|
var allowedBlockTags = map[string]bool{
|
||||||
|
|
@ -97,12 +98,12 @@ var htmlBlockType7Regexp = regexp.MustCompile(`^[ ]{0,3}<(/)?([a-zA-Z0-9]+)(` +
|
||||||
type htmlBlockParser struct {
|
type htmlBlockParser struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultHtmlBlockParser = &htmlBlockParser{}
|
var defaultHTMLBlockParser = &htmlBlockParser{}
|
||||||
|
|
||||||
// NewHTMLBlockParser return a new BlockParser that can parse html
|
// NewHTMLBlockParser return a new BlockParser that can parse html
|
||||||
// blocks.
|
// blocks.
|
||||||
func NewHTMLBlockParser() BlockParser {
|
func NewHTMLBlockParser() BlockParser {
|
||||||
return defaultHtmlBlockParser
|
return defaultHTMLBlockParser
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *htmlBlockParser) Trigger() []byte {
|
func (b *htmlBlockParser) Trigger() []byte {
|
||||||
|
|
@ -133,7 +134,7 @@ func (b *htmlBlockParser) Open(parent ast.Node, reader text.Reader, pc Context)
|
||||||
isCloseTag := match[2] > -1 && bytes.Equal(line[match[2]:match[3]], []byte("/"))
|
isCloseTag := match[2] > -1 && bytes.Equal(line[match[2]:match[3]], []byte("/"))
|
||||||
hasAttr := match[6] != match[7]
|
hasAttr := match[6] != match[7]
|
||||||
tagName = strings.ToLower(string(line[match[4]:match[5]]))
|
tagName = strings.ToLower(string(line[match[4]:match[5]]))
|
||||||
_, ok := allowedBlockTags[strings.ToLower(string(tagName))]
|
_, ok := allowedBlockTags[tagName]
|
||||||
if ok {
|
if ok {
|
||||||
node = ast.NewHTMLBlock(ast.HTMLBlockType6)
|
node = ast.NewHTMLBlock(ast.HTMLBlockType6)
|
||||||
} else if tagName != "script" && tagName != "style" && tagName != "pre" && !ast.IsParagraph(last) && !(isCloseTag && hasAttr) { // type 7 can not interrupt paragraph
|
} else if tagName != "script" && tagName != "style" && tagName != "pre" && !ast.IsParagraph(last) && !(isCloseTag && hasAttr) { // type 7 can not interrupt paragraph
|
||||||
|
|
|
||||||
|
|
@ -903,7 +903,7 @@ retry:
|
||||||
if w > 3 && !bp.CanAcceptIndentedLine() {
|
if w > 3 && !bp.CanAcceptIndentedLine() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
lastBlock := pc.LastOpenedBlock()
|
lastBlock = pc.LastOpenedBlock()
|
||||||
last := lastBlock.Node
|
last := lastBlock.Node
|
||||||
node, state := bp.Open(parent, reader, pc)
|
node, state := bp.Open(parent, reader, pc)
|
||||||
if node != nil {
|
if node != nil {
|
||||||
|
|
@ -1177,5 +1177,4 @@ func (p *parser) parseBlock(block text.BlockReader, parent ast.Node, pc Context)
|
||||||
for _, ip := range p.closeBlockers {
|
for _, ip := range p.closeBlockers {
|
||||||
ip.CloseBlock(parent, block, pc)
|
ip.CloseBlock(parent, block, pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ type thematicBreakPraser struct {
|
||||||
|
|
||||||
var defaultThematicBreakPraser = &thematicBreakPraser{}
|
var defaultThematicBreakPraser = &thematicBreakPraser{}
|
||||||
|
|
||||||
// NewThematicBreakPraser returns a new BlockParser that
|
// NewThematicBreakParser returns a new BlockParser that
|
||||||
// parses thematic breaks.
|
// parses thematic breaks.
|
||||||
func NewThematicBreakParser() BlockParser {
|
func NewThematicBreakParser() BlockParser {
|
||||||
return defaultThematicBreakPraser
|
return defaultThematicBreakPraser
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
// Package renderer renders the given AST to certain formats.
|
// Package renderer renders the given AST to certain formats.
|
||||||
package renderer
|
package renderer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"io"
|
"io"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/yuin/goldmark/ast"
|
"github.com/yuin/goldmark/ast"
|
||||||
"github.com/yuin/goldmark/util"
|
"github.com/yuin/goldmark/util"
|
||||||
|
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Config struct is a data structure that holds configuration of the Renderer.
|
// A Config struct is a data structure that holds configuration of the Renderer.
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ type TestingT interface {
|
||||||
FailNow()
|
FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarkdownTestCase represents a test case.
|
||||||
type MarkdownTestCase struct {
|
type MarkdownTestCase struct {
|
||||||
No int
|
No int
|
||||||
Markdown string
|
Markdown string
|
||||||
|
|
@ -30,6 +31,7 @@ type MarkdownTestCase struct {
|
||||||
const attributeSeparator = "//- - - - - - - - -//"
|
const attributeSeparator = "//- - - - - - - - -//"
|
||||||
const caseSeparator = "//= = = = = = = = = = = = = = = = = = = = = = = =//"
|
const caseSeparator = "//= = = = = = = = = = = = = = = = = = = = = = = =//"
|
||||||
|
|
||||||
|
// DoTestCaseFile runs test cases in a given file.
|
||||||
func DoTestCaseFile(m goldmark.Markdown, filename string, t TestingT) {
|
func DoTestCaseFile(m goldmark.Markdown, filename string, t TestingT) {
|
||||||
fp, err := os.Open(filename)
|
fp, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -86,12 +88,14 @@ func DoTestCaseFile(m goldmark.Markdown, filename string, t TestingT) {
|
||||||
DoTestCases(m, cases, t)
|
DoTestCases(m, cases, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DoTestCases runs a set of test cases.
|
||||||
func DoTestCases(m goldmark.Markdown, cases []MarkdownTestCase, t TestingT) {
|
func DoTestCases(m goldmark.Markdown, cases []MarkdownTestCase, t TestingT) {
|
||||||
for _, testCase := range cases {
|
for _, testCase := range cases {
|
||||||
DoTestCase(m, testCase, t)
|
DoTestCase(m, testCase, t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DoTestCase runs a test case.
|
||||||
func DoTestCase(m goldmark.Markdown, testCase MarkdownTestCase, t TestingT) {
|
func DoTestCase(m goldmark.Markdown, testCase MarkdownTestCase, t TestingT) {
|
||||||
var ok bool
|
var ok bool
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package text
|
package text
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/yuin/goldmark/util"
|
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/yuin/goldmark/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const invalidValue = -1
|
const invalidValue = -1
|
||||||
|
|
@ -138,7 +139,7 @@ func (r *reader) LineOffset() int {
|
||||||
if r.source[i] == '\t' {
|
if r.source[i] == '\t' {
|
||||||
v += util.TabWidth(v)
|
v += util.TabWidth(v)
|
||||||
} else {
|
} else {
|
||||||
v += 1
|
v++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r.lineOffset = v - r.pos.Padding
|
r.lineOffset = v - r.pos.Padding
|
||||||
|
|
@ -355,7 +356,7 @@ func (r *blockReader) LineOffset() int {
|
||||||
if r.source[i] == '\t' {
|
if r.source[i] == '\t' {
|
||||||
v += util.TabWidth(v)
|
v += util.TabWidth(v)
|
||||||
} else {
|
} else {
|
||||||
v += 1
|
v++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r.lineOffset = v - r.pos.Padding
|
r.lineOffset = v - r.pos.Padding
|
||||||
|
|
|
||||||
|
|
@ -631,7 +631,7 @@ func URLEscape(v []byte, resolveReference bool) []byte {
|
||||||
cob.Write(v[n:i])
|
cob.Write(v[n:i])
|
||||||
stop := i + int(u8len)
|
stop := i + int(u8len)
|
||||||
if stop > len(v) {
|
if stop > len(v) {
|
||||||
i += 1
|
i++
|
||||||
n = i
|
n = i
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue