Skip to content

Commit 9d99f6a

Browse files
lunnyzeripath
andauthored
Refactor renders (#15175)
* Refactor renders * Some performance optimization * Fix comment * Transform reader * Fix csv test * Fix test * Fix tests * Improve optimaziation * Fix test * Fix test * Detect file encoding with reader * Improve optimaziation * reduce memory usage * improve code * fix build * Fix test * Fix for go1.15 * Fix render * Fix comment * Fix lint * Fix test * Don't use NormalEOF when unnecessary * revert change on util.go * Apply suggestions from code review Co-authored-by: zeripath <[email protected]> * rename function * Take NormalEOF back Co-authored-by: zeripath <[email protected]>
1 parent c9cc669 commit 9d99f6a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1026
-626
lines changed

contrib/pr/checkout.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func runPR() {
114114

115115
log.Printf("[PR] Setting up router\n")
116116
//routers.GlobalInit()
117-
external.RegisterParsers()
117+
external.RegisterRenderers()
118118
markup.Init()
119119
c := routes.NormalRoutes()
120120

models/issue_comment.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
"code.gitea.io/gitea/modules/git"
1818
"code.gitea.io/gitea/modules/log"
19+
"code.gitea.io/gitea/modules/markup"
1920
"code.gitea.io/gitea/modules/markup/markdown"
2021
"code.gitea.io/gitea/modules/references"
2122
"code.gitea.io/gitea/modules/structs"
@@ -1178,8 +1179,13 @@ func findCodeComments(e Engine, opts FindCommentsOptions, issue *Issue, currentU
11781179
return nil, err
11791180
}
11801181

1181-
comment.RenderedContent = string(markdown.Render([]byte(comment.Content), issue.Repo.Link(),
1182-
issue.Repo.ComposeMetas()))
1182+
var err error
1183+
if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
1184+
URLPrefix: issue.Repo.Link(),
1185+
Metas: issue.Repo.ComposeMetas(),
1186+
}, comment.Content); err != nil {
1187+
return nil, err
1188+
}
11831189
}
11841190
return comments[:n], nil
11851191
}

models/repo.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,10 @@ func (repo *Repository) getUsersWithAccessMode(e Engine, mode AccessMode) (_ []*
863863

864864
// DescriptionHTML does special handles to description and return HTML string.
865865
func (repo *Repository) DescriptionHTML() template.HTML {
866-
desc, err := markup.RenderDescriptionHTML([]byte(repo.Description), repo.HTMLURL(), repo.ComposeMetas())
866+
desc, err := markup.RenderDescriptionHTML(&markup.RenderContext{
867+
URLPrefix: repo.HTMLURL(),
868+
Metas: repo.ComposeMetas(),
869+
}, repo.Description)
867870
if err != nil {
868871
log.Error("Failed to render description for %s (ID: %d): %v", repo.Name, repo.ID, err)
869872
return template.HTML(markup.Sanitize(repo.Description))

models/repo_generate.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
package models
66

77
import (
8+
"bufio"
9+
"bytes"
810
"strconv"
911
"strings"
1012

1113
"code.gitea.io/gitea/modules/git"
1214
"code.gitea.io/gitea/modules/log"
1315
"code.gitea.io/gitea/modules/storage"
14-
"code.gitea.io/gitea/modules/util"
1516

1617
"github.com/gobwas/glob"
1718
)
@@ -49,9 +50,9 @@ func (gt GiteaTemplate) Globs() []glob.Glob {
4950
}
5051

5152
gt.globs = make([]glob.Glob, 0)
52-
lines := strings.Split(string(util.NormalizeEOL(gt.Content)), "\n")
53-
for _, line := range lines {
54-
line = strings.TrimSpace(line)
53+
scanner := bufio.NewScanner(bytes.NewReader(gt.Content))
54+
for scanner.Scan() {
55+
line := strings.TrimSpace(scanner.Text())
5556
if line == "" || strings.HasPrefix(line, "#") {
5657
continue
5758
}

modules/charset/charset.go

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ package charset
77
import (
88
"bytes"
99
"fmt"
10+
"io"
11+
"io/ioutil"
1012
"strings"
1113
"unicode/utf8"
1214

@@ -21,6 +23,33 @@ import (
2123
// UTF8BOM is the utf-8 byte-order marker
2224
var UTF8BOM = []byte{'\xef', '\xbb', '\xbf'}
2325

26+
// ToUTF8WithFallbackReader detects the encoding of content and coverts to UTF-8 reader if possible
27+
func ToUTF8WithFallbackReader(rd io.Reader) io.Reader {
28+
var buf = make([]byte, 2048)
29+
n, err := rd.Read(buf)
30+
if err != nil {
31+
return rd
32+
}
33+
34+
charsetLabel, err := DetectEncoding(buf[:n])
35+
if err != nil || charsetLabel == "UTF-8" {
36+
return io.MultiReader(bytes.NewReader(RemoveBOMIfPresent(buf[:n])), rd)
37+
}
38+
39+
encoding, _ := charset.Lookup(charsetLabel)
40+
if encoding == nil {
41+
return io.MultiReader(bytes.NewReader(buf[:n]), rd)
42+
}
43+
44+
return transform.NewReader(
45+
io.MultiReader(
46+
bytes.NewReader(RemoveBOMIfPresent(buf[:n])),
47+
rd,
48+
),
49+
encoding.NewDecoder(),
50+
)
51+
}
52+
2453
// ToUTF8WithErr converts content to UTF8 encoding
2554
func ToUTF8WithErr(content []byte) (string, error) {
2655
charsetLabel, err := DetectEncoding(content)
@@ -49,24 +78,8 @@ func ToUTF8WithErr(content []byte) (string, error) {
4978

5079
// ToUTF8WithFallback detects the encoding of content and coverts to UTF-8 if possible
5180
func ToUTF8WithFallback(content []byte) []byte {
52-
charsetLabel, err := DetectEncoding(content)
53-
if err != nil || charsetLabel == "UTF-8" {
54-
return RemoveBOMIfPresent(content)
55-
}
56-
57-
encoding, _ := charset.Lookup(charsetLabel)
58-
if encoding == nil {
59-
return content
60-
}
61-
62-
// If there is an error, we concatenate the nicely decoded part and the
63-
// original left over. This way we won't lose data.
64-
result, n, err := transform.Bytes(encoding.NewDecoder(), content)
65-
if err != nil {
66-
return append(result, content[n:]...)
67-
}
68-
69-
return RemoveBOMIfPresent(result)
81+
bs, _ := ioutil.ReadAll(ToUTF8WithFallbackReader(bytes.NewReader(content)))
82+
return bs
7083
}
7184

7285
// ToUTF8 converts content to UTF8 encoding and ignore error

modules/csv/csv.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ package csv
77
import (
88
"bytes"
99
"encoding/csv"
10+
stdcsv "encoding/csv"
1011
"errors"
12+
"io"
1113
"regexp"
1214
"strings"
1315

@@ -18,17 +20,31 @@ import (
1820
var quoteRegexp = regexp.MustCompile(`["'][\s\S]+?["']`)
1921

2022
// CreateReader creates a csv.Reader with the given delimiter.
21-
func CreateReader(rawBytes []byte, delimiter rune) *csv.Reader {
22-
rd := csv.NewReader(bytes.NewReader(rawBytes))
23+
func CreateReader(input io.Reader, delimiter rune) *stdcsv.Reader {
24+
rd := stdcsv.NewReader(input)
2325
rd.Comma = delimiter
2426
rd.TrimLeadingSpace = true
2527
return rd
2628
}
2729

2830
// CreateReaderAndGuessDelimiter tries to guess the field delimiter from the content and creates a csv.Reader.
29-
func CreateReaderAndGuessDelimiter(rawBytes []byte) *csv.Reader {
30-
delimiter := guessDelimiter(rawBytes)
31-
return CreateReader(rawBytes, delimiter)
31+
func CreateReaderAndGuessDelimiter(rd io.Reader) (*stdcsv.Reader, error) {
32+
var data = make([]byte, 1e4)
33+
size, err := rd.Read(data)
34+
if err != nil {
35+
return nil, err
36+
}
37+
38+
delimiter := guessDelimiter(data[:size])
39+
40+
var newInput io.Reader
41+
if size < 1e4 {
42+
newInput = bytes.NewReader(data[:size])
43+
} else {
44+
newInput = io.MultiReader(bytes.NewReader(data), rd)
45+
}
46+
47+
return CreateReader(newInput, delimiter), nil
3248
}
3349

3450
// guessDelimiter scores the input CSV data against delimiters, and returns the best match.

modules/csv/csv_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@
55
package csv
66

77
import (
8+
"bytes"
9+
"strings"
810
"testing"
911

1012
"github.com/stretchr/testify/assert"
1113
)
1214

1315
func TestCreateReader(t *testing.T) {
14-
rd := CreateReader([]byte{}, ',')
16+
rd := CreateReader(bytes.NewReader([]byte{}), ',')
1517
assert.Equal(t, ',', rd.Comma)
1618
}
1719

1820
func TestCreateReaderAndGuessDelimiter(t *testing.T) {
1921
input := "a;b;c\n1;2;3\n4;5;6"
2022

21-
rd := CreateReaderAndGuessDelimiter([]byte(input))
23+
rd, err := CreateReaderAndGuessDelimiter(strings.NewReader(input))
24+
assert.NoError(t, err)
2225
assert.Equal(t, ';', rd.Comma)
2326
}
2427

modules/markup/csv/csv.go

Lines changed: 84 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
package markup
66

77
import (
8+
"bufio"
89
"bytes"
910
"html"
1011
"io"
12+
"io/ioutil"
1113
"strconv"
1214

1315
"code.gitea.io/gitea/modules/csv"
@@ -16,55 +18,89 @@ import (
1618
)
1719

1820
func init() {
19-
markup.RegisterParser(Parser{})
21+
markup.RegisterRenderer(Renderer{})
2022
}
2123

22-
// Parser implements markup.Parser for csv files
23-
type Parser struct {
24+
// Renderer implements markup.Renderer for csv files
25+
type Renderer struct {
2426
}
2527

26-
// Name implements markup.Parser
27-
func (Parser) Name() string {
28+
// Name implements markup.Renderer
29+
func (Renderer) Name() string {
2830
return "csv"
2931
}
3032

31-
// NeedPostProcess implements markup.Parser
32-
func (Parser) NeedPostProcess() bool { return false }
33+
// NeedPostProcess implements markup.Renderer
34+
func (Renderer) NeedPostProcess() bool { return false }
3335

34-
// Extensions implements markup.Parser
35-
func (Parser) Extensions() []string {
36+
// Extensions implements markup.Renderer
37+
func (Renderer) Extensions() []string {
3638
return []string{".csv", ".tsv"}
3739
}
3840

39-
// Render implements markup.Parser
40-
func (Parser) Render(rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) []byte {
41-
var tmpBlock bytes.Buffer
42-
43-
if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < int64(len(rawBytes)) {
44-
tmpBlock.WriteString("<pre>")
45-
tmpBlock.WriteString(html.EscapeString(string(rawBytes)))
46-
tmpBlock.WriteString("</pre>")
47-
return tmpBlock.Bytes()
41+
func writeField(w io.Writer, element, class, field string) error {
42+
if _, err := io.WriteString(w, "<"); err != nil {
43+
return err
44+
}
45+
if _, err := io.WriteString(w, element); err != nil {
46+
return err
47+
}
48+
if len(class) > 0 {
49+
if _, err := io.WriteString(w, " class=\""); err != nil {
50+
return err
51+
}
52+
if _, err := io.WriteString(w, class); err != nil {
53+
return err
54+
}
55+
if _, err := io.WriteString(w, "\""); err != nil {
56+
return err
57+
}
58+
}
59+
if _, err := io.WriteString(w, ">"); err != nil {
60+
return err
61+
}
62+
if _, err := io.WriteString(w, html.EscapeString(field)); err != nil {
63+
return err
4864
}
65+
if _, err := io.WriteString(w, "</"); err != nil {
66+
return err
67+
}
68+
if _, err := io.WriteString(w, element); err != nil {
69+
return err
70+
}
71+
_, err := io.WriteString(w, ">")
72+
return err
73+
}
4974

50-
rd := csv.CreateReaderAndGuessDelimiter(rawBytes)
75+
// Render implements markup.Renderer
76+
func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
77+
var tmpBlock = bufio.NewWriter(output)
5178

52-
writeField := func(element, class, field string) {
53-
tmpBlock.WriteString("<")
54-
tmpBlock.WriteString(element)
55-
if len(class) > 0 {
56-
tmpBlock.WriteString(" class=\"")
57-
tmpBlock.WriteString(class)
58-
tmpBlock.WriteString("\"")
79+
// FIXME: don't read all to memory
80+
rawBytes, err := ioutil.ReadAll(input)
81+
if err != nil {
82+
return err
83+
}
84+
85+
if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < int64(len(rawBytes)) {
86+
if _, err := tmpBlock.WriteString("<pre>"); err != nil {
87+
return err
5988
}
60-
tmpBlock.WriteString(">")
61-
tmpBlock.WriteString(html.EscapeString(field))
62-
tmpBlock.WriteString("</")
63-
tmpBlock.WriteString(element)
64-
tmpBlock.WriteString(">")
89+
if _, err := tmpBlock.WriteString(html.EscapeString(string(rawBytes))); err != nil {
90+
return err
91+
}
92+
_, err = tmpBlock.WriteString("</pre>")
93+
return err
94+
}
95+
96+
rd, err := csv.CreateReaderAndGuessDelimiter(bytes.NewReader(rawBytes))
97+
if err != nil {
98+
return err
6599
}
66100

67-
tmpBlock.WriteString(`<table class="data-table">`)
101+
if _, err := tmpBlock.WriteString(`<table class="data-table">`); err != nil {
102+
return err
103+
}
68104
row := 1
69105
for {
70106
fields, err := rd.Read()
@@ -74,20 +110,29 @@ func (Parser) Render(rawBytes []byte, urlPrefix string, metas map[string]string,
74110
if err != nil {
75111
continue
76112
}
77-
tmpBlock.WriteString("<tr>")
113+
if _, err := tmpBlock.WriteString("<tr>"); err != nil {
114+
return err
115+
}
78116
element := "td"
79117
if row == 1 {
80118
element = "th"
81119
}
82-
writeField(element, "line-num", strconv.Itoa(row))
120+
if err := writeField(tmpBlock, element, "line-num", strconv.Itoa(row)); err != nil {
121+
return err
122+
}
83123
for _, field := range fields {
84-
writeField(element, "", field)
124+
if err := writeField(tmpBlock, element, "", field); err != nil {
125+
return err
126+
}
127+
}
128+
if _, err := tmpBlock.WriteString("</tr>"); err != nil {
129+
return err
85130
}
86-
tmpBlock.WriteString("</tr>")
87131

88132
row++
89133
}
90-
tmpBlock.WriteString("</table>")
91-
92-
return tmpBlock.Bytes()
134+
if _, err = tmpBlock.WriteString("</table>"); err != nil {
135+
return err
136+
}
137+
return tmpBlock.Flush()
93138
}

0 commit comments

Comments
 (0)