Generate anchor links for headings

This will generate `id` attributes for all heading levels.  The labels
are generated using steps 1-4 of the gitlab flavored markdown algorithm
which seems to be pretty standard for generating anchors:
https://docs.gitlab.com/ee/user/markdown.html#header-ids-and-links

A unique ID isn't appended to avoid having to store a list of previous
headers to compare against.
This commit is contained in:
nytpu 2021-11-04 11:04:50 -06:00 committed by Drew DeVault
parent a8c54c1a32
commit 857f8c97eb

25
main.go
View file

@ -13,6 +13,7 @@ import (
"os" "os"
"strings" "strings"
"time" "time"
"unicode"
"git.sr.ht/~adnano/go-gemini" "git.sr.ht/~adnano/go-gemini"
"git.sr.ht/~sircmpwn/getopt" "git.sr.ht/~sircmpwn/getopt"
@ -24,11 +25,11 @@ var gemtextPage = template.Must(template.
"heading": func(line gemini.Line) *GemtextHeading { "heading": func(line gemini.Line) *GemtextHeading {
switch l := line.(type) { switch l := line.(type) {
case gemini.LineHeading1: case gemini.LineHeading1:
return &GemtextHeading{1, string(l)} return &GemtextHeading{1, string(l), createAnchor(string(l))}
case gemini.LineHeading2: case gemini.LineHeading2:
return &GemtextHeading{2, string(l)} return &GemtextHeading{2, string(l), createAnchor(string(l))}
case gemini.LineHeading3: case gemini.LineHeading3:
return &GemtextHeading{3, string(l)} return &GemtextHeading{3, string(l), createAnchor(string(l))}
default: default:
return nil return nil
} }
@ -147,7 +148,7 @@ var gemtextPage = template.Must(template.
{{- with . | heading }} {{- with . | heading }}
{{- $isList = false -}} {{- $isList = false -}}
<h{{.Level}}>{{.Text}}</h{{.Level}}> <h{{.Level}} id="{{.Anchor}}">{{.Text}}</h{{.Level}}>
{{- end -}} {{- end -}}
{{- with . | link }} {{- with . | link }}
@ -390,6 +391,22 @@ type InputContext struct {
type GemtextHeading struct { type GemtextHeading struct {
Level int Level int
Text string Text string
Anchor string
}
func createAnchor(heading string) string {
var anchor strings.Builder
prev := '-'
for _, c := range heading {
if unicode.IsLetter(c) || unicode.IsDigit(c) {
anchor.WriteRune(unicode.ToLower(c))
prev = c
} else if (unicode.IsSpace(c) || c == '-') && prev != '-' {
anchor.WriteRune('-')
prev = '-'
}
}
return strings.ToLower(anchor.String())
} }
func proxyGemini(req gemini.Request, external bool, root *url.URL, func proxyGemini(req gemini.Request, external bool, root *url.URL,