doing 2.0.20 → 2.0.24
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/doing.rdoc +1 -1
- data/lib/doing/log_adapter.rb +3 -1
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +19 -8
- data/lib/helpers/fzf/.goreleaser.yml +119 -0
- data/lib/helpers/fzf/.rubocop.yml +28 -0
- data/lib/helpers/fzf/ADVANCED.md +565 -0
- data/lib/helpers/fzf/BUILD.md +49 -0
- data/lib/helpers/fzf/CHANGELOG.md +1193 -0
- data/lib/helpers/fzf/Dockerfile +11 -0
- data/lib/helpers/fzf/LICENSE +21 -0
- data/lib/helpers/fzf/Makefile +166 -0
- data/lib/helpers/fzf/README-VIM.md +486 -0
- data/lib/helpers/fzf/README.md +712 -0
- data/lib/helpers/fzf/bin/fzf-tmux +233 -0
- data/lib/helpers/fzf/doc/fzf.txt +512 -0
- data/lib/helpers/fzf/go.mod +17 -0
- data/lib/helpers/fzf/go.sum +31 -0
- data/lib/helpers/fzf/install +382 -0
- data/lib/helpers/fzf/install.ps1 +65 -0
- data/lib/helpers/fzf/main.go +14 -0
- data/lib/helpers/fzf/man/man1/fzf-tmux.1 +68 -0
- data/lib/helpers/fzf/man/man1/fzf.1 +1001 -0
- data/lib/helpers/fzf/plugin/fzf.vim +1048 -0
- data/lib/helpers/fzf/shell/completion.bash +381 -0
- data/lib/helpers/fzf/shell/completion.zsh +329 -0
- data/lib/helpers/fzf/shell/key-bindings.bash +96 -0
- data/lib/helpers/fzf/shell/key-bindings.fish +172 -0
- data/lib/helpers/fzf/shell/key-bindings.zsh +114 -0
- data/lib/helpers/fzf/src/LICENSE +21 -0
- data/lib/helpers/fzf/src/algo/algo.go +884 -0
- data/lib/helpers/fzf/src/algo/algo_test.go +197 -0
- data/lib/helpers/fzf/src/algo/normalize.go +492 -0
- data/lib/helpers/fzf/src/ansi.go +409 -0
- data/lib/helpers/fzf/src/ansi_test.go +427 -0
- data/lib/helpers/fzf/src/cache.go +81 -0
- data/lib/helpers/fzf/src/cache_test.go +39 -0
- data/lib/helpers/fzf/src/chunklist.go +89 -0
- data/lib/helpers/fzf/src/chunklist_test.go +80 -0
- data/lib/helpers/fzf/src/constants.go +85 -0
- data/lib/helpers/fzf/src/core.go +351 -0
- data/lib/helpers/fzf/src/history.go +96 -0
- data/lib/helpers/fzf/src/history_test.go +68 -0
- data/lib/helpers/fzf/src/item.go +44 -0
- data/lib/helpers/fzf/src/item_test.go +23 -0
- data/lib/helpers/fzf/src/matcher.go +235 -0
- data/lib/helpers/fzf/src/merger.go +120 -0
- data/lib/helpers/fzf/src/merger_test.go +88 -0
- data/lib/helpers/fzf/src/options.go +1691 -0
- data/lib/helpers/fzf/src/options_test.go +457 -0
- data/lib/helpers/fzf/src/pattern.go +425 -0
- data/lib/helpers/fzf/src/pattern_test.go +209 -0
- data/lib/helpers/fzf/src/protector/protector.go +8 -0
- data/lib/helpers/fzf/src/protector/protector_openbsd.go +10 -0
- data/lib/helpers/fzf/src/reader.go +201 -0
- data/lib/helpers/fzf/src/reader_test.go +63 -0
- data/lib/helpers/fzf/src/result.go +243 -0
- data/lib/helpers/fzf/src/result_others.go +16 -0
- data/lib/helpers/fzf/src/result_test.go +159 -0
- data/lib/helpers/fzf/src/result_x86.go +16 -0
- data/lib/helpers/fzf/src/terminal.go +2832 -0
- data/lib/helpers/fzf/src/terminal_test.go +638 -0
- data/lib/helpers/fzf/src/terminal_unix.go +26 -0
- data/lib/helpers/fzf/src/terminal_windows.go +45 -0
- data/lib/helpers/fzf/src/tokenizer.go +253 -0
- data/lib/helpers/fzf/src/tokenizer_test.go +112 -0
- data/lib/helpers/fzf/src/tui/dummy.go +46 -0
- data/lib/helpers/fzf/src/tui/light.go +987 -0
- data/lib/helpers/fzf/src/tui/light_unix.go +110 -0
- data/lib/helpers/fzf/src/tui/light_windows.go +145 -0
- data/lib/helpers/fzf/src/tui/tcell.go +721 -0
- data/lib/helpers/fzf/src/tui/tcell_test.go +392 -0
- data/lib/helpers/fzf/src/tui/ttyname_unix.go +47 -0
- data/lib/helpers/fzf/src/tui/ttyname_windows.go +14 -0
- data/lib/helpers/fzf/src/tui/tui.go +625 -0
- data/lib/helpers/fzf/src/tui/tui_test.go +20 -0
- data/lib/helpers/fzf/src/util/atomicbool.go +34 -0
- data/lib/helpers/fzf/src/util/atomicbool_test.go +17 -0
- data/lib/helpers/fzf/src/util/chars.go +198 -0
- data/lib/helpers/fzf/src/util/chars_test.go +46 -0
- data/lib/helpers/fzf/src/util/eventbox.go +96 -0
- data/lib/helpers/fzf/src/util/eventbox_test.go +61 -0
- data/lib/helpers/fzf/src/util/slab.go +12 -0
- data/lib/helpers/fzf/src/util/util.go +138 -0
- data/lib/helpers/fzf/src/util/util_test.go +40 -0
- data/lib/helpers/fzf/src/util/util_unix.go +47 -0
- data/lib/helpers/fzf/src/util/util_windows.go +83 -0
- data/lib/helpers/fzf/test/fzf.vader +175 -0
- data/lib/helpers/fzf/test/test_go.rb +2626 -0
- data/lib/helpers/fzf/uninstall +117 -0
- metadata +88 -2
@@ -0,0 +1,235 @@
|
|
1
|
+
package fzf
|
2
|
+
|
3
|
+
import (
|
4
|
+
"fmt"
|
5
|
+
"runtime"
|
6
|
+
"sort"
|
7
|
+
"sync"
|
8
|
+
"time"
|
9
|
+
|
10
|
+
"github.com/junegunn/fzf/src/util"
|
11
|
+
)
|
12
|
+
|
13
|
+
// MatchRequest represents a search request
|
14
|
+
type MatchRequest struct {
|
15
|
+
chunks []*Chunk
|
16
|
+
pattern *Pattern
|
17
|
+
final bool
|
18
|
+
sort bool
|
19
|
+
clearCache bool
|
20
|
+
}
|
21
|
+
|
22
|
+
// Matcher is responsible for performing search
|
23
|
+
type Matcher struct {
|
24
|
+
patternBuilder func([]rune) *Pattern
|
25
|
+
sort bool
|
26
|
+
tac bool
|
27
|
+
eventBox *util.EventBox
|
28
|
+
reqBox *util.EventBox
|
29
|
+
partitions int
|
30
|
+
slab []*util.Slab
|
31
|
+
mergerCache map[string]*Merger
|
32
|
+
}
|
33
|
+
|
34
|
+
const (
|
35
|
+
reqRetry util.EventType = iota
|
36
|
+
reqReset
|
37
|
+
)
|
38
|
+
|
39
|
+
// NewMatcher returns a new Matcher
|
40
|
+
func NewMatcher(patternBuilder func([]rune) *Pattern,
|
41
|
+
sort bool, tac bool, eventBox *util.EventBox) *Matcher {
|
42
|
+
partitions := util.Min(numPartitionsMultiplier*runtime.NumCPU(), maxPartitions)
|
43
|
+
return &Matcher{
|
44
|
+
patternBuilder: patternBuilder,
|
45
|
+
sort: sort,
|
46
|
+
tac: tac,
|
47
|
+
eventBox: eventBox,
|
48
|
+
reqBox: util.NewEventBox(),
|
49
|
+
partitions: partitions,
|
50
|
+
slab: make([]*util.Slab, partitions),
|
51
|
+
mergerCache: make(map[string]*Merger)}
|
52
|
+
}
|
53
|
+
|
54
|
+
// Loop puts Matcher in action
|
55
|
+
func (m *Matcher) Loop() {
|
56
|
+
prevCount := 0
|
57
|
+
|
58
|
+
for {
|
59
|
+
var request MatchRequest
|
60
|
+
|
61
|
+
m.reqBox.Wait(func(events *util.Events) {
|
62
|
+
for _, val := range *events {
|
63
|
+
switch val := val.(type) {
|
64
|
+
case MatchRequest:
|
65
|
+
request = val
|
66
|
+
default:
|
67
|
+
panic(fmt.Sprintf("Unexpected type: %T", val))
|
68
|
+
}
|
69
|
+
}
|
70
|
+
events.Clear()
|
71
|
+
})
|
72
|
+
|
73
|
+
if request.sort != m.sort || request.clearCache {
|
74
|
+
m.sort = request.sort
|
75
|
+
m.mergerCache = make(map[string]*Merger)
|
76
|
+
clearChunkCache()
|
77
|
+
}
|
78
|
+
|
79
|
+
// Restart search
|
80
|
+
patternString := request.pattern.AsString()
|
81
|
+
var merger *Merger
|
82
|
+
cancelled := false
|
83
|
+
count := CountItems(request.chunks)
|
84
|
+
|
85
|
+
foundCache := false
|
86
|
+
if count == prevCount {
|
87
|
+
// Look up mergerCache
|
88
|
+
if cached, found := m.mergerCache[patternString]; found {
|
89
|
+
foundCache = true
|
90
|
+
merger = cached
|
91
|
+
}
|
92
|
+
} else {
|
93
|
+
// Invalidate mergerCache
|
94
|
+
prevCount = count
|
95
|
+
m.mergerCache = make(map[string]*Merger)
|
96
|
+
}
|
97
|
+
|
98
|
+
if !foundCache {
|
99
|
+
merger, cancelled = m.scan(request)
|
100
|
+
}
|
101
|
+
|
102
|
+
if !cancelled {
|
103
|
+
if merger.cacheable() {
|
104
|
+
m.mergerCache[patternString] = merger
|
105
|
+
}
|
106
|
+
merger.final = request.final
|
107
|
+
m.eventBox.Set(EvtSearchFin, merger)
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
func (m *Matcher) sliceChunks(chunks []*Chunk) [][]*Chunk {
|
113
|
+
partitions := m.partitions
|
114
|
+
perSlice := len(chunks) / partitions
|
115
|
+
|
116
|
+
if perSlice == 0 {
|
117
|
+
partitions = len(chunks)
|
118
|
+
perSlice = 1
|
119
|
+
}
|
120
|
+
|
121
|
+
slices := make([][]*Chunk, partitions)
|
122
|
+
for i := 0; i < partitions; i++ {
|
123
|
+
start := i * perSlice
|
124
|
+
end := start + perSlice
|
125
|
+
if i == partitions-1 {
|
126
|
+
end = len(chunks)
|
127
|
+
}
|
128
|
+
slices[i] = chunks[start:end]
|
129
|
+
}
|
130
|
+
return slices
|
131
|
+
}
|
132
|
+
|
133
|
+
type partialResult struct {
|
134
|
+
index int
|
135
|
+
matches []Result
|
136
|
+
}
|
137
|
+
|
138
|
+
func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
|
139
|
+
startedAt := time.Now()
|
140
|
+
|
141
|
+
numChunks := len(request.chunks)
|
142
|
+
if numChunks == 0 {
|
143
|
+
return EmptyMerger, false
|
144
|
+
}
|
145
|
+
pattern := request.pattern
|
146
|
+
if pattern.IsEmpty() {
|
147
|
+
return PassMerger(&request.chunks, m.tac), false
|
148
|
+
}
|
149
|
+
|
150
|
+
cancelled := util.NewAtomicBool(false)
|
151
|
+
|
152
|
+
slices := m.sliceChunks(request.chunks)
|
153
|
+
numSlices := len(slices)
|
154
|
+
resultChan := make(chan partialResult, numSlices)
|
155
|
+
countChan := make(chan int, numChunks)
|
156
|
+
waitGroup := sync.WaitGroup{}
|
157
|
+
|
158
|
+
for idx, chunks := range slices {
|
159
|
+
waitGroup.Add(1)
|
160
|
+
if m.slab[idx] == nil {
|
161
|
+
m.slab[idx] = util.MakeSlab(slab16Size, slab32Size)
|
162
|
+
}
|
163
|
+
go func(idx int, slab *util.Slab, chunks []*Chunk) {
|
164
|
+
defer func() { waitGroup.Done() }()
|
165
|
+
count := 0
|
166
|
+
allMatches := make([][]Result, len(chunks))
|
167
|
+
for idx, chunk := range chunks {
|
168
|
+
matches := request.pattern.Match(chunk, slab)
|
169
|
+
allMatches[idx] = matches
|
170
|
+
count += len(matches)
|
171
|
+
if cancelled.Get() {
|
172
|
+
return
|
173
|
+
}
|
174
|
+
countChan <- len(matches)
|
175
|
+
}
|
176
|
+
sliceMatches := make([]Result, 0, count)
|
177
|
+
for _, matches := range allMatches {
|
178
|
+
sliceMatches = append(sliceMatches, matches...)
|
179
|
+
}
|
180
|
+
if m.sort {
|
181
|
+
if m.tac {
|
182
|
+
sort.Sort(ByRelevanceTac(sliceMatches))
|
183
|
+
} else {
|
184
|
+
sort.Sort(ByRelevance(sliceMatches))
|
185
|
+
}
|
186
|
+
}
|
187
|
+
resultChan <- partialResult{idx, sliceMatches}
|
188
|
+
}(idx, m.slab[idx], chunks)
|
189
|
+
}
|
190
|
+
|
191
|
+
wait := func() bool {
|
192
|
+
cancelled.Set(true)
|
193
|
+
waitGroup.Wait()
|
194
|
+
return true
|
195
|
+
}
|
196
|
+
|
197
|
+
count := 0
|
198
|
+
matchCount := 0
|
199
|
+
for matchesInChunk := range countChan {
|
200
|
+
count++
|
201
|
+
matchCount += matchesInChunk
|
202
|
+
|
203
|
+
if count == numChunks {
|
204
|
+
break
|
205
|
+
}
|
206
|
+
|
207
|
+
if m.reqBox.Peek(reqReset) {
|
208
|
+
return nil, wait()
|
209
|
+
}
|
210
|
+
|
211
|
+
if time.Since(startedAt) > progressMinDuration {
|
212
|
+
m.eventBox.Set(EvtSearchProgress, float32(count)/float32(numChunks))
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
partialResults := make([][]Result, numSlices)
|
217
|
+
for range slices {
|
218
|
+
partialResult := <-resultChan
|
219
|
+
partialResults[partialResult.index] = partialResult.matches
|
220
|
+
}
|
221
|
+
return NewMerger(pattern, partialResults, m.sort, m.tac), false
|
222
|
+
}
|
223
|
+
|
224
|
+
// Reset is called to interrupt/signal the ongoing search
|
225
|
+
func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool, final bool, sort bool, clearCache bool) {
|
226
|
+
pattern := m.patternBuilder(patternRunes)
|
227
|
+
|
228
|
+
var event util.EventType
|
229
|
+
if cancel {
|
230
|
+
event = reqReset
|
231
|
+
} else {
|
232
|
+
event = reqRetry
|
233
|
+
}
|
234
|
+
m.reqBox.Set(event, MatchRequest{chunks, pattern, final, sort && pattern.sortable, clearCache})
|
235
|
+
}
|
@@ -0,0 +1,120 @@
|
|
1
|
+
package fzf
|
2
|
+
|
3
|
+
import "fmt"
|
4
|
+
|
5
|
+
// EmptyMerger is a Merger with no data
|
6
|
+
var EmptyMerger = NewMerger(nil, [][]Result{}, false, false)
|
7
|
+
|
8
|
+
// Merger holds a set of locally sorted lists of items and provides the view of
|
9
|
+
// a single, globally-sorted list
|
10
|
+
type Merger struct {
|
11
|
+
pattern *Pattern
|
12
|
+
lists [][]Result
|
13
|
+
merged []Result
|
14
|
+
chunks *[]*Chunk
|
15
|
+
cursors []int
|
16
|
+
sorted bool
|
17
|
+
tac bool
|
18
|
+
final bool
|
19
|
+
count int
|
20
|
+
}
|
21
|
+
|
22
|
+
// PassMerger returns a new Merger that simply returns the items in the
|
23
|
+
// original order
|
24
|
+
func PassMerger(chunks *[]*Chunk, tac bool) *Merger {
|
25
|
+
mg := Merger{
|
26
|
+
pattern: nil,
|
27
|
+
chunks: chunks,
|
28
|
+
tac: tac,
|
29
|
+
count: 0}
|
30
|
+
|
31
|
+
for _, chunk := range *mg.chunks {
|
32
|
+
mg.count += chunk.count
|
33
|
+
}
|
34
|
+
return &mg
|
35
|
+
}
|
36
|
+
|
37
|
+
// NewMerger returns a new Merger
|
38
|
+
func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool) *Merger {
|
39
|
+
mg := Merger{
|
40
|
+
pattern: pattern,
|
41
|
+
lists: lists,
|
42
|
+
merged: []Result{},
|
43
|
+
chunks: nil,
|
44
|
+
cursors: make([]int, len(lists)),
|
45
|
+
sorted: sorted,
|
46
|
+
tac: tac,
|
47
|
+
final: false,
|
48
|
+
count: 0}
|
49
|
+
|
50
|
+
for _, list := range mg.lists {
|
51
|
+
mg.count += len(list)
|
52
|
+
}
|
53
|
+
return &mg
|
54
|
+
}
|
55
|
+
|
56
|
+
// Length returns the number of items
|
57
|
+
func (mg *Merger) Length() int {
|
58
|
+
return mg.count
|
59
|
+
}
|
60
|
+
|
61
|
+
// Get returns the pointer to the Result object indexed by the given integer
|
62
|
+
func (mg *Merger) Get(idx int) Result {
|
63
|
+
if mg.chunks != nil {
|
64
|
+
if mg.tac {
|
65
|
+
idx = mg.count - idx - 1
|
66
|
+
}
|
67
|
+
chunk := (*mg.chunks)[idx/chunkSize]
|
68
|
+
return Result{item: &chunk.items[idx%chunkSize]}
|
69
|
+
}
|
70
|
+
|
71
|
+
if mg.sorted {
|
72
|
+
return mg.mergedGet(idx)
|
73
|
+
}
|
74
|
+
|
75
|
+
if mg.tac {
|
76
|
+
idx = mg.count - idx - 1
|
77
|
+
}
|
78
|
+
for _, list := range mg.lists {
|
79
|
+
numItems := len(list)
|
80
|
+
if idx < numItems {
|
81
|
+
return list[idx]
|
82
|
+
}
|
83
|
+
idx -= numItems
|
84
|
+
}
|
85
|
+
panic(fmt.Sprintf("Index out of bounds (unsorted, %d/%d)", idx, mg.count))
|
86
|
+
}
|
87
|
+
|
88
|
+
func (mg *Merger) cacheable() bool {
|
89
|
+
return mg.count < mergerCacheMax
|
90
|
+
}
|
91
|
+
|
92
|
+
func (mg *Merger) mergedGet(idx int) Result {
|
93
|
+
for i := len(mg.merged); i <= idx; i++ {
|
94
|
+
minRank := minRank()
|
95
|
+
minIdx := -1
|
96
|
+
for listIdx, list := range mg.lists {
|
97
|
+
cursor := mg.cursors[listIdx]
|
98
|
+
if cursor < 0 || cursor == len(list) {
|
99
|
+
mg.cursors[listIdx] = -1
|
100
|
+
continue
|
101
|
+
}
|
102
|
+
if cursor >= 0 {
|
103
|
+
rank := list[cursor]
|
104
|
+
if minIdx < 0 || compareRanks(rank, minRank, mg.tac) {
|
105
|
+
minRank = rank
|
106
|
+
minIdx = listIdx
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
if minIdx >= 0 {
|
112
|
+
chosen := mg.lists[minIdx]
|
113
|
+
mg.merged = append(mg.merged, chosen[mg.cursors[minIdx]])
|
114
|
+
mg.cursors[minIdx]++
|
115
|
+
} else {
|
116
|
+
panic(fmt.Sprintf("Index out of bounds (sorted, %d/%d)", i, mg.count))
|
117
|
+
}
|
118
|
+
}
|
119
|
+
return mg.merged[idx]
|
120
|
+
}
|
@@ -0,0 +1,88 @@
|
|
1
|
+
package fzf
|
2
|
+
|
3
|
+
import (
|
4
|
+
"fmt"
|
5
|
+
"math/rand"
|
6
|
+
"sort"
|
7
|
+
"testing"
|
8
|
+
|
9
|
+
"github.com/junegunn/fzf/src/util"
|
10
|
+
)
|
11
|
+
|
12
|
+
func assert(t *testing.T, cond bool, msg ...string) {
|
13
|
+
if !cond {
|
14
|
+
t.Error(msg)
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
func randResult() Result {
|
19
|
+
str := fmt.Sprintf("%d", rand.Uint32())
|
20
|
+
chars := util.ToChars([]byte(str))
|
21
|
+
chars.Index = rand.Int31()
|
22
|
+
return Result{item: &Item{text: chars}}
|
23
|
+
}
|
24
|
+
|
25
|
+
func TestEmptyMerger(t *testing.T) {
|
26
|
+
assert(t, EmptyMerger.Length() == 0, "Not empty")
|
27
|
+
assert(t, EmptyMerger.count == 0, "Invalid count")
|
28
|
+
assert(t, len(EmptyMerger.lists) == 0, "Invalid lists")
|
29
|
+
assert(t, len(EmptyMerger.merged) == 0, "Invalid merged list")
|
30
|
+
}
|
31
|
+
|
32
|
+
func buildLists(partiallySorted bool) ([][]Result, []Result) {
|
33
|
+
numLists := 4
|
34
|
+
lists := make([][]Result, numLists)
|
35
|
+
cnt := 0
|
36
|
+
for i := 0; i < numLists; i++ {
|
37
|
+
numResults := rand.Int() % 20
|
38
|
+
cnt += numResults
|
39
|
+
lists[i] = make([]Result, numResults)
|
40
|
+
for j := 0; j < numResults; j++ {
|
41
|
+
item := randResult()
|
42
|
+
lists[i][j] = item
|
43
|
+
}
|
44
|
+
if partiallySorted {
|
45
|
+
sort.Sort(ByRelevance(lists[i]))
|
46
|
+
}
|
47
|
+
}
|
48
|
+
items := []Result{}
|
49
|
+
for _, list := range lists {
|
50
|
+
items = append(items, list...)
|
51
|
+
}
|
52
|
+
return lists, items
|
53
|
+
}
|
54
|
+
|
55
|
+
func TestMergerUnsorted(t *testing.T) {
|
56
|
+
lists, items := buildLists(false)
|
57
|
+
cnt := len(items)
|
58
|
+
|
59
|
+
// Not sorted: same order
|
60
|
+
mg := NewMerger(nil, lists, false, false)
|
61
|
+
assert(t, cnt == mg.Length(), "Invalid Length")
|
62
|
+
for i := 0; i < cnt; i++ {
|
63
|
+
assert(t, items[i] == mg.Get(i), "Invalid Get")
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
func TestMergerSorted(t *testing.T) {
|
68
|
+
lists, items := buildLists(true)
|
69
|
+
cnt := len(items)
|
70
|
+
|
71
|
+
// Sorted sorted order
|
72
|
+
mg := NewMerger(nil, lists, true, false)
|
73
|
+
assert(t, cnt == mg.Length(), "Invalid Length")
|
74
|
+
sort.Sort(ByRelevance(items))
|
75
|
+
for i := 0; i < cnt; i++ {
|
76
|
+
if items[i] != mg.Get(i) {
|
77
|
+
t.Error("Not sorted", items[i], mg.Get(i))
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
// Inverse order
|
82
|
+
mg2 := NewMerger(nil, lists, true, false)
|
83
|
+
for i := cnt - 1; i >= 0; i-- {
|
84
|
+
if items[i] != mg2.Get(i) {
|
85
|
+
t.Error("Not sorted", items[i], mg2.Get(i))
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|