mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2025-12-30 09:38:26 -05:00
140 lines
3.0 KiB
Go
140 lines
3.0 KiB
Go
// Package markdown allows reading and editing Markdown files
|
|
package markdown
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"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", strings.ToLower(strings.Replace(h.Header, " ", "-", -1)))
|
|
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, " "),
|
|
}
|
|
}
|