loading up the forgejo repo on tangled to test page performance
at forgejo 157 lines 3.8 kB view raw
1// Copyright 2018 The Gitea Authors. All rights reserved. 2// SPDX-License-Identifier: MIT 3 4package markup 5 6import ( 7 "bufio" 8 "html" 9 "io" 10 "regexp" 11 "strconv" 12 13 "forgejo.org/modules/csv" 14 "forgejo.org/modules/markup" 15 "forgejo.org/modules/setting" 16 "forgejo.org/modules/translation" 17 "forgejo.org/modules/util" 18) 19 20func init() { 21 markup.RegisterRenderer(Renderer{}) 22} 23 24// Renderer implements markup.Renderer for csv files 25type Renderer struct{} 26 27// Name implements markup.Renderer 28func (Renderer) Name() string { 29 return "csv" 30} 31 32// Extensions implements markup.Renderer 33func (Renderer) Extensions() []string { 34 return []string{".csv", ".tsv"} 35} 36 37// SanitizerRules implements markup.Renderer 38func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule { 39 return []setting.MarkupSanitizerRule{ 40 {Element: "table", AllowAttr: "class", Regexp: regexp.MustCompile(`^data-table$`)}, 41 {Element: "th", AllowAttr: "class", Regexp: regexp.MustCompile(`^line-num$`)}, 42 {Element: "td", AllowAttr: "class", Regexp: regexp.MustCompile(`^line-num$`)}, 43 } 44} 45 46func writeField(w io.Writer, element, class, field string) error { 47 if _, err := io.WriteString(w, "<"); err != nil { 48 return err 49 } 50 if _, err := io.WriteString(w, element); err != nil { 51 return err 52 } 53 if len(class) > 0 { 54 if _, err := io.WriteString(w, " class=\""); err != nil { 55 return err 56 } 57 if _, err := io.WriteString(w, class); err != nil { 58 return err 59 } 60 if _, err := io.WriteString(w, "\""); err != nil { 61 return err 62 } 63 } 64 if _, err := io.WriteString(w, ">"); err != nil { 65 return err 66 } 67 if _, err := io.WriteString(w, html.EscapeString(field)); err != nil { 68 return err 69 } 70 if _, err := io.WriteString(w, "</"); err != nil { 71 return err 72 } 73 if _, err := io.WriteString(w, element); err != nil { 74 return err 75 } 76 _, err := io.WriteString(w, ">") 77 return err 78} 79 80// Render implements markup.Renderer 81func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { 82 tmpBlock := bufio.NewWriter(output) 83 maxSize := setting.UI.CSV.MaxFileSize 84 maxRows := setting.UI.CSV.MaxRows 85 86 if maxSize != 0 { 87 input = io.LimitReader(input, maxSize+1) 88 } 89 90 rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, input) 91 if err != nil { 92 return err 93 } 94 if _, err := tmpBlock.WriteString(`<table class="data-table">`); err != nil { 95 return err 96 } 97 98 row := 0 99 for { 100 fields, err := rd.Read() 101 if err == io.EOF || (row >= maxRows && maxRows != 0) { 102 break 103 } 104 if err != nil { 105 continue 106 } 107 108 if _, err := tmpBlock.WriteString("<tr>"); err != nil { 109 return err 110 } 111 element := "td" 112 if row == 0 { 113 element = "th" 114 } 115 if err := writeField(tmpBlock, element, "line-num", strconv.Itoa(row+1)); err != nil { 116 return err 117 } 118 for _, field := range fields { 119 if err := writeField(tmpBlock, element, "", field); err != nil { 120 return err 121 } 122 } 123 if _, err := tmpBlock.WriteString("</tr>"); err != nil { 124 return err 125 } 126 127 row++ 128 } 129 130 if _, err = tmpBlock.WriteString("</table>"); err != nil { 131 return err 132 } 133 134 // Check if maxRows or maxSize is reached, and if true, warn. 135 if (row >= maxRows && maxRows != 0) || (rd.InputOffset() >= maxSize && maxSize != 0) { 136 warn := `<table class="data-table"><tr><td>` 137 rawLink := ` <a href="` + ctx.Links.RawLink() + `/` + util.PathEscapeSegments(ctx.RelativePath) + `">` 138 139 // Try to get the user translation 140 if locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale); ok { 141 warn += locale.TrString("repo.file_too_large") 142 rawLink += locale.TrString("repo.file_view_raw") 143 } else { 144 warn += "The file is too large to be shown." 145 rawLink += "View Raw" 146 } 147 148 warn += rawLink + `</a></td></tr></table>` 149 150 // Write the HTML string to the output 151 if _, err := tmpBlock.WriteString(warn); err != nil { 152 return err 153 } 154 } 155 156 return tmpBlock.Flush() 157}