func mentionProcessor(ctx *RenderContext, node *html.Node) {
start := 0
- next := node.NextSibling
- for node != nil && node != next && start < len(node.Data) {
- // We replace only the first mention; other mentions will be addressed later
- found, loc := references.FindFirstMentionBytes([]byte(node.Data[start:]))
+ for node != nil {
+ found, loc := references.FindFirstMentionBytes(util.UnsafeStringToBytes(node.Data[start:]))
if !found {
- return
+ node = node.NextSibling
+ start = 0
+ continue
}
loc.Start += start
loc.End += start
mention := node.Data[loc.Start:loc.End]
- var teams string
teams, ok := ctx.Metas["teams"]
// FIXME: util.URLJoin may not be necessary here:
// - setting.AppURL is defined to have a terminal '/' so unless mention[1:]
if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "mention"))
node = node.NextSibling.NextSibling
+ start = 0
} else {
- node = node.NextSibling
+ start = loc.End
}
- start = 0
}
}
// TODO: fix invalid linking issue
// mentionPattern matches all mentions in the form of "@user" or "@org/team"
- mentionPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@[0-9a-zA-Z-_]+|@[0-9a-zA-Z-_]+\/?[0-9a-zA-Z-_]+|@[0-9a-zA-Z-_][0-9a-zA-Z-_.]+\/?[0-9a-zA-Z-_.]+[0-9a-zA-Z-_])(?:\s|[:,;.?!]\s|[:,;.?!]?$|\)|\])`)
+ mentionPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@[-\w][-.\w]*?|@[-\w][-.\w]*?/[-\w][-.\w]*?)(?:\s|$|[:,;.?!](\s|$)|'|\)|\])`)
// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\'|\")([#!][0-9]+)(?:\s|$|\)|\]|\'|\"|[:;,.?!]\s|[:;,.?!]$)`)
// issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
{"@gitea,", "@gitea"},
{"@gitea;", "@gitea"},
{"@gitea/team1;", "@gitea/team1"},
+ {"@user's idea", "@user"},
}
falseTestCases := []string{
"@ 0",
for _, testCase := range trueTestCases {
found := mentionPattern.FindStringSubmatch(testCase.pat)
- assert.Len(t, found, 2)
assert.Equal(t, testCase.exp, found[1])
}
for _, testCase := range falseTestCases {
expected = `/owner/repo/pulls?labels=123`
assert.Contains(t, RenderLabels(ctx, locale, []*issues.Label{label}, "/owner/repo", issue), expected)
}
+
+func TestUserMention(t *testing.T) {
+ rendered := RenderMarkdownToHtml(context.Background(), "@no-such-user @mention-user @mention-user")
+ assert.EqualValues(t, `<p>@no-such-user <a href="/mention-user" rel="nofollow">@mention-user</a> <a href="/mention-user" rel="nofollow">@mention-user</a></p>`, strings.TrimSpace(string(rendered)))
+}