-
Notifications
You must be signed in to change notification settings - Fork 3
/
utils.go
144 lines (136 loc) · 3.11 KB
/
utils.go
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package main
import (
"strings"
"unicode/utf8"
)
// RuneLen returns the rune count, not the byte count
func RuneLen(s string) int {
return utf8.RuneCountInString(s)
}
// Dimensions returns the width and height of a given ASCII art string
func Dimensions(asciiArt string) (int, int) {
maxWidth := 0
maxHeight := 0
lineCounter := 0
for _, line := range strings.Split(asciiArt, "\n") {
l := RuneLen(line)
if l > maxWidth {
maxWidth = l
}
lineCounter++
}
if lineCounter > maxHeight {
maxHeight = lineCounter
}
return maxWidth, maxHeight
}
// get returns a character at (x,y) in a multiline string.
// If anything go wrong, or if (x,y) is out of bounds, it return a space rune.
func get(s []rune, x, y, w, h int) rune {
if x < 0 || y < 0 {
return ' '
}
if x >= w || y >= h {
return ' '
}
// +1 to account for the trailing newlines
pos := y*w + x
if pos >= len(s) {
return ' '
}
r := s[pos]
switch r {
case '\n', '\t', '\r', '\v':
return ' '
}
return r
}
// toMap can convert from a multiline-string to an indexed slice of runes (y*w+x style)
func toMap(s string, w int) []rune {
rs := make([]rune, 0)
for _, line := range strings.Split(s, "\n") {
rs = append(rs, []rune(line)...)
linelen := RuneLen(line)
if linelen < w {
// Fill out the rest of the line with spaces
rs = append(rs, []rune(strings.Repeat(" ", w-linelen))...)
}
}
return rs
}
// SplitWords can split a string into words, keeping punctuation and trailing spaces
func SplitWords(s string) []string {
var (
splitpoint bool
words []string
letters strings.Builder
tmp string
)
lenS := RuneLen(s)
for i, r := range s {
splitpoint = false
switch r {
case '.', '!', ',', ':', '-', ' ', '?', ';', '\n':
// Check if the next character is not an end quote
if i+1 < lenS && s[i+1] != '"' && s[i+1] != '\'' {
splitpoint = true
}
}
// Combine repeated dashes
if r == '-' && i+1 < lenS && s[i+1] == '-' {
splitpoint = false
}
// Combine repeated dots
if r == '.' && i+1 < lenS && s[i+1] == '.' {
splitpoint = false
}
if splitpoint || i == lenS {
letters.WriteRune(r)
tmp = letters.String()
if RuneLen(tmp) > 0 {
words = append(words, tmp)
}
letters.Reset()
} else {
letters.WriteRune(r)
}
}
tmp = strings.TrimSpace(letters.String())
if RuneLen(tmp) > 0 {
words = append(words, tmp)
}
return words
}
// SplitWidthWords can split a string by words, then combine to form lines maximum w long
func SplitWidthWords(s string, w int) []string {
var (
sl []string
line string
)
for _, word := range SplitWords(s) {
if RuneLen(line)+RuneLen(word) < w {
line += word
} else {
trimmedLine := strings.TrimSpace(line)
if strings.HasSuffix(trimmedLine, "--") {
// Move the double dash to the beginning of the next line
trimmedLine = trimmedLine[:RuneLen(trimmedLine)-2]
sl = append(sl, trimmedLine)
line = "-- " + word
} else {
sl = append(sl, trimmedLine)
line = word
}
}
}
if RuneLen(line) == 0 {
return sl
}
return append(sl, strings.TrimSpace(line))
}
func min(a, b int) int {
if a < b {
return a
}
return b
}