package generator
import (
"bytes"
"fmt"
"html/template"
"os"
"path/filepath"
"strings"
"github.com/alecthomas/chroma/v2/formatters/html"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
"mokhan.ca/xlgmokha/gitmal/internal/git"
"mokhan.ca/xlgmokha/gitmal/internal/links"
"mokhan.ca/xlgmokha/gitmal/internal/pool"
"mokhan.ca/xlgmokha/gitmal/internal/templates"
)
func GenerateBlobs(files []git.Blob, params Params) error {
formatterOptions := []html.Option{
html.WithLineNumbers(true),
html.WithLinkableLineNumbers(true, "L"),
html.WithClasses(true),
html.WithCSSComments(false),
}
md := createMarkdown()
formatter := html.New(formatterOptions...)
style := styles.Get("github")
dirsSet := links.BuildDirSet(files)
filesSet := links.BuildFileSet(files)
return pool.Run(files, func(blob git.Blob) error {
var content string
data, isBin, err := git.BlobContent(params.Ref, blob.Path, params.RepoDir)
if err != nil {
return err
}
isImg := isImage(blob.Path)
if !isBin {
content = string(data)
}
outPath := filepath.Join(params.OutputDir, "blob", params.Ref.DirName(), blob.Path) + ".html"
if err := os.MkdirAll(filepath.Dir(outPath), 0o755); err != nil {
return err
}
f, err := os.Create(outPath)
if err != nil {
return err
}
defer f.Close()
depth := 0
if strings.Contains(blob.Path, "/") {
depth = len(strings.Split(blob.Path, "/")) - 1
}
rootHref := strings.Repeat("../", depth+2)
if isMarkdown(blob.Path) {
var b bytes.Buffer
if err := md.Convert([]byte(content), &b); err != nil {
return err
}
contentHTML := links.Resolve(
b.String(),
blob.Path,
rootHref,
params.Ref.DirName(),
dirsSet,
filesSet,
)
return templates.MarkdownTemplate.ExecuteTemplate(f, "layout.gohtml", templates.MarkdownParams{
LayoutParams: templates.LayoutParams{
Title: fmt.Sprintf("%s/%s at %s", params.Name, blob.Path, params.Ref),
Name: params.Name,
RootHref: rootHref,
CurrentRefDir: params.Ref.DirName(),
Selected: "code",
NeedsMarkdownCSS: true,
NeedsSyntaxCSS: true,
},
HeaderParams: templates.HeaderParams{
Ref: params.Ref,
Breadcrumbs: breadcrumbs(params.Name, blob.Path, true),
},
Blob: blob,
Content: template.HTML(contentHTML),
})
}
var contentHTML template.HTML
if !isBin {
var b bytes.Buffer
lx := lexers.Match(blob.Path)
if lx == nil {
lx = lexers.Fallback
}
iterator, _ := lx.Tokenise(nil, content)
if err := formatter.Format(&b, style, iterator); err != nil {
return err
}
contentHTML = template.HTML(b.String())
} else if isImg {
rawPath := filepath.Join(params.OutputDir, "raw", params.Ref.DirName(), blob.Path)
if err := os.MkdirAll(filepath.Dir(rawPath), 0o755); err != nil {
return err
}
rf, err := os.Create(rawPath)
if err != nil {
return err
}
if _, err := rf.Write(data); err != nil {
rf.Close()
return err
}
if err := rf.Close(); err != nil {
return err
}
relativeRawPath := filepath.Join(rootHref, "raw", params.Ref.DirName(), blob.Path)
contentHTML = template.HTML(fmt.Sprintf(`
`, relativeRawPath, blob.FileName))
}
return templates.BlobTemplate.ExecuteTemplate(f, "layout.gohtml", templates.BlobParams{
LayoutParams: templates.LayoutParams{
Title: fmt.Sprintf("%s/%s at %s", params.Name, blob.Path, params.Ref),
Name: params.Name,
RootHref: rootHref,
CurrentRefDir: params.Ref.DirName(),
Selected: "code",
NeedsSyntaxCSS: !isBin,
},
HeaderParams: templates.HeaderParams{
Ref: params.Ref,
Breadcrumbs: breadcrumbs(params.Name, blob.Path, true),
},
Blob: blob,
IsBinary: isBin,
IsImage: isImg,
Content: contentHTML,
})
})
}