From c38ea95aae2fc9320c8e1a350421d5751fee462b Mon Sep 17 00:00:00 2001
From: Sarah Jamie Lewis <sarah@openprivacy.ca>
Date: Sun, 16 Feb 2025 10:43:11 -0800
Subject: [PATCH 1/1] Add Syntax Highlighting with Chroma

---
 gen/generate.go            | 42 ++++++++++++++++++++++++++++++--------
 go.mod                     |  3 +++
 go.sum                     | 15 ++++++++++++++
 static/css/custom.css      | 15 ++++++++++++++
 templates/listing.tpl.html |  8 ++------
 5 files changed, 69 insertions(+), 14 deletions(-)

diff --git a/gen/generate.go b/gen/generate.go
index 5ed7ee7..69b7b73 100644
--- a/gen/generate.go
+++ b/gen/generate.go
@@ -1,18 +1,22 @@
 package gen
 
 import (
+	"bytes"
 	"fmt"
+	"html/template"
 	"os/exec"
 	"senary/common"
 	"slices"
 
-	"os"
-	"path/filepath"
-	"strings"
-
+	htmlform "github.com/alecthomas/chroma/v2/formatters/html"
+	"github.com/alecthomas/chroma/v2/lexers"
+	"github.com/alecthomas/chroma/v2/styles"
 	"github.com/go-git/go-git/v5"
 	"github.com/go-git/go-git/v5/plumbing"
 	"github.com/go-git/go-git/v5/plumbing/object"
+	"os"
+	"path/filepath"
+	"strings"
 )
 
 type StaticBase struct {
@@ -43,7 +47,7 @@ type StaticCommit struct {
 type StaticListing struct {
 	StaticBase
 	FileName string
-	Contents []string
+	Contents template.HTML
 }
 
 func GenerateRepoPages(config *common.Config, repo string) error {
@@ -83,11 +87,33 @@ func GenerateRepoPages(config *common.Config, repo string) error {
 
 			} else {
 				data, _ := f.Contents()
-				lines := strings.Split(data, "\n")
+
 				file, _ := os.Create(staticPath + ".html")
 				defer file.Close()
 
-				config.Templates.ExecuteTemplate(file, "listing.tpl.html", StaticListing{StaticBase: StaticBase{Repo: filepath.Base(repo)}, FileName: f.Name, Contents: lines})
+				langLexer := lexers.Match(f.Name)
+				if langLexer == nil {
+					langLexer = lexers.Analyse(data)
+					if langLexer == nil {
+						langLexer = lexers.Fallback
+					}
+				}
+				style := styles.Get("dracula")
+				if style == nil {
+					style = styles.Fallback
+				}
+				formatter := htmlform.New(htmlform.Standalone(false), htmlform.LineNumbersInTable(true), htmlform.WithLineNumbers(true), htmlform.WithLinkableLineNumbers(true, ""))
+
+				iterator, err := langLexer.Tokenise(nil, string(strings.TrimSpace(data)))
+				var doc bytes.Buffer
+				formatter.Format(&doc, style, iterator)
+				// lines := strings.Split(doc.String(), "\n")
+				// htmlLines := []template.HTML{}
+				// for _, line := range lines {
+				// 	htmlLines = append(htmlLines, template.HTML(line))
+				// }
+
+				config.Templates.ExecuteTemplate(file, "listing.tpl.html", StaticListing{StaticBase: StaticBase{Repo: filepath.Base(repo)}, FileName: f.Name, Contents: template.HTML(doc.String())})
 				if err != nil {
 					fmt.Printf("[error] %v\n", err)
 				}
@@ -100,7 +126,7 @@ func GenerateRepoPages(config *common.Config, repo string) error {
 					os.MkdirAll(dir, 0755)
 					file, _ := os.Create(staticPath + ".html")
 					defer file.Close()
-					config.Templates.ExecuteTemplate(file, "listing.tpl.html", StaticListing{StaticBase: StaticBase{Repo: filepath.Base(repo)}, FileName: f.Name, Contents: lines})
+					config.Templates.ExecuteTemplate(file, "listing.tpl.html", StaticListing{StaticBase: StaticBase{Repo: filepath.Base(repo)}, FileName: f.Name, Contents: template.HTML(doc.String())})
 				}
 
 			}
diff --git a/go.mod b/go.mod
index 066f87e..d19ed6d 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,8 @@ go 1.23.2
 require github.com/go-git/go-git/v5 v5.13.2
 
 require (
+	github.com/alecthomas/chroma v0.10.0
+	github.com/alecthomas/chroma/v2 v2.15.0
 	github.com/bluekeyes/go-gitdiff v0.8.1
 	go.hacdias.com/indielib v0.4.3
 )
@@ -15,6 +17,7 @@ require (
 	github.com/ProtonMail/go-crypto v1.1.5 // indirect
 	github.com/cloudflare/circl v1.5.0 // indirect
 	github.com/cyphar/filepath-securejoin v0.3.6 // indirect
+	github.com/dlclark/regexp2 v1.11.4 // indirect
 	github.com/emirpasic/gods v1.18.1 // indirect
 	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
 	github.com/go-git/go-billy/v5 v5.6.2 // indirect
diff --git a/go.sum b/go.sum
index d83e3fb..18d0da0 100644
--- a/go.sum
+++ b/go.sum
@@ -5,6 +5,14 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
 github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
 github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
 github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
+github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
+github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
+github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
+github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
+github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc=
+github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio=
+github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
+github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
 github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
@@ -19,6 +27,9 @@ github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGL
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
+github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
+github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
 github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
@@ -38,6 +49,8 @@ github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUv
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
+github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
 github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
@@ -67,6 +80,7 @@ github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=
@@ -110,6 +124,7 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 willnorris.com/go/microformats v1.2.1-0.20240301064101-b5d1b9d2120e h1:TRIOwo0NxN4KVSgYlYmiQktd9I96YgZ3942/JVzhwTM=
diff --git a/static/css/custom.css b/static/css/custom.css
index c14c262..afd8a43 100644
--- a/static/css/custom.css
+++ b/static/css/custom.css
@@ -82,4 +82,19 @@ mark.info {
 
 .clone {
   font-size:0.8em;
+}
+
+pre {
+  border-radius: 0px;
+  padding:10px;
+}
+
+pre > code{
+  display: initial;
+  line-height: normal;
+  padding:0px;
+}
+
+td {
+  line-height: normal;
 }
\ No newline at end of file
diff --git a/templates/listing.tpl.html b/templates/listing.tpl.html
index 7e22f1b..594c8c4 100644
--- a/templates/listing.tpl.html
+++ b/templates/listing.tpl.html
@@ -1,6 +1,2 @@
-
-<pre class="code">
-
-{{range $line := .Contents}}<code>{{$line}}</code>{{end}}
- 
-</pre>
+<div class="listing">{{.Contents}}
+</div>
\ No newline at end of file
-- 
2.43.0

