From 3e48c02dc66df41ff4183b1e0c4a247e8a1a8831 Mon Sep 17 00:00:00 2001
From: odknt <4185305+odknt@users.noreply.github.com>
Date: Tue, 2 Apr 2019 17:11:41 +0900
Subject: [PATCH] Add task list support
---
handler.go | 8 ++++-
renderer.go | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 105 insertions(+), 1 deletion(-)
create mode 100644 renderer.go
diff --git a/handler.go b/handler.go
index a71462a..d3ce0d0 100644
--- a/handler.go
+++ b/handler.go
@@ -137,5 +137,11 @@ func markdownFileToHTML(filename string) []byte {
panic(err)
}
- return blackfriday.Run(bytes, blackfriday.WithExtensions(blackfriday.CommonExtensions))
+ return blackfriday.Run(
+ bytes,
+ blackfriday.WithExtensions(blackfriday.CommonExtensions),
+ blackfriday.WithRenderer(newRenderer(blackfriday.HTMLRendererParameters{
+ Flags: blackfriday.CommonHTMLFlags,
+ })),
+ )
}
diff --git a/renderer.go b/renderer.go
new file mode 100644
index 0000000..d352665
--- /dev/null
+++ b/renderer.go
@@ -0,0 +1,98 @@
+package main
+
+import (
+ "bytes"
+ "io"
+
+ "github.com/russross/blackfriday/v2"
+)
+
+type renderer struct {
+ *blackfriday.HTMLRenderer
+
+ inTaskList bool
+}
+
+var (
+ checkedTag = []byte(``)
+ uncheckedTag = []byte(``)
+
+ taskListTagOpen = []byte(`
`)
+ taskListTagClose = []byte(`
`)
+
+ listItemTagOpen = []byte(``)
+ listItemTagClose = []byte(``)
+)
+
+func newRenderer(params blackfriday.HTMLRendererParameters) *renderer {
+ return &renderer{
+ HTMLRenderer: blackfriday.NewHTMLRenderer(params),
+ }
+}
+
+func (r *renderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
+ switch node.Type {
+ case blackfriday.List:
+ if node.ListFlags == blackfriday.ListItemBeginningOfList {
+ if entering {
+ r.inTaskList = r.processTaskList(node)
+ if r.inTaskList {
+ w.Write(taskListTagOpen)
+ return blackfriday.GoToNext
+ }
+ } else {
+ r.inTaskList = false
+ }
+ }
+ case blackfriday.Item:
+ if r.inTaskList {
+ if entering {
+ w.Write(listItemTagOpen)
+ } else {
+ w.Write(listItemTagClose)
+ }
+ return blackfriday.GoToNext
+ }
+ case blackfriday.Text:
+ if r.inTaskList {
+ prefix := r.getTaskListItemPrefix(node)
+ if len(prefix) > 0 {
+ w.Write(prefix)
+ node.Literal = node.Literal[3:]
+ }
+ }
+ }
+ // fallback to blackfriday.Renderer
+ return r.HTMLRenderer.RenderNode(w, node, entering)
+}
+
+func (r *renderer) processTaskList(node *blackfriday.Node) bool {
+ cur := node.FirstChild
+ for cur != nil {
+ child := cur.FirstChild
+ if child.Type != blackfriday.Paragraph {
+ continue
+ }
+
+ child = child.FirstChild
+ if child.Type != blackfriday.Text {
+ continue
+ }
+
+ prefix := r.getTaskListItemPrefix(child)
+ if len(prefix) > 0 {
+ return true
+ }
+ cur = cur.Next
+ }
+ return false
+}
+
+func (r *renderer) getTaskListItemPrefix(node *blackfriday.Node) []byte {
+ if bytes.HasPrefix(node.Literal, []byte("[ ] ")) {
+ return uncheckedTag
+ } else if bytes.HasPrefix(node.Literal, []byte("[x] ")) {
+ return checkedTag
+ }
+ return []byte{}
+}