Files
forge/modules/markup/renderer.go
zeekayandHanzo Dev aa87442fdd Record upstream authorship centrally in NOTICE instead of in every file header
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the
Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in
NOTICE, alongside LICENSE. The header lint accepts any copyright line above the
SPDX line, so it no longer demands an upstream one.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-10 17:50:35 -07:00

109 lines
2.9 KiB
Go

// Copyright 2026 Hanzo AI, Inc. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"io"
"path"
"strings"
"github.com/hanzoai/git/modules/setting"
"github.com/hanzoai/git/modules/typesniffer"
)
// Renderer defines an interface for rendering markup file to HTML
type Renderer interface {
Name() string // markup format name, also the renderer type, also the external tool name
FileNamePatterns() []string
SanitizerRules() []setting.MarkupSanitizerRule
Render(ctx *RenderContext, input io.Reader, output io.Writer) error
}
// PostProcessRenderer defines an interface for renderers who need post process
type PostProcessRenderer interface {
NeedPostProcess() bool
}
type ExternalRendererOptions struct {
SanitizerDisabled bool
DisplayInIframe bool
ContentSandbox string
}
// ExternalRenderer defines an interface for external renderers
type ExternalRenderer interface {
GetExternalRendererOptions() ExternalRendererOptions
}
// RendererContentDetector detects if the content can be rendered
// by specified renderer
type RendererContentDetector interface {
CanRender(filename string, sniffedType typesniffer.SniffedType, prefetchBuf []byte) bool
}
var (
fileNameRenderers = make(map[string]Renderer)
renderers = make(map[string]Renderer)
)
// RegisterRenderer registers a new markup file renderer
func RegisterRenderer(renderer Renderer) {
// TODO: need to handle conflicts
renderers[renderer.Name()] = renderer
}
func RefreshFileNamePatterns() {
// TODO: need to handle conflicts
fileNameRenderers = make(map[string]Renderer)
for _, renderer := range renderers {
for _, ext := range renderer.FileNamePatterns() {
fileNameRenderers[strings.ToLower(ext)] = renderer
}
}
}
func DetectRendererTypeByFilename(filename string) Renderer {
basename := path.Base(strings.ToLower(filename))
ext1 := path.Ext(basename)
if renderer := fileNameRenderers[basename]; renderer != nil {
return renderer
}
if renderer := fileNameRenderers["*"+ext1]; renderer != nil {
return renderer
}
if basename, ok := strings.CutSuffix(basename, ext1); ok {
ext2 := path.Ext(basename)
if renderer := fileNameRenderers["*"+ext2+ext1]; renderer != nil {
return renderer
}
}
return nil
}
// DetectRendererTypeByPrefetch detects the markup type of the content
func DetectRendererTypeByPrefetch(filename string, sniffedType typesniffer.SniffedType, prefetchBuf []byte) string {
if filename != "" {
byExt := DetectRendererTypeByFilename(filename)
if byExt != nil {
return byExt.Name()
}
}
for _, renderer := range renderers {
if detector, ok := renderer.(RendererContentDetector); ok && detector.CanRender(filename, sniffedType, prefetchBuf) {
return renderer.Name()
}
}
return ""
}
func PreviewableExtensions() []string {
exts := make([]string, 0, len(fileNameRenderers))
for p := range fileNameRenderers {
if s, ok := strings.CutPrefix(p, "*"); ok {
exts = append(exts, s)
}
}
return exts
}