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 }