From 9c80f18328d4a168b0ee4fa55f930c322c67b966 Mon Sep 17 00:00:00 2001 From: Nikita Shubin Date: Sun, 8 May 2022 10:13:27 +0300 Subject: [PATCH] hugolib: markdown_title: add title from markdown Getting title from the page itself, as first encountered header in markdown file. Currently it's inefficient as we parsing page twice, the first in some ealier point when hugo renders markdown, so it should be embedded somewhere in that place. But currently i don't care about if a PoC implementation. Signed-off-by: Nikita Shubin --- hugolib/content_map_page.go | 6 ++++ hugolib/hugo_sites.go | 39 ++++++++++++++++++++++ hugolib/markdown_title.go | 66 +++++++++++++++++++++++++++++++++++++ hugolib/page.go | 4 +++ hugolib/page__common.go | 2 ++ 5 files changed, 117 insertions(+) create mode 100644 hugolib/markdown_title.go diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go index 21a4e8f2..7954c606 100644 --- a/hugolib/content_map_page.go +++ b/hugolib/content_map_page.go @@ -141,6 +141,12 @@ func (m *pageMap) newPageFromContentNode(n *contentNode, parentBucket *pagesMapB } ps.codeowners = owners + mi, err := s.h.markdownTitleInfoForPage(ps) + if err != nil { + return nil, errors.Wrap(err, "failed to load MarkdownTitle data") + } + ps.markdownTitleInfo = mi + r, err := content() if err != nil { return nil, err diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index d67652da..b69cd61c 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -81,6 +81,8 @@ type HugoSites struct { gitInfo *gitInfo codeownerInfo *codeownerInfo + markdownTitleInfo *markdownTitleInfo + // As loaded from the /data dirs data map[string]any @@ -181,6 +183,8 @@ type hugoSitesInit struct { // Loads the Git info and CODEOWNERS for all the pages if enabled. gitInfo *lazy.Init + markdownTitleInfo *lazy.Init + // Maps page translations. translations *lazy.Init } @@ -189,6 +193,7 @@ func (h *hugoSitesInit) Reset() { h.data.Reset() h.layouts.Reset() h.gitInfo.Reset() + h.markdownTitleInfo.Reset() h.translations.Reset() } @@ -224,6 +229,19 @@ func (h *HugoSites) codeownersForPage(p page.Page) ([]string, error) { return h.codeownerInfo.forPage(p), nil } + +func (h *HugoSites) markdownTitleInfoForPage(p page.Page) (*MarkdownTitleInfo, error) { + if _, err := h.init.markdownTitleInfo.Do(); err != nil { + return nil, err + } + + if h.markdownTitleInfo == nil { + return nil, nil + } + + return h.markdownTitleInfo.forPage(p), nil +} + func (h *HugoSites) siteInfos() page.Sites { infos := make(page.Sites, len(h.Sites)) for i, site := range h.Sites { @@ -353,6 +371,7 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) { data: lazy.New(), layouts: lazy.New(), gitInfo: lazy.New(), + markdownTitleInfo: lazy.New(), translations: lazy.New(), }, } @@ -396,6 +415,14 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) { return nil, nil }) + h.init.markdownTitleInfo.Add(func() (any, error) { + err := h.loadMarkdownTitleInfo() + if err != nil { + return nil, errors.Wrap(err, "failed to load MarkdownTitle info") + } + return nil, nil + }) + for _, s := range sites { s.h = h } @@ -444,6 +471,18 @@ func (h *HugoSites) loadGitInfo() error { return nil } +func (h *HugoSites) loadMarkdownTitleInfo() error { + if h.Cfg.GetBool("enableMarkdownTitleInfo") { + mi, err := newMarkdownTitleInfo(h.Cfg) + if err != nil { + h.Log.Errorln("Failed to read MarkdownTitle:", err) + } else { + h.markdownTitleInfo = mi + } + } + return nil +} + func (l configLoader) applyDeps(cfg deps.DepsCfg, sites ...*Site) error { if cfg.TemplateProvider == nil { cfg.TemplateProvider = tplimpl.DefaultTemplateProvider diff --git a/hugolib/markdown_title.go b/hugolib/markdown_title.go new file mode 100644 index 00000000..67d4ea77 --- /dev/null +++ b/hugolib/markdown_title.go @@ -0,0 +1,66 @@ +package hugolib + +import ( + "path/filepath" + "strings" + "os" + + "github.com/yuin/goldmark" + "github.com/yuin/goldmark/text" + "github.com/yuin/goldmark/ast" + + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/resources/page" +) + +type MarkdownTitleInfo struct { + Title string `json:"title"` // Title +} + +type MarkdownTitleMap map[string]*MarkdownTitleInfo + +type markdownTitleInfo struct { + contentDir string + Map MarkdownTitleMap +} + +func findTitle(n ast.Node, source []byte) (string, bool) { + ret := "" + if n.Kind() == ast.KindHeading { + return string(n.Text(source)[:]), true + } + for c := n.FirstChild(); c != nil; c = c.NextSibling() { + ret, res := findTitle(c, source) + if res { + return ret, true + } + } + + return ret, false +} + +func (m *markdownTitleInfo) forPage(p page.Page) *MarkdownTitleInfo { + markdown := goldmark.New() + name := strings.TrimPrefix(filepath.ToSlash(p.File().Filename()), m.contentDir) + // name = strings.TrimPrefix(name, "/") + + source, err := os.ReadFile(name) + if err != nil { + return nil + } + + // Parse file and return title + n := markdown.Parser().Parse(text.NewReader([]byte(source))) + title, res := findTitle(n, source) + if !res { + return &MarkdownTitleInfo{Title: ""} + } + + return &MarkdownTitleInfo{Title: title} +} + +func newMarkdownTitleInfo(cfg config.Provider) (*markdownTitleInfo, error) { + contentDir := cfg.GetString("contentDir") + + return &markdownTitleInfo{contentDir: contentDir}, nil +} diff --git a/hugolib/page.go b/hugolib/page.go index 77165c07..5fae15ee 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -160,6 +160,10 @@ func (p *pageState) CodeOwners() []string { return p.codeowners } +func (p *pageState) MarkdownTitleInfo() *MarkdownTitleInfo { + return p.markdownTitleInfo +} + // GetTerms gets the terms defined on this page in the given taxonomy. // The pages returned will be ordered according to the front matter. func (p *pageState) GetTerms(taxonomy string) page.Pages { diff --git a/hugolib/page__common.go b/hugolib/page__common.go index e55bb7e2..b0ab4489 100644 --- a/hugolib/page__common.go +++ b/hugolib/page__common.go @@ -106,6 +106,8 @@ type pageCommon struct { gitInfo *gitmap.GitInfo codeowners []string + markdownTitleInfo *MarkdownTitleInfo + // Positional navigation posNextPrev *nextPrev posNextPrevSection *nextPrev -- 2.30.2