Skip to content

Commit

Permalink
labsite: Add tooling for Next button
Browse files Browse the repository at this point in the history
Add markdown tooling for Next button generation from

	[Next]

A `<details>...</details>` HTMLBlock generated from Markdown also stops when a
`[Next]` button is found (in addition to thematic break and heading as end
marker).

As we were developing this we uncovered a bug around newlines inside code blocks
after conversion to HTMLBlock used again as MD. I've snuck in a fix and test
for that too.
  • Loading branch information
juliaogris committed Aug 26, 2024
1 parent c0d3fba commit c56a4d3
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 5 deletions.
49 changes: 44 additions & 5 deletions build-tools/labsite-gen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ import (
"rsc.io/markdown"
)

const marker = "[>]"
const (
detailsMarker = "[>]"
nextButtonMarker = "[Next]"
)

var nextButton = newNextButton()

func main() {
if len(os.Args) != 3 {
Expand All @@ -34,6 +39,7 @@ func run(mdFile, htmlfFile string) error {
return err
}
doc := parse(string(md))
replaceNextButton(doc)
doc.Blocks = collapse(doc.Blocks)
html := markdown.ToHTML(doc)

Expand All @@ -45,6 +51,29 @@ func parse(md string) *markdown.Document {
return p.Parse(md)
}

func replaceNextButton(doc *markdown.Document) {
for i, block := range doc.Blocks {
if isNextButton(block) {
doc.Blocks[i] = nextButton
}
}
}

func isNextButton(block markdown.Block) bool {
paragraph, ok := block.(*markdown.Paragraph)
if !ok {
return false
}
if len(paragraph.Text.Inline) != 1 {
return false
}
plain, ok := paragraph.Text.Inline[0].(*markdown.Plain)
if !ok {
return false
}
return plain.Text == nextButtonMarker
}

func collapse(blocks []markdown.Block) []markdown.Block {
var result []markdown.Block
for idx := 0; idx < len(blocks); {
Expand Down Expand Up @@ -75,19 +104,22 @@ func isCollapsible(block markdown.Block) bool {
return false
}
plain, ok := heading.Text.Inline[0].(*markdown.Plain)
return ok && strings.HasPrefix(plain.Text, marker)
return ok && strings.HasPrefix(plain.Text, detailsMarker)
}

func deleteCollapseMarker(heading *markdown.Heading) {
// assumes isCollapsible returned true
plain := heading.Text.Inline[0].(*markdown.Plain)
s := strings.TrimPrefix(plain.Text, marker)
s := strings.TrimPrefix(plain.Text, detailsMarker)
s = strings.TrimLeftFunc(s, unicode.IsSpace)
plain.Text = s
}

func findEndIdx(level, start int, blocks []markdown.Block) int {
for i := start; i < len(blocks); i++ {
if blocks[i] == nextButton {
return i
}
if _, ok := blocks[i].(*markdown.ThematicBreak); ok {
return i
}
Expand All @@ -110,8 +142,15 @@ func toDetailsHTML(heading *markdown.Heading, blocks []markdown.Block) *markdown
buf.WriteString(markdown.ToHTML(block))
}
buf.WriteString("</details>")
doc := parse(buf.String())
return doc.Blocks[0].(*markdown.HTMLBlock)
htmlBlock := &markdown.HTMLBlock{
Text: strings.Split(buf.String(), "\n"),
}
return htmlBlock
}

func newNextButton() *markdown.Paragraph {
doc := parse(`<button class="next-btn">Next</button>`)
return doc.Blocks[0].(*markdown.Paragraph)
}

func isThematicBreak(blocks []markdown.Block, idx int) bool {
Expand Down
78 changes: 78 additions & 0 deletions build-tools/labsite-gen/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,26 @@ anybody here?`,
<summary>goodbye</summary>
</details>
</details>
`[1:],
}, {
name: "codeblocks",
in: `
# [>] hallo
` + "```evy" + `
print 1
print 2
` + "```" + `
`,
want: `
<details>
<summary>hallo</summary>
<pre><code class="language-evy">print 1
print 2
</code></pre>
</details>
`[1:],
},
}
Expand All @@ -78,3 +98,61 @@ func TestCollapse(t *testing.T) {
})
}
}

var nextButtonTests = []struct {
name string
in string
wantMD string
wantHTML string
}{
{
name: "no marker",
in: "hallo",
wantMD: "hallo\n",
wantHTML: "<p>hallo</p>\n",
},
{
name: "single button",
in: `[Next]`,
wantMD: `<button class="next-btn">Next</button>` + "\n",
wantHTML: `<p><button class="next-btn">Next</button></p>` + "\n",
},
{
name: "multiple buttons",
in: `# Heading
[Next]
paragraph
[Next]`,
wantMD: `
# Heading
<button class="next-btn">Next</button>
paragraph
<button class="next-btn">Next</button>
`[1:],
wantHTML: `
<h1>Heading</h1>
<p><button class="next-btn">Next</button></p>
<p>paragraph</p>
<p><button class="next-btn">Next</button></p>
`[1:],
},
}

func TestReplaceNextButton(t *testing.T) {
for _, tt := range nextButtonTests {
t.Run(tt.name, func(t *testing.T) {
p := markdown.Parser{}
doc := p.Parse(tt.in)
replaceNextButton(doc)
gotMD := markdown.Format(doc)
assert.Equal(t, tt.wantMD, gotMD)
gotHTML := markdown.ToHTML(doc)
assert.Equal(t, tt.wantHTML, gotHTML)
})
}
}

0 comments on commit c56a4d3

Please sign in to comment.