present: fix Markdown bugs
The handling of subsubsections was not completely right,
causing unexpected subsubsubsections (#### inside ##)
to go into an infinite loop. Handle that.
Also, my usage of goldmark's (not completely documented)
SetAttributeString was wrong. Need []byte, not string.
Change-Id: Ib127a72b94b5a46adc9047fdb88dd2a8d03e73fe
Reviewed-on: https://go-review.googlesource.com/c/tools/+/223601
Reviewed-by: Andrew Bonventre <andybons@golang.org>
diff --git a/present/parse.go b/present/parse.go
index 672a6ff..c4c7d94 100644
--- a/present/parse.go
+++ b/present/parse.go
@@ -316,7 +316,7 @@
}
if isSpeakerNote(lines.text[i]) {
- doc.TitleNotes = append(doc.TitleNotes, lines.text[i][2:])
+ doc.TitleNotes = append(doc.TitleNotes, trimSpeakerNote(lines.text[i]))
}
}
@@ -349,11 +349,14 @@
}
// isHeading matches any section heading.
-var isHeading = regexp.MustCompile(`^\*+ `)
+var (
+ isHeadingLegacy = regexp.MustCompile(`^\*+( |$)`)
+ isHeadingMarkdown = regexp.MustCompile(`^\#+( |$)`)
+)
// lesserHeading returns true if text is a heading of a lesser or equal level
// than that denoted by prefix.
-func lesserHeading(text, prefix string) bool {
+func lesserHeading(isHeading *regexp.Regexp, text, prefix string) bool {
return isHeading.MatchString(text) && !strings.HasPrefix(text, prefix+prefix[:1])
}
@@ -361,6 +364,10 @@
// number (a nil number indicates the top level).
func parseSections(ctx *Context, name, prefix string, lines *Lines, number []int) ([]Section, error) {
isMarkdown := prefix[0] == '#'
+ isHeading := isHeadingLegacy
+ if isMarkdown {
+ isHeading = isHeadingMarkdown
+ }
var sections []Section
for i := 1; ; i++ {
// Next non-empty line is title.
@@ -392,7 +399,7 @@
ID: id,
}
text, ok = lines.nextNonEmpty()
- for ok && !lesserHeading(text, prefix) {
+ for ok && !lesserHeading(isHeading, text, prefix) {
var e Elem
r, _ := utf8.DecodeRuneInString(text)
switch {
@@ -435,8 +442,8 @@
lines.back()
e = List{Bullet: b}
case isSpeakerNote(text):
- section.Notes = append(section.Notes, text[2:])
- case strings.HasPrefix(text, prefix+prefix[:1]+" "):
+ section.Notes = append(section.Notes, trimSpeakerNote(text))
+ case strings.HasPrefix(text, prefix+prefix[:1]+" ") || text == prefix+prefix[:1]:
lines.back()
subsecs, err := parseSections(ctx, name, prefix+prefix[:1], lines, section.Number)
if err != nil {
@@ -445,6 +452,8 @@
for _, ss := range subsecs {
section.Elem = append(section.Elem, ss)
}
+ case strings.HasPrefix(text, prefix+prefix[:1]):
+ return nil, fmt.Errorf("%s:%d: badly nested section inside %s: %s", name, lines.line, prefix, text)
case strings.HasPrefix(text, "."):
args := strings.Fields(text)
if args[0] == ".background" {
@@ -466,7 +475,7 @@
for ok && strings.TrimSpace(text) != "" {
// Command breaks text block.
// Section heading breaks text block in markdown.
- if text[0] == '.' || isMarkdown && text[0] == '#' {
+ if text[0] == '.' || isMarkdown && text[0] == '#' || isSpeakerNote(text) {
lines.back()
break
}
@@ -514,6 +523,10 @@
}
sections = append(sections, section)
}
+
+ if len(sections) == 0 {
+ return nil, fmt.Errorf("%s:%d: unexpected line: %s", name, lines.line+1, lines.text[lines.line])
+ }
return sections, nil
}
@@ -649,7 +662,14 @@
}
func isSpeakerNote(s string) bool {
- return strings.HasPrefix(s, ": ")
+ return strings.HasPrefix(s, ": ") || s == ":"
+}
+
+func trimSpeakerNote(s string) string {
+ if s == ":" {
+ return ""
+ }
+ return strings.TrimPrefix(s, ": ")
}
func renderMarkdown(input []byte) (template.HTML, error) {
@@ -669,7 +689,9 @@
if entering {
switch n := n.(type) {
case *ast.Link:
- n.SetAttributeString("target", "_blank")
+ n.SetAttributeString("target", []byte("_blank"))
+ // https://developers.google.com/web/tools/lighthouse/audits/noopener
+ n.SetAttributeString("rel", []byte("noopener"))
}
}
return ast.WalkContinue, nil