+// telegramRewriteProse returns markdown with rewrite applied to each part of a
+// line outside inline code and fenced code blocks. A code block that does not
+// close runs to the end, as in Markdown.
+func telegramRewriteProse(markdown string, rewrite func(string) string) string {
+ var out strings.Builder
+
+ lines := strings.Split(markdown, "\n")
+ for index := 0; index < len(lines); index++ {
+ if index > 0 {
+ out.WriteString("\n")
+ }
+
+ line := lines[index]
+ _, fence := telegramCodeFence(line)
+ if fence == "" {
+ telegramRewriteLineProse(&out, line, rewrite)
+
+ continue
+ }
+
+ end := telegramClosingFence(lines, index, fence)
+ if end < 0 {
+ end = len(lines) - 1
+ }
+
+ out.WriteString(strings.Join(lines[index:end+1], "\n"))
+
+ index = end
+ }
+
+ return out.String()
+}
+
+// telegramRewriteLineProse writes line to out with rewrite applied to each part
+// outside inline code. A backtick after a backslash opens no code. A run of
+// backticks that opens no inline code stays as text whole, as in Markdown.
+func telegramRewriteLineProse(out *strings.Builder, line string, rewrite func(string) string) {
+ prose := 0
+ for index := 0; index < len(line); {
+ switch line[index] {
+ case '\\':
+ if index+1 < len(line) && strings.IndexByte(telegramMarkdownPunctuation, line[index+1]) >= 0 {
+ index += 2
+ continue
+ }
+ case '`':
+ end := index + telegramBackticks(line[index:])
+ span, found := telegramCodeSpan(line, index)
+ if found {
+ end = span.end
+ }
+
+ out.WriteString(rewrite(line[prose:index]))
+ out.WriteString(line[index:end])
+
+ index = end
+ prose = end
+
+ continue
+ }
+
+ index++
+ }
+
+ out.WriteString(rewrite(line[prose:]))
+}
+