gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

tasklist.go (3087B)


      1 package extension
      2 
      3 import (
      4 	"github.com/yuin/goldmark"
      5 	gast "github.com/yuin/goldmark/ast"
      6 	"github.com/yuin/goldmark/extension/ast"
      7 	"github.com/yuin/goldmark/parser"
      8 	"github.com/yuin/goldmark/renderer"
      9 	"github.com/yuin/goldmark/renderer/html"
     10 	"github.com/yuin/goldmark/text"
     11 	"github.com/yuin/goldmark/util"
     12 	"regexp"
     13 )
     14 
     15 var taskListRegexp = regexp.MustCompile(`^\[([\sxX])\]\s*`)
     16 
     17 type taskCheckBoxParser struct {
     18 }
     19 
     20 var defaultTaskCheckBoxParser = &taskCheckBoxParser{}
     21 
     22 // NewTaskCheckBoxParser returns a new  InlineParser that can parse
     23 // checkboxes in list items.
     24 // This parser must take precedence over the parser.LinkParser.
     25 func NewTaskCheckBoxParser() parser.InlineParser {
     26 	return defaultTaskCheckBoxParser
     27 }
     28 
     29 func (s *taskCheckBoxParser) Trigger() []byte {
     30 	return []byte{'['}
     31 }
     32 
     33 func (s *taskCheckBoxParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
     34 	// Given AST structure must be like
     35 	// - List
     36 	//   - ListItem         : parent.Parent
     37 	//     - TextBlock      : parent
     38 	//       (current line)
     39 	if parent.Parent() == nil || parent.Parent().FirstChild() != parent {
     40 		return nil
     41 	}
     42 
     43 	if _, ok := parent.Parent().(*gast.ListItem); !ok {
     44 		return nil
     45 	}
     46 	line, _ := block.PeekLine()
     47 	m := taskListRegexp.FindSubmatchIndex(line)
     48 	if m == nil {
     49 		return nil
     50 	}
     51 	value := line[m[2]:m[3]][0]
     52 	block.Advance(m[1])
     53 	checked := value == 'x' || value == 'X'
     54 	return ast.NewTaskCheckBox(checked)
     55 }
     56 
     57 func (s *taskCheckBoxParser) CloseBlock(parent gast.Node, pc parser.Context) {
     58 	// nothing to do
     59 }
     60 
     61 // TaskCheckBoxHTMLRenderer is a renderer.NodeRenderer implementation that
     62 // renders checkboxes in list items.
     63 type TaskCheckBoxHTMLRenderer struct {
     64 	html.Config
     65 }
     66 
     67 // NewTaskCheckBoxHTMLRenderer returns a new TaskCheckBoxHTMLRenderer.
     68 func NewTaskCheckBoxHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
     69 	r := &TaskCheckBoxHTMLRenderer{
     70 		Config: html.NewConfig(),
     71 	}
     72 	for _, opt := range opts {
     73 		opt.SetHTMLOption(&r.Config)
     74 	}
     75 	return r
     76 }
     77 
     78 // RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
     79 func (r *TaskCheckBoxHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
     80 	reg.Register(ast.KindTaskCheckBox, r.renderTaskCheckBox)
     81 }
     82 
     83 func (r *TaskCheckBoxHTMLRenderer) renderTaskCheckBox(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
     84 	if !entering {
     85 		return gast.WalkContinue, nil
     86 	}
     87 	n := node.(*ast.TaskCheckBox)
     88 
     89 	if n.IsChecked {
     90 		w.WriteString(`<input checked="" disabled="" type="checkbox"`)
     91 	} else {
     92 		w.WriteString(`<input disabled="" type="checkbox"`)
     93 	}
     94 	if r.XHTML {
     95 		w.WriteString(" /> ")
     96 	} else {
     97 		w.WriteString("> ")
     98 	}
     99 	return gast.WalkContinue, nil
    100 }
    101 
    102 type taskList struct {
    103 }
    104 
    105 // TaskList is an extension that allow you to use GFM task lists.
    106 var TaskList = &taskList{}
    107 
    108 func (e *taskList) Extend(m goldmark.Markdown) {
    109 	m.Parser().AddOptions(parser.WithInlineParsers(
    110 		util.Prioritized(NewTaskCheckBoxParser(), 0),
    111 	))
    112 	m.Renderer().AddOptions(renderer.WithNodeRenderers(
    113 		util.Prioritized(NewTaskCheckBoxHTMLRenderer(), 500),
    114 	))
    115 }