...

Source file src/github.com/yuin/goldmark/extension/tasklist.go

Documentation: github.com/yuin/goldmark/extension

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

View as plain text