Convert html blocks inside table cells
Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -580,7 +580,7 @@ func (c *converter) convertTableCells(row ast.Node, cellType NodeType) ([]Node,
|
||||
continue
|
||||
}
|
||||
|
||||
inlineContent, err := c.convertInlineChildren(child)
|
||||
content, err := c.convertTableCellContent(child)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -598,13 +598,86 @@ func (c *converter) convertTableCells(row ast.Node, cellType NodeType) ([]Node,
|
||||
cells = append(cells, Node{
|
||||
Type: cellType,
|
||||
Attrs: attrs,
|
||||
Content: []Node{{Type: NodeParagraph, Content: inlineContent}},
|
||||
Content: content,
|
||||
})
|
||||
}
|
||||
|
||||
return cells, nil
|
||||
}
|
||||
|
||||
func (c *converter) convertTableCellContent(cell ast.Node) ([]Node, error) {
|
||||
if c.cellHasBlockHTML(cell) {
|
||||
raw := c.collectCellRawContent(cell)
|
||||
nodes, err := convertProseMirrorFromHTMLBlock(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(nodes) > 0 {
|
||||
return nodes, nil
|
||||
}
|
||||
}
|
||||
|
||||
inlineContent, err := c.convertInlineChildren(cell)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []Node{{Type: NodeParagraph, Content: inlineContent}}, nil
|
||||
}
|
||||
|
||||
func (c *converter) cellHasBlockHTML(cell ast.Node) bool {
|
||||
for ch := cell.FirstChild(); ch != nil; ch = ch.NextSibling() {
|
||||
if ch.Kind() != ast.KindRawHTML {
|
||||
continue
|
||||
}
|
||||
raw := ch.(*ast.RawHTML)
|
||||
for i := 0; i < raw.Segments.Len(); i++ {
|
||||
seg := raw.Segments.At(i)
|
||||
val := strings.ToLower(string(seg.Value(c.source)))
|
||||
if containsBlockOpenTag(val) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func containsBlockOpenTag(s string) bool {
|
||||
for _, tag := range []string{
|
||||
"<ul", "<ol", "<table", "<blockquote", "<pre", "<div",
|
||||
"<h1", "<h2", "<h3", "<h4", "<h5", "<h6", "<hr",
|
||||
} {
|
||||
if strings.Contains(s, tag) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *converter) collectCellRawContent(cell ast.Node) string {
|
||||
var buf bytes.Buffer
|
||||
for ch := cell.FirstChild(); ch != nil; ch = ch.NextSibling() {
|
||||
switch ch.Kind() {
|
||||
case ast.KindRawHTML:
|
||||
raw := ch.(*ast.RawHTML)
|
||||
for i := 0; i < raw.Segments.Len(); i++ {
|
||||
seg := raw.Segments.At(i)
|
||||
buf.Write(seg.Value(c.source))
|
||||
}
|
||||
case ast.KindText:
|
||||
t := ch.(*ast.Text)
|
||||
buf.Write(t.Segment.Value(c.source))
|
||||
if t.SoftLineBreak() {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
case ast.KindString:
|
||||
buf.Write(ch.(*ast.String).Value)
|
||||
default:
|
||||
buf.WriteString(c.extractText(ch))
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// extractText recursively collects the text content of all descendant nodes.
|
||||
func (c *converter) extractText(n ast.Node) string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
@@ -644,6 +644,55 @@ func TestParseMarkdown_MarkdownTableWithInlineMarks(t *testing.T) {
|
||||
assert.Equal(t, MarkEm, p.Content[2].Marks[0].Type)
|
||||
}
|
||||
|
||||
func TestParseMarkdown_MarkdownTableCellWithBlockList(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
md := "| Header |\n| --- |\n| <ul><li>one</li><li>two</li></ul> |\n"
|
||||
doc, err := ParseMarkdown(md)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, doc.Content, 1)
|
||||
assert.Equal(t, NodeTable, doc.Content[0].Type)
|
||||
|
||||
dataRow := doc.Content[0].Content[1]
|
||||
assert.Equal(t, NodeTableRow, dataRow.Type)
|
||||
cell := dataRow.Content[0]
|
||||
assert.Equal(t, NodeTableCell, cell.Type)
|
||||
|
||||
require.Len(t, cell.Content, 1)
|
||||
assert.Equal(t, NodeBulletList, cell.Content[0].Type)
|
||||
require.Len(t, cell.Content[0].Content, 2)
|
||||
assert.Equal(t, NodeListItem, cell.Content[0].Content[0].Type)
|
||||
assert.Equal(t, NodeListItem, cell.Content[0].Content[1].Type)
|
||||
|
||||
li1Para := cell.Content[0].Content[0].Content[0]
|
||||
require.Equal(t, NodeParagraph, li1Para.Type)
|
||||
assert.Equal(t, "one", *li1Para.Content[0].Text)
|
||||
|
||||
li2Para := cell.Content[0].Content[1].Content[0]
|
||||
require.Equal(t, NodeParagraph, li2Para.Type)
|
||||
assert.Equal(t, "two", *li2Para.Content[0].Text)
|
||||
}
|
||||
|
||||
func TestParseMarkdown_MarkdownTableCellWithOrderedList(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
md := "| Header |\n| --- |\n| <ol><li>first</li><li>second</li></ol> |\n"
|
||||
doc, err := ParseMarkdown(md)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, doc.Content, 1)
|
||||
assert.Equal(t, NodeTable, doc.Content[0].Type)
|
||||
|
||||
dataRow := doc.Content[0].Content[1]
|
||||
cell := dataRow.Content[0]
|
||||
assert.Equal(t, NodeTableCell, cell.Type)
|
||||
|
||||
require.Len(t, cell.Content, 1)
|
||||
assert.Equal(t, NodeOrderedList, cell.Content[0].Type)
|
||||
require.Len(t, cell.Content[0].Content, 2)
|
||||
assert.Equal(t, NodeListItem, cell.Content[0].Content[0].Type)
|
||||
assert.Equal(t, NodeListItem, cell.Content[0].Content[1].Type)
|
||||
}
|
||||
|
||||
func TestParseMarkdown_BlockHTMLScriptRemovedKeepsSafeContent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user