// SPDX-License-Identifier: AGPL-3.0-or-later
package web
// Commit is a commit: the message and facts, the diff tools, the diff.
templ Commit(l Layout, p CommitPage) {
@Shell(l, commitRail(p)) {
{ p.Subject }
if p.Body != "" {
{ p.Body }
}
commit
{ p.SHA }
author
@personView(p.Author)
if p.Committer != nil {
committer
@personView(*p.Committer)
}
if p.Merge {
merge of
} else {
parent
}
if p.Root {
none (root commit)
}
for i, par := range p.Parents {
if i > 0 {
{ " + " }
}
{ par.Label }
}
if p.Merge {
{ " · diff against the first parent" }
}
this diff holds invisible characters (direction controls, zero-width characters). They are shown as
<U+XXXX> markers, never applied.
}
if p.Note != "" {
{ p.Note }
}
for _, f := range p.Files {
@DiffFileView(f, p.DiffView)
}
if len(p.Files) == 0 {
no changed files{ " " }
if p.Merge {
against the first parent.
} else {
in this commit.
}
}
}
}
// diffViewMenu is the layout of the diff (unified, the default, or split) and
// the fold of its files, in one menu: a that works without scripts;
// u, d and x still act without opening it. Its button names the layout shown.
templ diffViewMenu(p DiffView) {
if p.Both {
{ viewLabel(p.View) }
} else {
{ viewLabel(p.View) }
}
}
// viewLink is a button of the diff tools. It is a link, so that the split
// layout works without JavaScript; with it, and when both layouts are in the
// page, a click only flips the layout.
templ viewLink(p DiffView, view, label, key, href string) {
{ label }{ key }
}
// collapseButton folds every file of the diff, or opens them all again when
// none is open; its label follows the state.
templ collapseButton() {
}
templ personView(pr Person) {
{ pr.Name }
if pr.WhenAt != "" {
{ " · " }{ " · " + pr.Abs }
}
}
templ commitRail(p CommitPage) {
Files { itoa(len(p.Files)) }
@FileList(p.Files)
}
// FileList is the table of contents of a diff: status letter, path, +n -m
// and an ASCII bar. The row of the file in view is marked.
templ FileList(files []DiffFile) {
if len(files) > fileFilterMin {
}
}
// DiffFileView is one file of the diff: a (per-file collapse). The
// unified and the split tables are both in it; #diffs[data-diff] shows one.
// Every changed line keeps its +/- marker, so a change is never conveyed by
// colour alone.
templ DiffFileView(f DiffFile, p DiffView) {
{ f.Status }{ " " }{ f.Path }{ " " }+{ itoa(f.Add) }{ " " }-{ itoa(f.Del) }
if f.Note != "" {
{ " " }{ f.Note }
}
if f.Lazy {
// A placeholder: mr.js fetches the window of the file when it comes
// near the screen and swaps the files of the window in. Without
// scripts the link opens the page of that window.
for _, h := range f.Hunks {
if h.Gap != nil {
@gapRow(*h.Gap, 4)
}
{ h.Header }
for _, ln := range h.Lines {
@gutter(ln.Old, ln.OldHref)
@gutter(ln.New, ln.NewHref)
{ ln.Mark }
@codeLine(ln)
if len(ln.Notes) > 0 || ln.Form != nil {
@lineNotes(ln.Notes, ln.Form, "")
}
}
}
if f.Tail != nil {
@gapRow(*f.Tail, 4)
}
}
if p.ShowSplit {
for _, h := range f.Hunks {
if h.Gap != nil {
@gapRow(*h.Gap, 6)
}
{ h.Header }
for _, row := range h.Split {
@splitSide(row.L, true)
@splitSide(row.R, false)
if notes, form := row.Notes(), row.Form(); len(notes) > 0 || form != nil {
@lineNotes(notes, form, "-s")
}
}
}
if f.Tail != nil {
@gapRow(*f.Tail, 6)
}
}
} else {
if f.Note != "" {
{ f.Note }
} else {
no textual change.
}
}
if len(f.Loose) > 0 {
for _, c := range f.Loose {
@commentView(c, "")
}
}
}
// MRFiles is a window of files of a merge request, for the placeholders of
// the page: the files only, which replace the placeholders of the same ids.
templ MRFiles(p MRFilesPage) {
for _, f := range p.Files {
@DiffFileView(f, p.DiffView)
}
}
// MRFilesMoved answers a window of a diff that is not the one of the page any
// more: the placeholder shows it instead of a file of another diff.
templ MRFilesMoved() {
this merge request changed since the page was loaded — reload
}
// gapRow is the row of the hidden lines before a hunk, or of the rest of the
// file: a button that loads them in their place (ermite.js, ermite.expand).
// Without scripts nothing loads them: hidden.
templ gapRow(g DiffGap, cols int) {
}
// DiffContext is the hidden lines of a gap, for /context: one body a layout,
// whose rows replace the rows of the gap in the table of that layout.
templ DiffContext(c ContextRows) {
for _, ln := range c.Lines {
{ ln.Old }
{ ln.New }
{ ln.Mark }
@codeLine(ln)
}
if c.More != nil {
@gapRow(*c.More, 4)
}
for i := range c.Lines {
}
// splitSide is one half of a row of the split view; a side without a line is
// an empty cell.
templ splitSide(ln *DiffLine, old bool) {
if ln == nil {
} else {
if old {
@gutter(ln.Old, ln.OldHref)
} else {
@gutter(ln.New, ln.NewHref)
}
{ ln.Mark }
@codeLine(*ln)
}
}
// gutter is a line number of the gutter. On a merge request that can be
// commented on, it links to the form that comments on that line.
templ gutter(n, href string) {
if href != "" {
{ n }
} else {
{ n }
}
}