...

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

Documentation: github.com/yuin/goldmark/extension

     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  }
   116  

View as plain text