commit 27a03de9bfae4946f394fe27ab5a7e87b87c0fac
parent 465050f2ef885a2bc9ee66635c9eafbd3c66c148
Author: maydayv7 <maydayv7@gmail.com>
Date: Fri, 2 Jan 2026 01:50:52 +0530
feat: Add Markdown Render and Syntax Highlight
Also add Nix Flake
Diffstat:
| A | flake.lock | | | 60 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | flake.nix | | | 44 | ++++++++++++++++++++++++++++++++++++++++++++ |
| M | stagit.c | | | 99 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------- |
3 files changed, 184 insertions(+), 19 deletions(-)
diff --git a/flake.lock b/flake.lock
@@ -0,0 +1,60 @@
+{
+ "nodes": {
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1767116409,
+ "narHash": "sha256-5vKw92l1GyTnjoLzEagJy5V5mDFck72LiQWZSOnSicw=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "cad22e7d996aea55ecab064e84834289143e44a0",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "nixpkgs": "nixpkgs",
+ "utils": "utils"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ },
+ "utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
@@ -0,0 +1,44 @@
+{
+ description = "Personal Stagit Fork";
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs";
+ utils.url = "github:numtide/flake-utils";
+ };
+
+ outputs =
+ { nixpkgs, utils, ... }:
+ utils.lib.eachDefaultSystem (
+ system: with nixpkgs.legacyPackages.${system}; {
+ packages.default = stdenv.mkDerivation {
+ pname = "stagit";
+ version = "1.2-custom";
+ src = ./.;
+
+ buildInputs = [
+ libgit2
+ md4c
+ ];
+
+ nativeBuildInputs = [
+ pkg-config
+ makeWrapper
+ ];
+
+ buildPhase = ''
+ make PREFIX=$out \
+ STAGIT_CFLAGS="-I${libgit2}/include -I${md4c}/include" \
+ STAGIT_LDFLAGS="-L${libgit2}/lib -lgit2 -L${md4c}/lib -lmd4c -lmd4c-html"
+ '';
+
+ installPhase = ''
+ mkdir -p $out/bin
+ mkdir -p $out/share/doc/stagit
+ cp stagit $out/bin/
+ cp stagit-index $out/bin/
+ cp style.css logo.png favicon.png $out/share/doc/stagit/
+ wrapProgram $out/bin/stagit --prefix PATH : ${lib.makeBinPath [ chroma ]}
+ '';
+ };
+ }
+ );
+}
diff --git a/stagit.c b/stagit.c
@@ -16,6 +16,9 @@
#include "compat.h"
+#include <md4c-html.h>
+#define CMD_BUFSIZE 255
+
#define LEN(s) (sizeof(s)/sizeof(*s))
struct deltainfo {
@@ -556,37 +559,86 @@ writefooter(FILE *fp)
fputs("</div>\n</body>\n</html>\n", fp);
}
+const char *
+get_ext(const char *filename)
+{
+ const char *dot = strrchr(filename, '.');
+ if (!dot || dot == filename) return "";
+ return dot + 1;
+}
+
+void
+processmd(const char* output, unsigned int len, void *fp)
+{
+ fprintf((FILE *)fp, "%.*s", len, output);
+}
+
size_t
-writeblobhtml(FILE *fp, const git_blob *blob)
+writeblobmd(FILE *fp, const git_blob *blob)
{
- size_t n = 0, i, len, prev;
- const char *nfmt = "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">%7zu</a> ";
+ size_t n = 0, i, len, prev, ret;
const char *s = git_blob_rawcontent(blob);
-
len = git_blob_rawsize(blob);
- fputs("<pre id=\"blob\">\n", fp);
+ fputs("<div id=\"md\">\n", fp);
if (len > 0) {
for (i = 0, prev = 0; i < len; i++) {
- if (s[i] != '\n')
- continue;
+ if (s[i] != '\n') continue;
n++;
- fprintf(fp, nfmt, n, n, n);
- xmlencodeline(fp, &s[prev], i - prev + 1);
- putc('\n', fp);
prev = i + 1;
}
- /* trailing data */
- if ((len - prev) > 0) {
- n++;
- fprintf(fp, nfmt, n, n, n);
- xmlencodeline(fp, &s[prev], len - prev);
- }
+ if ((len - prev) > 0) n++;
+
+ ret = md_html(s, len, processmd, fp, MD_FLAG_TABLES | MD_FLAG_TASKLISTS |
+ MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS, 0);
}
+ fputs("</div>\n", fp);
+ return n;
+}
- fputs("</pre>\n", fp);
+size_t
+syntax_highlight(const char *filename, FILE *fp, const char *s, size_t len)
+{
+ size_t lc = 0;
+ fflush(fp);
- return n;
+ int stdout_copy = dup(1);
+ dup2(fileno(fp), 1);
+
+ char cmd[CMD_BUFSIZE];
+ snprintf(cmd, sizeof(cmd), "chroma --html --html-only --html-lines "
+ "--html-lines-table --filename %s", filename);
+
+ FILE *child = popen(cmd, "w");
+ if (child == NULL) {
+ dup2(stdout_copy, 1);
+ fprintf(stderr, "stagit: chroma failed\n");
+ return 0;
+ }
+
+ size_t i;
+ for (i = 0; *s && i < len; s++, i++) {
+ if (*s == '\n') lc++;
+ fprintf(child, "%c", *s);
+ }
+
+ pclose(child);
+ fflush(stdout);
+ dup2(stdout_copy, 1);
+ return lc;
+}
+
+size_t
+writeblobhtml(const char *filename, FILE *fp, const git_blob *blob)
+{
+ size_t lc = 0;
+ const char *s = git_blob_rawcontent(blob);
+ size_t len = git_blob_rawsize(blob);
+
+ if (len > 0) {
+ lc = syntax_highlight(filename, fp, s, len);
+ }
+ return lc;
}
void
@@ -946,6 +998,13 @@ writeatom(FILE *fp, int all)
return 0;
}
+int
+git_blob_is_markdown_file(const char *filename)
+{
+ const char *ext = strrchr(filename, '.');
+ return ((ext != NULL && (!strcmp (ext+1, "md"))));
+}
+
size_t
writeblob(git_object *obj, const char *fpath, const char *filename, size_t filesize)
{
@@ -976,8 +1035,10 @@ writeblob(git_object *obj, const char *fpath, const char *filename, size_t files
if (git_blob_is_binary((git_blob *)obj))
fputs("<p>Binary file.</p>\n", fp);
+ else if (git_blob_is_markdown_file(filename))
+ lc = writeblobmd(fp, (git_blob *)obj);
else
- lc = writeblobhtml(fp, (git_blob *)obj);
+ lc = writeblobhtml(filename, fp, (git_blob *)obj);
writefooter(fp);
checkfileerror(fp, fpath, 'w');