Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gohugoio/hugo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'markup/asciidocext/convert.go')
-rw-r--r--markup/asciidocext/convert.go129
1 files changed, 127 insertions, 2 deletions
diff --git a/markup/asciidocext/convert.go b/markup/asciidocext/convert.go
index 7dd3e3533..73ed85daa 100644
--- a/markup/asciidocext/convert.go
+++ b/markup/asciidocext/convert.go
@@ -17,6 +17,7 @@
package asciidocext
import (
+ "bytes"
"os/exec"
"path/filepath"
@@ -24,6 +25,8 @@ import (
"github.com/gohugoio/hugo/markup/asciidocext/asciidocext_config"
"github.com/gohugoio/hugo/markup/converter"
"github.com/gohugoio/hugo/markup/internal"
+ "github.com/gohugoio/hugo/markup/tableofcontents"
+ "golang.org/x/net/html"
)
/* ToDo: RelPermalink patch for svg posts not working*/
@@ -45,16 +48,32 @@ func (p provider) New(cfg converter.ProviderConfig) (converter.Provider, error)
}), nil
}
+type asciidocResult struct {
+ converter.Result
+ toc tableofcontents.Root
+}
+
+func (r asciidocResult) TableOfContents() tableofcontents.Root {
+ return r.toc
+}
+
type asciidocConverter struct {
ctx converter.DocumentContext
cfg converter.ProviderConfig
}
func (a *asciidocConverter) Convert(ctx converter.RenderContext) (converter.Result, error) {
- return converter.Bytes(a.getAsciidocContent(ctx.Src, a.ctx)), nil
+ content, toc, err := extractTOC(a.getAsciidocContent(ctx.Src, a.ctx))
+ if err != nil {
+ return nil, err
+ }
+ return asciidocResult{
+ Result: converter.Bytes(content),
+ toc: toc,
+ }, nil
}
-func (c *asciidocConverter) Supports(feature identity.Identity) bool {
+func (a *asciidocConverter) Supports(_ identity.Identity) bool {
return false
}
@@ -182,6 +201,112 @@ func getAsciidoctorExecPath() string {
return path
}
+// extractTOC extracts the toc from the given src html.
+// It returns the html without the TOC, and the TOC data
+func extractTOC(src []byte) ([]byte, tableofcontents.Root, error) {
+ var buf bytes.Buffer
+ buf.Write(src)
+ node, err := html.Parse(&buf)
+ if err != nil {
+ return nil, tableofcontents.Root{}, err
+ }
+ var (
+ f func(*html.Node) bool
+ toc tableofcontents.Root
+ toVisit []*html.Node
+ )
+ f = func(n *html.Node) bool {
+ if n.Type == html.ElementNode && n.Data == "div" {
+ for _, a := range n.Attr {
+ if a.Key == "id" && a.Val == "toc" {
+ toc, err = parseTOC(n)
+ if err != nil {
+ return false
+ }
+ n.Parent.RemoveChild(n)
+ return true
+ }
+ }
+ }
+ if n.FirstChild != nil {
+ toVisit = append(toVisit, n.FirstChild)
+ }
+ if n.NextSibling != nil {
+ if ok := f(n.NextSibling); ok {
+ return true
+ }
+ }
+ for len(toVisit) > 0 {
+ nv := toVisit[0]
+ toVisit = toVisit[1:]
+ if ok := f(nv); ok {
+ return true
+ }
+ }
+ return false
+ }
+ f(node)
+ if err != nil {
+ return nil, tableofcontents.Root{}, err
+ }
+ buf.Reset()
+ err = html.Render(&buf, node)
+ if err != nil {
+ return nil, tableofcontents.Root{}, err
+ }
+ // ltrim <html><head></head><body> and rtrim </body></html> which are added by html.Render
+ res := buf.Bytes()[25:]
+ res = res[:len(res)-14]
+ return res, toc, nil
+}
+
+// parseTOC returns a TOC root from the given toc Node
+func parseTOC(doc *html.Node) (tableofcontents.Root, error) {
+ var (
+ toc tableofcontents.Root
+ f func(*html.Node, int, int)
+ )
+ f = func(n *html.Node, parent, level int) {
+ if n.Type == html.ElementNode {
+ switch n.Data {
+ case "ul":
+ if level == 0 {
+ parent += 1
+ }
+ level += 1
+ f(n.FirstChild, parent, level)
+ case "li":
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ if c.Type != html.ElementNode || c.Data != "a" {
+ continue
+ }
+ var href string
+ for _, a := range c.Attr {
+ if a.Key == "href" {
+ href = a.Val[1:]
+ break
+ }
+ }
+ for d := c.FirstChild; d != nil; d = d.NextSibling {
+ if d.Type == html.TextNode {
+ toc.AddAt(tableofcontents.Header{
+ Text: d.Data,
+ ID: href,
+ }, parent, level)
+ }
+ }
+ }
+ f(n.FirstChild, parent, level)
+ }
+ }
+ if n.NextSibling != nil {
+ f(n.NextSibling, parent, level)
+ }
+ }
+ f(doc.FirstChild, 0, 0)
+ return toc, nil
+}
+
// Supports returns whether Asciidoctor is installed on this computer.
func Supports() bool {
return getAsciidoctorExecPath() != ""