forked from tangled.org/core
this repo has no description

appview/blob: fix code escaping and syntax highlight

anirudh.fi e26055b7 6d13f220

verified
Changed files
+98 -36
appview
pages
templates
repo
knotserver
+43 -2
appview/pages/pages.go
···
package pages
import (
+
"bytes"
"embed"
"fmt"
+
"html"
"html/template"
"io"
"io/fs"
"log"
"net/http"
"path"
+
"path/filepath"
"strings"
+
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
+
"github.com/alecthomas/chroma/v2/lexers"
+
"github.com/alecthomas/chroma/v2/styles"
"github.com/dustin/go-humanize"
"github.com/sotangled/tangled/appview/auth"
"github.com/sotangled/tangled/appview/db"
···
"splitN": func(s, sep string, n int) []string {
return strings.SplitN(s, sep, n)
},
-
"escapeHtml": func(s string) string {
-
return template.HTMLEscapeString(s)
+
"escapeHtml": func(s string) template.HTML {
+
if s == "" {
+
return template.HTML("<br>")
+
}
+
return template.HTML(s)
+
},
+
"unescapeHtml": func(s string) string {
+
return html.UnescapeString(s)
},
"nl2br": func(text string) template.HTML {
return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1))
···
}
func (p *Pages) RepoBlob(w io.Writer, params RepoBlobParams) error {
+
if params.Lines < 5000 {
+
c := params.Contents
+
style := styles.Get("xcode")
+
formatter := chromahtml.New(
+
chromahtml.InlineCode(true),
+
chromahtml.WithLineNumbers(true),
+
chromahtml.WithLinkableLineNumbers(true, "L"),
+
chromahtml.Standalone(false),
+
)
+
+
lexer := lexers.Get(filepath.Base(params.Path))
+
if lexer == nil {
+
lexer = lexers.Fallback
+
}
+
+
iterator, err := lexer.Tokenise(nil, c)
+
if err != nil {
+
return fmt.Errorf("chroma tokenize: %w", err)
+
}
+
+
var code bytes.Buffer
+
err = formatter.Format(&code, style, iterator)
+
if err != nil {
+
return fmt.Errorf("chroma format: %w", err)
+
}
+
+
params.Contents = code.String()
+
}
+
params.Active = "overview"
return p.executeRepo("repo/blob", w, params)
}
+47 -32
appview/pages/templates/repo/blob.html
···
{{ define "repoContent" }}
-
{{ $lines := split .Contents }}
-
{{ $tot_lines := len $lines }}
-
{{ $tot_chars := len (printf "%d" $tot_lines) }}
-
{{ $code_number_style := "text-gray-400 left-0 bg-white text-right mr-2 select-none" }}
-
{{ $linkstyle := "no-underline hover:underline" }}
-
<div class="pb-2 text-base">
-
<div class="flex justify-between">
-
<div id="breadcrumbs">
-
{{ range $idx, $value := .BreadCrumbs }}
-
{{ if ne $idx (sub (len $.BreadCrumbs) 1) }}
-
<a href="{{ index . 1}}" class="text-bold text-gray-500 {{ $linkstyle }}">{{ index . 0 }}</a> /
-
{{ else }}
-
<span class="text-bold text-gray-500">{{ index . 0 }}</span>
-
{{ end }}
-
{{ end }}
+
{{ $lines := split .Contents }}
+
{{ $tot_lines := len $lines }}
+
{{ $tot_chars := len (printf "%d" $tot_lines) }}
+
{{ $code_number_style := "text-gray-400 left-0 bg-white text-right mr-6 select-none" }}
+
{{ $linkstyle := "no-underline hover:underline" }}
+
<div class="pb-2 text-base">
+
<div class="flex justify-between">
+
<div id="breadcrumbs">
+
{{ range $idx, $value := .BreadCrumbs }}
+
{{ if ne $idx (sub (len $.BreadCrumbs) 1) }}
+
<a
+
href="{{ index . 1 }}"
+
class="text-bold text-gray-500 {{ $linkstyle }}"
+
>{{ index . 0 }}</a
+
>
+
/
+
{{ else }}
+
<span class="text-bold text-gray-500"
+
>{{ index . 0 }}</span
+
>
+
{{ end }}
+
{{ end }}
+
</div>
+
<div id="file-info">
+
{{ .Lines }} lines
+
<span class="select-none px-2 [&:before]:content-['·']"></span>
+
{{ byteFmt .SizeHint }}
+
</div>
+
</div>
</div>
-
<div id="file-info">
-
{{ .Lines }} lines
-
<span class="select-none px-2 [&:before]:content-['·']"></span>
-
{{ byteFmt .SizeHint }}
-
</div>
-
</div>
-
</div>
-
-
{{ if eq .Lines 0 }}
-
<p class="text-center text-gray-400">This file is empty.</p>
-
{{ else }}
-
-
{{ if .IsBinary }}<p class="text-center text-gray-400">This is a binary file and will not be displayed.</p>{{ else }}
-
<pre class="font-mono text-sm overflow-auto relative text-ellipsis"><code>{{ range $idx, $line := $lines }}<span class="flex">
-
<span class="{{ $code_number_style }}" style="min-width: {{$tot_chars}}ch;">{{ add $idx 1 }}</span>
-
<span class="whitespace-pre">{{ escapeHtml $line }}</span></span>{{ else }}<em class="text-gray-400">this file is empty</em>{{ end }}</code></pre>{{ end}}
+
{{ if .IsBinary }}
+
<p class="text-center text-gray-400">
+
This is a binary file and will not be displayed.
+
</p>
+
{{ else }}
+
<div class="overflow-auto relative text-ellipsis">
+
{{ range $idx, $line := $lines }}
+
<div class="flex">
+
<span
+
class="{{ $code_number_style }}"
+
style="min-width: {{ $tot_chars }}ch;"
+
>{{ add $idx 1 }}</span
+
>
+
<div class="whitespace-pre">{{ $line | escapeHtml }}</div>
+
</div>
+
{{ end }}
+
</div>
+
{{ end }}
{{ end }}
-
{{ end }}
+2
go.mod
···
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
+
github.com/alecthomas/chroma/v2 v2.15.0 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
···
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.4.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
+
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
+4
go.sum
···
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
+
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/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
···
github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
+
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
+
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+2 -2
knotserver/routes.go
···
}
bytes := []byte(contents)
-
safe := string(sanitize(bytes))
+
// safe := string(sanitize(bytes))
sizeHint := len(bytes)
resp := types.RepoBlobResponse{
Ref: ref,
-
Contents: string(safe),
+
Contents: string(bytes),
Path: treePath,
IsBinary: isBinaryFile,
SizeHint: uint64(sizeHint),