commit 5161c1dbfbfb3df8d5c4c9cc3e63083f7c64ed5b
parent d27987f0a36fd3809a579907dffe019b2d7d9852
Author: Jack Sangdahl <jack@pngu.org>
Date: Fri, 10 Jul 2026 17:24:11 -0600
Rework syntax highlighting to not use Python
Diffstat:
| M | highlight | | | 63 | ++++++++++++++++++--------------------------------------------- |
| M | readme.md | | | 166 | ++++++++++++++++++++++++++++++++++++------------------------------------------- |
| D | requirements.txt | | | 3 | --- |
| M | style.css | | | 224 | ++++++++++++++++++++++++++++++++++++++++++++++++------------------------------- |
4 files changed, 231 insertions(+), 225 deletions(-)
diff --git a/highlight b/highlight
@@ -1,50 +1,23 @@
-#!/usr/bin/env python3
+#!/usr/bin/env bash
-from sys import stdin
+read -r FILENAME
-import pygments
-from markdown import markdown
-from pygments import highlight
-from pygments.formatters import HtmlFormatter
-from pygments.lexers import TextLexer, guess_lexer, guess_lexer_for_filename
+EXT="${FILENAME##*.}"
+EXT=$(echo "$EXT" | tr '[:upper:]' '[:lower:]')
-filename = stdin.readline().strip()
-contents = stdin.read()
-lexer = None
-rendered = None
+if [ "$EXT" = "md" ] || [ "$EXT" = "markdown" ]; then
+ echo '<article class="markup markdown">'
+ pandoc --from gfm --to html
+ echo '</article>'
+ exit 0
+fi
-try:
- lexer = guess_lexer_for_filename(filename, contents)
-except pygments.util.ClassNotFound:
- try:
- lexer = guess_lexer(contents)
- except pygments.util.ClassNotFound:
- pass
+if [ "$EXT" = "$FILENAME" ]; then
+ SYNTAX_ARG="$FILENAME"
+else
+ SYNTAX_ARG="$EXT"
+fi
-if lexer is None:
- lexer = TextLexer()
-
-if lexer.__class__ is pygments.lexers.MarkdownLexer:
- rendered = markdown(
- contents,
- extensions=["codehilite", "extra", "sane_lists", "smarty", "pymdownx.tasklist"],
- )
-
-format = HtmlFormatter(
- style="murphy",
- cssclass="highlight",
- linenos="table",
- lineanchors="loc",
- anchorlinenos=True,
-)
-
-if rendered:
- print('<article class="markup markdown">')
- print(rendered)
- print("</article>")
-print('<div id="blob">')
-print(highlight(contents, lexer, format))
-print("</div>")
-print("<style>")
-print(format.get_style_defs(""))
-print("</style>")
+echo '<pre id="blob">'
+highlight --syntax="$SYNTAX_ARG" --fragment --style=github --quiet
+echo '</pre>'
diff --git a/readme.md b/readme.md
@@ -1,54 +1,49 @@
-stagit
-------
+## stagit
static git page generator.
It generates static HTML pages for a git repository.
-(personal fork)
+(personal fork with markdown rendering and syntax highlighting)
-Usage
------
+syntax highlighting is provided by [highlight](https://gitlab.com/saalen/highlight), and markdown rendering is provided by pandoc.
+
+see the `highlight` shell script for more, and the `stagit-generate.sh` script for generation as a whole.
+
+## Usage
Make files per repository:
- $ mkdir -p htmlroot/htmlrepo1 && cd htmlroot/htmlrepo1
- $ stagit path/to/gitrepo1
- repeat for other repositories
- $ ...
+ $ mkdir -p htmlroot/htmlrepo1 && cd htmlroot/htmlrepo1
+ $ stagit path/to/gitrepo1
+ repeat for other repositories
+ $ ...
Make index file for repositories:
- $ cd htmlroot
- $ stagit-index path/to/gitrepo1 \
- path/to/gitrepo2 \
- path/to/gitrepo3 > index.html
-
+ $ cd htmlroot
+ $ stagit-index path/to/gitrepo1 \
+ path/to/gitrepo2 \
+ path/to/gitrepo3 > index.html
-Build and install
------------------
+## Build and install
$ make
-# make install
+# make install
-Dependencies
-------------
+## Dependencies
- C compiler (C99).
- libc (tested with OpenBSD, FreeBSD, NetBSD, Linux: glibc and musl).
- libgit2 (v0.22+).
- POSIX make (optional).
-
-Documentation
--------------
+## Documentation
See man pages: stagit(1) and stagit-index(1).
-
-Building a static binary
-------------------------
+## Building a static binary
It may be useful to build static binaries, for example to run in a chroot.
@@ -57,46 +52,42 @@ It can be done like this at the time of writing (v0.24):
cd libgit2-src
# change the options in the CMake file: CMakeLists.txt
+
BUILD_SHARED_LIBS to OFF (static)
-CURL to OFF (not needed)
-USE_SSH OFF (not needed)
-THREADSAFE OFF (not needed)
-USE_OPENSSL OFF (not needed, use builtin)
+CURL to OFF (not needed)
+USE_SSH OFF (not needed)
+THREADSAFE OFF (not needed)
+USE_OPENSSL OFF (not needed, use builtin)
mkdir -p build && cd build
cmake ../
make
make install
-
-Extract owner field from git config
------------------------------------
+## Extract owner field from git config
A way to extract the gitweb owner for example in the format:
- [gitweb]
- owner = Name here
+ [gitweb]
+ owner = Name here
Script:
- #!/bin/sh
- awk '/^[ ]*owner[ ]=/ {
- sub(/^[^=]*=[ ]*/, "");
- print $0;
- }'
+ #!/bin/sh
+ awk '/^[ ]*owner[ ]=/ {
+ sub(/^[^=]*=[ ]*/, "");
+ print $0;
+ }'
+## Set clone URL for a directory of repos
-Set clone URL for a directory of repos
---------------------------------------
- #!/bin/sh
- cd "$dir"
- for i in *; do
- test -d "$i" && echo "git://git.codemadness.org/$i" > "$i/url"
- done
+ #!/bin/sh
+ cd "$dir"
+ for i in *; do
+ test -d "$i" && echo "git://git.codemadness.org/$i" > "$i/url"
+ done
-
-Update files on git push
-------------------------
+## Update files on git push
Using a post-receive hook the static files can be automatically updated.
Keep in mind git push -f can change the history and the commits may need
@@ -106,44 +97,41 @@ history. See stagit(1).
git post-receive hook (repo/.git/hooks/post-receive):
- #!/bin/sh
- # detect git push -f
- force=0
- while read -r old new ref; do
- hasrevs=$(git rev-list "$old" "^$new" | sed 1q)
- if test -n "$hasrevs"; then
- force=1
- break
- fi
- done
-
- # remove commits and .cache on git push -f
- #if test "$force" = "1"; then
- # ...
- #fi
-
- # see example_create.sh for normal creation of the files.
-
-
-Create .tar.gz archives by tag
-------------------------------
- #!/bin/sh
- name="stagit"
- mkdir -p archives
- git tag -l | while read -r t; do
- f="archives/${name}-$(echo "${t}" | tr '/' '_').tar.gz"
- test -f "${f}" && continue
- git archive \
- --format tar.gz \
- --prefix "${t}/" \
- -o "${f}" \
- -- \
- "${t}"
- done
-
-
-Features
---------
+ #!/bin/sh
+ # detect git push -f
+ force=0
+ while read -r old new ref; do
+ hasrevs=$(git rev-list "$old" "^$new" | sed 1q)
+ if test -n "$hasrevs"; then
+ force=1
+ break
+ fi
+ done
+
+ # remove commits and .cache on git push -f
+ #if test "$force" = "1"; then
+ # ...
+ #fi
+
+ # see example_create.sh for normal creation of the files.
+
+## Create .tar.gz archives by tag
+
+ #!/bin/sh
+ name="stagit"
+ mkdir -p archives
+ git tag -l | while read -r t; do
+ f="archives/${name}-$(echo "${t}" | tr '/' '_').tar.gz"
+ test -f "${f}" && continue
+ git archive \
+ --format tar.gz \
+ --prefix "${t}/" \
+ -o "${f}" \
+ -- \
+ "${t}"
+ done
+
+## Features
- Log of all commits from HEAD.
- Log and diffstat per commit.
@@ -159,9 +147,7 @@ Features
a HTTP file server is required.
- Usable with text-browsers such as dillo, links, lynx and w3m.
-
-Cons
-----
+## Cons
- Not suitable for large repositories (2000+ commits), because diffstats are
an expensive operation, the cache (-c flag) is a workaround for this in
diff --git a/requirements.txt b/requirements.txt
@@ -1,3 +0,0 @@
-markdown
-pygments
-pymdown-extensions
diff --git a/style.css b/style.css
@@ -1,144 +1,153 @@
html {
- font-size: 12px;
- height: 100%;
+ font-size: 12px;
+ height: 100%;
}
body {
- margin: 5rem auto;
- color: #222;
- background-color: #fff;
- font-family: monospace;
- width: 66rem;
+ margin: 5rem auto;
+ color: #222;
+ background-color: #fff;
+ font-family: monospace;
+ width: 66rem;
}
-pre { -moz-tab-size: 4; }
-pre { tab-size: 4; }
+pre {
+ -moz-tab-size: 4;
+}
+pre {
+ tab-size: 4;
+}
-h1, h2, h3, h4, h5, h6 {
- font-size: 1em;
- margin: 0;
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-size: 1em;
+ margin: 0;
}
-img, h1, h2 {
- vertical-align: middle;
+img,
+h1,
+h2 {
+ vertical-align: middle;
}
img {
- border: 0;
+ border: 0;
}
a:target {
- background-color: #ccc;
+ background-color: #ccc;
}
a.d,
a.h,
a.i,
a.line {
- text-decoration: none;
+ text-decoration: none;
}
#blob {
- display: block;
- max-width: 100%;
- overflow-x: scroll;
+ display: block;
+ max-width: 100%;
+ overflow-x: scroll;
}
+/* Keeps Python Markdown files looking excellent */
article.markup {
- font-size: 15px;
- border: 2px solid #00000017;
- border-radius: 10px;
- font-family: sans-serif;
- padding: 2.5em;
- margin: 2em 0;
+ font-size: 15px;
+ border: 2px solid #00000017;
+ border-radius: 10px;
+ font-family: sans-serif;
+ padding: 2.5em;
+ margin: 2em 0;
}
article.markup code {
- font-size: 0.9em;
- border: 1px solid #dbdbdb;
- background-color: #f7f7f7;
- padding: 0 0.3em;
- border-radius: 0.3em
+ font-size: 0.9em;
+ border: 1px solid #dbdbdb;
+ background-color: #f7f7f7;
+ padding: 0 0.3em;
+ border-radius: 0.3em;
}
article.markup pre code {
- border: none;
- background: none;
- padding: 0;
- border-radius: 0;
+ border: none;
+ background: none;
+ padding: 0;
+ border-radius: 0;
}
article.markup pre {
- background-color: #f7f7f7;
- padding: 1em;
- border: 1px solid #dbdbdb;
- border-radius: 0.3em;
+ background-color: #f7f7f7;
+ padding: 1em;
+ border: 1px solid #dbdbdb;
+ border-radius: 0.3em;
}
article.markup h1 {
- font-size: 2.4em;
- padding-bottom: 6px;
- border-bottom: 5px solid #0000000a;
+ font-size: 2.4em;
+ padding-bottom: 6px;
+ border-bottom: 5px solid #0000000a;
}
article.markup h2 {
- font-size: 1.9em;
- padding-bottom: 5px;
- border-bottom: 2px solid #00000014;
+ font-size: 1.9em;
+ padding-bottom: 5px;
+ border-bottom: 2px solid #00000014;
}
article.markup h3 {
- font-size: 1.5em;
+ font-size: 1.5em;
}
-
article.markup h4 {
- font-size: 1.3em;
+ font-size: 1.3em;
}
-
article.markup h5 {
- font-size: 1.1em;
+ font-size: 1.1em;
}
-
article.markup h6 {
- font-size: 1em;
+ font-size: 1em;
}
article img {
- max-width: 100%;
+ max-width: 100%;
}
.linenos {
- margin-right: 0;
- border-right: 1px solid rgb(0 0 0 / 8%);
- user-select: none;
+ margin-right: 0;
+ border-right: 1px solid rgb(0 0 0 / 8%);
+ user-select: none;
}
.linenos a {
- margin-right: 0.9em;
- user-select: none;
- text-decoration: none;
+ margin-right: 0.9em;
+ user-select: none;
+ text-decoration: none;
}
#blob a {
- color: #777;
+ color: #777;
}
#blob a:hover {
- color: blue;
- text-decoration: none;
+ color: blue;
+ text-decoration: none;
}
table thead td {
- font-weight: bold;
+ font-weight: bold;
}
table td {
- padding: 0 0.4em;
+ padding: 0 0.4em;
}
#content table td {
- vertical-align: top;
- white-space: nowrap;
+ vertical-align: top;
+ white-space: nowrap;
}
#branches tr:hover td,
@@ -146,68 +155,109 @@ table td {
#index tr:hover td,
#log tr:hover td,
#files tr:hover td {
- background-color: #eee;
+ background-color: #eee;
}
#index tr td:nth-child(2),
#tags tr td:nth-child(3),
#branches tr td:nth-child(3),
#log tr td:nth-child(2) {
- white-space: normal;
+ white-space: normal;
}
td.num {
- text-align: right;
+ text-align: right;
}
.desc {
- color: #777;
+ color: #777;
}
hr {
- border: 0;
- border-top: 1px solid #777;
- height: 1px;
+ border: 0;
+ border-top: 1px solid #777;
+ height: 1px;
}
pre {
- font-family: monospace;
+ font-family: monospace;
}
pre a.h {
- color: #00a;
+ color: #00a;
}
-
.A,
span.i,
pre a.i {
- color: #070;
+ color: #070;
}
-
.D,
span.d,
pre a.d {
- color: #e00;
+ color: #e00;
}
pre a.h:hover,
pre a.i:hover,
pre a.d:hover {
- text-decoration: none;
+ text-decoration: none;
}
.url td:nth-child(2) {
- padding-top: 0.2em;
- padding-bottom: 0.9em;
+ padding-top: 0.2em;
+ padding-bottom: 0.9em;
}
.url td:nth-child(2) span {
- padding: 1px 5px;
- background-color: #eee;
- border: 1px solid #ddd;
- border-radius: 5px;
+ padding: 1px 5px;
+ background-color: #eee;
+ border: 1px solid #ddd;
+ border-radius: 5px;
}
.url td:nth-child(2) span a {
- color: #444;
+ color: #444;
+}
+
+.hl.num {
+ color: #0086b3;
+}
+.hl.esc {
+ color: #183691;
+}
+.hl.str {
+ color: #183691;
+}
+.hl.pps {
+ color: #183691;
+}
+.hl.slc {
+ color: #969896;
+}
+.hl.com {
+ color: #969896;
+}
+.hl.ppc {
+ color: #a71d5d;
+}
+.hl.opt {
+ color: #000000;
+}
+.hl.ipl {
+ color: #183691;
+}
+.hl.lin {
+ color: #b2b2b2;
+}
+.hl.kwa {
+ color: #a71d5d;
+}
+.hl.kwb {
+ color: #0086b3;
+}
+.hl.kwc {
+ color: #0086b3;
+}
+.hl.kwd {
+ color: #0086b3;
}