Commit 7d2dddc

Nick Faro committed on
Render Markdown file pages
commit 7d2dddc2a9f55a520b2064cd90f99a411885a83c parent b1460e6
4 changed files +40−7
Modifiedinternal/site/site.go +33−7
@@ -110,11 +110,13 @@ type pageData struct {
110 ContributorCount int 110 ContributorCount int
111 ReadmeName string 111 ReadmeName string
112 Readme template.HTML 112 Readme template.HTML
113 Markdown template.HTML
113 Code template.HTML 114 Code template.HTML
114 RawURL string 115 RawURL string
115 Language string 116 Language string
116 FileSize int64 117 FileSize int64
117 IsImage bool 118 IsImage bool
119 IsMarkdown bool
118 } 120 }
119 121
120 func New(git gitutil.Git, baseURL, repoName, owner string) (*Generator, error) { 122 func New(git gitutil.Git, baseURL, repoName, owner string) (*Generator, error) {
@@ -379,6 +381,13 @@ func (g *Generator) blobPages(ctx context.Context, branch, oid string, entry Tre
379 data.Language = "Binary" 381 data.Language = "Binary"
380 } else if len(content) > 2*1024*1024 { 382 } else if len(content) > 2*1024*1024 {
381 data.Language = "Large text file" 383 data.Language = "Large text file"
384 } else if isMarkdownFile(entry.Name) {
385 data.Markdown, err = renderMarkdown(content)
386 if err != nil {
387 return nil, err
388 }
389 data.IsMarkdown = true
390 data.Language = "Markdown"
382 } else { 391 } else {
383 code, language, err := highlight(entry.Name, content) 392 code, language, err := highlight(entry.Name, content)
384 if err != nil { 393 if err != nil {
@@ -406,19 +415,36 @@ func (g *Generator) readme(ctx context.Context, oid string, entries []TreeEntry)
406 if err != nil { 415 if err != nil {
407 return "", "", err 416 return "", "", err
408 } 417 }
409 var rendered bytes.Buffer 418 rendered, err := renderMarkdown(content)
410 markdown := goldmark.New( 419 if err != nil {
411 goldmark.WithExtensions(extension.GFM),
412 goldmark.WithParserOptions(parser.WithAutoHeadingID()),
413 )
414 if err := markdown.Convert(content, &rendered); err != nil {
415 return "", "", err 420 return "", "", err
416 } 421 }
417 return entry.Name, template.HTML(rendered.String()), nil 422 return entry.Name, rendered, nil
418 } 423 }
419 return "", "", nil 424 return "", "", nil
420 } 425 }
421 426
427 func isMarkdownFile(filename string) bool {
428 switch strings.ToLower(path.Ext(filename)) {
429 case ".md", ".markdown", ".mdown", ".mkd", ".mkdn":
430 return true
431 default:
432 return false
433 }
434 }
435
436 func renderMarkdown(content []byte) (template.HTML, error) {
437 var rendered bytes.Buffer
438 markdown := goldmark.New(
439 goldmark.WithExtensions(extension.GFM),
440 goldmark.WithParserOptions(parser.WithAutoHeadingID()),
441 )
442 if err := markdown.Convert(content, &rendered); err != nil {
443 return "", err
444 }
445 return template.HTML(rendered.String()), nil
446 }
447
422 func highlight(filename string, content []byte) (template.HTML, string, error) { 448 func highlight(filename string, content []byte) (template.HTML, string, error) {
423 lexer := lexers.Match(filename) 449 lexer := lexers.Match(filename)
424 if lexer == nil { 450 if lexer == nil {
Modifiedinternal/site/site_test.go +5−0
@@ -17,6 +17,7 @@ func TestFullAndIncrementalBuild(t *testing.T) {
17 runGit(t, dir, "config", "user.name", "Site Test") 17 runGit(t, dir, "config", "user.name", "Site Test")
18 runGit(t, dir, "config", "user.email", "[email protected]") 18 runGit(t, dir, "config", "user.email", "[email protected]")
19 write(t, dir, "README.md", "# Hello bucket\n") 19 write(t, dir, "README.md", "# Hello bucket\n")
20 write(t, dir, "docs/guide.md", "# Guide\n\n- first\n- second\n")
20 write(t, dir, "src/main.go", "package main\n\nfunc main() {}\n") 21 write(t, dir, "src/main.go", "package main\n\nfunc main() {}\n")
21 runGit(t, dir, "add", ".") 22 runGit(t, dir, "add", ".")
22 runGit(t, dir, "commit", "-m", "initial") 23 runGit(t, dir, "commit", "-m", "initial")
@@ -44,6 +45,10 @@ func TestFullAndIncrementalBuild(t *testing.T) {
44 assertPage(t, full, "", "octicon-folder") 45 assertPage(t, full, "", "octicon-folder")
45 assertPage(t, full, "blob/main/src/main.go", "chroma") 46 assertPage(t, full, "blob/main/src/main.go", "chroma")
46 assertPage(t, full, "blob/main/src/main.go", "data-file-tree") 47 assertPage(t, full, "blob/main/src/main.go", "data-file-tree")
48 assertPage(t, full, "blob/main/docs/guide.md", `<article class="markdown-file">`)
49 assertPage(t, full, "blob/main/docs/guide.md", `<h1 id="guide">Guide</h1>`)
50 assertPage(t, full, "blob/main/docs/guide.md", "<li>first</li>")
51 assertPageDoesNotContain(t, full, "blob/main/docs/guide.md", `<div class="code-view">`)
47 assertPage(t, full, ".dumbforge/assets/gabriel-mark.svg", "<svg") 52 assertPage(t, full, ".dumbforge/assets/gabriel-mark.svg", "<svg")
48 assertPage(t, full, ".dumbforge/assets/gabriel-mark.png", "PNG") 53 assertPage(t, full, ".dumbforge/assets/gabriel-mark.png", "PNG")
49 assertPage(t, full, "tree/main/src", "main.go") 54 assertPage(t, full, "tree/main/src", "main.go")
Modifiedinternal/site/style.go +1−0
@@ -190,6 +190,7 @@ button, input { font: inherit; }
190 .blob-actions { margin-left: auto; display: flex; gap: 6px; } 190 .blob-actions { margin-left: auto; display: flex; gap: 6px; }
191 .blob-actions a, .blob-actions button { min-height: 28px; padding: 3px 10px; } 191 .blob-actions a, .blob-actions button { min-height: 28px; padding: 3px 10px; }
192 .blob-actions button.is-copied { color: var(--green); background: #dafbe1; } 192 .blob-actions button.is-copied { color: var(--green); background: #dafbe1; }
193 .markdown-file { min-height: 240px; background: var(--bg); }
193 .code-view { overflow: auto; font: 12px/1.5 ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace; } 194 .code-view { overflow: auto; font: 12px/1.5 ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace; }
194 .code-view .chroma { margin: 0; padding: 0; background: white; } 195 .code-view .chroma { margin: 0; padding: 0; background: white; }
195 .code-view .chroma table { width: 100%; border-spacing: 0; } 196 .code-view .chroma table { width: 100%; border-spacing: 0; }
Modifiedinternal/site/template.go +1−0
@@ -135,6 +135,7 @@ const pageTemplate = `<!doctype html>
135 <div class="blob-actions"><a href="{{.RawURL}}">Raw</a><button type="button" data-copy="{{.RawURL}}"><span data-copy-icon>{{icon "copy"}}</span><span data-copied-icon hidden>{{icon "check"}}</span><span data-copy-label>Copy link</span></button></div> 135 <div class="blob-actions"><a href="{{.RawURL}}">Raw</a><button type="button" data-copy="{{.RawURL}}"><span data-copy-icon>{{icon "copy"}}</span><span data-copied-icon hidden>{{icon "check"}}</span><span data-copy-label>Copy link</span></button></div>
136 </div> 136 </div>
137 {{if .IsImage}}<div class="image-preview"><img src="{{.RawURL}}" alt="{{.Path}}"></div> 137 {{if .IsImage}}<div class="image-preview"><img src="{{.RawURL}}" alt="{{.Path}}"></div>
138 {{else if .IsMarkdown}}<article class="markdown-file"><div class="markdown-body">{{.Markdown}}</div></article>
138 {{else if .Code}}<div class="code-view">{{.Code}}</div> 139 {{else if .Code}}<div class="code-view">{{.Code}}</div>
139 {{else}}<div class="binary-notice"><span>{{icon "file"}}</span><strong>Preview unavailable</strong><p>This file is binary or too large to render safely.</p><a href="{{.RawURL}}">Download raw file</a></div>{{end}} 140 {{else}}<div class="binary-notice"><span>{{icon "file"}}</span><strong>Preview unavailable</strong><p>This file is binary or too large to render safely.</p><a href="{{.RawURL}}">Download raw file</a></div>{{end}}
140 </div> 141 </div>