commit 010cff4221ed23f5f54d3aa8c52dba9b15378e33
parent 7a4c864ad9ebef818f57a75b5606ec70d3c5e28b
Author: Thomas Vigouroux <thomas.vigouroux@univ-grenoble-alpes.fr>
Date: Thu, 11 Apr 2024 17:10:58 +0200
refactor(template): use template fragment instead of being dumb
This is inspired by https://htmx.org/essays/template-fragments/
Diffstat:
10 files changed, 191 insertions(+), 228 deletions(-)
diff --git a/file.go b/file.go
@@ -1,115 +1,110 @@
package main
import (
- // "github.com/gorilla/mux"
+ "github.com/gorilla/mux"
// "fmt"
- "github.com/libgit2/git2go/v31"
"html/template"
- // "log"
+ "github.com/go-git/go-git/v5/plumbing"
+ "log"
"net/http"
- // "path/filepath"
- // "strings"
+ "path/filepath"
+ "strings"
// Chroma
- // chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
- // "github.com/alecthomas/chroma/v2/lexers"
- // "github.com/alecthomas/chroma/v2/styles"
+ chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
+ "github.com/alecthomas/chroma/v2/lexers"
+ "github.com/alecthomas/chroma/v2/styles"
)
type FilePageData struct {
Name string
Content template.HTML
- Ref *git.Oid
+ Ref string
}
func getFile(w http.ResponseWriter, r *http.Request) {
- // logger := log.Default()
- // vars := mux.Vars(r)
- // reponame := vars["repository"]
- //
- // repo, err := checkRepo(reponame)
- // if err != nil {
- // w.WriteHeader(http.StatusNotFound)
- // return
- // }
- //
- // ref, err := repo.RevparseSingle(vars["ref"])
- // if err != nil {
- // w.WriteHeader(http.StatusNotFound)
- // return
- // }
- //
- // commit, err := ref.AsCommit()
- // if err != nil {
- // w.WriteHeader(http.StatusNotFound)
- // return
- // }
- //
- // tree, err := commit.Tree()
- // if err != nil {
- // logger.Fatalf("Could not get tree")
- // }
- //
- // // Get the path of the entry
- // path := r.RequestURI[len("/repos/") + len(reponame) + len("/file/") + len(vars["ref"]) + 1:len(r.RequestURI)]
- // logger.Printf("Trying to get file: %s (%s)", path, r.RequestURI)
- // entry, err := tree.EntryByPath(path)
- // if err != nil || entry.Type != git.ObjectBlob {
- // w.WriteHeader(http.StatusNotFound)
- // return
- // }
- //
- // blob, err := repo.LookupBlob(entry.Id)
- // if err != nil {
- // logger.Fatalf("Could not find file")
- // }
- //
- // lexer := lexers.Match(path)
- // if lexer == nil {
- // lexer = lexers.Fallback
- // }
- //
- // if err != nil {
- // logger.Fatal(err)
- // }
- //
- // var formatter = chromahtml.New(
- // chromahtml.WithLineNumbers(true),
- // chromahtml.WithClasses(true),
- // chromahtml.WithLinkableLineNumbers(true, ""),
- // )
- //
- // if formatter == nil {
- // logger.Fatal("Could not get html formatter")
- // }
- //
- // iterator, err := lexer.Tokenise(nil, fmt.Sprintf("%s", blob.Contents()))
- // if err != nil {
- // w.WriteHeader(http.StatusInternalServerError)
- // return
- // }
- //
- // sbuilder := new(strings.Builder)
- // formatter.Format(sbuilder, styles.Fallback, iterator)
- //
- // w.WriteHeader(http.StatusOK)
- //
- // tmpl := template.Must(template.ParseFiles(
- // filepath.Join("templates", "file.html"),
- // filepath.Join("templates", "wrap.html"),
- // ))
- //
- // _, ishtmx := r.Header["Hx-Request"]
- //
- // data := FilePageData {
- // Name: reponame,
- // Content: template.HTML(sbuilder.String()),
- // Ref: ref.Id(),
- // }
- //
- // if ishtmx {
- // tmpl.ExecuteTemplate(w, "file_content", data)
- // } else {
- // tmpl.ExecuteTemplate(w, "file_full", data)
- // }
+ logger := log.Default()
+ vars := mux.Vars(r)
+ reponame := vars["repository"]
+
+ repo, err := checkRepo(reponame)
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
+
+ hash, err := repo.ResolveRevision(plumbing.Revision(vars["ref"]))
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
+
+ commit, err := repo.CommitObject(*hash)
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
+
+ // Get the path of the entry
+ path := r.RequestURI[len("/repos/") + len(reponame) + len("/file/") + len(vars["ref"]) + 1:len(r.RequestURI)]
+ logger.Printf("Trying to get file: %s (%s)", path, r.RequestURI)
+ entry, err := commit.File(path)
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
+
+ content, err := entry.Contents()
+ if err != nil {
+ logger.Fatal(err)
+ }
+
+ lexer := lexers.Match(path)
+ if lexer == nil {
+ lexer = lexers.Fallback
+ }
+
+ if err != nil {
+ logger.Fatal(err)
+ }
+
+ var formatter = chromahtml.New(
+ chromahtml.WithLineNumbers(true),
+ chromahtml.WithClasses(true),
+ chromahtml.WithLinkableLineNumbers(true, ""),
+ )
+
+ if formatter == nil {
+ logger.Fatal("Could not get html formatter")
+ }
+
+ iterator, err := lexer.Tokenise(nil, content)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ sbuilder := new(strings.Builder)
+ formatter.Format(sbuilder, styles.Fallback, iterator)
+
+ w.WriteHeader(http.StatusOK)
+
+ tmpl := template.Must(template.ParseFiles(
+ filepath.Join("templates", "file.html"),
+ filepath.Join("templates", "wrap.html"),
+ ))
+
+ _, ishtmx := r.Header["Hx-Request"]
+
+ data := FilePageData {
+ Name: reponame,
+ Content: template.HTML(sbuilder.String()),
+ Ref: commit.Hash.String(),
+ }
+
+ if ishtmx {
+ tmpl.ExecuteTemplate(w, "file_content", data)
+ } else {
+ tmpl.Execute(w, data)
+ }
}
diff --git a/gitshow.go b/gitshow.go
@@ -35,7 +35,7 @@ var reporouter *mux.Router
// Got from mux repository
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- log.Println(r.Method, r.RequestURI)
+ log.Println(r.Method, r.RequestURI, r.Header["Hx-Request"])
next.ServeHTTP(w, r)
})
}
diff --git a/log.go b/log.go
@@ -69,6 +69,6 @@ func getLog(w http.ResponseWriter, r *http.Request) {
if ishtmx {
tmpl.ExecuteTemplate(w, "log_content", data)
} else {
- tmpl.ExecuteTemplate(w, "log_full", data)
+ tmpl.Execute(w, data)
}
}
diff --git a/repo.go b/repo.go
@@ -78,6 +78,6 @@ func getRepo(w http.ResponseWriter, r *http.Request) {
if ishtmx {
tmpl.ExecuteTemplate(w, "repo_content", data)
} else {
- tmpl.ExecuteTemplate(w, "repo_full", data)
+ tmpl.Execute(w, data)
}
}
diff --git a/templates/commit.html b/templates/commit.html
@@ -1,4 +1,6 @@
-{{define "file_content"}}
+{{template "header" .}}
+
+{{block "file_content" .}}
<div id="tab-list" role="tablist" hx-push-url="true">
<button hx-get="/repos/{{.Name}}">about</button>
<button hx-get="/repos/{{.Name}}/log">log</button>
@@ -7,8 +9,4 @@
{{.Content}}
{{end}}
-{{define "file_full"}}
-{{template "header" .}}
-{{template "file_content" .}}
{{template "footer" .}}
-{{end}}
diff --git a/templates/file.html b/templates/file.html
@@ -1,14 +1,12 @@
-{{define "file_content"}}
+{{template "header" .}}
+
+{{block "file_content" .}}
<div id="tab-list" role="tablist" hx-push-url="true">
<button hx-get="/repos/{{.Name}}">about</button>
<button hx-get="/repos/{{.Name}}/log">log</button>
- <button hx-get="/repos/{{.Name}}/tree/{{.Ref.String}}">tree</button>
+ <button hx-get="/repos/{{.Name}}/tree/{{.Ref}}/">tree</button>
</div>
{{.Content}}
{{end}}
-{{define "file_full"}}
-{{template "header" .}}
-{{template "file_content" .}}
{{template "footer" .}}
-{{end}}
diff --git a/templates/log.html b/templates/log.html
@@ -1,4 +1,6 @@
-{{define "log_content"}}
+{{template "header" .}}
+
+{{block "log_content" .}}
<div id="tab-list" role="tablist" hx-push-url="true">
<button hx-get="/repos/{{.Name}}">about</button>
<button hx-get="/repos/{{.Name}}/log" class="selected">log</button>
@@ -17,8 +19,4 @@
</table>
{{end}}
-{{define "log_full"}}
-{{template "header" .}}
-{{template "log_content" .}}
{{template "footer" .}}
-{{end}}
diff --git a/templates/repo.html b/templates/repo.html
@@ -1,4 +1,6 @@
-{{define "repo_content"}}
+{{template "header" .}}
+
+{{block "repo_content" .}}
<div id="tab-list" role="tablist" hx-push-url="true">
<button hx-get="/repos/{{.Name}}" class="selected">about</button>
<button hx-get="/repos/{{.Name}}/log">log</button>
@@ -9,8 +11,4 @@
</pre>
{{end}}
-{{define "repo_full"}}
-{{template "header" .}}
-{{template "repo_content" .}}
{{template "footer" .}}
-{{end}}
diff --git a/templates/tree.html b/templates/tree.html
@@ -1,11 +1,13 @@
-{{define "tree_content"}}
+{{template "header" .}}
+
+{{block "tree_content" .}}
<div id="tab-list" role="tablist" hx-push-url="true">
<button hx-get="/repos/{{.Name}}">about</button>
<button hx-get="/repos/{{.Name}}/log">log</button>
- <button hx-get="/repos/{{.Name}}/tree/{{.Ref.String}}" class="selected">tree</button>
+ <button hx-get="/repos/{{.Name}}/tree/{{.Ref}}/" class="selected">tree</button>
</div>
{{$repoName:=.Name}}
-{{$branchName:=.Ref.String}}
+{{$branchName:=.Ref}}
<table>
{{range .Entries}}
<tr>
@@ -16,8 +18,4 @@
</table>
{{end}}
-{{define "tree_full"}}
-{{template "header" .}}
-{{template "tree_content" .}}
{{template "footer" .}}
-{{end}}
diff --git a/tree.go b/tree.go
@@ -1,115 +1,93 @@
package main
import (
- // "github.com/gorilla/mux"
+ "github.com/gorilla/mux"
// "fmt"
- "github.com/libgit2/git2go/v31"
- // "html/template"
- // "log"
+ // "github.com/libgit2/git2go/v31"
+ "html/template"
+ "log"
+ "github.com/go-git/go-git/v5/plumbing/object"
+ "github.com/go-git/go-git/v5/plumbing"
"net/http"
- // "path/filepath"
+ "path/filepath"
)
type TreePageData struct {
Name string
- Entries []*git.TreeEntry
- Ref *git.Oid
+ Ref string
+ Entries []object.TreeEntry
}
func getTree(w http.ResponseWriter, r *http.Request) {
- // logger := log.Default()
- // vars := mux.Vars(r)
- // reponame := vars["repository"]
- //
- // repo, err := checkRepo(reponame)
- // if err != nil {
- // w.WriteHeader(http.StatusNotFound)
- // return
- // }
- //
- // var commit *git.Commit
- // refname, present := vars["ref"]
- //
- // if !present {
- // head, err := repo.Head()
- // if err != nil {
- // // TODO: handle no head
- // w.WriteHeader(http.StatusInternalServerError)
- // return
- // }
- //
- // branch := head.Branch()
- // // Now get the corresponding commit
- // ref, err := branch.Resolve()
- // if err != nil {
- // logger.Fatalf("Malformed repository")
- // }
- //
- // commit, err = repo.LookupCommit(ref.Target())
- // if err != nil {
- // logger.Fatalf("Could not get commit")
- // }
- // } else {
- // obj, err := repo.RevparseSingle(refname)
- // if err != nil {
- // w.WriteHeader(http.StatusNotFound)
- // return
- // }
- // commit, err = obj.AsCommit()
- // if err != nil {
- // w.WriteHeader(http.StatusNotFound)
- // return
- // }
- // }
- // logger.Printf("Called %s: ref to %s", refname, commit.Id().String())
- //
- // tree, err := commit.Tree()
- // if err != nil {
- // logger.Fatalf("Could not get tree")
- // }
- //
- // url, err := reporouter.Get("tree").URL("repository", reponame, "ref", commit.Id().String())
+ logger := log.Default()
+ vars := mux.Vars(r)
+ reponame := vars["repository"]
+
+ repo, err := checkRepo(reponame)
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
+
+ var revstr string
+ revstr, present := vars["ref"]
+
+ if !present {
+ head, err := repo.Head()
+ if err != nil {
+ // TODO: handle no head
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ revstr = head.Name().String()
+ }
+
+ logger.Printf("In repo %s, search for %s", reponame, revstr)
+
+ hash, err := repo.ResolveRevision(plumbing.Revision(revstr))
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
+
+ commit, err := repo.CommitObject(*hash)
+ if err != nil {
+ logger.Fatal(err)
+ }
+ logger.Printf("Called %s: ref to %s", revstr, commit.Hash.String())
+
+ tree, err := commit.Tree()
+ if err != nil {
+ logger.Fatalf("Could not get tree")
+ }
+
+ // url, err := reporouter.Get("tree").URL("repository", reponame, "ref", commit.Hash.String())
// if err != nil {
// logger.Fatalf("Could not generate URL")
// }
- //
- // url.Host = r.URL.Host
- // url.Scheme = r.URL.Scheme
- //
- // w.Header().Add("HX-Push-Url", url.String())
- // w.WriteHeader(http.StatusOK)
- //
- // tmpl := template.Must(template.New("tree_root").Funcs(template.FuncMap{
- // "isFile": func(obj *git.TreeEntry) bool {
- // blob, err := repo.LookupBlob(obj.Id)
- // if err != nil {
- // return false
- // }
- //
- //
- // return obj.Type == git.ObjectBlob && !blob.IsBinary()
- // },
- // }).ParseFiles(
- // filepath.Join("templates", "tree.html"),
- // filepath.Join("templates", "wrap.html"),
- // ))
- //
- // _, ishtmx := r.Header["Hx-Request"]
- //
- // var entries []*git.TreeEntry = make([]*git.TreeEntry, 0)
- // for i := uint64(0); i < tree.EntryCount(); i++ {
- // entries = append(entries, tree.EntryByIndex(i))
- // }
- //
- // data := TreePageData{
- // Name: reponame,
- // Entries: entries,
- // Ref: commit.Id(),
- // }
- //
- // if ishtmx {
- // tmpl.ExecuteTemplate(w, "tree_content", data)
- // } else {
- // tmpl.ExecuteTemplate(w, "tree_full", data)
- // }
+
+ w.WriteHeader(http.StatusOK)
+
+ tmpl := template.Must(template.New("tree_root").Funcs(template.FuncMap{
+ "isFile": func(obj *object.TreeEntry) bool {
+ return obj.Mode.IsFile()
+ },
+ }).ParseFiles(
+ filepath.Join("templates", "tree.html"),
+ filepath.Join("templates", "wrap.html"),
+ ))
+
+ _, ishtmx := r.Header["Hx-Request"]
+
+ data := TreePageData{
+ Name: reponame,
+ Entries: tree.Entries,
+ Ref: commit.Hash.String(),
+ }
+
+ if ishtmx {
+ tmpl.ExecuteTemplate(w, "tree_content", data)
+ } else {
+ tmpl.Execute(w, data)
+ }
}