forked from tangled.org/core
Monorepo for Tangled

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
··· 1 1 package pages 2 2 3 3 import ( 4 + "bytes" 4 5 "embed" 5 6 "fmt" 7 + "html" 6 8 "html/template" 7 9 "io" 8 10 "io/fs" 9 11 "log" 10 12 "net/http" 11 13 "path" 14 + "path/filepath" 12 15 "strings" 13 16 17 + chromahtml "github.com/alecthomas/chroma/v2/formatters/html" 18 + "github.com/alecthomas/chroma/v2/lexers" 19 + "github.com/alecthomas/chroma/v2/styles" 14 20 "github.com/dustin/go-humanize" 15 21 "github.com/sotangled/tangled/appview/auth" 16 22 "github.com/sotangled/tangled/appview/db" ··· 78 84 "splitN": func(s, sep string, n int) []string { 79 85 return strings.SplitN(s, sep, n) 80 86 }, 81 - "escapeHtml": func(s string) string { 82 - return template.HTMLEscapeString(s) 87 + "escapeHtml": func(s string) template.HTML { 88 + if s == "" { 89 + return template.HTML("<br>") 90 + } 91 + return template.HTML(s) 92 + }, 93 + "unescapeHtml": func(s string) string { 94 + return html.UnescapeString(s) 83 95 }, 84 96 "nl2br": func(text string) template.HTML { 85 97 return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1)) ··· 351 363 } 352 364 353 365 func (p *Pages) RepoBlob(w io.Writer, params RepoBlobParams) error { 366 + if params.Lines < 5000 { 367 + c := params.Contents 368 + style := styles.Get("xcode") 369 + formatter := chromahtml.New( 370 + chromahtml.InlineCode(true), 371 + chromahtml.WithLineNumbers(true), 372 + chromahtml.WithLinkableLineNumbers(true, "L"), 373 + chromahtml.Standalone(false), 374 + ) 375 + 376 + lexer := lexers.Get(filepath.Base(params.Path)) 377 + if lexer == nil { 378 + lexer = lexers.Fallback 379 + } 380 + 381 + iterator, err := lexer.Tokenise(nil, c) 382 + if err != nil { 383 + return fmt.Errorf("chroma tokenize: %w", err) 384 + } 385 + 386 + var code bytes.Buffer 387 + err = formatter.Format(&code, style, iterator) 388 + if err != nil { 389 + return fmt.Errorf("chroma format: %w", err) 390 + } 391 + 392 + params.Contents = code.String() 393 + } 394 + 354 395 params.Active = "overview" 355 396 return p.executeRepo("repo/blob", w, params) 356 397 }
+47 -32
appview/pages/templates/repo/blob.html
··· 1 1 {{ define "repoContent" }} 2 - {{ $lines := split .Contents }} 3 - {{ $tot_lines := len $lines }} 4 - {{ $tot_chars := len (printf "%d" $tot_lines) }} 5 - {{ $code_number_style := "text-gray-400 left-0 bg-white text-right mr-2 select-none" }} 6 - {{ $linkstyle := "no-underline hover:underline" }} 7 - <div class="pb-2 text-base"> 8 - <div class="flex justify-between"> 9 - <div id="breadcrumbs"> 10 - {{ range $idx, $value := .BreadCrumbs }} 11 - {{ if ne $idx (sub (len $.BreadCrumbs) 1) }} 12 - <a href="{{ index . 1}}" class="text-bold text-gray-500 {{ $linkstyle }}">{{ index . 0 }}</a> / 13 - {{ else }} 14 - <span class="text-bold text-gray-500">{{ index . 0 }}</span> 15 - {{ end }} 16 - {{ end }} 2 + {{ $lines := split .Contents }} 3 + {{ $tot_lines := len $lines }} 4 + {{ $tot_chars := len (printf "%d" $tot_lines) }} 5 + {{ $code_number_style := "text-gray-400 left-0 bg-white text-right mr-6 select-none" }} 6 + {{ $linkstyle := "no-underline hover:underline" }} 7 + <div class="pb-2 text-base"> 8 + <div class="flex justify-between"> 9 + <div id="breadcrumbs"> 10 + {{ range $idx, $value := .BreadCrumbs }} 11 + {{ if ne $idx (sub (len $.BreadCrumbs) 1) }} 12 + <a 13 + href="{{ index . 1 }}" 14 + class="text-bold text-gray-500 {{ $linkstyle }}" 15 + >{{ index . 0 }}</a 16 + > 17 + / 18 + {{ else }} 19 + <span class="text-bold text-gray-500" 20 + >{{ index . 0 }}</span 21 + > 22 + {{ end }} 23 + {{ end }} 24 + </div> 25 + <div id="file-info"> 26 + {{ .Lines }} lines 27 + <span class="select-none px-2 [&:before]:content-['·']"></span> 28 + {{ byteFmt .SizeHint }} 29 + </div> 30 + </div> 17 31 </div> 18 - <div id="file-info"> 19 - {{ .Lines }} lines 20 - <span class="select-none px-2 [&:before]:content-['·']"></span> 21 - {{ byteFmt .SizeHint }} 22 - </div> 23 - </div> 24 - </div> 25 - 26 - {{ if eq .Lines 0 }} 27 - <p class="text-center text-gray-400">This file is empty.</p> 28 - {{ else }} 29 - 30 - {{ if .IsBinary }}<p class="text-center text-gray-400">This is a binary file and will not be displayed.</p>{{ else }} 31 - <pre class="font-mono text-sm overflow-auto relative text-ellipsis"><code>{{ range $idx, $line := $lines }}<span class="flex"> 32 - <span class="{{ $code_number_style }}" style="min-width: {{$tot_chars}}ch;">{{ add $idx 1 }}</span> 33 - <span class="whitespace-pre">{{ escapeHtml $line }}</span></span>{{ else }}<em class="text-gray-400">this file is empty</em>{{ end }}</code></pre>{{ end}} 32 + {{ if .IsBinary }} 33 + <p class="text-center text-gray-400"> 34 + This is a binary file and will not be displayed. 35 + </p> 36 + {{ else }} 37 + <div class="overflow-auto relative text-ellipsis"> 38 + {{ range $idx, $line := $lines }} 39 + <div class="flex"> 40 + <span 41 + class="{{ $code_number_style }}" 42 + style="min-width: {{ $tot_chars }}ch;" 43 + >{{ add $idx 1 }}</span 44 + > 45 + <div class="whitespace-pre">{{ $line | escapeHtml }}</div> 46 + </div> 47 + {{ end }} 48 + </div> 49 + {{ end }} 34 50 {{ end }} 35 - {{ end }}
+2
go.mod
··· 30 30 github.com/Microsoft/go-winio v0.6.2 // indirect 31 31 github.com/ProtonMail/go-crypto v1.0.0 // indirect 32 32 github.com/acomagu/bufpipe v1.0.4 // indirect 33 + github.com/alecthomas/chroma/v2 v2.15.0 // indirect 33 34 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect 34 35 github.com/aymerick/douceur v0.2.0 // indirect 35 36 github.com/beorn7/perks v1.0.1 // indirect ··· 39 40 github.com/cespare/xxhash/v2 v2.3.0 // indirect 40 41 github.com/cloudflare/circl v1.4.0 // indirect 41 42 github.com/davecgh/go-spew v1.1.1 // indirect 43 + github.com/dlclark/regexp2 v1.11.5 // indirect 42 44 github.com/emirpasic/gods v1.18.1 // indirect 43 45 github.com/felixge/httpsnoop v1.0.4 // indirect 44 46 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
+4
go.sum
··· 9 9 github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= 10 10 github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= 11 11 github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= 12 + github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc= 13 + github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio= 12 14 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= 13 15 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= 14 16 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= ··· 54 56 github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU= 55 57 github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= 56 58 github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 59 + github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= 60 + github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 57 61 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 58 62 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 59 63 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+2 -2
knotserver/routes.go
··· 216 216 } 217 217 218 218 bytes := []byte(contents) 219 - safe := string(sanitize(bytes)) 219 + // safe := string(sanitize(bytes)) 220 220 sizeHint := len(bytes) 221 221 222 222 resp := types.RepoBlobResponse{ 223 223 Ref: ref, 224 - Contents: string(safe), 224 + Contents: string(bytes), 225 225 Path: treePath, 226 226 IsBinary: isBinaryFile, 227 227 SizeHint: uint64(sizeHint),