Initial QSfera import
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
// Package markdown allows reading and editing Markdown files
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Heading represents a markdown Heading
|
||||
type Heading struct {
|
||||
Level int // the level of the heading. 1 means it's the H1
|
||||
Content string // the text of the heading
|
||||
Header string // the heading itself
|
||||
}
|
||||
|
||||
// MD represents a markdown file
|
||||
type MD struct {
|
||||
Headings []Heading
|
||||
}
|
||||
|
||||
// Bytes returns the markdown as []bytes, ignoring errors
|
||||
func (md MD) Bytes() []byte {
|
||||
var b bytes.Buffer
|
||||
_, _ = md.WriteContent(&b)
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
// String returns the markdown as string, ignoring errors
|
||||
func (md MD) String() string {
|
||||
var b strings.Builder
|
||||
_, _ = md.WriteContent(&b)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// TocBytes returns the table of contents as []byte, ignoring errors
|
||||
func (md MD) TocBytes() []byte {
|
||||
var b bytes.Buffer
|
||||
_, _ = md.WriteToc(&b)
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
// TocString returns the table of contents as string, ignoring errors
|
||||
func (md MD) TocString() string {
|
||||
var b strings.Builder
|
||||
_, _ = md.WriteToc(&b)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// WriteContent writes the MDs content to the given writer
|
||||
func (md MD) WriteContent(w io.Writer) (int64, error) {
|
||||
written := int64(0)
|
||||
write := func(s string) error {
|
||||
n, err := w.Write([]byte(s))
|
||||
written += int64(n)
|
||||
return err
|
||||
}
|
||||
for _, h := range md.Headings {
|
||||
if err := write(strings.Repeat("#", h.Level) + " " + h.Header + "\n"); err != nil {
|
||||
return written, err
|
||||
}
|
||||
if len(h.Content) > 0 {
|
||||
if err := write(h.Content); err != nil {
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
|
||||
// WriteToc writes the table of contents to the given writer
|
||||
func (md MD) WriteToc(w io.Writer) (int64, error) {
|
||||
var written int64
|
||||
for _, h := range md.Headings {
|
||||
if h.Level == 1 {
|
||||
// main title not in toc
|
||||
continue
|
||||
}
|
||||
link := fmt.Sprintf("#%s", toAnchor(h.Header))
|
||||
s := fmt.Sprintf("%s* [%s](%s)\n", strings.Repeat(" ", h.Level-2), h.Header, link)
|
||||
n, err := w.Write([]byte(s))
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
written += int64(n)
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
|
||||
// NewMD parses a new Markdown
|
||||
func NewMD(b []byte) MD {
|
||||
var (
|
||||
md MD
|
||||
heading Heading
|
||||
content strings.Builder
|
||||
)
|
||||
sendHeading := func() {
|
||||
if heading.Header != "" {
|
||||
heading.Content = content.String()
|
||||
md.Headings = append(md.Headings, heading)
|
||||
content = strings.Builder{}
|
||||
}
|
||||
}
|
||||
parts := strings.Split("\n"+string(b), "\n#")
|
||||
numparts := len(parts) - 1
|
||||
for i, p := range parts {
|
||||
if i == 0 {
|
||||
// omit part before first heading
|
||||
continue
|
||||
}
|
||||
|
||||
all := strings.SplitN(p, "\n", 2)
|
||||
if len(all) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
head, con := all[0], all[1]
|
||||
// readd lost "#"
|
||||
heading = headingFromString("#" + head)
|
||||
_, _ = content.WriteString(con)
|
||||
// readd lost "\n" - omit for last part
|
||||
if i < numparts {
|
||||
_, _ = content.WriteString("\n")
|
||||
}
|
||||
// add heading
|
||||
sendHeading()
|
||||
}
|
||||
return md
|
||||
}
|
||||
|
||||
func headingFromString(s string) Heading {
|
||||
i := strings.LastIndex(s, "#")
|
||||
levs, con := s[:i+1], s[i+1:]
|
||||
return Heading{
|
||||
Level: len(levs),
|
||||
Header: strings.TrimPrefix(con, " "),
|
||||
}
|
||||
}
|
||||
|
||||
func toAnchor(header string) string {
|
||||
// Remove everything except letters, numbers, and spaces
|
||||
reg := regexp.MustCompile(`[^a-zA-Z0-9 ]+`)
|
||||
anchor := reg.ReplaceAllString(header, "")
|
||||
// Replace spaces with hyphens and convert to lowercase
|
||||
anchor = strings.ReplaceAll(anchor, " ", "-")
|
||||
return strings.ToLower(anchor)
|
||||
}
|
||||
Reference in New Issue
Block a user