repos / pico

pico services - prose.sh, pastes.sh, imgs.sh, feeds.sh, pgs.sh
git clone https://github.com/picosh/pico.git

commit
c4b0a8a
parent
02808fb
author
ChloƩ Vulquin
date
2024-03-21 22:07:41 +0000 UTC
md/toc: make it nice, flexible, and fast
1 files changed,  +64, -18
M shared/mdparser.go
+64, -18
  1@@ -80,6 +80,28 @@ func toBool(obj interface{}) (bool, error) {
  2 	}
  3 }
  4 
  5+// N = maxdepth - 1
  6+// -1: disable, 0: enable (no max), 1: enable (only top-level ##)
  7+func toToc(obj interface{}) (int, error) {
  8+	if obj == nil {
  9+		return -1, nil
 10+	}
 11+	switch val := obj.(type) {
 12+	case bool:
 13+		if val {
 14+			return 0, nil
 15+		}
 16+		return -1, nil
 17+	case int:
 18+		if val < -1 {
 19+			val = -1
 20+		}
 21+		return val, nil
 22+	default:
 23+		return -1, fmt.Errorf("incorrect type for value: %T, should be bool or int", val)
 24+	}
 25+}
 26+
 27 func toLinks(orderedMetaData yaml.MapSlice) ([]Link, error) {
 28 	var navData interface{}
 29 	for i := 0; i < len(orderedMetaData); i++ {
 30@@ -216,24 +238,6 @@ func ParseText(text string) (*ParsedText, error) {
 31 	doc := md.Parser().Parse(gtext.NewReader(btext), parser.WithContext(context))
 32 	metaData := meta.Get(context)
 33 
 34-	showToc, err := toBool(metaData["toc"])
 35-	if err != nil {
 36-		return &parsed, fmt.Errorf("front-matter field (%s): %w", "toc", err)
 37-	}
 38-	// we need to add an extension after parsing frontmatter
 39-	if showToc {
 40-		extenders = append(extenders, &toc.Extender{
 41-			Title:      "toc",
 42-			TitleDepth: 2,
 43-			Compact:    true,
 44-			TitleID:    "toc",
 45-			ListID:     "toc-list",
 46-		})
 47-		md = CreateGoldmark(extenders...)
 48-		context := parser.NewContext()
 49-		doc = md.Parser().Parse(gtext.NewReader(btext), parser.WithContext(context))
 50-	}
 51-
 52 	// title:
 53 	// 1. if specified in frontmatter, use that
 54 	title, err := toString(metaData["title"])
 55@@ -248,6 +252,15 @@ func ParseText(text string) (*ParsedText, error) {
 56 	// this is implicit since it's already ""
 57 	parsed.MetaData.Title = title
 58 
 59+	// only handle toc after the title is extracted (if it's getting extracted)
 60+	mtoc, err := toToc(metaData["toc"])
 61+	if err != nil {
 62+		return &parsed, fmt.Errorf("front-matter field (%s): %w", "toc", err)
 63+	}
 64+	if mtoc >= 0 {
 65+		AstToc(doc, btext, mtoc)
 66+	}
 67+
 68 	description, err := toString(metaData["description"])
 69 	if err != nil {
 70 		return &parsed, fmt.Errorf("front-matter field (%s): %w", "description", err)
 71@@ -400,3 +413,36 @@ func AstTitle(doc ast.Node, src []byte, clean bool) string {
 72 	}
 73 	return out
 74 }
 75+
 76+func AstToc(doc ast.Node, src []byte, mtoc int) {
 77+	var tree *toc.TOC
 78+	if mtoc >= 0 {
 79+		var err error
 80+		if mtoc > 0 {
 81+			tree, err = toc.Inspect(doc, src, toc.Compact(true), toc.MinDepth(2), toc.MaxDepth(mtoc + 1))
 82+		} else {
 83+			tree, err = toc.Inspect(doc, src, toc.Compact(true), toc.MinDepth(2))
 84+		}
 85+		if err != nil {
 86+			return // TODO: further processing?
 87+		}
 88+		if tree == nil {
 89+			return
 90+		}
 91+	}
 92+	list := toc.RenderList(tree)
 93+	if list == nil {
 94+		return // no headings
 95+	}
 96+
 97+	list.SetAttributeString("id", []byte("toc-list"))
 98+
 99+	// generate # toc
100+	heading := ast.NewHeading(2)
101+	heading.SetAttributeString("id", []byte("toc"))
102+	heading.AppendChild(heading, ast.NewString([]byte("toc")))
103+
104+	// insert
105+	doc.InsertBefore(doc, doc.FirstChild(), list)
106+	doc.InsertBefore(doc, doc.FirstChild(), heading)
107+}