Skip to content

Commit 4fe8ea3

Browse files
committed
Automatically render wiki TOC
Automatically add sidebar in the wiki view containing a TOC for the wiki page. Fix go-gitea#822 Signed-off-by: Andrew Thornton <[email protected]>
1 parent 6171ea7 commit 4fe8ea3

File tree

8 files changed

+117
-50
lines changed

8 files changed

+117
-50
lines changed

modules/markup/markdown/goldmark.go

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,6 @@ import (
2727

2828
var byteMailto = []byte("mailto:")
2929

30-
// Header holds the data about a header.
31-
type Header struct {
32-
Level int
33-
Text string
34-
ID string
35-
}
36-
3730
// ASTTransformer is a default transformer of the goldmark tree.
3831
type ASTTransformer struct{}
3932

@@ -42,12 +35,13 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
4235
metaData := meta.GetItems(pc)
4336
firstChild := node.FirstChild()
4437
createTOC := false
45-
toc := []Header{}
38+
ctx := pc.Get(renderContextKey).(*markup.RenderContext)
4639
rc := &RenderConfig{
4740
Meta: "table",
4841
Icon: "table",
4942
Lang: "",
5043
}
44+
5145
if metaData != nil {
5246
rc.ToRenderConfig(metaData)
5347

@@ -56,7 +50,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
5650
node.InsertBefore(node, firstChild, metaNode)
5751
}
5852
createTOC = rc.TOC
59-
toc = make([]Header, 0, 100)
53+
ctx.TableOfContents = make([]markup.Header, 0, 100)
6054
}
6155

6256
_ = 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
6660

6761
switch v := n.(type) {
6862
case *ast.Heading:
69-
if createTOC {
70-
text := n.Text(reader.Source())
71-
header := Header{
72-
Text: util.BytesToReadOnlyString(text),
73-
Level: v.Level,
74-
}
75-
if id, found := v.AttributeString("id"); found {
76-
header.ID = util.BytesToReadOnlyString(id.([]byte))
77-
}
78-
toc = append(toc, header)
79-
} else {
80-
for _, attr := range v.Attributes() {
81-
if _, ok := attr.Value.([]byte); !ok {
82-
v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value)))
83-
}
63+
for _, attr := range v.Attributes() {
64+
if _, ok := attr.Value.([]byte); !ok {
65+
v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value)))
8466
}
8567
}
68+
text := n.Text(reader.Source())
69+
header := markup.Header{
70+
Text: util.BytesToReadOnlyString(text),
71+
Level: v.Level,
72+
}
73+
if id, found := v.AttributeString("id"); found {
74+
header.ID = util.BytesToReadOnlyString(id.([]byte))
75+
}
76+
ctx.TableOfContents = append(ctx.TableOfContents, header)
8677
case *ast.Image:
8778
// Images need two things:
8879
//
@@ -199,12 +190,12 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
199190
return ast.WalkContinue, nil
200191
})
201192

202-
if createTOC && len(toc) > 0 {
193+
if createTOC && len(ctx.TableOfContents) > 0 {
203194
lang := rc.Lang
204195
if len(lang) == 0 {
205196
lang = setting.Langs[0]
206197
}
207-
tocNode := createTOCNode(toc, lang)
198+
tocNode := createTOCNode(ctx.TableOfContents, lang)
208199
if tocNode != nil {
209200
node.InsertBefore(node, firstChild, tocNode)
210201
}

modules/markup/markdown/markdown.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ var (
3434
)
3535

3636
var (
37-
urlPrefixKey = parser.NewContextKey()
38-
isWikiKey = parser.NewContextKey()
39-
renderMetasKey = parser.NewContextKey()
37+
urlPrefixKey = parser.NewContextKey()
38+
isWikiKey = parser.NewContextKey()
39+
renderMetasKey = parser.NewContextKey()
40+
renderContextKey = parser.NewContextKey()
4041
)
4142

4243
type limitWriter struct {
@@ -67,6 +68,7 @@ func newParserContext(ctx *markup.RenderContext) parser.Context {
6768
pc.Set(urlPrefixKey, ctx.URLPrefix)
6869
pc.Set(isWikiKey, ctx.IsWiki)
6970
pc.Set(renderMetasKey, ctx.Metas)
71+
pc.Set(renderContextKey, ctx)
7072
return pc
7173
}
7274

modules/markup/markdown/toc.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import (
88
"fmt"
99
"net/url"
1010

11+
"code.gitea.io/gitea/modules/markup"
1112
"code.gitea.io/gitea/modules/translation/i18n"
1213

1314
"github.com/yuin/goldmark/ast"
1415
)
1516

16-
func createTOCNode(toc []Header, lang string) ast.Node {
17+
func createTOCNode(toc []markup.Header, lang string) ast.Node {
1718
details := NewDetails()
1819
summary := NewSummary()
1920

modules/markup/renderer.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,26 @@ func Init() {
3333
}
3434
}
3535

36+
// Header holds the data about a header.
37+
type Header struct {
38+
Level int
39+
Text string
40+
ID string
41+
}
42+
3643
// RenderContext represents a render context
3744
type RenderContext struct {
38-
Ctx context.Context
39-
Filename string
40-
Type string
41-
IsWiki bool
42-
URLPrefix string
43-
Metas map[string]string
44-
DefaultLink string
45-
GitRepo *git.Repository
46-
ShaExistCache map[string]bool
47-
cancelFn func()
45+
Ctx context.Context
46+
Filename string
47+
Type string
48+
IsWiki bool
49+
URLPrefix string
50+
Metas map[string]string
51+
DefaultLink string
52+
GitRepo *git.Repository
53+
ShaExistCache map[string]bool
54+
cancelFn func()
55+
TableOfContents []Header
4856
}
4957

5058
// Cancel runs any cleanup functions that have been registered for this Ctx

modules/templates/helper.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,42 @@ func NewFuncMap() []template.FuncMap {
390390
"Join": strings.Join,
391391
"QueryEscape": url.QueryEscape,
392392
"DotEscape": DotEscape,
393+
"Iterate": func(arg interface{}) (items []uint64) {
394+
count := uint64(0)
395+
switch arg.(type) {
396+
case uint64:
397+
count = arg.(uint64)
398+
case *uint64:
399+
count = *(arg.(*uint64))
400+
case int64:
401+
count = uint64(arg.(int64))
402+
case *int64:
403+
count = uint64(*(arg.(*int64)))
404+
case int:
405+
count = uint64(arg.(int))
406+
case *int:
407+
count = uint64(*arg.(*int))
408+
case uint:
409+
count = uint64(arg.(uint))
410+
case *uint:
411+
count = uint64(*arg.(*uint))
412+
case int32:
413+
count = uint64(arg.(int32))
414+
case *int32:
415+
count = uint64(*arg.(*int32))
416+
case uint32:
417+
count = uint64(arg.(uint32))
418+
case *uint32:
419+
count = uint64(*arg.(*uint32))
420+
}
421+
if count <= 0 {
422+
return items
423+
}
424+
for i := uint64(0); i < count; i++ {
425+
items = append(items, i)
426+
}
427+
return items
428+
},
393429
}}
394430
}
395431

routers/web/repo/wiki.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
280280
ctx.Data["footerPresent"] = false
281281
}
282282

283+
ctx.Data["toc"] = rctx.TableOfContents
284+
283285
// get commit count - wiki revisions
284286
commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
285287
ctx.Data["CommitCount"] = commitsCount

templates/repo/wiki/view.tmpl

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,35 @@
6464
<p>{{.FormatWarning}}</p>
6565
</div>
6666
{{end}}
67-
<div class="ui {{if .sidebarPresent}}grid equal width{{end}}" style="margin-top: 1rem;">
68-
<div class="ui {{if .sidebarPresent}}eleven wide column{{end}} segment markup wiki-content-main">
67+
<div class="ui {{if or .sidebarPresent .toc}}grid equal width{{end}}" style="margin-top: 1rem;">
68+
<div class="ui {{if or .sidebarPresent .toc}}eleven wide column{{end}} segment markup wiki-content-main">
6969
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
7070
{{.content | Safe}}
7171
</div>
72-
{{if .sidebarPresent}}
72+
{{if or .sidebarPresent .toc}}
7373
<div class="column" style="padding-top: 0;">
74-
<div class="ui segment wiki-content-sidebar">
75-
{{if and .CanWriteWiki (not .Repository.IsMirror)}}
76-
<a class="ui right floated muted" href="{{.RepoLink}}/wiki/_Sidebar?action=_edit" aria-label="{{.i18n.Tr "repo.wiki.edit_page_button"}}">{{svg "octicon-pencil"}}</a>
77-
{{end}}
78-
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .sidebarEscapeStatus "root" $}}
79-
{{.sidebarContent | Safe}}
80-
</div>
74+
{{if .toc}}
75+
<div class="ui segment wiki-content-toc">
76+
<div class="ui header">{{.i18n.Tr "toc"}}</div>
77+
{{$level := 0}}
78+
{{range .toc}}
79+
{{if lt $level .Level}}{{range Iterate (Subtract .Level $level)}}<ul>{{end}}{{end}}
80+
{{if gt $level .Level}}{{range Iterate (Subtract $level .Level)}}</ul>{{end}}{{end}}
81+
{{$level = .Level}}
82+
<li><a href="#{{.ID}}">{{.Text}}</a></li>
83+
{{end}}
84+
{{range Iterate $level}}</ul>{{end}}
85+
</div>
86+
{{end}}
87+
{{if .sidebarPresent}}
88+
<div class="ui segment wiki-content-sidebar">
89+
{{if and .CanWriteWiki (not .Repository.IsMirror)}}
90+
<a class="ui right floated muted" href="{{.RepoLink}}/wiki/_Sidebar?action=_edit" aria-label="{{.i18n.Tr "repo.wiki.edit_page_button"}}">{{svg "octicon-pencil"}}</a>
91+
{{end}}
92+
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .sidebarEscapeStatus "root" $}}
93+
{{.sidebarContent | Safe}}
94+
</div>
95+
{{end}}
8196
</div>
8297
{{end}}
8398
</div>

web_src/less/_repository.less

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3080,6 +3080,18 @@ td.blob-excerpt {
30803080
}
30813081
}
30823082

3083+
.wiki-content-toc {
3084+
> ul > li {
3085+
margin-bottom: 4px;
3086+
}
3087+
3088+
ul {
3089+
margin: 0;
3090+
list-style: none;
3091+
padding-left: 1em;
3092+
}
3093+
}
3094+
30833095
/* fomantic's last-child selector does not work with hidden last child */
30843096
.ui.buttons .unescape-button {
30853097
border-top-right-radius: .28571429rem;

0 commit comments

Comments
 (0)