]> git.giacomolli.uk - onechat/blobdiff - telegram_markdown.go
Escape tags in the Markdown of rich replies
[onechat] / telegram_markdown.go
index 54bfbcb36031565e89fa67621ceaf2f78e480d92..16b83f2f851d81bddd19353a1af8e6c43dfb6c97 100644 (file)
@@ -68,6 +68,37 @@ func telegramMarkdownEntities(markdown string) (string, []TelegramEntity) {
        return out.text.String(), out.entities
 }
 
        return out.text.String(), out.entities
 }
 
+// telegramEscapeTags puts a backslash before each < of markdown outside inline
+// code and fenced code blocks, so that rich Markdown shows a tag name as text
+// and reads no raw HTML. A < that a backslash already makes plain stays as it
+// is. A code block that does not close runs to the end, as in Markdown.
+func telegramEscapeTags(markdown 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 == "" {
+                       telegramEscapeLineTags(&out, line)
+                       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()
+}
+
 // telegramCodeFence returns the language of a line that opens a fenced code
 // block, and the fence. The fence is "" when line does not open a block.
 func telegramCodeFence(line string) (string, string) {
 // telegramCodeFence returns the language of a line that opens a fenced code
 // block, and the fence. The fence is "" when line does not open a block.
 func telegramCodeFence(line string) (string, string) {
@@ -225,6 +256,39 @@ func telegramHeadingText(line string) string {
        return rest
 }
 
        return rest
 }
 
+// telegramEscapeLineTags writes line to out with a backslash before each < that
+// is outside inline code and not already plain. A run of backticks that opens
+// no inline code stays as text whole, as in Markdown.
+func telegramEscapeLineTags(out *strings.Builder, line string) {
+       for index := 0; index < len(line); {
+               switch line[index] {
+               case '\\':
+                       if index+1 < len(line) && strings.IndexByte(telegramMarkdownPunctuation, line[index+1]) >= 0 {
+                               out.WriteString(line[index : index+2])
+                               index += 2
+
+                               continue
+                       }
+               case '`':
+                       end := index + telegramBackticks(line[index:])
+                       span, found := telegramCodeSpan(line, index)
+                       if found {
+                               end = span.end
+                       }
+
+                       out.WriteString(line[index:end])
+                       index = end
+
+                       continue
+               case '<':
+                       out.WriteByte('\\')
+               }
+
+               out.WriteByte(line[index])
+               index++
+       }
+}
+
 // telegramMarkSpan returns the inline mark that starts at index of line and
 // closes in line. It returns false when no mark starts there or the mark does
 // not close.
 // telegramMarkSpan returns the inline mark that starts at index of line and
 // closes in line. It returns false when no mark starts there or the mark does
 // not close.