-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsplit.go
More file actions
61 lines (57 loc) · 1.79 KB
/
Copy pathsplit.go
File metadata and controls
61 lines (57 loc) · 1.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// SPDX-License-Identifier: Apache-2.0
package tg
import "strings"
// MaxMessageRunes is Telegram's limit for sendMessage text. Rich messages are
// not bound by it, which is why long output belongs in one of those.
const MaxMessageRunes = 4096
// SplitText cuts plain text into parts of at most limit runes, preferring a
// line break and then a space near the end of each part, so words and lines
// survive. Runes are never split.
//
// It deliberately knows nothing about HTML: cutting markup safely means
// closing the open tags at the cut and reopening them after it, which needs a
// parser and a decision about which tags a bot allows. A caller that escapes
// or wraps its text must measure the result it actually sends — see how voicy
// splits an escaped, quoted transcript — rather than assume this function's
// count matches.
func SplitText(text string, limit int) []string {
if limit <= 0 {
return nil
}
runes := []rune(text)
if len(runes) == 0 {
return nil
}
var out []string
for len(runes) > 0 {
if len(runes) <= limit {
if part := strings.TrimSpace(string(runes)); part != "" {
out = append(out, part)
}
break
}
cut := limit
// Look back over the last 40% of the budget for a natural boundary: a
// line break first, a space second. Anything earlier would waste too
// much of the message.
floor := limit * 3 / 5
if i := lastIndexIn(runes, floor, limit, '\n'); i >= 0 {
cut = i + 1
} else if i := lastIndexIn(runes, floor, limit, ' '); i >= 0 {
cut = i + 1
}
if part := strings.TrimSpace(string(runes[:cut])); part != "" {
out = append(out, part)
}
runes = runes[cut:]
}
return out
}
func lastIndexIn(runes []rune, from, to int, target rune) int {
for i := to - 1; i >= from; i-- {
if runes[i] == target {
return i
}
}
return -1
}