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(``) + + 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{} +}