Add TODO list support for Blackfriday

* Add CSS class to TODO list and list items
* Add a flag to turn task list support off

Fixes #2269
This commit is contained in:
Bjørn Erik Pedersen 2016-09-09 13:08:20 +02:00 committed by GitHub
parent 76bf2dcdd2
commit eaf2f9bce5
6 changed files with 145 additions and 3 deletions

View file

@ -0,0 +1,49 @@
---
aliases:
- /doc/supported-formats/
lastmod: 2016-07-22
date: 2016-07-22
menu:
main:
parent: content
prev: /content/summaries
next: /content/example
title: Markdown Extras
weight: 66
toc: false
---
Hugo provides some convenient markdown extensions.
## Task lists
Hugo supports GitHub styled task lists (TODO lists) for the Blackfriday renderer (md-files). See [Blackfriday config](/overview/configuration/#configure-blackfriday-rendering) for how to turn it off.
Example:
```markdown
- [ ] a task list item
- [ ] list syntax required
- [ ] incomplete
- [x] completed
```
Renders as:
- [ ] a task list item
- [ ] list syntax required
- [ ] incomplete
- [x] completed
And produces this HTML:
```html
<ul class="task-list">
<li><input type="checkbox" disabled="" class="task-list-item"> a task list item</li>
<li><input type="checkbox" disabled="" class="task-list-item"> list syntax required</li>
<li><input type="checkbox" disabled="" class="task-list-item"> incomplete</li>
<li><input type="checkbox" checked="" disabled="" class="task-list-item"> completed</li>
</ul>
```

View file

@ -4,9 +4,9 @@ date: 2013-07-01
menu:
main:
parent: content
next: /content/example
notoc: true
prev: /content/ordering
next: /content/markdown-extras
title: Summaries
weight: 65
---

View file

@ -1,14 +1,14 @@
---
aliases:
- /doc/configuration/
lastmod: 2015-12-08
lastmod: 2016-07-22
date: 2013-07-01
linktitle: Configuration
menu:
main:
parent: getting started
next: /overview/source-directory
notoc: true
toc: true
prev: /overview/usage
title: Configuring Hugo
weight: 40
@ -195,6 +195,18 @@ But Hugo does expose some options---as listed in the table below, matched with t
</thead>
<tbody>
<tr>
<td><code><strong>taskLists</strong></code></td>
<td><code>true</code></td>
<td><code></code></td>
</tr>
<tr>
<td class="purpose-title">Purpose:</td>
<td class="purpose-description" colspan="2">Turn off GitHub styled automatic task/TODO list generation.
</td>
</tr>
<tr>
<td><code><strong>smartypants</strong></code></td>
<td><code>true</code></td>

View file

@ -51,6 +51,7 @@ type Blackfriday struct {
HrefTargetBlank bool
SmartDashes bool
LatexDashes bool
TaskLists bool
PlainIDAnchors bool
SourceRelativeLinksEval bool
SourceRelativeLinksProjectFolder string
@ -68,6 +69,7 @@ func NewBlackfriday(c ConfigProvider) *Blackfriday {
"smartDashes": true,
"latexDashes": true,
"plainIDAnchors": true,
"taskLists": true,
"sourceRelativeLinks": false,
"sourceRelativeLinksProjectFolder": "/docs/content",
}

View file

@ -72,6 +72,44 @@ func (renderer *HugoHTMLRenderer) Image(out *bytes.Buffer, link []byte, title []
}
}
// ListItem adds task list support to the Blackfriday renderer.
func (renderer *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
if !renderer.Config.TaskLists {
renderer.Renderer.ListItem(out, text, flags)
return
}
switch {
case bytes.HasPrefix(text, []byte("[ ] ")):
text = append([]byte(`<input type="checkbox" disabled class="task-list-item">`), text[3:]...)
case bytes.HasPrefix(text, []byte("[x] ")) || bytes.HasPrefix(text, []byte("[X] ")):
text = append([]byte(`<input type="checkbox" checked disabled class="task-list-item">`), text[3:]...)
}
renderer.Renderer.ListItem(out, text, flags)
}
// List adds task list support to the Blackfriday renderer.
func (renderer *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flags int) {
if !renderer.Config.TaskLists {
renderer.Renderer.List(out, text, flags)
return
}
marker := out.Len()
renderer.Renderer.List(out, text, flags)
if out.Len() > marker {
list := out.Bytes()[marker:]
if bytes.Contains(list, []byte("task-list-item")) {
// Rewrite the buffer from the marker
out.Truncate(marker)
// May be either dl, ul or ol
list := append(list[:4], append([]byte(` class="task-list"`), list[4:]...)...)
out.Write(list)
}
}
}
// HugoMmarkHTMLRenderer wraps a mmark.Renderer, typically a mmark.html
// Enabling Hugo to customise the rendering experience
type HugoMmarkHTMLRenderer struct {

View file

@ -88,3 +88,44 @@ func TestCodeFence(t *testing.T) {
}
}
}
func TestBlackfridayTaskList(t *testing.T) {
for i, this := range []struct {
markdown string
taskListEnabled bool
expect string
}{
{`
TODO:
- [x] On1
- [X] On2
- [ ] Off
END
`, true, `<p>TODO:</p>
<ul class="task-list">
<li><input type="checkbox" checked disabled class="task-list-item"> On1</li>
<li><input type="checkbox" checked disabled class="task-list-item"> On2</li>
<li><input type="checkbox" disabled class="task-list-item"> Off</li>
</ul>
<p>END</p>
`},
{`- [x] On1`, false, `<ul>
<li>[x] On1</li>
</ul>
`},
} {
blackFridayConfig := NewBlackfriday(viper.GetViper())
blackFridayConfig.TaskLists = this.taskListEnabled
ctx := &RenderingContext{Content: []byte(this.markdown), PageFmt: "markdown", Config: blackFridayConfig}
result := string(RenderBytes(ctx))
if result != this.expect {
t.Errorf("[%d] got \n%v but expected \n%v", i, result, this.expect)
}
}
}