package reporttemplate import ( "strings" "unicode" ) var inlineMarkdownReplacer = strings.NewReplacer( `\`, `\\`, "`", "\\`", "*", "\\*", "_", "\\_", "[", "\\[", "]", "\\]", "(", "\\(", ")", "\\)", "<", "\\<", ">", "\\>", "!", "\\!", "~", "\\~", "|", "\\|", "&", "\\&", ) // plainText preserves prose and paragraph breaks while preventing dynamic // values from creating Markdown structure owned by repository templates. func plainText(value string) string { value = strings.ReplaceAll(value, "\r\n", "\n") value = strings.ReplaceAll(value, "\r", "\n") lines := strings.Split(value, "\n") for index, line := range lines { lines[index] = escapeMarkdownLine(line) } return strings.Join(lines, "\n") } func escapeMarkdownLine(line string) string { indentEnd := 0 spaces := 0 hasTab := false for indentEnd < len(line) { switch line[indentEnd] { case ' ': spaces++ indentEnd++ case '\t': hasTab = true indentEnd++ default: goto escaped } } escaped: indent := line[:indentEnd] if hasTab || spaces >= 4 { indent = "" } return indent + escapeMarkdownBlockPrefix(escapeMarkdownInline(line[indentEnd:])) } func escapeMarkdownInline(value string) string { value = inlineMarkdownReplacer.Replace(value) return strings.Map(func(r rune) rune { if unicode.IsControl(r) { return ' ' } return r }, value) } func escapeMarkdownBlockPrefix(value string) string { if value == "" { return value } switch value[0] { case '#', '+', '-', '>', '=': return "\\" + value } index := 0 for index < len(value) && value[index] >= '0' && value[index] <= '9' { index++ } if index > 0 && index < len(value) && value[index] == '.' { return value[:index] + "\\" + value[index:] } return value }