doing 2.0.20 → 2.0.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +1 -1
  5. data/doing.rdoc +1 -1
  6. data/lib/doing/version.rb +1 -1
  7. data/lib/helpers/fzf/.goreleaser.yml +119 -0
  8. data/lib/helpers/fzf/.rubocop.yml +28 -0
  9. data/lib/helpers/fzf/ADVANCED.md +565 -0
  10. data/lib/helpers/fzf/BUILD.md +49 -0
  11. data/lib/helpers/fzf/CHANGELOG.md +1193 -0
  12. data/lib/helpers/fzf/Dockerfile +11 -0
  13. data/lib/helpers/fzf/LICENSE +21 -0
  14. data/lib/helpers/fzf/Makefile +166 -0
  15. data/lib/helpers/fzf/README-VIM.md +486 -0
  16. data/lib/helpers/fzf/README.md +712 -0
  17. data/lib/helpers/fzf/bin/fzf-tmux +233 -0
  18. data/lib/helpers/fzf/doc/fzf.txt +512 -0
  19. data/lib/helpers/fzf/go.mod +17 -0
  20. data/lib/helpers/fzf/go.sum +31 -0
  21. data/lib/helpers/fzf/install +382 -0
  22. data/lib/helpers/fzf/install.ps1 +65 -0
  23. data/lib/helpers/fzf/main.go +14 -0
  24. data/lib/helpers/fzf/man/man1/fzf-tmux.1 +68 -0
  25. data/lib/helpers/fzf/man/man1/fzf.1 +1001 -0
  26. data/lib/helpers/fzf/plugin/fzf.vim +1048 -0
  27. data/lib/helpers/fzf/shell/completion.bash +381 -0
  28. data/lib/helpers/fzf/shell/completion.zsh +329 -0
  29. data/lib/helpers/fzf/shell/key-bindings.bash +96 -0
  30. data/lib/helpers/fzf/shell/key-bindings.fish +172 -0
  31. data/lib/helpers/fzf/shell/key-bindings.zsh +114 -0
  32. data/lib/helpers/fzf/src/LICENSE +21 -0
  33. data/lib/helpers/fzf/src/algo/algo.go +884 -0
  34. data/lib/helpers/fzf/src/algo/algo_test.go +197 -0
  35. data/lib/helpers/fzf/src/algo/normalize.go +492 -0
  36. data/lib/helpers/fzf/src/ansi.go +409 -0
  37. data/lib/helpers/fzf/src/ansi_test.go +427 -0
  38. data/lib/helpers/fzf/src/cache.go +81 -0
  39. data/lib/helpers/fzf/src/cache_test.go +39 -0
  40. data/lib/helpers/fzf/src/chunklist.go +89 -0
  41. data/lib/helpers/fzf/src/chunklist_test.go +80 -0
  42. data/lib/helpers/fzf/src/constants.go +85 -0
  43. data/lib/helpers/fzf/src/core.go +351 -0
  44. data/lib/helpers/fzf/src/history.go +96 -0
  45. data/lib/helpers/fzf/src/history_test.go +68 -0
  46. data/lib/helpers/fzf/src/item.go +44 -0
  47. data/lib/helpers/fzf/src/item_test.go +23 -0
  48. data/lib/helpers/fzf/src/matcher.go +235 -0
  49. data/lib/helpers/fzf/src/merger.go +120 -0
  50. data/lib/helpers/fzf/src/merger_test.go +88 -0
  51. data/lib/helpers/fzf/src/options.go +1691 -0
  52. data/lib/helpers/fzf/src/options_test.go +457 -0
  53. data/lib/helpers/fzf/src/pattern.go +425 -0
  54. data/lib/helpers/fzf/src/pattern_test.go +209 -0
  55. data/lib/helpers/fzf/src/protector/protector.go +8 -0
  56. data/lib/helpers/fzf/src/protector/protector_openbsd.go +10 -0
  57. data/lib/helpers/fzf/src/reader.go +201 -0
  58. data/lib/helpers/fzf/src/reader_test.go +63 -0
  59. data/lib/helpers/fzf/src/result.go +243 -0
  60. data/lib/helpers/fzf/src/result_others.go +16 -0
  61. data/lib/helpers/fzf/src/result_test.go +159 -0
  62. data/lib/helpers/fzf/src/result_x86.go +16 -0
  63. data/lib/helpers/fzf/src/terminal.go +2832 -0
  64. data/lib/helpers/fzf/src/terminal_test.go +638 -0
  65. data/lib/helpers/fzf/src/terminal_unix.go +26 -0
  66. data/lib/helpers/fzf/src/terminal_windows.go +45 -0
  67. data/lib/helpers/fzf/src/tokenizer.go +253 -0
  68. data/lib/helpers/fzf/src/tokenizer_test.go +112 -0
  69. data/lib/helpers/fzf/src/tui/dummy.go +46 -0
  70. data/lib/helpers/fzf/src/tui/light.go +987 -0
  71. data/lib/helpers/fzf/src/tui/light_unix.go +110 -0
  72. data/lib/helpers/fzf/src/tui/light_windows.go +145 -0
  73. data/lib/helpers/fzf/src/tui/tcell.go +721 -0
  74. data/lib/helpers/fzf/src/tui/tcell_test.go +392 -0
  75. data/lib/helpers/fzf/src/tui/ttyname_unix.go +47 -0
  76. data/lib/helpers/fzf/src/tui/ttyname_windows.go +14 -0
  77. data/lib/helpers/fzf/src/tui/tui.go +625 -0
  78. data/lib/helpers/fzf/src/tui/tui_test.go +20 -0
  79. data/lib/helpers/fzf/src/util/atomicbool.go +34 -0
  80. data/lib/helpers/fzf/src/util/atomicbool_test.go +17 -0
  81. data/lib/helpers/fzf/src/util/chars.go +198 -0
  82. data/lib/helpers/fzf/src/util/chars_test.go +46 -0
  83. data/lib/helpers/fzf/src/util/eventbox.go +96 -0
  84. data/lib/helpers/fzf/src/util/eventbox_test.go +61 -0
  85. data/lib/helpers/fzf/src/util/slab.go +12 -0
  86. data/lib/helpers/fzf/src/util/util.go +138 -0
  87. data/lib/helpers/fzf/src/util/util_test.go +40 -0
  88. data/lib/helpers/fzf/src/util/util_unix.go +47 -0
  89. data/lib/helpers/fzf/src/util/util_windows.go +83 -0
  90. data/lib/helpers/fzf/test/fzf.vader +175 -0
  91. data/lib/helpers/fzf/test/test_go.rb +2626 -0
  92. data/lib/helpers/fzf/uninstall +117 -0
  93. metadata +87 -1
@@ -0,0 +1,425 @@
1
+ package fzf
2
+
3
+ import (
4
+ "fmt"
5
+ "regexp"
6
+ "strings"
7
+
8
+ "github.com/junegunn/fzf/src/algo"
9
+ "github.com/junegunn/fzf/src/util"
10
+ )
11
+
12
+ // fuzzy
13
+ // 'exact
14
+ // ^prefix-exact
15
+ // suffix-exact$
16
+ // !inverse-exact
17
+ // !'inverse-fuzzy
18
+ // !^inverse-prefix-exact
19
+ // !inverse-suffix-exact$
20
+
21
+ type termType int
22
+
23
+ const (
24
+ termFuzzy termType = iota
25
+ termExact
26
+ termPrefix
27
+ termSuffix
28
+ termEqual
29
+ )
30
+
31
+ type term struct {
32
+ typ termType
33
+ inv bool
34
+ text []rune
35
+ caseSensitive bool
36
+ normalize bool
37
+ }
38
+
39
+ // String returns the string representation of a term.
40
+ func (t term) String() string {
41
+ return fmt.Sprintf("term{typ: %d, inv: %v, text: []rune(%q), caseSensitive: %v}", t.typ, t.inv, string(t.text), t.caseSensitive)
42
+ }
43
+
44
+ type termSet []term
45
+
46
+ // Pattern represents search pattern
47
+ type Pattern struct {
48
+ fuzzy bool
49
+ fuzzyAlgo algo.Algo
50
+ extended bool
51
+ caseSensitive bool
52
+ normalize bool
53
+ forward bool
54
+ text []rune
55
+ termSets []termSet
56
+ sortable bool
57
+ cacheable bool
58
+ cacheKey string
59
+ delimiter Delimiter
60
+ nth []Range
61
+ procFun map[termType]algo.Algo
62
+ }
63
+
64
+ var (
65
+ _patternCache map[string]*Pattern
66
+ _splitRegex *regexp.Regexp
67
+ _cache ChunkCache
68
+ )
69
+
70
+ func init() {
71
+ _splitRegex = regexp.MustCompile(" +")
72
+ clearPatternCache()
73
+ clearChunkCache()
74
+ }
75
+
76
+ func clearPatternCache() {
77
+ // We can uniquely identify the pattern for a given string since
78
+ // search mode and caseMode do not change while the program is running
79
+ _patternCache = make(map[string]*Pattern)
80
+ }
81
+
82
+ func clearChunkCache() {
83
+ _cache = NewChunkCache()
84
+ }
85
+
86
+ // BuildPattern builds Pattern object from the given arguments
87
+ func BuildPattern(fuzzy bool, fuzzyAlgo algo.Algo, extended bool, caseMode Case, normalize bool, forward bool,
88
+ cacheable bool, nth []Range, delimiter Delimiter, runes []rune) *Pattern {
89
+
90
+ var asString string
91
+ if extended {
92
+ asString = strings.TrimLeft(string(runes), " ")
93
+ for strings.HasSuffix(asString, " ") && !strings.HasSuffix(asString, "\\ ") {
94
+ asString = asString[:len(asString)-1]
95
+ }
96
+ } else {
97
+ asString = string(runes)
98
+ }
99
+
100
+ cached, found := _patternCache[asString]
101
+ if found {
102
+ return cached
103
+ }
104
+
105
+ caseSensitive := true
106
+ sortable := true
107
+ termSets := []termSet{}
108
+
109
+ if extended {
110
+ termSets = parseTerms(fuzzy, caseMode, normalize, asString)
111
+ // We should not sort the result if there are only inverse search terms
112
+ sortable = false
113
+ Loop:
114
+ for _, termSet := range termSets {
115
+ for idx, term := range termSet {
116
+ if !term.inv {
117
+ sortable = true
118
+ }
119
+ // If the query contains inverse search terms or OR operators,
120
+ // we cannot cache the search scope
121
+ if !cacheable || idx > 0 || term.inv || fuzzy && term.typ != termFuzzy || !fuzzy && term.typ != termExact {
122
+ cacheable = false
123
+ if sortable {
124
+ // Can't break until we see at least one non-inverse term
125
+ break Loop
126
+ }
127
+ }
128
+ }
129
+ }
130
+ } else {
131
+ lowerString := strings.ToLower(asString)
132
+ normalize = normalize &&
133
+ lowerString == string(algo.NormalizeRunes([]rune(lowerString)))
134
+ caseSensitive = caseMode == CaseRespect ||
135
+ caseMode == CaseSmart && lowerString != asString
136
+ if !caseSensitive {
137
+ asString = lowerString
138
+ }
139
+ }
140
+
141
+ ptr := &Pattern{
142
+ fuzzy: fuzzy,
143
+ fuzzyAlgo: fuzzyAlgo,
144
+ extended: extended,
145
+ caseSensitive: caseSensitive,
146
+ normalize: normalize,
147
+ forward: forward,
148
+ text: []rune(asString),
149
+ termSets: termSets,
150
+ sortable: sortable,
151
+ cacheable: cacheable,
152
+ nth: nth,
153
+ delimiter: delimiter,
154
+ procFun: make(map[termType]algo.Algo)}
155
+
156
+ ptr.cacheKey = ptr.buildCacheKey()
157
+ ptr.procFun[termFuzzy] = fuzzyAlgo
158
+ ptr.procFun[termEqual] = algo.EqualMatch
159
+ ptr.procFun[termExact] = algo.ExactMatchNaive
160
+ ptr.procFun[termPrefix] = algo.PrefixMatch
161
+ ptr.procFun[termSuffix] = algo.SuffixMatch
162
+
163
+ _patternCache[asString] = ptr
164
+ return ptr
165
+ }
166
+
167
+ func parseTerms(fuzzy bool, caseMode Case, normalize bool, str string) []termSet {
168
+ str = strings.Replace(str, "\\ ", "\t", -1)
169
+ tokens := _splitRegex.Split(str, -1)
170
+ sets := []termSet{}
171
+ set := termSet{}
172
+ switchSet := false
173
+ afterBar := false
174
+ for _, token := range tokens {
175
+ typ, inv, text := termFuzzy, false, strings.Replace(token, "\t", " ", -1)
176
+ lowerText := strings.ToLower(text)
177
+ caseSensitive := caseMode == CaseRespect ||
178
+ caseMode == CaseSmart && text != lowerText
179
+ normalizeTerm := normalize &&
180
+ lowerText == string(algo.NormalizeRunes([]rune(lowerText)))
181
+ if !caseSensitive {
182
+ text = lowerText
183
+ }
184
+ if !fuzzy {
185
+ typ = termExact
186
+ }
187
+
188
+ if len(set) > 0 && !afterBar && text == "|" {
189
+ switchSet = false
190
+ afterBar = true
191
+ continue
192
+ }
193
+ afterBar = false
194
+
195
+ if strings.HasPrefix(text, "!") {
196
+ inv = true
197
+ typ = termExact
198
+ text = text[1:]
199
+ }
200
+
201
+ if text != "$" && strings.HasSuffix(text, "$") {
202
+ typ = termSuffix
203
+ text = text[:len(text)-1]
204
+ }
205
+
206
+ if strings.HasPrefix(text, "'") {
207
+ // Flip exactness
208
+ if fuzzy && !inv {
209
+ typ = termExact
210
+ text = text[1:]
211
+ } else {
212
+ typ = termFuzzy
213
+ text = text[1:]
214
+ }
215
+ } else if strings.HasPrefix(text, "^") {
216
+ if typ == termSuffix {
217
+ typ = termEqual
218
+ } else {
219
+ typ = termPrefix
220
+ }
221
+ text = text[1:]
222
+ }
223
+
224
+ if len(text) > 0 {
225
+ if switchSet {
226
+ sets = append(sets, set)
227
+ set = termSet{}
228
+ }
229
+ textRunes := []rune(text)
230
+ if normalizeTerm {
231
+ textRunes = algo.NormalizeRunes(textRunes)
232
+ }
233
+ set = append(set, term{
234
+ typ: typ,
235
+ inv: inv,
236
+ text: textRunes,
237
+ caseSensitive: caseSensitive,
238
+ normalize: normalizeTerm})
239
+ switchSet = true
240
+ }
241
+ }
242
+ if len(set) > 0 {
243
+ sets = append(sets, set)
244
+ }
245
+ return sets
246
+ }
247
+
248
+ // IsEmpty returns true if the pattern is effectively empty
249
+ func (p *Pattern) IsEmpty() bool {
250
+ if !p.extended {
251
+ return len(p.text) == 0
252
+ }
253
+ return len(p.termSets) == 0
254
+ }
255
+
256
+ // AsString returns the search query in string type
257
+ func (p *Pattern) AsString() string {
258
+ return string(p.text)
259
+ }
260
+
261
+ func (p *Pattern) buildCacheKey() string {
262
+ if !p.extended {
263
+ return p.AsString()
264
+ }
265
+ cacheableTerms := []string{}
266
+ for _, termSet := range p.termSets {
267
+ if len(termSet) == 1 && !termSet[0].inv && (p.fuzzy || termSet[0].typ == termExact) {
268
+ cacheableTerms = append(cacheableTerms, string(termSet[0].text))
269
+ }
270
+ }
271
+ return strings.Join(cacheableTerms, "\t")
272
+ }
273
+
274
+ // CacheKey is used to build string to be used as the key of result cache
275
+ func (p *Pattern) CacheKey() string {
276
+ return p.cacheKey
277
+ }
278
+
279
+ // Match returns the list of matches Items in the given Chunk
280
+ func (p *Pattern) Match(chunk *Chunk, slab *util.Slab) []Result {
281
+ // ChunkCache: Exact match
282
+ cacheKey := p.CacheKey()
283
+ if p.cacheable {
284
+ if cached := _cache.Lookup(chunk, cacheKey); cached != nil {
285
+ return cached
286
+ }
287
+ }
288
+
289
+ // Prefix/suffix cache
290
+ space := _cache.Search(chunk, cacheKey)
291
+
292
+ matches := p.matchChunk(chunk, space, slab)
293
+
294
+ if p.cacheable {
295
+ _cache.Add(chunk, cacheKey, matches)
296
+ }
297
+ return matches
298
+ }
299
+
300
+ func (p *Pattern) matchChunk(chunk *Chunk, space []Result, slab *util.Slab) []Result {
301
+ matches := []Result{}
302
+
303
+ if space == nil {
304
+ for idx := 0; idx < chunk.count; idx++ {
305
+ if match, _, _ := p.MatchItem(&chunk.items[idx], false, slab); match != nil {
306
+ matches = append(matches, *match)
307
+ }
308
+ }
309
+ } else {
310
+ for _, result := range space {
311
+ if match, _, _ := p.MatchItem(result.item, false, slab); match != nil {
312
+ matches = append(matches, *match)
313
+ }
314
+ }
315
+ }
316
+ return matches
317
+ }
318
+
319
+ // MatchItem returns true if the Item is a match
320
+ func (p *Pattern) MatchItem(item *Item, withPos bool, slab *util.Slab) (*Result, []Offset, *[]int) {
321
+ if p.extended {
322
+ if offsets, bonus, pos := p.extendedMatch(item, withPos, slab); len(offsets) == len(p.termSets) {
323
+ result := buildResult(item, offsets, bonus)
324
+ return &result, offsets, pos
325
+ }
326
+ return nil, nil, nil
327
+ }
328
+ offset, bonus, pos := p.basicMatch(item, withPos, slab)
329
+ if sidx := offset[0]; sidx >= 0 {
330
+ offsets := []Offset{offset}
331
+ result := buildResult(item, offsets, bonus)
332
+ return &result, offsets, pos
333
+ }
334
+ return nil, nil, nil
335
+ }
336
+
337
+ func (p *Pattern) basicMatch(item *Item, withPos bool, slab *util.Slab) (Offset, int, *[]int) {
338
+ var input []Token
339
+ if len(p.nth) == 0 {
340
+ input = []Token{{text: &item.text, prefixLength: 0}}
341
+ } else {
342
+ input = p.transformInput(item)
343
+ }
344
+ if p.fuzzy {
345
+ return p.iter(p.fuzzyAlgo, input, p.caseSensitive, p.normalize, p.forward, p.text, withPos, slab)
346
+ }
347
+ return p.iter(algo.ExactMatchNaive, input, p.caseSensitive, p.normalize, p.forward, p.text, withPos, slab)
348
+ }
349
+
350
+ func (p *Pattern) extendedMatch(item *Item, withPos bool, slab *util.Slab) ([]Offset, int, *[]int) {
351
+ var input []Token
352
+ if len(p.nth) == 0 {
353
+ input = []Token{{text: &item.text, prefixLength: 0}}
354
+ } else {
355
+ input = p.transformInput(item)
356
+ }
357
+ offsets := []Offset{}
358
+ var totalScore int
359
+ var allPos *[]int
360
+ if withPos {
361
+ allPos = &[]int{}
362
+ }
363
+ for _, termSet := range p.termSets {
364
+ var offset Offset
365
+ var currentScore int
366
+ matched := false
367
+ for _, term := range termSet {
368
+ pfun := p.procFun[term.typ]
369
+ off, score, pos := p.iter(pfun, input, term.caseSensitive, term.normalize, p.forward, term.text, withPos, slab)
370
+ if sidx := off[0]; sidx >= 0 {
371
+ if term.inv {
372
+ continue
373
+ }
374
+ offset, currentScore = off, score
375
+ matched = true
376
+ if withPos {
377
+ if pos != nil {
378
+ *allPos = append(*allPos, *pos...)
379
+ } else {
380
+ for idx := off[0]; idx < off[1]; idx++ {
381
+ *allPos = append(*allPos, int(idx))
382
+ }
383
+ }
384
+ }
385
+ break
386
+ } else if term.inv {
387
+ offset, currentScore = Offset{0, 0}, 0
388
+ matched = true
389
+ continue
390
+ }
391
+ }
392
+ if matched {
393
+ offsets = append(offsets, offset)
394
+ totalScore += currentScore
395
+ }
396
+ }
397
+ return offsets, totalScore, allPos
398
+ }
399
+
400
+ func (p *Pattern) transformInput(item *Item) []Token {
401
+ if item.transformed != nil {
402
+ return *item.transformed
403
+ }
404
+
405
+ tokens := Tokenize(item.text.ToString(), p.delimiter)
406
+ ret := Transform(tokens, p.nth)
407
+ item.transformed = &ret
408
+ return ret
409
+ }
410
+
411
+ func (p *Pattern) iter(pfun algo.Algo, tokens []Token, caseSensitive bool, normalize bool, forward bool, pattern []rune, withPos bool, slab *util.Slab) (Offset, int, *[]int) {
412
+ for _, part := range tokens {
413
+ if res, pos := pfun(caseSensitive, normalize, forward, part.text, pattern, withPos, slab); res.Start >= 0 {
414
+ sidx := int32(res.Start) + part.prefixLength
415
+ eidx := int32(res.End) + part.prefixLength
416
+ if pos != nil {
417
+ for idx := range *pos {
418
+ (*pos)[idx] += int(part.prefixLength)
419
+ }
420
+ }
421
+ return Offset{sidx, eidx}, res.Score, pos
422
+ }
423
+ }
424
+ return Offset{-1, -1}, 0, nil
425
+ }
@@ -0,0 +1,209 @@
1
+ package fzf
2
+
3
+ import (
4
+ "reflect"
5
+ "testing"
6
+
7
+ "github.com/junegunn/fzf/src/algo"
8
+ "github.com/junegunn/fzf/src/util"
9
+ )
10
+
11
+ var slab *util.Slab
12
+
13
+ func init() {
14
+ slab = util.MakeSlab(slab16Size, slab32Size)
15
+ }
16
+
17
+ func TestParseTermsExtended(t *testing.T) {
18
+ terms := parseTerms(true, CaseSmart, false,
19
+ "aaa 'bbb ^ccc ddd$ !eee !'fff !^ggg !hhh$ | ^iii$ ^xxx | 'yyy | zzz$ | !ZZZ |")
20
+ if len(terms) != 9 ||
21
+ terms[0][0].typ != termFuzzy || terms[0][0].inv ||
22
+ terms[1][0].typ != termExact || terms[1][0].inv ||
23
+ terms[2][0].typ != termPrefix || terms[2][0].inv ||
24
+ terms[3][0].typ != termSuffix || terms[3][0].inv ||
25
+ terms[4][0].typ != termExact || !terms[4][0].inv ||
26
+ terms[5][0].typ != termFuzzy || !terms[5][0].inv ||
27
+ terms[6][0].typ != termPrefix || !terms[6][0].inv ||
28
+ terms[7][0].typ != termSuffix || !terms[7][0].inv ||
29
+ terms[7][1].typ != termEqual || terms[7][1].inv ||
30
+ terms[8][0].typ != termPrefix || terms[8][0].inv ||
31
+ terms[8][1].typ != termExact || terms[8][1].inv ||
32
+ terms[8][2].typ != termSuffix || terms[8][2].inv ||
33
+ terms[8][3].typ != termExact || !terms[8][3].inv {
34
+ t.Errorf("%v", terms)
35
+ }
36
+ for _, termSet := range terms[:8] {
37
+ term := termSet[0]
38
+ if len(term.text) != 3 {
39
+ t.Errorf("%v", term)
40
+ }
41
+ }
42
+ }
43
+
44
+ func TestParseTermsExtendedExact(t *testing.T) {
45
+ terms := parseTerms(false, CaseSmart, false,
46
+ "aaa 'bbb ^ccc ddd$ !eee !'fff !^ggg !hhh$")
47
+ if len(terms) != 8 ||
48
+ terms[0][0].typ != termExact || terms[0][0].inv || len(terms[0][0].text) != 3 ||
49
+ terms[1][0].typ != termFuzzy || terms[1][0].inv || len(terms[1][0].text) != 3 ||
50
+ terms[2][0].typ != termPrefix || terms[2][0].inv || len(terms[2][0].text) != 3 ||
51
+ terms[3][0].typ != termSuffix || terms[3][0].inv || len(terms[3][0].text) != 3 ||
52
+ terms[4][0].typ != termExact || !terms[4][0].inv || len(terms[4][0].text) != 3 ||
53
+ terms[5][0].typ != termFuzzy || !terms[5][0].inv || len(terms[5][0].text) != 3 ||
54
+ terms[6][0].typ != termPrefix || !terms[6][0].inv || len(terms[6][0].text) != 3 ||
55
+ terms[7][0].typ != termSuffix || !terms[7][0].inv || len(terms[7][0].text) != 3 {
56
+ t.Errorf("%v", terms)
57
+ }
58
+ }
59
+
60
+ func TestParseTermsEmpty(t *testing.T) {
61
+ terms := parseTerms(true, CaseSmart, false, "' ^ !' !^")
62
+ if len(terms) != 0 {
63
+ t.Errorf("%v", terms)
64
+ }
65
+ }
66
+
67
+ func TestExact(t *testing.T) {
68
+ defer clearPatternCache()
69
+ clearPatternCache()
70
+ pattern := BuildPattern(true, algo.FuzzyMatchV2, true, CaseSmart, false, true, true,
71
+ []Range{}, Delimiter{}, []rune("'abc"))
72
+ chars := util.ToChars([]byte("aabbcc abc"))
73
+ res, pos := algo.ExactMatchNaive(
74
+ pattern.caseSensitive, pattern.normalize, pattern.forward, &chars, pattern.termSets[0][0].text, true, nil)
75
+ if res.Start != 7 || res.End != 10 {
76
+ t.Errorf("%v / %d / %d", pattern.termSets, res.Start, res.End)
77
+ }
78
+ if pos != nil {
79
+ t.Errorf("pos is expected to be nil")
80
+ }
81
+ }
82
+
83
+ func TestEqual(t *testing.T) {
84
+ defer clearPatternCache()
85
+ clearPatternCache()
86
+ pattern := BuildPattern(true, algo.FuzzyMatchV2, true, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune("^AbC$"))
87
+
88
+ match := func(str string, sidxExpected int, eidxExpected int) {
89
+ chars := util.ToChars([]byte(str))
90
+ res, pos := algo.EqualMatch(
91
+ pattern.caseSensitive, pattern.normalize, pattern.forward, &chars, pattern.termSets[0][0].text, true, nil)
92
+ if res.Start != sidxExpected || res.End != eidxExpected {
93
+ t.Errorf("%v / %d / %d", pattern.termSets, res.Start, res.End)
94
+ }
95
+ if pos != nil {
96
+ t.Errorf("pos is expected to be nil")
97
+ }
98
+ }
99
+ match("ABC", -1, -1)
100
+ match("AbC", 0, 3)
101
+ match("AbC ", 0, 3)
102
+ match(" AbC ", 1, 4)
103
+ match(" AbC", 2, 5)
104
+ }
105
+
106
+ func TestCaseSensitivity(t *testing.T) {
107
+ defer clearPatternCache()
108
+ clearPatternCache()
109
+ pat1 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune("abc"))
110
+ clearPatternCache()
111
+ pat2 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune("Abc"))
112
+ clearPatternCache()
113
+ pat3 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseIgnore, false, true, true, []Range{}, Delimiter{}, []rune("abc"))
114
+ clearPatternCache()
115
+ pat4 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseIgnore, false, true, true, []Range{}, Delimiter{}, []rune("Abc"))
116
+ clearPatternCache()
117
+ pat5 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseRespect, false, true, true, []Range{}, Delimiter{}, []rune("abc"))
118
+ clearPatternCache()
119
+ pat6 := BuildPattern(true, algo.FuzzyMatchV2, false, CaseRespect, false, true, true, []Range{}, Delimiter{}, []rune("Abc"))
120
+
121
+ if string(pat1.text) != "abc" || pat1.caseSensitive != false ||
122
+ string(pat2.text) != "Abc" || pat2.caseSensitive != true ||
123
+ string(pat3.text) != "abc" || pat3.caseSensitive != false ||
124
+ string(pat4.text) != "abc" || pat4.caseSensitive != false ||
125
+ string(pat5.text) != "abc" || pat5.caseSensitive != true ||
126
+ string(pat6.text) != "Abc" || pat6.caseSensitive != true {
127
+ t.Error("Invalid case conversion")
128
+ }
129
+ }
130
+
131
+ func TestOrigTextAndTransformed(t *testing.T) {
132
+ pattern := BuildPattern(true, algo.FuzzyMatchV2, true, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune("jg"))
133
+ tokens := Tokenize("junegunn", Delimiter{})
134
+ trans := Transform(tokens, []Range{{1, 1}})
135
+
136
+ origBytes := []byte("junegunn.choi")
137
+ for _, extended := range []bool{false, true} {
138
+ chunk := Chunk{count: 1}
139
+ chunk.items[0] = Item{
140
+ text: util.ToChars([]byte("junegunn")),
141
+ origText: &origBytes,
142
+ transformed: &trans}
143
+ pattern.extended = extended
144
+ matches := pattern.matchChunk(&chunk, nil, slab) // No cache
145
+ if !(matches[0].item.text.ToString() == "junegunn" &&
146
+ string(*matches[0].item.origText) == "junegunn.choi" &&
147
+ reflect.DeepEqual(*matches[0].item.transformed, trans)) {
148
+ t.Error("Invalid match result", matches)
149
+ }
150
+
151
+ match, offsets, pos := pattern.MatchItem(&chunk.items[0], true, slab)
152
+ if !(match.item.text.ToString() == "junegunn" &&
153
+ string(*match.item.origText) == "junegunn.choi" &&
154
+ offsets[0][0] == 0 && offsets[0][1] == 5 &&
155
+ reflect.DeepEqual(*match.item.transformed, trans)) {
156
+ t.Error("Invalid match result", match, offsets, extended)
157
+ }
158
+ if !((*pos)[0] == 4 && (*pos)[1] == 0) {
159
+ t.Error("Invalid pos array", *pos)
160
+ }
161
+ }
162
+ }
163
+
164
+ func TestCacheKey(t *testing.T) {
165
+ test := func(extended bool, patStr string, expected string, cacheable bool) {
166
+ clearPatternCache()
167
+ pat := BuildPattern(true, algo.FuzzyMatchV2, extended, CaseSmart, false, true, true, []Range{}, Delimiter{}, []rune(patStr))
168
+ if pat.CacheKey() != expected {
169
+ t.Errorf("Expected: %s, actual: %s", expected, pat.CacheKey())
170
+ }
171
+ if pat.cacheable != cacheable {
172
+ t.Errorf("Expected: %t, actual: %t (%s)", cacheable, pat.cacheable, patStr)
173
+ }
174
+ clearPatternCache()
175
+ }
176
+ test(false, "foo !bar", "foo !bar", true)
177
+ test(false, "foo | bar !baz", "foo | bar !baz", true)
178
+ test(true, "foo bar baz", "foo\tbar\tbaz", true)
179
+ test(true, "foo !bar", "foo", false)
180
+ test(true, "foo !bar baz", "foo\tbaz", false)
181
+ test(true, "foo | bar baz", "baz", false)
182
+ test(true, "foo | bar | baz", "", false)
183
+ test(true, "foo | bar !baz", "", false)
184
+ test(true, "| | foo", "", false)
185
+ test(true, "| | | foo", "foo", false)
186
+ }
187
+
188
+ func TestCacheable(t *testing.T) {
189
+ test := func(fuzzy bool, str string, expected string, cacheable bool) {
190
+ clearPatternCache()
191
+ pat := BuildPattern(fuzzy, algo.FuzzyMatchV2, true, CaseSmart, true, true, true, []Range{}, Delimiter{}, []rune(str))
192
+ if pat.CacheKey() != expected {
193
+ t.Errorf("Expected: %s, actual: %s", expected, pat.CacheKey())
194
+ }
195
+ if cacheable != pat.cacheable {
196
+ t.Errorf("Invalid Pattern.cacheable for \"%s\": %v (expected: %v)", str, pat.cacheable, cacheable)
197
+ }
198
+ clearPatternCache()
199
+ }
200
+ test(true, "foo bar", "foo\tbar", true)
201
+ test(true, "foo 'bar", "foo\tbar", false)
202
+ test(true, "foo !bar", "foo", false)
203
+
204
+ test(false, "foo bar", "foo\tbar", true)
205
+ test(false, "foo 'bar", "foo", false)
206
+ test(false, "foo '", "foo", true)
207
+ test(false, "foo 'bar", "foo", false)
208
+ test(false, "foo !bar", "foo", false)
209
+ }
@@ -0,0 +1,8 @@
1
+ // +build !openbsd
2
+
3
+ package protector
4
+
5
+ // Protect calls OS specific protections like pledge on OpenBSD
6
+ func Protect() {
7
+ return
8
+ }
@@ -0,0 +1,10 @@
1
+ // +build openbsd
2
+
3
+ package protector
4
+
5
+ import "golang.org/x/sys/unix"
6
+
7
+ // Protect calls OS specific protections like pledge on OpenBSD
8
+ func Protect() {
9
+ unix.PledgePromises("stdio rpath tty proc exec")
10
+ }