+
+// telegramRichCut returns where telegramSplitRich cuts markdown, which is
+// longer than limit. For a cut in a code block, it also returns the fence that
+// closes the block and the line that opens it. Each part before a cut holds
+// text other than space, and a cut in a code block leaves room for the fence,
+// so each cut makes the rest shorter.
+func telegramRichCut(markdown string, limit int) (int, string, string) {
+ blank, line, code := 0, 0, 0
+
+ var fence, opening, inFence, inOpening string
+
+ for start := 0; start < limit; {
+ end := strings.IndexByte(markdown[start:], '\n')
+ if end < 0 || start+end+1 > limit {
+ break
+ }
+
+ end += start + 1
+ text := strings.TrimRight(markdown[start:end], "\r\n")
+ _, opens := markdownCodeFence(text)
+ switch {
+ case fence != "":
+ if markdownClosesFence(text, fence) {
+ fence = ""
+ } else if end > len(opening)+1 && end+len(fence)+1 <= limit {
+ code, inFence, inOpening = end, fence, opening
+ }
+ case opens != "":
+ fence, opening = opens, text
+ case strings.TrimSpace(markdown[:end]) == "":
+ case strings.TrimSpace(text) == "":
+ blank = end
+ }
+
+ if fence == "" && strings.TrimSpace(markdown[:end]) != "" {
+ line = end
+ }
+
+ start = end
+ }
+
+ switch {
+ case blank > 0:
+ return blank, "", ""
+ case line > 0:
+ return line, "", ""
+ case code > 0:
+ return code, inFence, inOpening
+ }
+
+ cut := limit
+ for !utf8.RuneStart(markdown[cut]) {
+ cut--
+ }
+
+ start := max(cut-len("&")+1, 0)
+ entity := strings.LastIndexByte(markdown[start:cut], '&')
+ if entity >= 0 && start+entity > 0 {
+ cut = start + entity
+ }
+
+ return cut, "", ""
+}
+
+// telegramSplitEntities cuts text into parts with telegramSplitMessage, and
+// gives each part the entities in it, with offsets from the start of the part.
+// An entity that crosses a cut becomes one entity in each part.
+func telegramSplitEntities(text string, entities []TelegramEntity, limit int) []TelegramPart {
+ var parts []TelegramPart
+
+ start := 0
+ for _, part := range telegramSplitMessage(text, limit) {
+ end := start + telegramUTF16Units(part)
+
+ var kept []TelegramEntity
+
+ for _, entity := range entities {
+ from := max(entity.Offset, start)
+ to := min(entity.Offset+entity.Length, end)
+ if to <= from {
+ continue
+ }
+
+ entity.Offset = from - start
+ entity.Length = to - from
+ kept = append(kept, entity)
+ }
+
+ parts = append(parts, TelegramPart{
+ text: part,
+ entities: kept,
+ })
+
+ start = end
+ }
+
+ return parts
+}