* perf: apply perfpsrint linter (#33967) * perf: apply perfpsrint linter * further simplifications * improved TestParseHashtags coverage * more simplifications * simplify renderBlockHTML further --------- Co-authored-by: Jesse Hallam <jesse@mattermost.com> * Fixed a bad merge --------- Co-authored-by: Catena cyber <35799796+catenacyber@users.noreply.github.com> Co-authored-by: Jesse Hallam <jesse@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
183e6c4a07
Коммит
b3d6c0c564
@@ -717,8 +717,8 @@ var (
|
||||
func ParseHashtags(text string) (string, string) {
|
||||
words := strings.Fields(text)
|
||||
|
||||
hashtagString := ""
|
||||
plainString := ""
|
||||
var hashtagStringSb strings.Builder
|
||||
var plainString strings.Builder
|
||||
for _, word := range words {
|
||||
// trim off surrounding punctuation
|
||||
word = puncStart.ReplaceAllString(word, "")
|
||||
@@ -728,11 +728,12 @@ func ParseHashtags(text string) (string, string) {
|
||||
word = hashtagStart.ReplaceAllString(word, "#")
|
||||
|
||||
if validHashtag.MatchString(word) {
|
||||
hashtagString += " " + word
|
||||
hashtagStringSb.WriteString(" " + word)
|
||||
} else {
|
||||
plainString += " " + word
|
||||
plainString.WriteString(" " + word)
|
||||
}
|
||||
}
|
||||
hashtagString := hashtagStringSb.String()
|
||||
|
||||
if len(hashtagString) > 1000 {
|
||||
hashtagString = hashtagString[:999]
|
||||
@@ -744,7 +745,7 @@ func ParseHashtags(text string) (string, string) {
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString)
|
||||
return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString.String())
|
||||
}
|
||||
|
||||
func ClearMentionTags(post string) string {
|
||||
|
||||
@@ -541,10 +541,58 @@ func TestStringArray_Equal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseHashtags(t *testing.T) {
|
||||
for input, output := range hashtags {
|
||||
o, _ := ParseHashtags(input)
|
||||
require.Equal(t, o, output, "failed to parse hashtags from input="+input+" expected="+output+" actual="+o)
|
||||
}
|
||||
t.Run("basic hashtag extraction", func(t *testing.T) {
|
||||
for input, output := range hashtags {
|
||||
o, _ := ParseHashtags(input)
|
||||
require.Equal(t, o, output, "failed to parse hashtags from input="+input+" expected="+output+" actual="+o)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("long hashtag string truncation", func(t *testing.T) {
|
||||
// Test case where hashtag string exceeds 1000 characters with a space to truncate at
|
||||
longHashtags := "#test " + strings.Repeat("#verylonghashtag ", 50)
|
||||
hashtagString, plainString := ParseHashtags(longHashtags)
|
||||
require.NotEmpty(t, hashtagString)
|
||||
require.LessOrEqual(t, len(hashtagString), 1000)
|
||||
require.Empty(t, plainString)
|
||||
// Ensure it truncated at a space
|
||||
require.NotEqual(t, "", hashtagString)
|
||||
require.True(t, hashtagString[len(hashtagString)-1] != ' ')
|
||||
})
|
||||
|
||||
t.Run("long hashtag string truncation without spaces", func(t *testing.T) {
|
||||
// Test case where hashtag string exceeds 1000 characters with no space after position 999
|
||||
// Create a single very long hashtag that will be truncated
|
||||
veryLongHashtag := "#" + strings.Repeat("a", 1010)
|
||||
hashtagString, plainString := ParseHashtags(veryLongHashtag)
|
||||
// Should be empty because no space was found to truncate at
|
||||
require.Equal(t, "", hashtagString)
|
||||
require.Empty(t, plainString)
|
||||
})
|
||||
|
||||
t.Run("plain text extraction", func(t *testing.T) {
|
||||
hashtagString, plainString := ParseHashtags("hello #world this is #test plain text")
|
||||
require.Equal(t, "#world #test", hashtagString)
|
||||
require.Equal(t, "hello this is plain text", plainString)
|
||||
})
|
||||
|
||||
t.Run("only plain text", func(t *testing.T) {
|
||||
hashtagString, plainString := ParseHashtags("no hashtags here")
|
||||
require.Empty(t, hashtagString)
|
||||
require.Equal(t, "no hashtags here", plainString)
|
||||
})
|
||||
|
||||
t.Run("only hashtags", func(t *testing.T) {
|
||||
hashtagString, plainString := ParseHashtags("#one #two #three")
|
||||
require.Equal(t, "#one #two #three", hashtagString)
|
||||
require.Empty(t, plainString)
|
||||
})
|
||||
|
||||
t.Run("empty string", func(t *testing.T) {
|
||||
hashtagString, plainString := ParseHashtags("")
|
||||
require.Empty(t, hashtagString)
|
||||
require.Empty(t, plainString)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsValidAlphaNum(t *testing.T) {
|
||||
|
||||
@@ -23,11 +23,12 @@ type FencedCode struct {
|
||||
RawCode []FencedCodeLine
|
||||
}
|
||||
|
||||
func (b *FencedCode) Code() (result string) {
|
||||
func (b *FencedCode) Code() string {
|
||||
var resultSb strings.Builder
|
||||
for _, code := range b.RawCode {
|
||||
result += strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End]
|
||||
resultSb.WriteString(strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End])
|
||||
}
|
||||
return
|
||||
return resultSb.String()
|
||||
}
|
||||
|
||||
func (b *FencedCode) Info() string {
|
||||
|
||||
@@ -27,69 +27,75 @@ func RenderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition) (
|
||||
return renderBlockHTML(block, referenceDefinitions, false)
|
||||
}
|
||||
|
||||
func renderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition, isTightList bool) (result string) {
|
||||
func renderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition, isTightList bool) string {
|
||||
var resultSb strings.Builder
|
||||
|
||||
switch v := block.(type) {
|
||||
case *Document:
|
||||
for _, block := range v.Children {
|
||||
result += RenderBlockHTML(block, referenceDefinitions)
|
||||
resultSb.WriteString(RenderBlockHTML(block, referenceDefinitions))
|
||||
}
|
||||
case *Paragraph:
|
||||
if len(v.Text) == 0 {
|
||||
return
|
||||
return ""
|
||||
}
|
||||
if !isTightList {
|
||||
result += "<p>"
|
||||
resultSb.WriteString("<p>")
|
||||
}
|
||||
for _, inline := range v.ParseInlines(referenceDefinitions) {
|
||||
result += RenderInlineHTML(inline)
|
||||
resultSb.WriteString(RenderInlineHTML(inline))
|
||||
}
|
||||
if !isTightList {
|
||||
result += "</p>"
|
||||
resultSb.WriteString("</p>")
|
||||
}
|
||||
case *List:
|
||||
if v.IsOrdered {
|
||||
if v.OrderedStart != 1 {
|
||||
result += fmt.Sprintf(`<ol start="%v">`, v.OrderedStart)
|
||||
resultSb.WriteString(fmt.Sprintf(`<ol start="%v">`, v.OrderedStart))
|
||||
} else {
|
||||
result += "<ol>"
|
||||
resultSb.WriteString("<ol>")
|
||||
}
|
||||
} else {
|
||||
result += "<ul>"
|
||||
resultSb.WriteString("<ul>")
|
||||
}
|
||||
for _, block := range v.Children {
|
||||
result += renderBlockHTML(block, referenceDefinitions, !v.IsLoose)
|
||||
resultSb.WriteString(renderBlockHTML(block, referenceDefinitions, !v.IsLoose))
|
||||
}
|
||||
if v.IsOrdered {
|
||||
result += "</ol>"
|
||||
resultSb.WriteString("</ol>")
|
||||
} else {
|
||||
result += "</ul>"
|
||||
resultSb.WriteString("</ul>")
|
||||
}
|
||||
case *ListItem:
|
||||
result += "<li>"
|
||||
resultSb.WriteString("<li>")
|
||||
for _, block := range v.Children {
|
||||
result += renderBlockHTML(block, referenceDefinitions, isTightList)
|
||||
resultSb.WriteString(renderBlockHTML(block, referenceDefinitions, isTightList))
|
||||
}
|
||||
result += "</li>"
|
||||
resultSb.WriteString("</li>")
|
||||
case *BlockQuote:
|
||||
result += "<blockquote>"
|
||||
resultSb.WriteString("<blockquote>")
|
||||
for _, block := range v.Children {
|
||||
result += RenderBlockHTML(block, referenceDefinitions)
|
||||
resultSb.WriteString(RenderBlockHTML(block, referenceDefinitions))
|
||||
}
|
||||
result += "</blockquote>"
|
||||
resultSb.WriteString("</blockquote>")
|
||||
case *FencedCode:
|
||||
if info := v.Info(); info != "" {
|
||||
language := strings.Fields(info)[0]
|
||||
result += `<pre><code class="language-` + htmlEscaper.Replace(language) + `">`
|
||||
resultSb.WriteString(`<pre><code class="language-` + htmlEscaper.Replace(language) + `">`)
|
||||
} else {
|
||||
result += "<pre><code>"
|
||||
resultSb.WriteString("<pre><code>")
|
||||
}
|
||||
result += htmlEscaper.Replace(v.Code()) + "</code></pre>"
|
||||
resultSb.WriteString(htmlEscaper.Replace(v.Code()))
|
||||
resultSb.WriteString("</code></pre>")
|
||||
case *IndentedCode:
|
||||
result += "<pre><code>" + htmlEscaper.Replace(v.Code()) + "</code></pre>"
|
||||
resultSb.WriteString("<pre><code>")
|
||||
resultSb.WriteString(htmlEscaper.Replace(v.Code()))
|
||||
resultSb.WriteString("</code></pre>")
|
||||
default:
|
||||
panic(fmt.Sprintf("missing case for type %T", v))
|
||||
}
|
||||
return
|
||||
|
||||
return resultSb.String()
|
||||
}
|
||||
|
||||
func escapeURL(url string) (result string) {
|
||||
@@ -137,31 +143,37 @@ func RenderInlineHTML(inline Inline) (result string) {
|
||||
}
|
||||
result += ` />`
|
||||
case *InlineLink:
|
||||
result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`
|
||||
var resultSb strings.Builder
|
||||
resultSb.WriteString(`<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`)
|
||||
if title := v.Title(); title != "" {
|
||||
result += ` title="` + htmlEscaper.Replace(title) + `"`
|
||||
resultSb.WriteString(` title="` + htmlEscaper.Replace(title) + `"`)
|
||||
}
|
||||
result += `>`
|
||||
resultSb.WriteString(`>`)
|
||||
for _, inline := range v.Children {
|
||||
result += RenderInlineHTML(inline)
|
||||
resultSb.WriteString(RenderInlineHTML(inline))
|
||||
}
|
||||
result += "</a>"
|
||||
resultSb.WriteString("</a>")
|
||||
return resultSb.String()
|
||||
case *ReferenceLink:
|
||||
result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`
|
||||
var resultSb strings.Builder
|
||||
resultSb.WriteString(`<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`)
|
||||
if title := v.Title(); title != "" {
|
||||
result += ` title="` + htmlEscaper.Replace(title) + `"`
|
||||
resultSb.WriteString(` title="` + htmlEscaper.Replace(title) + `"`)
|
||||
}
|
||||
result += `>`
|
||||
resultSb.WriteString(`>`)
|
||||
for _, inline := range v.Children {
|
||||
result += RenderInlineHTML(inline)
|
||||
resultSb.WriteString(RenderInlineHTML(inline))
|
||||
}
|
||||
result += "</a>"
|
||||
resultSb.WriteString("</a>")
|
||||
return resultSb.String()
|
||||
case *Autolink:
|
||||
result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `">`
|
||||
var resultSb strings.Builder
|
||||
resultSb.WriteString(`<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `">`)
|
||||
for _, inline := range v.Children {
|
||||
result += RenderInlineHTML(inline)
|
||||
resultSb.WriteString(RenderInlineHTML(inline))
|
||||
}
|
||||
result += "</a>"
|
||||
resultSb.WriteString("</a>")
|
||||
return resultSb.String()
|
||||
case *Emoji:
|
||||
escapedName := htmlEscaper.Replace(v.Name)
|
||||
result += fmt.Sprintf(`<span data-emoji-name="%s" data-literal=":%s:" />`, escapedName, escapedName)
|
||||
@@ -172,25 +184,30 @@ func RenderInlineHTML(inline Inline) (result string) {
|
||||
return
|
||||
}
|
||||
|
||||
func renderImageAltText(children []Inline) (result string) {
|
||||
func renderImageAltText(children []Inline) string {
|
||||
var resultSb strings.Builder
|
||||
for _, inline := range children {
|
||||
result += renderImageChildAltText(inline)
|
||||
resultSb.WriteString(renderImageChildAltText(inline))
|
||||
}
|
||||
return
|
||||
return resultSb.String()
|
||||
}
|
||||
|
||||
func renderImageChildAltText(inline Inline) (result string) {
|
||||
func renderImageChildAltText(inline Inline) string {
|
||||
switch v := inline.(type) {
|
||||
case *Text:
|
||||
return v.Text
|
||||
case *InlineImage:
|
||||
var resultSb strings.Builder
|
||||
for _, inline := range v.Children {
|
||||
result += renderImageChildAltText(inline)
|
||||
resultSb.WriteString(renderImageChildAltText(inline))
|
||||
}
|
||||
return resultSb.String()
|
||||
case *InlineLink:
|
||||
var resultSb strings.Builder
|
||||
for _, inline := range v.Children {
|
||||
result += renderImageChildAltText(inline)
|
||||
resultSb.WriteString(renderImageChildAltText(inline))
|
||||
}
|
||||
return resultSb.String()
|
||||
}
|
||||
return
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -19,11 +19,12 @@ type IndentedCode struct {
|
||||
RawCode []IndentedCodeLine
|
||||
}
|
||||
|
||||
func (b *IndentedCode) Code() (result string) {
|
||||
func (b *IndentedCode) Code() string {
|
||||
var resultSb strings.Builder
|
||||
for _, code := range b.RawCode {
|
||||
result += strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End]
|
||||
resultSb.WriteString(strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End])
|
||||
}
|
||||
return
|
||||
return resultSb.String()
|
||||
}
|
||||
|
||||
func (b *IndentedCode) Continuation(indentation int, r Range) *continuation {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
package markdown
|
||||
|
||||
import "strings"
|
||||
|
||||
type ReferenceDefinition struct {
|
||||
RawDestination Range
|
||||
|
||||
@@ -24,10 +26,11 @@ func (d *ReferenceDefinition) Title() string {
|
||||
}
|
||||
|
||||
func parseReferenceDefinition(markdown string, ranges []Range) (*ReferenceDefinition, []Range) {
|
||||
raw := ""
|
||||
var rawSb strings.Builder
|
||||
for _, r := range ranges {
|
||||
raw += markdown[r.Position:r.End]
|
||||
rawSb.WriteString(markdown[r.Position:r.End])
|
||||
}
|
||||
raw := rawSb.String()
|
||||
|
||||
label, next, ok := parseLinkLabel(raw, 0)
|
||||
if !ok {
|
||||
|
||||
Ссылка в новой задаче
Block a user