diff options
author | zeripath <art27@cantab.net> | 2022-06-08 09:59:16 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-08 16:59:16 +0800 |
commit | ac88f21ecc5612befe51f7ab6ffcb76c681daba5 (patch) | |
tree | cbe281221d0703da0f1ecce428888e036ef311d7 /modules | |
parent | c1c07e533c9ca3be13b4af057b6b989402c12f6e (diff) | |
download | gitea-ac88f21ecc5612befe51f7ab6ffcb76c681daba5.tar.gz gitea-ac88f21ecc5612befe51f7ab6ffcb76c681daba5.zip |
Automatically render wiki TOC (#19873)
Automatically add sidebar in the wiki view containing a TOC for the wiki page.
Make the TOC collapsable
Signed-off-by: Andrew Thornton <art27@cantab.net>
Diffstat (limited to 'modules')
-rw-r--r-- | modules/markup/markdown/goldmark.go | 43 | ||||
-rw-r--r-- | modules/markup/markdown/markdown.go | 8 | ||||
-rw-r--r-- | modules/markup/markdown/toc.go | 3 | ||||
-rw-r--r-- | modules/markup/renderer.go | 28 | ||||
-rw-r--r-- | modules/templates/helper.go | 61 |
5 files changed, 103 insertions, 40 deletions
diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index 9b6cd3aaef..1750128dec 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -27,13 +27,6 @@ import ( var byteMailto = []byte("mailto:") -// Header holds the data about a header. -type Header struct { - Level int - Text string - ID string -} - // ASTTransformer is a default transformer of the goldmark tree. type ASTTransformer struct{} @@ -42,12 +35,13 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa metaData := meta.GetItems(pc) firstChild := node.FirstChild() createTOC := false - toc := []Header{} + ctx := pc.Get(renderContextKey).(*markup.RenderContext) rc := &RenderConfig{ Meta: "table", Icon: "table", Lang: "", } + if metaData != nil { rc.ToRenderConfig(metaData) @@ -56,7 +50,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa node.InsertBefore(node, firstChild, metaNode) } createTOC = rc.TOC - toc = make([]Header, 0, 100) + ctx.TableOfContents = make([]markup.Header, 0, 100) } _ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) { @@ -66,23 +60,20 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa switch v := n.(type) { case *ast.Heading: - if createTOC { - text := n.Text(reader.Source()) - header := Header{ - Text: util.BytesToReadOnlyString(text), - Level: v.Level, - } - if id, found := v.AttributeString("id"); found { - header.ID = util.BytesToReadOnlyString(id.([]byte)) - } - toc = append(toc, header) - } else { - for _, attr := range v.Attributes() { - if _, ok := attr.Value.([]byte); !ok { - v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value))) - } + for _, attr := range v.Attributes() { + if _, ok := attr.Value.([]byte); !ok { + v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value))) } } + text := n.Text(reader.Source()) + header := markup.Header{ + Text: util.BytesToReadOnlyString(text), + Level: v.Level, + } + if id, found := v.AttributeString("id"); found { + header.ID = util.BytesToReadOnlyString(id.([]byte)) + } + ctx.TableOfContents = append(ctx.TableOfContents, header) case *ast.Image: // Images need two things: // @@ -199,12 +190,12 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa return ast.WalkContinue, nil }) - if createTOC && len(toc) > 0 { + if createTOC && len(ctx.TableOfContents) > 0 { lang := rc.Lang if len(lang) == 0 { lang = setting.Langs[0] } - tocNode := createTOCNode(toc, lang) + tocNode := createTOCNode(ctx.TableOfContents, lang) if tocNode != nil { node.InsertBefore(node, firstChild, tocNode) } diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index 320c2f7f82..7ebdfea6c4 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -34,9 +34,10 @@ var ( ) var ( - urlPrefixKey = parser.NewContextKey() - isWikiKey = parser.NewContextKey() - renderMetasKey = parser.NewContextKey() + urlPrefixKey = parser.NewContextKey() + isWikiKey = parser.NewContextKey() + renderMetasKey = parser.NewContextKey() + renderContextKey = parser.NewContextKey() ) type limitWriter struct { @@ -67,6 +68,7 @@ func newParserContext(ctx *markup.RenderContext) parser.Context { pc.Set(urlPrefixKey, ctx.URLPrefix) pc.Set(isWikiKey, ctx.IsWiki) pc.Set(renderMetasKey, ctx.Metas) + pc.Set(renderContextKey, ctx) return pc } diff --git a/modules/markup/markdown/toc.go b/modules/markup/markdown/toc.go index 9d11b771f7..fec45103e5 100644 --- a/modules/markup/markdown/toc.go +++ b/modules/markup/markdown/toc.go @@ -8,12 +8,13 @@ import ( "fmt" "net/url" + "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/translation/i18n" "github.com/yuin/goldmark/ast" ) -func createTOCNode(toc []Header, lang string) ast.Node { +func createTOCNode(toc []markup.Header, lang string) ast.Node { details := NewDetails() summary := NewSummary() diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index cf8b9bace7..53ecbfce2b 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -33,18 +33,26 @@ func Init() { } } +// Header holds the data about a header. +type Header struct { + Level int + Text string + ID string +} + // RenderContext represents a render context type RenderContext struct { - Ctx context.Context - Filename string - Type string - IsWiki bool - URLPrefix string - Metas map[string]string - DefaultLink string - GitRepo *git.Repository - ShaExistCache map[string]bool - cancelFn func() + Ctx context.Context + Filename string + Type string + IsWiki bool + URLPrefix string + Metas map[string]string + DefaultLink string + GitRepo *git.Repository + ShaExistCache map[string]bool + cancelFn func() + TableOfContents []Header } // Cancel runs any cleanup functions that have been registered for this Ctx diff --git a/modules/templates/helper.go b/modules/templates/helper.go index cc0fed7442..ef7b70c09f 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -18,6 +18,7 @@ import ( "reflect" "regexp" "runtime" + "strconv" "strings" texttmpl "text/template" "time" @@ -390,6 +391,66 @@ func NewFuncMap() []template.FuncMap { "Join": strings.Join, "QueryEscape": url.QueryEscape, "DotEscape": DotEscape, + "Iterate": func(arg interface{}) (items []uint64) { + count := uint64(0) + switch val := arg.(type) { + case uint64: + count = val + case *uint64: + count = *val + case int64: + if val < 0 { + val = 0 + } + count = uint64(val) + case *int64: + if *val < 0 { + *val = 0 + } + count = uint64(*val) + case int: + if val < 0 { + val = 0 + } + count = uint64(val) + case *int: + if *val < 0 { + *val = 0 + } + count = uint64(*val) + case uint: + count = uint64(val) + case *uint: + count = uint64(*val) + case int32: + if val < 0 { + val = 0 + } + count = uint64(val) + case *int32: + if *val < 0 { + *val = 0 + } + count = uint64(*val) + case uint32: + count = uint64(val) + case *uint32: + count = uint64(*val) + case string: + cnt, _ := strconv.ParseInt(val, 10, 64) + if cnt < 0 { + cnt = 0 + } + count = uint64(cnt) + } + if count <= 0 { + return items + } + for i := uint64(0); i < count; i++ { + items = append(items, i) + } + return items + }, }} } |