1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
package render
import (
"html/template"
"path"
"strings"
)
// Module is a pluggable file-view renderer. The blob view picks a module to
// turn a file's bytes into HTML; new file-type views are added by registering
// another Module (see NewRegistry) without touching the handler or template.
type Module struct {
ID string // stable ?render= value
Label string // shown in the control bar
Eligible func(filename string, content []byte) bool // offered for this file by default?
Render func(filename string, content []byte) (template.HTML, error)
}
// Registry is an ordered set of modules. Index 0 is the default: the renderer
// used when none is selected, for every file type and the unknown case.
type Registry struct{ modules []Module }
// NewRegistry builds the default registry. r supplies markdown rendering, so the
// markdown module reuses the same goldmark + bluemonday pipeline as READMEs.
func NewRegistry(r *Renderer) *Registry {
return &Registry{modules: []Module{
{
ID: "source",
Label: "Source",
Eligible: func(string, []byte) bool { return true },
Render: func(name string, c []byte) (template.HTML, error) {
return Highlight(name, c)
},
},
{
ID: "markdown",
Label: "Markdown",
Eligible: func(name string, _ []byte) bool { return isMarkdownName(name) },
Render: func(_ string, c []byte) (template.HTML, error) {
h, err := r.ToHTML(c)
return template.HTML(h), err
},
},
}}
}
// Default is the renderer used when none is selected.
func (reg *Registry) Default() Module { return reg.modules[0] }
// All returns every registered module (used when force is on).
func (reg *Registry) All() []Module { return reg.modules }
// Eligible returns the modules offered for this file without forcing. The
// default is always eligible, so the result is never empty.
func (reg *Registry) Eligible(name string, content []byte) []Module {
out := make([]Module, 0, len(reg.modules))
for _, m := range reg.modules {
if m.Eligible(name, content) {
out = append(out, m)
}
}
return out
}
// Get finds a module by id.
func (reg *Registry) Get(id string) (Module, bool) {
for _, m := range reg.modules {
if m.ID == id {
return m, true
}
}
return Module{}, false
}
// Select resolves the module to run. It falls back to the default when id is
// empty, unknown, or (not forced and not eligible for this file).
func (reg *Registry) Select(name string, content []byte, id string, force bool) Module {
m, ok := reg.Get(id)
if !ok {
return reg.Default()
}
if force || m.Eligible(name, content) {
return m
}
return reg.Default()
}
// isMarkdownName reports whether a filename is markdown by extension. Mirrors
// the plain-namespace check in the server package.
func isMarkdownName(p string) bool {
switch strings.ToLower(path.Ext(p)) {
case ".md", ".markdown":
return true
}
return false
}
|