mirror of
https://github.com/yuin/goldmark
synced 2025-03-04 23:04:52 +00:00
parent
9e0189df27
commit
036c8738df
6 changed files with 105 additions and 17 deletions
|
|
@ -174,7 +174,7 @@ type Node interface {
|
||||||
RemoveAttributes()
|
RemoveAttributes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// A BaseNode struct implements the Node interface.
|
// A BaseNode struct implements the Node interface partialliy.
|
||||||
type BaseNode struct {
|
type BaseNode struct {
|
||||||
firstChild Node
|
firstChild Node
|
||||||
lastChild Node
|
lastChild Node
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
textm "github.com/yuin/goldmark/text"
|
textm "github.com/yuin/goldmark/text"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A BaseBlock struct implements the Node interface.
|
// A BaseBlock struct implements the Node interface partialliy.
|
||||||
type BaseBlock struct {
|
type BaseBlock struct {
|
||||||
BaseNode
|
BaseNode
|
||||||
blankPreviousLines bool
|
blankPreviousLines bool
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/yuin/goldmark/util"
|
"github.com/yuin/goldmark/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A BaseInline struct implements the Node interface.
|
// A BaseInline struct implements the Node interface partialliy.
|
||||||
type BaseInline struct {
|
type BaseInline struct {
|
||||||
BaseNode
|
BaseNode
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ bar | baz
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>b <code>\|</code> az</td>
|
<td>b <code>|</code> az</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>b <strong>|</strong> im</td>
|
<td>b <strong>|</strong> im</td>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,13 @@ import (
|
||||||
"github.com/yuin/goldmark/util"
|
"github.com/yuin/goldmark/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var escapedPipeCellListKey = parser.NewContextKey()
|
||||||
|
|
||||||
|
type escapedPipeCell struct {
|
||||||
|
Cell *ast.TableCell
|
||||||
|
Pos []int
|
||||||
|
}
|
||||||
|
|
||||||
// TableCellAlignMethod indicates how are table cells aligned in HTML format.indicates how are table cells aligned in HTML format.
|
// TableCellAlignMethod indicates how are table cells aligned in HTML format.indicates how are table cells aligned in HTML format.
|
||||||
type TableCellAlignMethod int
|
type TableCellAlignMethod int
|
||||||
|
|
||||||
|
|
@ -148,7 +155,7 @@ func (b *tableParagraphTransformer) Transform(node *gast.Paragraph, reader text.
|
||||||
if alignments == nil {
|
if alignments == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
header := b.parseRow(lines.At(i-1), alignments, true, reader)
|
header := b.parseRow(lines.At(i-1), alignments, true, reader, pc)
|
||||||
if header == nil || len(alignments) != header.ChildCount() {
|
if header == nil || len(alignments) != header.ChildCount() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -156,7 +163,7 @@ func (b *tableParagraphTransformer) Transform(node *gast.Paragraph, reader text.
|
||||||
table.Alignments = alignments
|
table.Alignments = alignments
|
||||||
table.AppendChild(table, ast.NewTableHeader(header))
|
table.AppendChild(table, ast.NewTableHeader(header))
|
||||||
for j := i + 1; j < lines.Len(); j++ {
|
for j := i + 1; j < lines.Len(); j++ {
|
||||||
table.AppendChild(table, b.parseRow(lines.At(j), alignments, false, reader))
|
table.AppendChild(table, b.parseRow(lines.At(j), alignments, false, reader, pc))
|
||||||
}
|
}
|
||||||
node.Lines().SetSliced(0, i-1)
|
node.Lines().SetSliced(0, i-1)
|
||||||
node.Parent().InsertAfter(node.Parent(), node, table)
|
node.Parent().InsertAfter(node.Parent(), node, table)
|
||||||
|
|
@ -170,7 +177,7 @@ func (b *tableParagraphTransformer) Transform(node *gast.Paragraph, reader text.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *tableParagraphTransformer) parseRow(segment text.Segment, alignments []ast.Alignment, isHeader bool, reader text.Reader) *ast.TableRow {
|
func (b *tableParagraphTransformer) parseRow(segment text.Segment, alignments []ast.Alignment, isHeader bool, reader text.Reader, pc parser.Context) *ast.TableRow {
|
||||||
source := reader.Source()
|
source := reader.Source()
|
||||||
line := segment.Value(source)
|
line := segment.Value(source)
|
||||||
pos := 0
|
pos := 0
|
||||||
|
|
@ -194,18 +201,39 @@ func (b *tableParagraphTransformer) parseRow(segment text.Segment, alignments []
|
||||||
} else {
|
} else {
|
||||||
alignment = alignments[i]
|
alignment = alignments[i]
|
||||||
}
|
}
|
||||||
closure := util.FindClosure(line[pos:], byte(0), '|', true, false)
|
|
||||||
if closure < 0 {
|
var escapedCell *escapedPipeCell
|
||||||
closure = len(line[pos:])
|
|
||||||
}
|
|
||||||
node := ast.NewTableCell()
|
node := ast.NewTableCell()
|
||||||
seg := text.NewSegment(segment.Start+pos, segment.Start+pos+closure)
|
node.Alignment = alignment
|
||||||
|
hasBacktick := false
|
||||||
|
closure := pos
|
||||||
|
for ; closure < limit; closure++ {
|
||||||
|
if line[closure] == '`' {
|
||||||
|
hasBacktick = true
|
||||||
|
}
|
||||||
|
if line[closure] == '|' {
|
||||||
|
if closure == 0 || line[closure-1] != '\\' {
|
||||||
|
break
|
||||||
|
} else if hasBacktick {
|
||||||
|
if escapedCell == nil {
|
||||||
|
escapedCell = &escapedPipeCell{node, []int{}}
|
||||||
|
escapedList := pc.ComputeIfAbsent(escapedPipeCellListKey,
|
||||||
|
func() interface{} {
|
||||||
|
return []*escapedPipeCell{}
|
||||||
|
}).([]*escapedPipeCell)
|
||||||
|
escapedList = append(escapedList, escapedCell)
|
||||||
|
pc.Set(escapedPipeCellListKey, escapedList)
|
||||||
|
}
|
||||||
|
escapedCell.Pos = append(escapedCell.Pos, segment.Start+closure-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seg := text.NewSegment(segment.Start+pos, segment.Start+closure)
|
||||||
seg = seg.TrimLeftSpace(source)
|
seg = seg.TrimLeftSpace(source)
|
||||||
seg = seg.TrimRightSpace(source)
|
seg = seg.TrimRightSpace(source)
|
||||||
node.Lines().Append(seg)
|
node.Lines().Append(seg)
|
||||||
node.Alignment = alignment
|
|
||||||
row.AppendChild(row, node)
|
row.AppendChild(row, node)
|
||||||
pos += closure + 1
|
pos = closure + 1
|
||||||
}
|
}
|
||||||
for ; i < len(alignments); i++ {
|
for ; i < len(alignments); i++ {
|
||||||
row.AppendChild(row, ast.NewTableCell())
|
row.AppendChild(row, ast.NewTableCell())
|
||||||
|
|
@ -243,6 +271,49 @@ func (b *tableParagraphTransformer) parseDelimiter(segment text.Segment, reader
|
||||||
return alignments
|
return alignments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type tableASTTransformer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultTableASTTransformer = &tableASTTransformer{}
|
||||||
|
|
||||||
|
// NewTableASTTransformer returns a parser.ASTTransformer for tables.
|
||||||
|
func NewTableASTTransformer() parser.ASTTransformer {
|
||||||
|
return defaultTableASTTransformer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tableASTTransformer) Transform(node *gast.Document, reader text.Reader, pc parser.Context) {
|
||||||
|
lst := pc.Get(escapedPipeCellListKey)
|
||||||
|
if lst == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pc.Set(escapedPipeCellListKey, nil)
|
||||||
|
for _, v := range lst.([]*escapedPipeCell) {
|
||||||
|
_ = gast.Walk(v.Cell, func(n gast.Node, entering bool) (gast.WalkStatus, error) {
|
||||||
|
if n.Kind() != gast.KindCodeSpan {
|
||||||
|
return gast.WalkContinue, nil
|
||||||
|
}
|
||||||
|
c := n.FirstChild()
|
||||||
|
for c != nil {
|
||||||
|
next := c.NextSibling()
|
||||||
|
if c.Kind() == gast.KindText {
|
||||||
|
t := c.(*gast.Text)
|
||||||
|
for _, pos := range v.Pos {
|
||||||
|
if t.Segment.Start <= pos && t.Segment.Stop > pos {
|
||||||
|
n1 := gast.NewRawTextSegment(t.Segment.WithStop(pos))
|
||||||
|
n2 := gast.NewRawTextSegment(t.Segment.WithStart(pos + 1))
|
||||||
|
n.InsertAfter(n, c, n1)
|
||||||
|
n.InsertAfter(n, n1, n2)
|
||||||
|
n.RemoveChild(n, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c = next
|
||||||
|
}
|
||||||
|
return gast.WalkContinue, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TableHTMLRenderer is a renderer.NodeRenderer implementation that
|
// TableHTMLRenderer is a renderer.NodeRenderer implementation that
|
||||||
// renders Table nodes.
|
// renders Table nodes.
|
||||||
type TableHTMLRenderer struct {
|
type TableHTMLRenderer struct {
|
||||||
|
|
@ -454,9 +525,14 @@ func NewTable(opts ...TableOption) goldmark.Extender {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *table) Extend(m goldmark.Markdown) {
|
func (e *table) Extend(m goldmark.Markdown) {
|
||||||
m.Parser().AddOptions(parser.WithParagraphTransformers(
|
m.Parser().AddOptions(
|
||||||
|
parser.WithParagraphTransformers(
|
||||||
util.Prioritized(NewTableParagraphTransformer(), 200),
|
util.Prioritized(NewTableParagraphTransformer(), 200),
|
||||||
))
|
),
|
||||||
|
parser.WithASTTransformers(
|
||||||
|
util.Prioritized(defaultTableASTTransformer, 0),
|
||||||
|
),
|
||||||
|
)
|
||||||
m.Renderer().AddOptions(renderer.WithNodeRenderers(
|
m.Renderer().AddOptions(renderer.WithNodeRenderers(
|
||||||
util.Prioritized(NewTableHTMLRenderer(e.options...), 500),
|
util.Prioritized(NewTableHTMLRenderer(e.options...), 500),
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,9 @@ type Context interface {
|
||||||
// Get returns a value associated with the given key.
|
// Get returns a value associated with the given key.
|
||||||
Get(ContextKey) interface{}
|
Get(ContextKey) interface{}
|
||||||
|
|
||||||
|
// ComputeIfAbsent computes a value if a value associated with the given key is absent and returns the value.
|
||||||
|
ComputeIfAbsent(ContextKey, func() interface{}) interface{}
|
||||||
|
|
||||||
// Set sets the given value to the context.
|
// Set sets the given value to the context.
|
||||||
Set(ContextKey, interface{})
|
Set(ContextKey, interface{})
|
||||||
|
|
||||||
|
|
@ -252,6 +255,15 @@ func (p *parseContext) Get(key ContextKey) interface{} {
|
||||||
return p.store[key]
|
return p.store[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *parseContext) ComputeIfAbsent(key ContextKey, f func() interface{}) interface{} {
|
||||||
|
v := p.store[key]
|
||||||
|
if v == nil {
|
||||||
|
v = f()
|
||||||
|
p.store[key] = v
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
func (p *parseContext) Set(key ContextKey, value interface{}) {
|
func (p *parseContext) Set(key ContextKey, value interface{}) {
|
||||||
p.store[key] = value
|
p.store[key] = value
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue