"github.com/hjson/hjson-go"
"go.cypherpunks.ru/netstring/v2"
+ "go.stargrave.org/sgblog"
"golang.org/x/crypto/blake2b"
"golang.org/x/tools/blog/atom"
"gopkg.in/src-d/go-git.v4"
const (
PageEntries = 50
- WhenFmt = "2006-01-02 15:04:05Z07:00"
AtomFeed = "feed.atom"
)
var (
- Version = "0.1.0"
sha1DigestRe = regexp.MustCompilePOSIX("([0-9a-f]{40,40})")
defaultLinks = []string{}
repo *git.Repository
+ notesTree *object.Tree
commentsTree *object.Tree
renderableSchemes = map[string]struct{}{
Branch string
Title string
- BaseURL string
URLPrefix string
- AtomId string
- AtomAuthor string
+ AtomBaseURL string
+ AtomId string
+ AtomAuthor string
CSS string
Webmaster string
return lines
}
-func getCommentsRaw(what plumbing.Hash) []byte {
- if commentsTree == nil {
+func lineURLize(urlPrefix, line string) string {
+ cols := strings.Split(html.EscapeString(line), " ")
+ for i, col := range cols {
+ if u := urlParse(col); u != nil {
+ cols[i] = makeA(col, col)
+ continue
+ }
+ cols[i] = sha1DigestRe.ReplaceAllString(col, makeA(
+ urlPrefix+"/$1", "$1",
+ ))
+ }
+ return strings.Join(cols, " ")
+}
+
+func getNote(tree *object.Tree, what plumbing.Hash) []byte {
+ if tree == nil {
return nil
}
- entry, err := commentsTree.FindEntry(what.String())
- if err != nil {
+ var entry *object.TreeEntry
+ var err error
+ paths := make([]string, 3)
+ paths[0] = what.String()
+ paths[1] = paths[0][:2] + "/" + paths[0][2:]
+ paths[2] = paths[1][:4+1] + "/" + paths[1][4+1:]
+ for _, p := range paths {
+ entry, err = tree.FindEntry(p)
+ if err == nil {
+ break
+ }
+ }
+ if entry == nil {
return nil
}
blob, err := repo.BlobObject(entry.Hash)
</head>
<body>
`,
- Version, title,
+ sgblog.Version, title,
strings.Join(append(defaultLinks, additional...), "\n "),
)
}
panic(err)
}
etagHash.Write([]byte("SGBLOG"))
+ etagHash.Write([]byte(sgblog.Version))
etagHash.Write([]byte(cfg.GitPath))
etagHash.Write([]byte(cfg.Branch))
etagHash.Write([]byte(cfg.Title))
- etagHash.Write([]byte(cfg.BaseURL))
etagHash.Write([]byte(cfg.URLPrefix))
+ etagHash.Write([]byte(cfg.AtomBaseURL))
etagHash.Write([]byte(cfg.AtomId))
etagHash.Write([]byte(cfg.AtomAuthor))
}
defaultLinks = append(defaultLinks, `<link rel="top" href="`+cfg.URLPrefix+`/" title="top">`)
- atomURL := cfg.BaseURL + cfg.URLPrefix + "/" + AtomFeed
+ atomURL := cfg.AtomBaseURL + cfg.URLPrefix + "/" + AtomFeed
defaultLinks = append(defaultLinks, `<link rel="alternate" title="Atom feed" href="`+atomURL+`" type="application/atom+xml">`)
repo, err = git.PlainOpen(cfg.GitPath)
makeErr(err)
}
- if cfg.CommentsNotesRef != "" {
- if notes, err := repo.Notes(); err == nil {
- var comments *plumbing.Reference
- notes.ForEach(func(ref *plumbing.Reference) error {
- if string(ref.Name()) == cfg.CommentsNotesRef {
- comments = ref
- }
- return nil
- })
- if comments != nil {
- if commentsCommit, err := repo.CommitObject(comments.Hash()); err == nil {
- commentsTree, _ = commentsCommit.Tree()
- }
+ if notes, err := repo.Notes(); err == nil {
+ var notesRef *plumbing.Reference
+ var commentsRef *plumbing.Reference
+ notes.ForEach(func(ref *plumbing.Reference) error {
+ switch string(ref.Name()) {
+ case "refs/notes/commits":
+ notesRef = ref
+ case cfg.CommentsNotesRef:
+ commentsRef = ref
+ }
+ return nil
+ })
+ if notesRef != nil {
+ if commentsCommit, err := repo.CommitObject(notesRef.Hash()); err == nil {
+ notesTree, _ = commentsCommit.Tree()
+ }
+ }
+ if commentsRef != nil {
+ if commentsCommit, err := repo.CommitObject(commentsRef.Hash()); err == nil {
+ commentsTree, _ = commentsCommit.Tree()
}
}
}
break
}
etagHash.Write(commit.Hash[:])
- commentsRaw := getCommentsRaw(commit.Hash)
+ commentsRaw := getNote(commentsTree, commit.Hash)
etagHash.Write(commentsRaw)
entries = append(entries, TableEntry{commit, commentsRaw})
}
var table bytes.Buffer
table.WriteString(
- "<table border=1>\n<tr>" +
+ "<table border=1>\n" +
+ "<caption>Comments</caption>\n<tr>" +
"<th>N</th>" +
"<th>When</th>" +
"<th>Title</th>" +
- "<th size=\"5%\">L</th>" +
- "<th size=\"5%\">C</th>" +
+ `<th size="5%"><a title="Lines">L</a></th>` +
+ `<th size="5%"><a title="Comments">C</a></th>` +
"<th>Linked to</th></tr>\n")
for _, entry := range entries {
commentN++
"<td>%s</td>"+
"<td>%d</td><td>%s</td>"+
"<td>%s</td></tr>\n",
- commentN, entry.commit.Author.When.Format(WhenFmt),
+ commentN, entry.commit.Author.When.Format(sgblog.WhenFmt),
makeA(cfg.URLPrefix+"/"+entry.commit.Hash.String(), lines[0]),
len(lines)-2,
commentsValue,
href = cfg.URLPrefix + "/"
}
links = append(links, `<link rel="prev" href="`+href+`" title="newer">`)
- refs.WriteString(makeA(href, " [prev]"))
+ refs.WriteString("\n" + makeA(href, "[prev]"))
}
if !logEnded {
href = cfg.URLPrefix + "/?offset=" + strconv.Itoa(offset+PageEntries)
links = append(links, `<link rel="next" href="`+href+`" title="older">`)
- refs.WriteString(makeA(href, " [next]"))
+ refs.WriteString("\n" + makeA(href, "[next]"))
}
os.Stdout.Write([]byte(startHeader(etagHash, gzipWriter != nil)))
ID: "urn:uuid:" + feedId,
Link: []atom.Link{{
Rel: "alternate",
- Href: cfg.BaseURL + cfg.URLPrefix + "/" + commit.Hash.String(),
+ Href: cfg.AtomBaseURL + cfg.URLPrefix + "/" + commit.Hash.String(),
}},
Published: atom.Time(commit.Author.When),
Updated: atom.Time(commit.Author.When),
}
etagHash.Write([]byte("ENTRY"))
etagHash.Write(commit.Hash[:])
- commentsRaw := getCommentsRaw(commit.Hash)
+ notesRaw := getNote(notesTree, commit.Hash)
+ etagHash.Write(notesRaw)
+ commentsRaw := getNote(commentsTree, commit.Hash)
etagHash.Write(commentsRaw)
checkETag(etagHash)
lines := msgSplit(commit.Message)
title := lines[0]
- when := commit.Author.When.Format(WhenFmt)
+ when := commit.Author.When.Format(sgblog.WhenFmt)
os.Stdout.Write([]byte(startHeader(etagHash, gzipWriter != nil)))
links := []string{}
var parent string
}
out.Write([]byte(startHTML(fmt.Sprintf("%s (%s)", title, when), links)))
if cfg.AboutURL != "" {
- out.Write([]byte(fmt.Sprintf("[%s] ", makeA(cfg.AboutURL, "about"))))
+ out.Write([]byte(fmt.Sprintf("[%s]\n", makeA(cfg.AboutURL, "about"))))
}
+ out.Write([]byte(fmt.Sprintf("[%s]\n", makeA(cfg.URLPrefix+"/", "index"))))
if parent != "" {
out.Write([]byte(fmt.Sprintf(
- "[%s] ",
+ "[%s]\n",
makeA(cfg.URLPrefix+"/"+parent, "older"),
)))
}
out.Write([]byte(fmt.Sprintf(
- "[<tt>%s</tt>] [<tt>%s</tt>]<hr/>\n<h2>%s</h2>\n<pre>\n",
+ "[<tt><a title=\"When\">%s</a></tt>]\n"+
+ "[<tt><a title=\"Hash\">%s</a></tt>]\n"+
+ "<hr/>\n<h2>%s</h2>\n<pre>\n",
when, commit.Hash.String(), title,
)))
for _, line := range lines[2:] {
- line = html.EscapeString(line)
- cols := strings.Split(line, " ")
- for i, col := range cols {
- if u := urlParse(col); u != nil {
- cols[i] = makeA(col, col)
- continue
- }
- cols[i] = sha1DigestRe.ReplaceAllString(col, makeA(
- cfg.URLPrefix+"/$1", "$1",
- ))
- }
- line = strings.Join(cols, " ")
- out.Write([]byte(line + "\n"))
+ out.Write([]byte(lineURLize(cfg.URLPrefix, line) + "\n"))
}
out.Write([]byte("</pre>\n<hr/>\n"))
+ if len(notesRaw) > 0 {
+ out.Write([]byte("Note:<pre>\n" + string(notesRaw) + "\n</pre>\n<hr/>\n"))
+ }
if cfg.CommentsEmail != "" {
out.Write([]byte("[" + makeA(
"mailto:"+cfg.CommentsEmail+"?subject="+commit.Hash.String(),
for i, comment := range parseComments(commentsRaw) {
out.Write([]byte(fmt.Sprintf(
"<dt><a name=\"comment%d\"><a href=\"#comment%d\">comment %d</a>:"+
- "</dt>\n<dd><pre>\n%s\n</pre></dd>\n",
- i, i, i, html.EscapeString(comment),
+ "</dt>\n<dd><pre>\n",
+ i, i, i,
)))
+ lines = strings.Split(comment, "\n")
+ for _, line := range lines[:3] {
+ out.Write([]byte(line + "\n"))
+ }
+ for _, line := range lines[3:] {
+ out.Write([]byte(lineURLize(cfg.URLPrefix, line) + "\n"))
+ }
+ out.Write([]byte("</pre></dd>\n"))
}
out.Write([]byte("</dl>\n"))
} else {