Skip to content

Commit 402d97f

Browse files
committed
Run emoji processors on whole of text
There is an inefficiency in the design of our processors which means that Emoji and other processors run in order n^2 time. This PR forces the emoji processors to process the entirety of text node before passing back up. The fundamental inefficiency remains but it should be significantly ameliorated. Signed-off-by: Andrew Thornton <[email protected]>
1 parent f374789 commit 402d97f

File tree

2 files changed

+45
-24
lines changed

2 files changed

+45
-24
lines changed

modules/emoji/emoji.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package emoji
77

88
import (
9+
"io"
910
"sort"
1011
"strings"
1112
"sync"
@@ -145,6 +146,8 @@ func (n *rememberSecondWriteWriter) Write(p []byte) (int, error) {
145146
if n.writecount == 2 {
146147
n.idx = n.pos
147148
n.end = n.pos + len(p)
149+
n.pos += len(p)
150+
return len(p), io.EOF
148151
}
149152
n.pos += len(p)
150153
return len(p), nil
@@ -155,6 +158,8 @@ func (n *rememberSecondWriteWriter) WriteString(s string) (int, error) {
155158
if n.writecount == 2 {
156159
n.idx = n.pos
157160
n.end = n.pos + len(s)
161+
n.pos += len(s)
162+
return len(s), io.EOF
158163
}
159164
n.pos += len(s)
160165
return len(s), nil

modules/markup/html.go

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -901,38 +901,54 @@ func fullSha1PatternProcessor(ctx *RenderContext, node *html.Node) {
901901

902902
// emojiShortCodeProcessor for rendering text like :smile: into emoji
903903
func emojiShortCodeProcessor(ctx *RenderContext, node *html.Node) {
904-
m := EmojiShortCodeRegex.FindStringSubmatchIndex(node.Data)
905-
if m == nil {
906-
return
907-
}
908-
909-
alias := node.Data[m[0]:m[1]]
910-
alias = strings.ReplaceAll(alias, ":", "")
911-
converted := emoji.FromAlias(alias)
912-
if converted == nil {
913-
// check if this is a custom reaction
914-
s := strings.Join(setting.UI.Reactions, " ") + "gitea"
915-
if strings.Contains(s, alias) {
916-
replaceContent(node, m[0], m[1], createCustomEmoji(alias, "emoji"))
904+
start := 0
905+
for node != nil && start < len(node.Data) {
906+
m := EmojiShortCodeRegex.FindStringSubmatchIndex(node.Data)
907+
if m == nil {
917908
return
918909
}
919-
return
920-
}
921910

922-
replaceContent(node, m[0], m[1], createEmoji(converted.Emoji, "emoji", converted.Description))
911+
start = m[1]
912+
913+
alias := node.Data[m[0]:m[1]]
914+
alias = strings.ReplaceAll(alias, ":", "")
915+
converted := emoji.FromAlias(alias)
916+
if converted == nil {
917+
// check if this is a custom reaction
918+
s := strings.Join(setting.UI.Reactions, " ") + "gitea"
919+
if strings.Contains(s, alias) {
920+
replaceContent(node, m[0], m[1], createCustomEmoji(alias, "emoji"))
921+
node = node.NextSibling.NextSibling
922+
start = 0
923+
continue
924+
}
925+
continue
926+
}
927+
928+
replaceContent(node, m[0], m[1], createEmoji(converted.Emoji, "emoji", converted.Description))
929+
node = node.NextSibling.NextSibling
930+
start = 0
931+
}
923932
}
924933

925934
// emoji processor to match emoji and add emoji class
926935
func emojiProcessor(ctx *RenderContext, node *html.Node) {
927-
m := emoji.FindEmojiSubmatchIndex(node.Data)
928-
if m == nil {
929-
return
930-
}
936+
start := 0
937+
for node != nil && start < len(node.Data) {
938+
m := emoji.FindEmojiSubmatchIndex(node.Data[start:])
939+
if m == nil {
940+
return
941+
}
931942

932-
codepoint := node.Data[m[0]:m[1]]
933-
val := emoji.FromCode(codepoint)
934-
if val != nil {
935-
replaceContent(node, m[0], m[1], createEmoji(codepoint, "emoji", val.Description))
943+
start = m[1]
944+
945+
codepoint := node.Data[m[0]:m[1]]
946+
val := emoji.FromCode(codepoint)
947+
if val != nil {
948+
replaceContent(node, m[0], m[1], createEmoji(codepoint, "emoji", val.Description))
949+
node = node.NextSibling.NextSibling
950+
start = 0
951+
}
936952
}
937953
}
938954

0 commit comments

Comments
 (0)