1package readline
2
3import (
4	"bytes"
5	"unicode"
6	"unicode/utf8"
7)
8
9var runes = Runes{}
10var TabWidth = 4
11
12type Runes struct{}
13
14func (Runes) EqualRune(a, b rune, fold bool) bool {
15	if a == b {
16		return true
17	}
18	if !fold {
19		return false
20	}
21	if a > b {
22		a, b = b, a
23	}
24	if b < utf8.RuneSelf && 'A' <= a && a <= 'Z' {
25		if b == a+'a'-'A' {
26			return true
27		}
28	}
29	return false
30}
31
32func (r Runes) EqualRuneFold(a, b rune) bool {
33	return r.EqualRune(a, b, true)
34}
35
36func (r Runes) EqualFold(a, b []rune) bool {
37	if len(a) != len(b) {
38		return false
39	}
40	for i := 0; i < len(a); i++ {
41		if r.EqualRuneFold(a[i], b[i]) {
42			continue
43		}
44		return false
45	}
46
47	return true
48}
49
50func (Runes) Equal(a, b []rune) bool {
51	if len(a) != len(b) {
52		return false
53	}
54	for i := 0; i < len(a); i++ {
55		if a[i] != b[i] {
56			return false
57		}
58	}
59	return true
60}
61
62func (rs Runes) IndexAllBckEx(r, sub []rune, fold bool) int {
63	for i := len(r) - len(sub); i >= 0; i-- {
64		found := true
65		for j := 0; j < len(sub); j++ {
66			if !rs.EqualRune(r[i+j], sub[j], fold) {
67				found = false
68				break
69			}
70		}
71		if found {
72			return i
73		}
74	}
75	return -1
76}
77
78// Search in runes from end to front
79func (rs Runes) IndexAllBck(r, sub []rune) int {
80	return rs.IndexAllBckEx(r, sub, false)
81}
82
83// Search in runes from front to end
84func (rs Runes) IndexAll(r, sub []rune) int {
85	return rs.IndexAllEx(r, sub, false)
86}
87
88func (rs Runes) IndexAllEx(r, sub []rune, fold bool) int {
89	for i := 0; i < len(r); i++ {
90		found := true
91		if len(r[i:]) < len(sub) {
92			return -1
93		}
94		for j := 0; j < len(sub); j++ {
95			if !rs.EqualRune(r[i+j], sub[j], fold) {
96				found = false
97				break
98			}
99		}
100		if found {
101			return i
102		}
103	}
104	return -1
105}
106
107func (Runes) Index(r rune, rs []rune) int {
108	for i := 0; i < len(rs); i++ {
109		if rs[i] == r {
110			return i
111		}
112	}
113	return -1
114}
115
116func (Runes) ColorFilter(r []rune) []rune {
117	newr := make([]rune, 0, len(r))
118	for pos := 0; pos < len(r); pos++ {
119		if r[pos] == '\033' && r[pos+1] == '[' {
120			idx := runes.Index('m', r[pos+2:])
121			if idx == -1 {
122				continue
123			}
124			pos += idx + 2
125			continue
126		}
127		newr = append(newr, r[pos])
128	}
129	return newr
130}
131
132var zeroWidth = []*unicode.RangeTable{
133	unicode.Mn,
134	unicode.Me,
135	unicode.Cc,
136	unicode.Cf,
137}
138
139var doubleWidth = []*unicode.RangeTable{
140	unicode.Han,
141	unicode.Hangul,
142	unicode.Hiragana,
143	unicode.Katakana,
144}
145
146func (Runes) Width(r rune) int {
147	if r == '\t' {
148		return TabWidth
149	}
150	if unicode.IsOneOf(zeroWidth, r) {
151		return 0
152	}
153	if unicode.IsOneOf(doubleWidth, r) {
154		return 2
155	}
156	return 1
157}
158
159func (Runes) WidthAll(r []rune) (length int) {
160	for i := 0; i < len(r); i++ {
161		length += runes.Width(r[i])
162	}
163	return
164}
165
166func (Runes) Backspace(r []rune) []byte {
167	return bytes.Repeat([]byte{'\b'}, runes.WidthAll(r))
168}
169
170func (Runes) Copy(r []rune) []rune {
171	n := make([]rune, len(r))
172	copy(n, r)
173	return n
174}
175
176func (Runes) HasPrefixFold(r, prefix []rune) bool {
177	if len(r) < len(prefix) {
178		return false
179	}
180	return runes.EqualFold(r[:len(prefix)], prefix)
181}
182
183func (Runes) HasPrefix(r, prefix []rune) bool {
184	if len(r) < len(prefix) {
185		return false
186	}
187	return runes.Equal(r[:len(prefix)], prefix)
188}
189
190func (Runes) Aggregate(candicate [][]rune) (same []rune, size int) {
191	for i := 0; i < len(candicate[0]); i++ {
192		for j := 0; j < len(candicate)-1; j++ {
193			if i >= len(candicate[j]) || i >= len(candicate[j+1]) {
194				goto aggregate
195			}
196			if candicate[j][i] != candicate[j+1][i] {
197				goto aggregate
198			}
199		}
200		size = i + 1
201	}
202aggregate:
203	if size > 0 {
204		same = runes.Copy(candicate[0][:size])
205		for i := 0; i < len(candicate); i++ {
206			n := runes.Copy(candicate[i])
207			copy(n, n[size:])
208			candicate[i] = n[:len(n)-size]
209		}
210	}
211	return
212}
213
214func (Runes) TrimSpaceLeft(in []rune) []rune {
215	firstIndex := len(in)
216	for i, r := range in {
217		if unicode.IsSpace(r) == false {
218			firstIndex = i
219			break
220		}
221	}
222	return in[firstIndex:]
223}
224