-
Notifications
You must be signed in to change notification settings - Fork 1
/
line.go
185 lines (154 loc) · 4.38 KB
/
line.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package main
import (
"strings"
"unicode/utf8"
)
//Line represents the line of a Stanza.
//A Line includes the Text, which is a string (i.e. the lyrics)
//A series of Chords that appear in this Line (may be empty)
//And an EchoIndex, indicating the portion of the Line that is an echo
// (so it can be printed differently, i.e. italic/lighter/...)
type Line struct {
Text string
Chords []Chord
EchoIndex int
}
//HasChords returns true if this Line contains any Chord objects,
//false otherwise.
func (line Line) HasChords() bool {
return len(line.Chords) > 0
}
//HasEcho returns true if this Line has an EchoIndex assigned,
//false otherwise.
func (line Line) HasEcho() bool {
return line.EchoIndex >= 0
}
//PreEchoText returns this Line's Text up to EchoIndex if it is set.
//If EchoIndex is not set, then the full Text is returned.
func (line Line) PreEchoText() string {
if line.EchoIndex < 0 || line.EchoIndex >= utf8.RuneCountInString(line.Text) {
return line.Text
}
return line.Text[0:line.EchoIndex]
}
//EchoText returns this Line's text from EchoIndex to the end of the Text if
//EchoIndex is set. If EchoIndex is not set, an empty string is returned.
func (line Line) EchoText() string {
if line.EchoIndex < 0 || line.EchoIndex >= utf8.RuneCountInString(line.Text) {
return ""
}
return line.Text[line.EchoIndex:utf8.RuneCountInString(line.Text)]
}
//PreChordText returns the substring of Text that occurs before the given Chord
//and after the previous Chord (or the start of the Text if there is no previous
// Chord).
func (line Line) PreChordText(chord Chord) string {
//first, find the chord
ind := -1
for i, ch := range line.Chords {
if ch == chord {
ind = i
break
}
}
if ind < 0 {
return ""
}
//We need the text from the previous chord up to this chord
ind--
//chord is the first chord
if ind < 0 {
return line.Text[0:chord.Position]
}
pos := line.Chords[ind].Position + utf8.RuneCountInString(line.Chords[ind].GetText())
//if the chord text is long,
//We might have a problem here
if pos > chord.Position {
return ""
}
return line.Text[pos:chord.Position]
}
//SplitLine splits a single Line into two lines.
//If the line contains an Echo, the echo is the split point,
//otherwise if there is a comma near the center it is used,
//otherwise if there is a space near the center it is used,
//if all else fails, it will split at the middle character
func (line Line) SplitLine() []Line {
var split int
//special case
// if utf8.RuneCountInString(line.Text) < 2 {
// res := make([]Line, 0)
// return append(res, line)
// }
//If the line has an echo, split at the echo
if line.HasEcho() && line.EchoIndex > 0 {
split = line.EchoIndex
} else {
center := utf8.RuneCountInString(line.Text) / 2
maxWindow := utf8.RuneCountInString(line.Text) / 4
if maxWindow < 1 {
maxWindow = 1
}
//check for comma
window := 1
split = center
for center > 0 && window <= maxWindow {
split = strings.Index(line.Text[center-window:center+window], ",")
if split > 0 {
break
}
window++
}
//check for space
if split < 0 {
window = 1
for window <= maxWindow {
split = strings.Index(line.Text[center-window:center+window], " ")
if split > 0 {
break
}
window++
}
}
if split < 0 {
split = center
} else {
split += center - window + 1 //+1 so we don't include the comma/space on the next line
}
for line.Text[split] == ' ' || line.Text[split] == ',' {
split++
}
}
if split == 0 {
res := make([]Line, 1)
res[0] = line
return res
}
return line.splitLineAt(split)
}
func (line Line) splitLineAt(split int) []Line {
text1 := line.Text[0:split]
text2 := line.Text[split:]
chords1 := make([]Chord, 0)
chords2 := make([]Chord, 0)
//now split up the chords
if line.HasChords() {
for _, c := range line.Chords {
if c.Position <= utf8.RuneCountInString(text1) {
chords1 = append(chords1, c)
} else {
chords2 = append(chords2, Chord{c.GetText(), c.Position - utf8.RuneCountInString(text1), c.Transpose})
}
}
}
res := make([]Line, 0)
res = append(res, Line{Text: text1, Chords: chords1, EchoIndex: -1})
res = append(res, Line{Text: text2, Chords: chords2, EchoIndex: -1})
if line.EchoIndex >= 0 && line.EchoIndex < split {
res[0].EchoIndex = line.EchoIndex
res[1].EchoIndex = 0
} else if line.EchoIndex >= split {
res[1].EchoIndex = line.EchoIndex - split
}
return res
}