Files

64 lines
1.6 KiB
Go

package ticket
import (
"fmt"
"regexp"
"strings"
)
// taskCheckboxRe matches GFM/YouTrack-style task list items:
//
// - [ ] unchecked
// - [x] checked
// * [X] also accepted
var taskCheckboxRe = regexp.MustCompile(`^(\s*)([-*+])\s+\[([ xX])\](\s*)(.*)$`)
// Checkbox is one task-list item inside a Markdown description.
type Checkbox struct {
Line int // 0-based line index in the source
Checked bool
Text string // label after the checkbox marker
}
// ListCheckboxes returns all task-list items in markdown source order.
func ListCheckboxes(markdown string) []Checkbox {
if markdown == "" {
return nil
}
lines := strings.Split(markdown, "\n")
var out []Checkbox
for i, line := range lines {
m := taskCheckboxRe.FindStringSubmatch(line)
if m == nil {
continue
}
out = append(out, Checkbox{
Line: i,
Checked: m[3] == "x" || m[3] == "X",
Text: m[5],
})
}
return out
}
// ToggleCheckbox flips the checked state of the checkbox at index
// (0-based among ListCheckboxes results) and returns the updated markdown.
func ToggleCheckbox(markdown string, index int) (string, error) {
boxes := ListCheckboxes(markdown)
if index < 0 || index >= len(boxes) {
return markdown, fmt.Errorf("checkbox %d: not found", index)
}
lines := strings.Split(markdown, "\n")
lineIdx := boxes[index].Line
m := taskCheckboxRe.FindStringSubmatch(lines[lineIdx])
if m == nil {
return markdown, fmt.Errorf("checkbox %d: line changed", index)
}
mark := " "
if !(m[3] == "x" || m[3] == "X") {
mark = "x"
}
lines[lineIdx] = m[1] + m[2] + " [" + mark + "]" + m[4] + m[5]
return strings.Join(lines, "\n"), nil
}