diff --git a/pkg/prosemirror/html_block.go b/pkg/prosemirror/html_block.go
index b7be1bb7c..3a38284c2 100644
--- a/pkg/prosemirror/html_block.go
+++ b/pkg/prosemirror/html_block.go
@@ -225,6 +225,16 @@ func (c *htmlBlockConverter) convertBlockElement(n *html.Node) ([]Node, error) {
}
func (c *htmlBlockConverter) unwrapBlockElement(n *html.Node) ([]Node, error) {
+ if !hasBlockElementChild(n) {
+ inlines, err := c.convertInlineFragments(n)
+ if err != nil {
+ return nil, err
+ }
+ if len(inlines) == 0 {
+ return nil, nil
+ }
+ return []Node{{Type: NodeParagraph, Content: inlines}}, nil
+ }
return c.convertBlockChildren(n)
}
@@ -395,26 +405,46 @@ func hasBlockElementChild(n *html.Node) bool {
func blockTagName(name string) bool {
switch name {
case "p", "div", "blockquote", "pre", "ul", "ol", "table",
- "h1", "h2", "h3", "h4", "h5", "h6", "hr":
+ "h1", "h2", "h3", "h4", "h5", "h6", "hr",
+ "section", "article", "aside", "main", "header", "footer", "nav",
+ "figure", "center",
+ "html", "body", "head":
return true
default:
return false
}
}
-func (c *htmlBlockConverter) convertTable(n *html.Node) ([]Node, error) {
+// collectTableRows returns
nodes that belong to this table only: direct
+// children of thead/tbody/tfoot, or direct
children of the table element
+// (implicit tbody). It does not descend into cells or nested tables, so inner
+// tables cannot contribute rows to the outer table.
+func collectTableRows(table *html.Node) []*html.Node {
var rows []*html.Node
- var collect func(*html.Node)
- collect = func(x *html.Node) {
- if x.Type == html.ElementNode && x.Data == "tr" {
- rows = append(rows, x)
- return
+ for ch := table.FirstChild; ch != nil; ch = ch.NextSibling {
+ if ch.Type != html.ElementNode {
+ continue
}
- for ch := x.FirstChild; ch != nil; ch = ch.NextSibling {
- collect(ch)
+ switch ch.Data {
+ case "thead", "tbody", "tfoot":
+ for tr := ch.FirstChild; tr != nil; tr = tr.NextSibling {
+ if tr.Type == html.ElementNode && tr.Data == "tr" {
+ rows = append(rows, tr)
+ }
+ }
+ case "tr":
+ rows = append(rows, ch)
+ case "caption", "colgroup", "col":
+ // Table metadata / columns; not row containers.
+ default:
+ // Ignore other direct children (e.g. invalid markup).
}
}
- collect(n)
+ return rows
+}
+
+func (c *htmlBlockConverter) convertTable(n *html.Node) ([]Node, error) {
+ rows := collectTableRows(n)
var rowNodes []Node
for _, tr := range rows {
diff --git a/pkg/prosemirror/markdown_test.go b/pkg/prosemirror/markdown_test.go
index d6136e270..061a45e0b 100644
--- a/pkg/prosemirror/markdown_test.go
+++ b/pkg/prosemirror/markdown_test.go
@@ -406,6 +406,42 @@ func TestParseMarkdown_BlockHTML(t *testing.T) {
assert.Equal(t, "block", *doc.Content[0].Content[0].Text)
}
+func TestParseMarkdown_BlockHTMLDivPreservesInlineMarks(t *testing.T) {
+ t.Parallel()
+
+ doc, err := ParseMarkdown("hello world
\n")
+ require.NoError(t, err)
+ require.Len(t, doc.Content, 1)
+ p := doc.Content[0]
+ require.Equal(t, NodeParagraph, p.Type)
+
+ var foundStrong bool
+ for _, ch := range p.Content {
+ if ch.Type != NodeText || ch.Text == nil {
+ continue
+ }
+ if *ch.Text != "world" {
+ continue
+ }
+ require.Len(t, ch.Marks, 1)
+ assert.Equal(t, MarkStrong, ch.Marks[0].Type)
+ foundStrong = true
+ }
+ assert.True(t, foundStrong, "expected bold mark on 'world' inside a single paragraph")
+}
+
+func TestParseMarkdown_BlockHTMLDivAroundSectionWithParagraphs(t *testing.T) {
+ t.Parallel()
+
+ doc, err := ParseMarkdown("\n")
+ require.NoError(t, err)
+ require.Len(t, doc.Content, 2)
+ assert.Equal(t, NodeParagraph, doc.Content[0].Type)
+ assert.Equal(t, NodeParagraph, doc.Content[1].Type)
+ assert.Equal(t, "a", *doc.Content[0].Content[0].Text)
+ assert.Equal(t, "b", *doc.Content[1].Content[0].Text)
+}
+
func TestParseMarkdown_BlockHTMLWithClosureLine(t *testing.T) {
t.Parallel()
@@ -467,6 +503,19 @@ func TestParseMarkdown_BlockHTMLTable(t *testing.T) {
assert.Equal(t, 1, tdAttrs.Rowspan)
}
+func TestParseMarkdown_BlockHTMLNestedTableDoesNotHoistInnerRows(t *testing.T) {
+ t.Parallel()
+
+ md := "\n"
+ doc, err := ParseMarkdown(md)
+ require.NoError(t, err)
+ require.Len(t, doc.Content, 1)
+ assert.Equal(t, NodeTable, doc.Content[0].Type)
+ // Outer table must have exactly one row; inner
must not become a second outer row.
+ require.Len(t, doc.Content[0].Content, 1)
+ assert.Equal(t, NodeTableRow, doc.Content[0].Content[0].Type)
+}
+
func TestParseMarkdown_BlockHTMLTableCellSpans(t *testing.T) {
t.Parallel()