You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
4.0 KiB
223 lines
4.0 KiB
package main |
|
|
|
import ( |
|
"bufio" |
|
"bytes" |
|
"io" |
|
"regexp" |
|
"sort" |
|
"strings" |
|
) |
|
|
|
const optionsReaderSize = 512 |
|
|
|
var ( |
|
startsWithCheckbox = regexp.MustCompile(`^\[[ *xX]].*`) |
|
noteHeader = []byte("[//]: # (") |
|
) |
|
|
|
type listItem struct { |
|
Indent int |
|
Text string |
|
} |
|
|
|
type note struct { |
|
ID string |
|
Label string |
|
ModifiedAt int64 |
|
ModifiedBy string |
|
Body string |
|
} |
|
|
|
func optionsReader(r io.Reader) []string { |
|
lr := io.LimitReader(r, 9) |
|
hdr := make([]byte, 9) |
|
|
|
n, err := lr.Read(hdr) |
|
if (err != nil && err != io.EOF) || n != 9 { |
|
return nil |
|
} |
|
|
|
if !bytes.Equal(hdr, noteHeader) { |
|
return nil |
|
} |
|
|
|
var ( |
|
opts []string |
|
|
|
buf = make([]byte, optionsReaderSize) |
|
opt []byte |
|
) |
|
|
|
ReadOptions: |
|
for { |
|
read, err := r.Read(buf) |
|
if err != nil { |
|
if err == io.EOF { |
|
break |
|
} |
|
|
|
return nil |
|
} |
|
|
|
for _, chr := range buf[0:read] { |
|
switch chr { |
|
case ',': |
|
if opt != nil { |
|
opts = append(opts, string(opt)) |
|
opt = nil |
|
} |
|
case ')': |
|
break ReadOptions |
|
case '\n': |
|
return nil |
|
default: |
|
opt = append(opt, chr) |
|
} |
|
} |
|
} |
|
if opt != nil { |
|
opts = append(opts, string(opt)) |
|
} |
|
|
|
return opts |
|
} |
|
|
|
func options(body string) []string { |
|
if len(body) <= 10 || body[0:9] != "[//]: # (" { |
|
return nil |
|
} |
|
|
|
endOptions := strings.IndexRune(body, ')') |
|
if endOptions == -1 { |
|
return nil |
|
} |
|
|
|
opts := strings.Split(body[9:endOptions], ",") |
|
options := make([]string, len(opts)) |
|
for _, a := range opts { |
|
options = append(options, strings.ToLower(strings.TrimSpace(a))) |
|
} |
|
|
|
return options |
|
} |
|
|
|
func printListItems(list []*listItem, bullet string, capitalize bool) string { |
|
var indents []int |
|
for _, item := range list { |
|
indents = append(indents, item.Indent) |
|
} |
|
indents = uniqueInts(indents) |
|
sort.Ints(indents) |
|
|
|
offsets := make(map[int]int, len(indents)) |
|
for i, indent := range indents { |
|
offsets[indent] = i |
|
} |
|
|
|
var ( |
|
listItems = make([][]string, len(list)) |
|
previousItems []string |
|
theseItems []string |
|
) |
|
for i, item := range list { |
|
if offsets[item.Indent] > 0 { |
|
for _, previtem := range previousItems[0:offsets[item.Indent]] { |
|
theseItems = append(theseItems, previtem) |
|
} |
|
} |
|
theseItems = append(theseItems, item.Text) |
|
|
|
listItems[i] = theseItems |
|
previousItems = theseItems |
|
theseItems = nil |
|
} |
|
|
|
sort.Slice(listItems, func(i, j int) bool { |
|
a := listItems[i] |
|
b := listItems[j] |
|
|
|
lena := len(a) |
|
lenb := len(b) |
|
for i := 0; i < lena; i++ { |
|
if i > lenb-1 { |
|
return false |
|
} |
|
|
|
if a[i] != b[i] { |
|
return stripLeadingCheckbox(a[i]) < stripLeadingCheckbox(b[i]) |
|
} |
|
} |
|
|
|
return true |
|
}) |
|
|
|
out := "" |
|
var ( |
|
item string |
|
itemTrimmed string |
|
chkoffset int |
|
lenitems int |
|
) |
|
for _, items := range listItems { |
|
lenitems = len(items) |
|
for i := 0; i < indents[lenitems-1]; i++ { |
|
out += " " |
|
} |
|
|
|
out += bullet + " " |
|
if capitalize { |
|
item = items[lenitems-1] |
|
chkoffset = 0 |
|
if startsWithCheckbox.MatchString(item) { |
|
chkoffset = strings.IndexRune(item, ']') |
|
|
|
if len(item) > chkoffset+1 { |
|
itemTrimmed = strings.TrimSpace(item[chkoffset+2:]) |
|
item = item[0:chkoffset+1] + " " + strings.ToUpper(itemTrimmed[0:1]) + itemTrimmed[1:] |
|
} |
|
} |
|
|
|
out += item |
|
} else { |
|
out += items[lenitems-1] |
|
} |
|
out += "\n" |
|
} |
|
return out |
|
} |
|
|
|
func sortListItems(body string, capitalize bool) string { |
|
var ( |
|
line string |
|
newBody string |
|
list []*listItem |
|
bullet string |
|
) |
|
|
|
scanner := bufio.NewScanner(strings.NewReader(body)) |
|
for scanner.Scan() { |
|
line = scanner.Text() |
|
thisIndent := strings.IndexAny(line, "*-") |
|
if thisIndent == -1 || len(line) == (thisIndent+1) || line[thisIndent+1:thisIndent+2] != " " { |
|
if list != nil { |
|
newBody += printListItems(list, bullet, capitalize) |
|
list = nil |
|
} |
|
|
|
newBody += line + "\n" |
|
} else { |
|
if bullet == "" { |
|
bullet = line[thisIndent : thisIndent+1] |
|
} |
|
|
|
list = append(list, &listItem{Indent: thisIndent, Text: strings.TrimSpace(line[thisIndent+2:])}) |
|
} |
|
} |
|
|
|
if list != nil { |
|
newBody += printListItems(list, bullet, capitalize) |
|
} |
|
|
|
return newBody |
|
}
|
|
|