Move drop-h1 to the renderer

This is more reliable than a regular expression.
This commit is contained in:
Manuel Rüger 2023-05-19 18:47:55 +02:00
parent 5602297459
commit 484f988f32
4 changed files with 42 additions and 23 deletions

View File

@ -667,7 +667,7 @@ GLOBAL OPTIONS:
--compile-only show resulting HTML and don't update Confluence page content. (default: false) [$MARK_COMPILE_ONLY] --compile-only show resulting HTML and don't update Confluence page content. (default: false) [$MARK_COMPILE_ONLY]
--dry-run resolve page and ancestry, show resulting HTML and exit. (default: false) [$MARK_DRY_RUN] --dry-run resolve page and ancestry, show resulting HTML and exit. (default: false) [$MARK_DRY_RUN]
--edit-lock, -k lock page editing to current user only to prevent accidental manual edits over Confluence Web UI. (default: false) [$MARK_EDIT_LOCK] --edit-lock, -k lock page editing to current user only to prevent accidental manual edits over Confluence Web UI. (default: false) [$MARK_EDIT_LOCK]
--drop-h1, --h1_drop don't include H1 headings in Confluence output. (default: false) [$MARK_H1_DROP] --drop-h1, --h1_drop don't include the first H1 heading in Confluence output. (default: false) [$MARK_H1_DROP]
--title-from-h1, --h1_title extract page title from a leading H1 heading. If no H1 heading on a page exists, then title must be set in the page metadata. (default: false) [$MARK_H1_TITLE] --title-from-h1, --h1_title extract page title from a leading H1 heading. If no H1 heading on a page exists, then title must be set in the page metadata. (default: false) [$MARK_H1_TITLE]
--minor-edit don't send notifications while updating Confluence page. (default: false) [$MARK_MINOR_EDIT] --minor-edit don't send notifications while updating Confluence page. (default: false) [$MARK_MINOR_EDIT]
--color value display logs in color. Possible values: auto, never. (default: "auto") [$MARK_COLOR] --color value display logs in color. Possible values: auto, never. (default: "auto") [$MARK_COLOR]

View File

@ -58,7 +58,7 @@ var flags = []cli.Flag{
Name: "drop-h1", Name: "drop-h1",
Value: false, Value: false,
Aliases: []string{"h1_drop"}, Aliases: []string{"h1_drop"},
Usage: "don't include H1 headings in Confluence output.", Usage: "don't include the first H1 heading in Confluence output.",
EnvVars: []string{"MARK_H1_DROP"}, EnvVars: []string{"MARK_H1_DROP"},
}), }),
altsrc.NewBoolFlag(&cli.BoolFlag{ altsrc.NewBoolFlag(&cli.BoolFlag{
@ -361,10 +361,9 @@ func processFile(
log.Info( log.Info(
"the leading H1 heading will be excluded from the Confluence output", "the leading H1 heading will be excluded from the Confluence output",
) )
markdown = mark.DropDocumentLeadingH1(markdown)
} }
html, _ := mark.CompileMarkdown(markdown, stdlib, file, cCtx.String("mermaid-provider")) html, _ := mark.CompileMarkdown(markdown, stdlib, file, cCtx.String("mermaid-provider"), cCtx.Bool("drop-h1"))
fmt.Println(html) fmt.Println(html)
os.Exit(0) os.Exit(0)
} }
@ -438,10 +437,9 @@ func processFile(
log.Info( log.Info(
"the leading H1 heading will be excluded from the Confluence output", "the leading H1 heading will be excluded from the Confluence output",
) )
markdown = mark.DropDocumentLeadingH1(markdown)
} }
html, inlineAttachments := mark.CompileMarkdown(markdown, stdlib, file, cCtx.String("mermaid-provider")) html, inlineAttachments := mark.CompileMarkdown(markdown, stdlib, file, cCtx.String("mermaid-provider"), cCtx.Bool("drop-h1"))
// Resolve attachements detected from markdown // Resolve attachements detected from markdown
_, err = mark.ResolveAttachments( _, err = mark.ResolveAttachments(

View File

@ -53,17 +53,19 @@ type ConfluenceRenderer struct {
Stdlib *stdlib.Lib Stdlib *stdlib.Lib
Path string Path string
MermaidProvider string MermaidProvider string
DropFirstH1 bool
LevelMap BlockQuoteLevelMap LevelMap BlockQuoteLevelMap
Attachments []Attachment Attachments []Attachment
} }
// NewConfluenceRenderer creates a new instance of the ConfluenceRenderer // NewConfluenceRenderer creates a new instance of the ConfluenceRenderer
func NewConfluenceRenderer(stdlib *stdlib.Lib, path string, mermaidProvider string, opts ...html.Option) renderer.NodeRenderer { func NewConfluenceRenderer(stdlib *stdlib.Lib, path string, mermaidProvider string, dropFirstH1 bool, opts ...html.Option) renderer.NodeRenderer {
return &ConfluenceRenderer{ return &ConfluenceRenderer{
Config: html.NewConfig(), Config: html.NewConfig(),
Stdlib: stdlib, Stdlib: stdlib,
Path: path, Path: path,
MermaidProvider: mermaidProvider, MermaidProvider: mermaidProvider,
DropFirstH1: dropFirstH1,
LevelMap: nil, LevelMap: nil,
Attachments: []Attachment{}, Attachments: []Attachment{},
} }
@ -73,7 +75,7 @@ func NewConfluenceRenderer(stdlib *stdlib.Lib, path string, mermaidProvider stri
func (r *ConfluenceRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { func (r *ConfluenceRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
// blocks // blocks
// reg.Register(ast.KindDocument, r.renderNode) // reg.Register(ast.KindDocument, r.renderNode)
// reg.Register(ast.KindHeading, r.renderNode) reg.Register(ast.KindHeading, r.renderHeading)
reg.Register(ast.KindBlockquote, r.renderBlockQuote) reg.Register(ast.KindBlockquote, r.renderBlockQuote)
reg.Register(ast.KindCodeBlock, r.renderCodeBlock) reg.Register(ast.KindCodeBlock, r.renderCodeBlock)
reg.Register(ast.KindFencedCodeBlock, r.renderFencedCodeBlock) reg.Register(ast.KindFencedCodeBlock, r.renderFencedCodeBlock)
@ -95,6 +97,37 @@ func (r *ConfluenceRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegister
// reg.Register(ast.KindString, r.renderNode) // reg.Register(ast.KindString, r.renderNode)
} }
func (r *ConfluenceRenderer) renderHeading(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Heading)
// If this is the first h1 heading of the document and we want to drop it, let's not render it at all.
if n.Level == 1 && r.DropFirstH1 {
if !entering {
r.DropFirstH1 = false
}
return ast.WalkSkipChildren, nil
}
return r.goldmarkRenderHeading(w, source, node, entering)
}
func (r *ConfluenceRenderer) goldmarkRenderHeading(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Heading)
if entering {
_, _ = w.WriteString("<h")
_ = w.WriteByte("0123456"[n.Level])
if n.Attributes() != nil {
html.RenderAttributes(w, node, html.HeadingAttributeFilter)
}
_ = w.WriteByte('>')
} else {
_, _ = w.WriteString("</h")
_ = w.WriteByte("0123456"[n.Level])
_, _ = w.WriteString(">\n")
}
return ast.WalkContinue, nil
}
func ParseLanguage(lang string) string { func ParseLanguage(lang string) string {
// lang takes the following form: language? "collapse"? ("title"? <any string>*)? // lang takes the following form: language? "collapse"? ("title"? <any string>*)?
// let's split it by spaces // let's split it by spaces
@ -612,10 +645,10 @@ func (r *ConfluenceRenderer) goldmarkRenderHTMLBlock(w util.BufWriter, source []
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
func CompileMarkdown(markdown []byte, stdlib *stdlib.Lib, path string, mermaidProvider string) (string, []Attachment) { func CompileMarkdown(markdown []byte, stdlib *stdlib.Lib, path string, mermaidProvider string, dropFirstH1 bool) (string, []Attachment) {
log.Tracef(nil, "rendering markdown:\n%s", string(markdown)) log.Tracef(nil, "rendering markdown:\n%s", string(markdown))
confluenceRenderer := NewConfluenceRenderer(stdlib, path, mermaidProvider) confluenceRenderer := NewConfluenceRenderer(stdlib, path, mermaidProvider, dropFirstH1)
converter := goldmark.New( converter := goldmark.New(
goldmark.WithExtensions( goldmark.WithExtensions(
@ -657,18 +690,6 @@ func CompileMarkdown(markdown []byte, stdlib *stdlib.Lib, path string, mermaidPr
} }
// DropDocumentLeadingH1 will drop leading H1 headings to prevent
// duplication of or visual conflict with page titles.
// NOTE: This is intended only to operate on the whole markdown document.
// Operating on individual lines will clear them if the begin with `#`.
func DropDocumentLeadingH1(
markdown []byte,
) []byte {
h1 := regexp.MustCompile(`^#[^#].*\n`)
markdown = h1.ReplaceAll(markdown, []byte(""))
return markdown
}
// ExtractDocumentLeadingH1 will extract leading H1 heading // ExtractDocumentLeadingH1 will extract leading H1 heading
func ExtractDocumentLeadingH1(markdown []byte) string { func ExtractDocumentLeadingH1(markdown []byte) string {
h1 := regexp.MustCompile(`#[^#]\s*(.*)\s*\n`) h1 := regexp.MustCompile(`#[^#]\s*(.*)\s*\n`)

View File

@ -36,7 +36,7 @@ func TestCompileMarkdown(t *testing.T) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
actual, _ := CompileMarkdown(markdown, lib, filename, "") actual, _ := CompileMarkdown(markdown, lib, filename, "", false)
test.EqualValues(string(html), actual, filename+" vs "+htmlname) test.EqualValues(string(html), actual, filename+" vs "+htmlname)
} }
} }