splux-site · log · files · refs · tags · releases · people · languages · GitHub · commit
4a8b50b
List repo contributors and make languages open a file list.
Parents
Stat
README.md | 13 ++- site/assets/git.js | 172 +++++++++++++++++++++++++-- site/assets/style.css | 19 +++ tools/build-git.sh | 318 +++++++++++++++++++++++++++++++++++++++++--------- tools/git-lang.awk | 6 +- tools/git.awk | 144 +++++++++++++++++++++-- 6 files changed, 586 insertions(+), 86 deletions(-)
Diff
diff --git a/README.md b/README.md
index 82d327c..ac4a92e 100644
--- a/README.md
+++ b/README.md
@@ -78,11 +78,14 @@ repository there. `SITE_PREFIX` stays empty so links are rooted at `/`.
## Git
`/git/` is a live HTML mirror of SPS, sps-core, sps-extra, and this
-repository. Languages match GitHub Linguist. Commits show the GitHub
-account that uploaded them, with avatars, and `/git/users/` holds
-profiles. The Pages workflow rebuilds those pages; the browser also
-asks GitHub for anything newer. There is no git server and no pull
-requests. Clone from GitHub:
+repository. Languages match GitHub Linguist. Open a language to see
+how many files use it (`/git/<repo>/lang/<Lang>/`). Commits show the
+GitHub account that uploaded them, with avatars. In a repository,
+People lists that tree's commit authors (`/git/<repo>/people/`).
+`/git/users/` holds profiles across all official trees. The Pages
+workflow rebuilds those pages; the browser also asks GitHub for
+anything newer. There is no git server and no pull requests. Clone
+from GitHub:
```sh
git clone https://github.com/RobertFlexx/SPS.git
diff --git a/site/assets/git.js b/site/assets/git.js
index 48b2dee..d8fff6d 100644
--- a/site/assets/git.js
+++ b/site/assets/git.js
@@ -131,6 +131,12 @@
out.sha = parts[3] || "";
else if (out.kind === "tree" || out.kind === "blob")
out.path = parts.slice(3).join("/");
+ else if (out.kind === "lang") {
+ if (parts.length === 3)
+ out.kind = "langs";
+ else
+ out.path = parts.slice(3).join("/");
+ }
return out;
}
@@ -176,6 +182,18 @@
return gitHost + "/" + encodeURIComponent(repo) + "/";
}
+ function slugLang(name) {
+ return String(name || "").replace(/[ \/]/g, "-");
+ }
+
+ function langHref(repo, name) {
+ return repoHref(repo) + "lang/" + encodeURIComponent(slugLang(name)) + "/";
+ }
+
+ function peopleHref(repo) {
+ return repoHref(repo) + "people/";
+ }
+
function avatarOf(login, url) {
if (url && /^https:\/\/(avatars\.githubusercontent\.com\/|github\.com\/)/.test(url))
return url;
@@ -242,7 +260,7 @@
return html;
}
- function paintLangs(root, langs) {
+ function paintLangs(root, langs, repo) {
if (!root || !langs || !langs.length)
return;
var total = 0;
@@ -256,17 +274,28 @@
var w = Math.round((langs[i].bytes * 1000) / total) / 10;
if (w <= 0 && langs[i].bytes > 0)
w = 0.1;
- html += "<span class=\"lang-seg\" style=\"width:" + w +
- "%;background:" + esc(langs[i].color || colorOf(langs[i].name)) +
- "\" title=\"" + esc(langs[i].name) + " " + w + "%\"></span>";
+ var href = repo ? langHref(repo, langs[i].name) : "";
+ var style = "width:" + w + "%;background:" +
+ esc(langs[i].color || colorOf(langs[i].name));
+ var title = esc(langs[i].name) + " " + w + "%";
+ if (href)
+ html += "<a class=\"lang-seg\" href=\"" + esc(href) +
+ "\" style=\"" + style + "\" title=\"" + title + "\"></a>";
+ else
+ html += "<span class=\"lang-seg\" style=\"" + style +
+ "\" title=\"" + title + "\"></span>";
}
html += "</div><ul class=\"lang-list\">";
for (i = 0; i < langs.length; i++) {
var p = Math.round((langs[i].bytes * 1000) / total) / 10;
- html += "<li><span class=\"lang-dot\" style=\"background:" +
+ var item = "<span class=\"lang-dot\" style=\"background:" +
esc(langs[i].color || colorOf(langs[i].name)) + "\"></span> " +
- esc(langs[i].name) + " <span class=\"muted\">" + p +
- "%</span></li>";
+ esc(langs[i].name) + " <span class=\"muted\">" + p + "%</span>";
+ if (repo)
+ html += "<li><a href=\"" + esc(langHref(repo, langs[i].name)) +
+ "\">" + item + "</a></li>";
+ else
+ html += "<li>" + item + "</li>";
}
html += "</ul>";
root.innerHTML = html;
@@ -313,8 +342,11 @@
if (!input)
return;
var table = document.getElementById("git-repos") ||
+ document.getElementById("git-people-table") ||
+ document.getElementById("git-lang-table") ||
document.querySelector("table.git-tree") ||
- document.querySelector("table.git-log");
+ document.querySelector("table.git-log") ||
+ document.querySelector("table.pkgs");
if (!table)
return;
function apply() {
@@ -354,6 +386,95 @@
return cached("git-user-" + login, API + "/users/" + login);
}
+ function fetchContributors(repo) {
+ return cached("git-contrib-" + repo, API + "/repos/" + OWNER + "/" +
+ repo + "/contributors?per_page=100");
+ }
+
+ function peopleRow(it) {
+ var search = ((it.login || "") + " " + (it.name || "")).toLowerCase();
+ var html = "<tr data-login=\"" + esc(it.login || "") +
+ "\" data-search=\"" + esc(search) + "\">";
+ html += "<td>" + whoHtml(it.login, it.name, it.avatar) + "</td>";
+ html += "<td class=\"muted\">" + esc(String(it.count || "")) + "</td>";
+ if (it.sha && it.repo)
+ html += "<td><a href=\"" + esc(commitHref(it.repo, it.sha)) + "\">" +
+ esc(it.subject || "") + "</a></td>";
+ else
+ html += "<td>" + esc(it.subject || "") + "</td>";
+ if (it.iso)
+ html += "<td><time datetime=\"" + esc(it.iso) + "\">" +
+ esc(dayOf(it.iso)) + "</time></td>";
+ else
+ html += "<td></td>";
+ return html + "</tr>";
+ }
+
+ function mergePeopleRows(table, items) {
+ if (!table || !items || !items.length)
+ return 0;
+ var body = table.tBodies[0] || table;
+ var seen = {};
+ var existing = body.querySelectorAll("tr[data-login]");
+ var i;
+ for (i = 0; i < existing.length; i++) {
+ var k = existing[i].getAttribute("data-login") ||
+ existing[i].textContent;
+ if (k)
+ seen[k] = true;
+ }
+ var added = 0;
+ var html = "";
+ for (i = 0; i < items.length; i++) {
+ var key = items[i].login || items[i].name || "";
+ if (!key || seen[key])
+ continue;
+ seen[key] = true;
+ html += peopleRow(items[i]);
+ added++;
+ }
+ if (html) {
+ body.insertAdjacentHTML("beforeend", html);
+ localizeTimes(body);
+ }
+ return added;
+ }
+
+ function refreshPeople(info) {
+ if (!knownRepo(info.repo))
+ return;
+ return fetchContributors(info.repo).then(function (list) {
+ if (!list || !list.length)
+ return;
+ var items = [];
+ for (var i = 0; i < list.length; i++) {
+ if (!list[i] || list[i].type === "Anonymous")
+ continue;
+ items.push({
+ login: list[i].login || "",
+ name: list[i].login || "",
+ avatar: list[i].avatar_url || "",
+ count: list[i].contributions || "",
+ repo: info.repo
+ });
+ }
+ var table = document.getElementById("git-people-table");
+ if (table && mergePeopleRows(table, items))
+ showLive();
+ var strip = document.getElementById("git-people");
+ if (strip && items.length && !strip.querySelector(".person")) {
+ var html = "";
+ for (var j = 0; j < items.length; j++)
+ html += "<span class=\"person\">" +
+ whoHtml(items[j].login, items[j].name, items[j].avatar) +
+ " <span class=\"muted\">" + esc(String(items[j].count)) +
+ "</span></span>";
+ strip.innerHTML = html;
+ showLive();
+ }
+ });
+ }
+
function fetchContents(repo, path) {
var url = API + "/repos/" + OWNER + "/" + repo + "/contents";
if (path)
@@ -535,7 +656,7 @@
if (langs && langs.length) {
var box = document.getElementById("git-langs");
if (box) {
- paintLangs(box, langs);
+ paintLangs(box, langs, repo);
changed++;
}
}
@@ -632,6 +753,36 @@
paintTree(info, item);
});
}
+ if (info.kind === "people" && knownRepo(info.repo)) {
+ return fetchContributors(info.repo).then(function (list) {
+ var items = [];
+ for (var i = 0; list && i < list.length; i++) {
+ if (!list[i] || list[i].type === "Anonymous")
+ continue;
+ items.push({
+ login: list[i].login || "",
+ name: list[i].login || "",
+ avatar: list[i].avatar_url || "",
+ count: list[i].contributions || "",
+ repo: info.repo
+ });
+ }
+ var html = "<p class=\"git-nav\"><a href=\"" +
+ esc(repoHref(info.repo)) + "\">" + esc(info.repo) +
+ "</a> · people</p>";
+ html += "<h2>People</h2><hr class=\"rule\">";
+ html += "<p>Everyone who authored a commit in " + esc(info.repo) +
+ ". <a href=\"" + esc(gitHost + "/users/") +
+ "\">All people</a></p>";
+ html += "<table class=\"pkgs git-people\" id=\"git-people-table\"><thead><tr><th>Person</th><th>Commits</th><th>Last commit</th><th>When</th></tr></thead><tbody>";
+ for (var j = 0; j < items.length; j++)
+ html += peopleRow(items[j]);
+ html += "</tbody></table>";
+ main.innerHTML = html;
+ document.title = "People - " + info.repo + " - Splux Git";
+ bindFilter();
+ });
+ }
if (info.kind === "user" && validLogin(info.user)) {
return fetchUser(info.user).then(function (u) {
return Promise.all(REPOS.map(function (repo) {
@@ -680,6 +831,9 @@
if (info.kind === "repo" || info.kind === "log")
work = work.then(function () { return refreshRepo(info); })
.catch(function () {});
+ if (info.kind === "people" || info.kind === "repo")
+ work = work.then(function () { return refreshPeople(info); })
+ .catch(function () {});
if (info.kind === "user")
work = work.then(function () { return refreshUser(info); })
.catch(function () {});
diff --git a/site/assets/style.css b/site/assets/style.css
index e454089..d451817 100644
--- a/site/assets/style.css
+++ b/site/assets/style.css
@@ -652,6 +652,11 @@ pre.block.git-blob {
height: 100%;
background: var(--accent);
min-width: 1px;
+ text-decoration: none;
+}
+
+a.lang-seg:hover {
+ opacity: 0.85;
}
.lang-list {
@@ -667,6 +672,15 @@ pre.block.git-blob {
margin-right: 1rem;
}
+.lang-list a {
+ color: inherit;
+ text-decoration: none;
+}
+
+.lang-list a:hover {
+ color: var(--fg);
+}
+
.lang-dot {
display: inline-block;
width: 0.65rem;
@@ -680,6 +694,11 @@ pre.block.git-blob {
margin: 0 0 0.5rem;
}
+.lang-badge a {
+ color: inherit;
+ text-decoration: none;
+}
+
.people {
display: flex;
flex-wrap: wrap;
diff --git a/tools/build-git.sh b/tools/build-git.sh
index ad97c80..28af5af 100755
--- a/tools/build-git.sh
+++ b/tools/build-git.sh
@@ -237,6 +237,63 @@ who_cell() {
-v avatar="$4" -v verified="$5"
}
+lang_slug() {
+ printf '%s\n' "$1" | tr ' /' '--'
+}
+
+# In-repo nav. Links go through ../../../../git/<repo>/ so they work
+# without a trailing slash on the current URL.
+git_nav() {
+ _repo=$1
+ _here=$2
+ _r="../../../../git/${_repo}/"
+ _gh=$(github_of "$_repo")
+ printf '<p class="git-nav">'
+ if [ "$_here" = home ]; then
+ printf '%s' "$(esc "$_repo")"
+ else
+ printf '<a href="%s">%s</a>' "$(esc "$_r")" "$(esc "$_repo")"
+ fi
+ printf ' · '
+ if [ "$_here" = log ]; then
+ printf 'log'
+ else
+ printf '<a href="%slog/">log</a>' "$(esc "$_r")"
+ fi
+ printf ' · '
+ if [ "$_here" = files ]; then
+ printf 'files'
+ else
+ printf '<a href="%stree/">files</a>' "$(esc "$_r")"
+ fi
+ printf ' · '
+ if [ "$_here" = refs ]; then
+ printf 'refs'
+ else
+ printf '<a href="%srefs/">refs</a>' "$(esc "$_r")"
+ fi
+ printf ' · '
+ if [ "$_here" = people ]; then
+ printf 'people'
+ else
+ printf '<a href="%speople/">people</a>' "$(esc "$_r")"
+ fi
+ printf ' · '
+ if [ "$_here" = langs ] || [ "$_here" = lang ]; then
+ printf 'languages'
+ else
+ printf '<a href="%slang/">languages</a>' "$(esc "$_r")"
+ fi
+ printf ' · <a href="%s">GitHub</a>' "$(esc "$_gh")"
+ if [ "$_here" = commit ]; then
+ printf ' · commit'
+ fi
+ if [ "$_here" = blob ]; then
+ printf ' · file'
+ fi
+ printf '</p>\n'
+}
+
mkdir -p "$OUT/git" "$OUT/data" "$OUT/git/users"
# Refresh Linguist map when the network allows; keep the committed map otherwise.
@@ -297,7 +354,10 @@ build_repo() {
nfiles=$(printf '%s' "$nfiles" | tr -d ' ')
base=$OUT/git/$name
- mkdir -p "$base/log" "$base/refs" "$base/tree" "$base/commit" "$base/blob"
+ mkdir -p "$base/log" "$base/refs" "$base/tree" "$base/commit" "$base/blob" \
+ "$base/people" "$base/lang"
+ userpfx="../../../../git/users/"
+ reporoot="../../../../git/$name/"
scan=$base/.scan.tsv
filelang=$base/.filelang.tsv
@@ -341,19 +401,102 @@ build_repo() {
}' "$clog" >>"$all_commits"
awk -F '\t' -v OFS='\t' '{
- if ($7 == "") next
login=$7
- av=$8
- c[login]++
- if (av != "" && !(login in avatar))
- avatar[login]=av
+ name=$6
+ key = (login != "") ? login : name
+ if (key == "") next
+ c[key]++
+ if (login != "")
+ haslogin[key]=login
+ if ($8 != "" && !(key in avatar))
+ avatar[key]=$8
+ if (name != "" && !(key in author))
+ author[key]=name
+ epoch=$3+0
+ if (!(key in last) || epoch >= last[key]) {
+ last[key]=epoch
+ iso[key]=$4
+ subj[key]=$5
+ sha[key]=$1
+ }
}
END {
- for (l in c)
- print l, avatar[l], c[l]
+ for (k in c)
+ print (k in haslogin) ? haslogin[k] : "", avatar[k], c[k], \
+ (k in author) ? author[k] : k, iso[k], subj[k], sha[k]
}' "$clog" | LC_ALL=C sort -t "$(printf '\t')" -k3,3nr >"$base/.people.tsv"
if [ -s "$base/.people.tsv" ]; then
- cat "$base/.people.tsv" >>"$all_people"
+ awk -F '\t' -v OFS='\t' '$1 != "" { print $1, $2, $3 }' \
+ "$base/.people.tsv" >>"$all_people"
+ fi
+
+ # Languages with file counts for the bar and the language index.
+ awk -F '\t' -v OFS='\t' -v fl="$filelang" '
+ BEGIN {
+ while ((getline < fl) > 0) {
+ path=$1
+ name=$2
+ col=$4
+ sz=$5+0
+ group=($6 != "") ? $6 : $2
+ if (name != "") {
+ key = name SUBSEP path
+ if (!(key in seen)) {
+ seen[key]=1
+ nf[name]++
+ fsz[name]+=sz
+ }
+ if (col != "" && !(name in color))
+ color[name]=col
+ }
+ if (group != "" && group != name) {
+ key = group SUBSEP path
+ if (!(key in seen)) {
+ seen[key]=1
+ nf[group]++
+ fsz[group]+=sz
+ }
+ }
+ }
+ close(fl)
+ }
+ {
+ gh[$1]=$2+0
+ if ($3 != "")
+ color[$1]=$3
+ bar[$1]=1
+ }
+ END {
+ for (k in nf)
+ all[k]=1
+ for (k in bar)
+ all[k]=1
+ n=0
+ for (k in all) {
+ n++
+ names[n]=k
+ }
+ for (i = 1; i <= n; i++) {
+ for (j = i + 1; j <= n; j++) {
+ bi = (names[i] in gh) ? gh[names[i]] : fsz[names[i]]+0
+ bj = (names[j] in gh) ? gh[names[j]] : fsz[names[j]]+0
+ if (bj > bi) {
+ t=names[i]; names[i]=names[j]; names[j]=t
+ }
+ }
+ }
+ for (i = 1; i <= n; i++) {
+ k=names[i]
+ print k, gh[k]+0, color[k], nf[k]+0, fsz[k]+0
+ }
+ }
+ ' "$langtmp" >"$base/.lang-index.tsv"
+ awk -F '\t' -v OFS='\t' '$2 + 0 > 0 { print $1, $2, $3, $4 }' \
+ "$base/.lang-index.tsv" >"$base/.langs-bar.tsv"
+ if [ -s "$base/.langs-bar.tsv" ]; then
+ mv "$base/.langs-bar.tsv" "$langtmp"
+ else
+ rm -f "$base/.langs-bar.tsv"
fi
GIT_KIND=repo
@@ -367,11 +510,7 @@ build_repo() {
printf '<h2>%s</h2>\n' "$(esc "$name")"
printf '%s\n' '<hr class="rule">'
printf '<p>%s</p>\n' "$(esc "$repo_desc")"
- printf '%s\n' '<p class="git-nav">'
- printf '<a href="log/">log</a> · <a href="tree/">files</a> · <a href="refs/">refs</a> · '
- printf '<a href="../users/">people</a> · '
- printf '<a href="%s">GitHub</a>\n' "$(esc "$gh")"
- printf '%s\n' '</p>'
+ git_nav "$name" home
printf '%s\n' '<p class="live-note" id="git-live" hidden>Showing newer commits from GitHub.</p>'
printf '%s\n' '<div class="info" id="git-info">'
printf '<div class="dl-row"><span class="muted">HEAD</span><span id="git-head"><a href="commit/%s/"><code>%s</code></a> (%s)</span></div>\n' \
@@ -386,19 +525,20 @@ build_repo() {
if [ -s "$langtmp" ]; then
printf '%s\n' '<h3>Languages</h3>'
printf '%s\n' '<div id="git-langs">'
- awk -f "$AWK" -v mode=langs "$langtmp"
+ awk -f "$AWK" -v mode=langs -v langpfx="${reporoot}lang/" "$langtmp"
printf '%s\n' '</div>'
else
printf '%s\n' '<div id="git-langs"></div>'
fi
if [ -s "$base/.people.tsv" ]; then
printf '%s\n' '<h3>People</h3>'
- awk -f "$AWK" -v mode=people -v userpfx="../users/" "$base/.people.tsv"
+ awk -f "$AWK" -v mode=people -v userpfx="$userpfx" "$base/.people.tsv"
+ printf '<p><a href="%speople/">All contributors</a></p>\n' "$(esc "$reporoot")"
fi
printf '%s\n' '<h3>Recent commits</h3>'
printf '%s\n' '<div id="git-recent">'
- awk -f "$AWK" -v mode=commits -v commitpfx="commit/" \
- -v userpfx="../users/" -v limit=20 "$clog"
+ awk -f "$AWK" -v mode=commits -v commitpfx="${reporoot}commit/" \
+ -v userpfx="$userpfx" -v limit=20 "$clog"
printf '%s\n' '</div>'
printf '%s\n' '<p><a href="log/">Full log</a></p>'
if git -C "$dir" cat-file -e HEAD:README.md 2>/dev/null; then
@@ -414,14 +554,13 @@ build_repo() {
GIT_KIND=log
page_begin "$base/log/index.html" "Log - $name - Splux Git" "Commit log for $name"
{
- printf '<p class="git-nav"><a href="../">%s</a> · log · <a href="../../users/">people</a></p>\n' \
- "$(esc "$name")"
+ git_nav "$name" log
printf '<h2>Log</h2>\n'
printf '%s\n' '<hr class="rule">'
printf '%s\n' '<p class="live-note" id="git-live" hidden>Showing newer commits from GitHub.</p>'
printf '%s\n' '<div id="git-log">'
- awk -f "$AWK" -v mode=commits -v commitpfx="../commit/" \
- -v userpfx="../../users/" "$clog"
+ awk -f "$AWK" -v mode=commits -v commitpfx="${reporoot}commit/" \
+ -v userpfx="$userpfx" "$clog"
printf '%s\n' '</div>'
} >>"$base/log/index.html"
page_end "$base/log/index.html"
@@ -429,7 +568,7 @@ build_repo() {
GIT_KIND=refs
page_begin "$base/refs/index.html" "Refs - $name - Splux Git" "Branches and tags for $name"
{
- printf '<p class="git-nav"><a href="../">%s</a> · refs</p>\n' "$(esc "$name")"
+ git_nav "$name" refs
printf '<h2>Refs</h2>\n'
printf '%s\n' '<hr class="rule">'
printf '%s\n' '<h3>Branches</h3>'
@@ -479,14 +618,13 @@ build_repo() {
GIT_PATH=
page_begin "$cdir/index.html" "$shortc - $name - Splux Git" "$subject"
{
- printf '<p class="git-nav"><a href="../../">%s</a> · <a href="../../log/">log</a> · commit</p>\n' \
- "$(esc "$name")"
+ git_nav "$name" commit
printf '<h2><code>%s</code></h2>\n' "$(esc "$shortc")"
printf '%s\n' '<hr class="rule">'
printf '<p>%s</p>\n' "$(esc "$subject")"
printf '%s\n' '<div class="info">'
printf '<div class="dl-row"><span class="muted">Author</span><span>%s</span></div>\n' \
- "$(who_cell "../../../users/" "$login" "$author" "$avatar" "$verified")"
+ "$(who_cell "$userpfx" "$login" "$author" "$avatar" "$verified")"
printf '<div class="dl-row"><span class="muted">Date</span><span><time datetime="%s">%s</time></span></div>\n' \
"$(esc "$iso")" "$(esc "$iso")"
printf '<div class="dl-row"><span class="muted">Commit</span><span><code>%s</code></span></div>\n' \
@@ -556,8 +694,7 @@ build_repo() {
fi
page_begin "$tdest" "$title" "HEAD tree for $name"
{
- printf '<p class="git-nav"><a href="%s">%s</a> · files</p>\n' \
- "$(esc "$up_repo")" "$(esc "$name")"
+ git_nav "$name" files
if [ -n "$treepath" ]; then
printf '<h2>%s</h2>\n' "$(esc "$treepath")"
else
@@ -598,7 +735,7 @@ build_repo() {
fi
done >"$ent"
if [ -s "$ent" ]; then
- awk -f "$AWK" -v mode=entries "$ent"
+ awk -f "$AWK" -v mode=entries -v langpfx="${reporoot}lang/" "$ent"
else
printf '%s\n' '<p class="muted">empty directory</p>'
fi
@@ -627,41 +764,21 @@ build_repo() {
GIT_SHA=
GIT_PATH=$path
page_begin "$bdest" "$path - $name - Splux Git" "$path at HEAD"
- parent=${path%/*}
- depth=1
- rest=$path
- while [ "$rest" != "${rest#*/}" ]
- do
- depth=$((depth + 1))
- rest=${rest#*/}
- done
- up=""
- j=0
- while [ "$j" -le "$depth" ]
- do
- up="../$up"
- j=$((j + 1))
- done
fl=$(lookup_lang "$path" "$filelang")
flang=${fl%%$tab*}
fcolor=${fl#*$tab}
[ "$fcolor" = "$fl" ] && fcolor=
{
- if [ "$parent" = "$path" ]; then
- dirhref="${up}tree/"
- else
- dirhref="${up}tree/${parent}/"
- fi
- printf '<p class="git-nav"><a href="%s">%s</a> · <a href="%s">directory</a></p>\n' \
- "$(esc "$up")" "$(esc "$name")" "$(esc "$dirhref")"
+ git_nav "$name" blob
printf '<h2>%s</h2>\n' "$(esc "$path")"
printf '%s\n' '<hr class="rule">'
if [ -n "$flang" ]; then
- printf '<p class="lang-badge"><span class="lang-dot" style="background:%s"></span> %s</p>\n' \
+ printf '<p class="lang-badge"><a href="%slang/%s/"><span class="lang-dot" style="background:%s"></span> %s</a></p>\n' \
+ "$(esc "$reporoot")" "$(esc "$(lang_slug "$flang")")" \
"$(esc "${fcolor:-#888888}")" "$(esc "$flang")"
fi
printf '<p class="muted">%s bytes at <a href="%scommit/%s/">%s</a> · <a href="%s/blob/main/%s">GitHub</a></p>\n' \
- "$(esc "$sz")" "$(esc "$up")" "$(esc "$head")" "$(esc "$short")" \
+ "$(esc "$sz")" "$(esc "$reporoot")" "$(esc "$head")" "$(esc "$short")" \
"$(esc "$gh")" "$(esc "$path")"
szn=$(printf '%s' "$sz" | tr -d ' ')
case $szn in
@@ -678,12 +795,97 @@ build_repo() {
page_end "$bdest"
done
+ GIT_KIND=people
+ GIT_SHA=
+ GIT_PATH=
+ page_begin "$base/people/index.html" "People - $name - Splux Git" \
+ "Commit authors in $name"
+ {
+ git_nav "$name" people
+ printf '%s\n' '<h2>People</h2>'
+ printf '%s\n' '<hr class="rule">'
+ printf '<p>Everyone who authored a commit in %s. GitHub accounts link to their profile. Authors without a GitHub login are listed by git name. <a href="../../../../git/users/">All people</a></p>\n' \
+ "$(esc "$name")"
+ printf '%s\n' '<p class="live-note" id="git-live" hidden>Updated from GitHub.</p>'
+ printf '%s\n' '<p><label for="git-filter">Filter</label> <input id="git-filter" type="search" placeholder="person"></p>'
+ if [ -s "$base/.people.tsv" ]; then
+ awk -f "$AWK" -v mode=peoplelist -v userpfx="$userpfx" \
+ -v commitpfx="${reporoot}commit/" "$base/.people.tsv"
+ else
+ printf '%s\n' '<table class="pkgs git-people" id="git-people-table"><thead><tr><th>Person</th><th>Commits</th><th>Last commit</th><th>When</th></tr></thead><tbody></tbody></table>'
+ fi
+ } >>"$base/people/index.html"
+ page_end "$base/people/index.html"
+
+ GIT_KIND=langs
+ GIT_PATH=
+ page_begin "$base/lang/index.html" "Languages - $name - Splux Git" \
+ "Languages in $name"
+ {
+ git_nav "$name" langs
+ printf '%s\n' '<h2>Languages</h2>'
+ printf '%s\n' '<hr class="rule">'
+ printf '%s\n' '<p>Click a language to see how many files use it.</p>'
+ printf '%s\n' '<p><label for="git-filter">Filter</label> <input id="git-filter" type="search" placeholder="language"></p>'
+ if [ -s "$base/.lang-index.tsv" ]; then
+ awk -f "$AWK" -v mode=langindex -v langpfx="${reporoot}lang/" \
+ "$base/.lang-index.tsv"
+ else
+ printf '%s\n' '<p class="muted">No languages recorded.</p>'
+ fi
+ } >>"$base/lang/index.html"
+ page_end "$base/lang/index.html"
+
+ if [ -s "$base/.lang-index.tsv" ]; then
+ while IFS= read -r _lline || [ -n "${_lline-}" ]
+ do
+ [ -n "${_lline-}" ] || continue
+ langname=$(tsv_cut "$_lline" 1)
+ nlangfiles=$(tsv_cut "$_lline" 4)
+ langbytes=$(tsv_cut "$_lline" 5)
+ [ -n "$langname" ] || continue
+ lslug=$(lang_slug "$langname")
+ ldest=$base/lang/$lslug/index.html
+ GIT_KIND=lang
+ GIT_PATH=$langname
+ page_begin "$ldest" "$langname - $name - Splux Git" \
+ "$langname files in $name"
+ {
+ git_nav "$name" lang
+ printf '<h2>%s</h2>\n' "$(esc "$langname")"
+ printf '%s\n' '<hr class="rule">'
+ printf '<p>%s files' "$(esc "${nlangfiles:-0}")"
+ if [ -n "$langbytes" ] && [ "$langbytes" != 0 ]; then
+ printf ', %s bytes' "$(esc "$langbytes")"
+ fi
+ printf ' · <a href="%slang/">all languages</a></p>\n' \
+ "$(esc "$reporoot")"
+ printf '%s\n' '<p><label for="git-filter">Filter</label> <input id="git-filter" type="search" placeholder="file"></p>'
+ lf=$base/.lang-files.tsv
+ awk -F '\t' -v OFS='\t' -v lang="$langname" \
+ -v blobpfx="${reporoot}blob/" '
+ $2 == lang || $6 == lang {
+ print "file", $5, $1, blobpfx $1 "/", $2
+ }
+ ' "$filelang" | LC_ALL=C sort -t "$(printf '\t')" -k3,3 >"$lf"
+ if [ -s "$lf" ]; then
+ awk -f "$AWK" -v mode=entries -v showlang=no "$lf"
+ else
+ printf '%s\n' '<p class="muted">No files listed for this language.</p>'
+ fi
+ } >>"$ldest"
+ page_end "$ldest"
+ rm -f "$lf"
+ done <"$base/.lang-index.tsv"
+ fi
+
day=${lastiso%%T*}
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
"$name" "$repo_desc" "$short" "$head" "$ncommits" "$nfiles" "$day" "$gh" \
>>"$index_rows"
- rm -f "$langtmp" "$clog" "$base/.entries.tsv" "$base/.people.tsv" "$filelang"
+ rm -f "$langtmp" "$clog" "$base/.entries.tsv" "$base/.people.tsv" \
+ "$filelang" "$base/.lang-index.tsv"
printf '%s\n' "git: $name ($ncommits commits, $nfiles files)" >&2
}
@@ -746,7 +948,7 @@ page_begin "$OUT/git/users/index.html" "People - Splux Git" "GitHub accounts tha
printf '%s\n' '<p class="live-note" id="git-live" hidden>Updated from GitHub.</p>'
if [ -s "$users_full" ]; then
awk -F '\t' -v OFS='\t' '{ print $1, $4, $11 }' "$users_full" |
- awk -f "$AWK" -v mode=people -v userpfx="./"
+ awk -f "$AWK" -v mode=people -v userpfx="../../../../git/users/"
else
printf '%s\n' '<p class="muted">No GitHub accounts recorded for these commits.</p>'
fi
@@ -827,8 +1029,8 @@ if [ -s "$users_full" ]; then
print $2, $3, $4, $5, $6, $7, $8, $9, $10, $1
}
' "$all_commits" |
- awk -f "$AWK" -v mode=commits -v repopfx="../../" \
- -v userpfx="../"
+ awk -f "$AWK" -v mode=commits -v repopfx="../../../../git/" \
+ -v userpfx="../../../../git/users/"
printf '%s\n' '</div>'
} >>"$udest"
page_end "$udest"
@@ -870,7 +1072,7 @@ page_begin "$OUT/git/index.html" "Git - Splux Linux" "Always-updating browse of
if [ -s "$users_full" ]; then
printf '%s\n' '<h3>People</h3>'
awk -F '\t' -v OFS='\t' '{ print $1, $4, $11 }' "$users_full" |
- awk -f "$AWK" -v mode=people -v userpfx="users/"
+ awk -f "$AWK" -v mode=people -v userpfx="../../../../git/users/"
fi
printf '%s\n' '<h3>Clone</h3>'
printf '%s\n' '<pre class="block">git clone https://github.com/RobertFlexx/SPS.git'
diff --git a/tools/git-lang.awk b/tools/git-lang.awk
index 5ba028a..e111cd6 100644
--- a/tools/git-lang.awk
+++ b/tools/git-lang.awk
@@ -157,8 +157,10 @@ BEGIN {
mode == "count" {
detect($2, $3)
- if (filelang != "")
- print $2 "\t" d_name "\t" d_type "\t" d_color >> filelang
+ if (filelang != "") {
+ g = (d_group != "") ? d_group : d_name
+ print $2 "\t" d_name "\t" d_type "\t" d_color "\t" $1 "\t" g >> filelang
+ }
if (skip_stats($2))
next
if (d_type != "programming" && d_type != "markup")
diff --git a/tools/git.awk b/tools/git.awk
index df59e48..7933240 100644
--- a/tools/git.awk
+++ b/tools/git.awk
@@ -2,12 +2,18 @@
# mode=one escape $0 (or -v text=) as one HTML string
# mode=who -v login= -v name= -v avatar= -v verified= -v userpfx=
# mode=pre escape stdin as a <pre class="block git-blob">
-# mode=langs TSV lang<TAB>bytes<TAB>color
+# mode=langs TSV lang<TAB>bytes<TAB>color[<TAB>nfiles]
+# -v langpfx=
+# mode=langindex TSV lang<TAB>ghbytes<TAB>color<TAB>nfiles<TAB>filebytes
+# -v langpfx=
# mode=commits TSV hash short epoch iso subject author login avatar verified [repo]
# -v commitpfx= -v userpfx= -v repopfx=
-# mode=people TSV login avatar count
+# mode=people TSV login avatar count [name]
# -v userpfx=
+# mode=peoplelist TSV login avatar count name iso subject hash
+# -v userpfx= -v commitpfx=
# mode=entries TSV kind size name href [lang]
+# -v langpfx= -v showlang=no
function esc(s) {
gsub(/&/, "\\&", s)
@@ -23,6 +29,17 @@ function pct(n, d) {
return int((n * 1000 + d / 2) / d) / 10
}
+function slug(s) {
+ gsub(/[ \/]/, "-", s)
+ return s
+}
+
+function langhref(name) {
+ if (langpfx == "" || name == "")
+ return ""
+ return langpfx slug(name) "/"
+}
+
function who(login, name, avatar, html, av, label) {
label = login != "" ? login : name
if (login != "") {
@@ -42,7 +59,7 @@ BEGIN {
FS = "\t"
limit = limit + 0
if (userpfx == "")
- userpfx = "../users/"
+ userpfx = "../../../../git/users/"
if (mode == "one") {
if (text != "")
printf "%s", esc(text)
@@ -73,9 +90,20 @@ mode == "langs" {
lang[n] = $1
bytes[n] = $2 + 0
col[n] = $3
+ nfiles[n] = $4 + 0
total += bytes[n]
}
+mode == "langindex" {
+ n++
+ lang[n] = $1
+ bytes[n] = $2 + 0
+ col[n] = $3
+ nfiles[n] = $4 + 0
+ fbytes[n] = $5 + 0
+ total += (bytes[n] > 0) ? bytes[n] : fbytes[n]
+}
+
mode == "commits" {
if (limit > 0 && n >= limit)
next
@@ -96,6 +124,18 @@ mode == "people" {
login[n] = $1
avatar[n] = $2
count[n] = $3
+ pname[n] = $4
+}
+
+mode == "peoplelist" {
+ n++
+ login[n] = $1
+ avatar[n] = $2
+ count[n] = $3
+ pname[n] = $4
+ iso[n] = $5
+ subject[n] = $6
+ hash[n] = $7
}
mode == "entries" {
@@ -126,17 +166,59 @@ END {
style = "width:" w "%"
if (col[i] != "")
style = style ";background:" col[i]
- printf "<span class=\"lang-seg\" style=\"%s\" title=\"%s %s%%\"></span>\n", \
- style, esc(lang[i]), w
+ href = langhref(lang[i])
+ title = esc(lang[i]) " " w "%"
+ if (href != "")
+ printf "<a class=\"lang-seg\" href=\"%s\" style=\"%s\" title=\"%s\"></a>\n", \
+ esc(href), style, title
+ else
+ printf "<span class=\"lang-seg\" style=\"%s\" title=\"%s\"></span>\n", \
+ style, title
}
print "</div>"
print "<ul class=\"lang-list\">"
- for (i = 1; i <= n; i++)
- printf "<li><span class=\"lang-dot\" style=\"background:%s\"></span> %s <span class=\"muted\">%s%%</span></li>\n", \
- esc(col[i] != "" ? col[i] : "#888888"), esc(lang[i]), pct(bytes[i], total)
+ for (i = 1; i <= n; i++) {
+ href = langhref(lang[i])
+ dot = esc(col[i] != "" ? col[i] : "#888888")
+ extra = pct(bytes[i], total) "%"
+ if (nfiles[i] > 0)
+ extra = extra " · " nfiles[i] " files"
+ if (href != "")
+ printf "<li><a href=\"%s\"><span class=\"lang-dot\" style=\"background:%s\"></span> %s <span class=\"muted\">%s</span></a></li>\n", \
+ esc(href), dot, esc(lang[i]), extra
+ else
+ printf "<li><span class=\"lang-dot\" style=\"background:%s\"></span> %s <span class=\"muted\">%s</span></li>\n", \
+ dot, esc(lang[i]), extra
+ }
print "</ul>"
exit
}
+ if (mode == "langindex") {
+ print "<table class=\"pkgs git-langs\" id=\"git-lang-table\">"
+ print "<thead><tr><th>Language</th><th>Files</th><th>Bytes</th><th></th></tr></thead>"
+ print "<tbody>"
+ for (i = 1; i <= n; i++) {
+ href = langhref(lang[i])
+ dot = esc(col[i] != "" ? col[i] : "#888888")
+ sz = bytes[i] > 0 ? bytes[i] : fbytes[i]
+ w = pct(sz, total)
+ printf "<tr data-search=\"%s\">", esc(tolower(lang[i]))
+ printf "<td>"
+ if (href != "")
+ printf "<a href=\"%s\"><span class=\"lang-dot\" style=\"background:%s\"></span> %s</a>", \
+ esc(href), dot, esc(lang[i])
+ else
+ printf "<span class=\"lang-dot\" style=\"background:%s\"></span> %s", \
+ dot, esc(lang[i])
+ printf "</td>"
+ printf "<td class=\"muted\">%s</td>", (nfiles[i] > 0 ? nfiles[i] : "")
+ printf "<td class=\"muted\">%s</td>", (sz > 0 ? sz : "")
+ printf "<td class=\"muted\">%s%%</td>", w
+ print "</tr>"
+ }
+ print "</tbody></table>"
+ exit
+ }
if (mode == "commits") {
print "<table class=\"pkgs git-log\">"
if (repopfx != "")
@@ -154,7 +236,7 @@ END {
printf "<tr data-sha=\"%s\" data-login=\"%s\" data-repo=\"%s\">", \
esc(hash[i]), esc(login[i]), esc(repo[i])
printf "<td><a href=\"%s\"><code>%s</code></a></td>", href, esc(short[i])
- if (repopfx != "")
+ if (repopfx != "" && repo[i] != "")
printf "<td><a href=\"%s%s/\">%s</a></td>", \
repopfx, esc(repo[i]), esc(repo[i])
printf "<td><a href=\"%s\">%s</a></td>", href, esc(subject[i])
@@ -171,7 +253,8 @@ END {
if (mode == "people") {
print "<div class=\"people\" id=\"git-people\">"
for (i = 1; i <= n; i++) {
- printf "<span class=\"person\">%s", who(login[i], login[i], avatar[i])
+ label = pname[i] != "" ? pname[i] : login[i]
+ printf "<span class=\"person\">%s", who(login[i], label, avatar[i])
if (count[i] != "")
printf " <span class=\"muted\">%s</span>", esc(count[i])
print "</span>"
@@ -179,9 +262,40 @@ END {
print "</div>"
exit
}
+ if (mode == "peoplelist") {
+ print "<table class=\"pkgs git-people\" id=\"git-people-table\">"
+ print "<thead><tr><th>Person</th><th>Commits</th><th>Last commit</th><th>When</th></tr></thead>"
+ print "<tbody>"
+ for (i = 1; i <= n; i++) {
+ label = pname[i] != "" ? pname[i] : login[i]
+ search = tolower(login[i] " " label)
+ printf "<tr data-login=\"%s\" data-search=\"%s\">", \
+ esc(login[i]), esc(search)
+ printf "<td>%s</td>", who(login[i], label, avatar[i])
+ printf "<td class=\"muted\">%s</td>", esc(count[i])
+ if (hash[i] != "" && commitpfx != "") {
+ chref = commitpfx esc(hash[i]) "/"
+ printf "<td><a href=\"%s\">%s</a></td>", chref, esc(subject[i])
+ } else
+ printf "<td>%s</td>", esc(subject[i])
+ day = iso[i]
+ sub(/T.*/, "", day)
+ if (iso[i] != "")
+ printf "<td><time datetime=\"%s\">%s</time></td>", \
+ esc(iso[i]), esc(day)
+ else
+ printf "<td></td>"
+ print "</tr>"
+ }
+ print "</tbody></table>"
+ exit
+ }
if (mode == "entries") {
print "<table class=\"pkgs git-tree\">"
- print "<thead><tr><th></th><th>Name</th><th>Language</th><th>Size</th></tr></thead>"
+ if (showlang == "no")
+ print "<thead><tr><th></th><th>Name</th><th>Size</th></tr></thead>"
+ else
+ print "<thead><tr><th></th><th>Name</th><th>Language</th><th>Size</th></tr></thead>"
print "<tbody>"
for (i = 1; i <= n; i++) {
label = kind[i] == "dir" ? "dir" : "file"
@@ -189,7 +303,13 @@ END {
printf "<tr data-search=\"%s %s\">", esc(tolower(name[i])), esc(tolower(elang[i]))
printf "<td class=\"muted\">%s</td>", label
printf "<td><a href=\"%s\">%s</a></td>", esc(href[i]), esc(name[i])
- printf "<td class=\"muted\">%s</td>", esc(elang[i])
+ if (showlang != "no") {
+ lhref = langhref(elang[i])
+ if (lhref != "" && elang[i] != "")
+ printf "<td><a href=\"%s\">%s</a></td>", esc(lhref), esc(elang[i])
+ else
+ printf "<td class=\"muted\">%s</td>", esc(elang[i])
+ }
printf "<td class=\"muted\">%s</td>", esc(sz)
print "</tr>"
}