doing 2.0.18 → 2.0.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/Gemfile.lock +15 -5
  4. data/README.md +1 -1
  5. data/bin/doing +2 -18
  6. data/doing.gemspec +5 -4
  7. data/doing.rdoc +2 -2
  8. data/generate_completions.sh +3 -3
  9. data/lib/doing/cli_status.rb +6 -2
  10. data/lib/doing/completion/bash_completion.rb +185 -0
  11. data/lib/doing/completion/fish_completion.rb +175 -0
  12. data/lib/doing/completion/string.rb +17 -0
  13. data/lib/doing/completion/zsh_completion.rb +140 -0
  14. data/lib/doing/completion.rb +39 -0
  15. data/lib/doing/version.rb +1 -1
  16. data/lib/doing/wwid.rb +18 -6
  17. data/lib/doing.rb +1 -1
  18. data/lib/helpers/fzf/.goreleaser.yml +119 -0
  19. data/lib/helpers/fzf/.rubocop.yml +28 -0
  20. data/lib/helpers/fzf/ADVANCED.md +565 -0
  21. data/lib/helpers/fzf/BUILD.md +49 -0
  22. data/lib/helpers/fzf/CHANGELOG.md +1193 -0
  23. data/lib/helpers/fzf/Dockerfile +11 -0
  24. data/lib/helpers/fzf/LICENSE +21 -0
  25. data/lib/helpers/fzf/Makefile +166 -0
  26. data/lib/helpers/fzf/README-VIM.md +486 -0
  27. data/lib/helpers/fzf/README.md +712 -0
  28. data/lib/helpers/fzf/bin/fzf-tmux +233 -0
  29. data/lib/helpers/fzf/doc/fzf.txt +512 -0
  30. data/lib/helpers/fzf/go.mod +17 -0
  31. data/lib/helpers/fzf/go.sum +31 -0
  32. data/lib/helpers/fzf/install +382 -0
  33. data/lib/helpers/fzf/install.ps1 +65 -0
  34. data/lib/helpers/fzf/main.go +14 -0
  35. data/lib/helpers/fzf/man/man1/fzf-tmux.1 +68 -0
  36. data/lib/helpers/fzf/man/man1/fzf.1 +1001 -0
  37. data/lib/helpers/fzf/plugin/fzf.vim +1048 -0
  38. data/lib/helpers/fzf/shell/completion.bash +381 -0
  39. data/lib/helpers/fzf/shell/completion.zsh +329 -0
  40. data/lib/helpers/fzf/shell/key-bindings.bash +96 -0
  41. data/lib/helpers/fzf/shell/key-bindings.fish +172 -0
  42. data/lib/helpers/fzf/shell/key-bindings.zsh +114 -0
  43. data/lib/helpers/fzf/src/LICENSE +21 -0
  44. data/lib/helpers/fzf/src/algo/algo.go +884 -0
  45. data/lib/helpers/fzf/src/algo/algo_test.go +197 -0
  46. data/lib/helpers/fzf/src/algo/normalize.go +492 -0
  47. data/lib/helpers/fzf/src/ansi.go +409 -0
  48. data/lib/helpers/fzf/src/ansi_test.go +427 -0
  49. data/lib/helpers/fzf/src/cache.go +81 -0
  50. data/lib/helpers/fzf/src/cache_test.go +39 -0
  51. data/lib/helpers/fzf/src/chunklist.go +89 -0
  52. data/lib/helpers/fzf/src/chunklist_test.go +80 -0
  53. data/lib/helpers/fzf/src/constants.go +85 -0
  54. data/lib/helpers/fzf/src/core.go +351 -0
  55. data/lib/helpers/fzf/src/history.go +96 -0
  56. data/lib/helpers/fzf/src/history_test.go +68 -0
  57. data/lib/helpers/fzf/src/item.go +44 -0
  58. data/lib/helpers/fzf/src/item_test.go +23 -0
  59. data/lib/helpers/fzf/src/matcher.go +235 -0
  60. data/lib/helpers/fzf/src/merger.go +120 -0
  61. data/lib/helpers/fzf/src/merger_test.go +88 -0
  62. data/lib/helpers/fzf/src/options.go +1691 -0
  63. data/lib/helpers/fzf/src/options_test.go +457 -0
  64. data/lib/helpers/fzf/src/pattern.go +425 -0
  65. data/lib/helpers/fzf/src/pattern_test.go +209 -0
  66. data/lib/helpers/fzf/src/protector/protector.go +8 -0
  67. data/lib/helpers/fzf/src/protector/protector_openbsd.go +10 -0
  68. data/lib/helpers/fzf/src/reader.go +201 -0
  69. data/lib/helpers/fzf/src/reader_test.go +63 -0
  70. data/lib/helpers/fzf/src/result.go +243 -0
  71. data/lib/helpers/fzf/src/result_others.go +16 -0
  72. data/lib/helpers/fzf/src/result_test.go +159 -0
  73. data/lib/helpers/fzf/src/result_x86.go +16 -0
  74. data/lib/helpers/fzf/src/terminal.go +2832 -0
  75. data/lib/helpers/fzf/src/terminal_test.go +638 -0
  76. data/lib/helpers/fzf/src/terminal_unix.go +26 -0
  77. data/lib/helpers/fzf/src/terminal_windows.go +45 -0
  78. data/lib/helpers/fzf/src/tokenizer.go +253 -0
  79. data/lib/helpers/fzf/src/tokenizer_test.go +112 -0
  80. data/lib/helpers/fzf/src/tui/dummy.go +46 -0
  81. data/lib/helpers/fzf/src/tui/light.go +987 -0
  82. data/lib/helpers/fzf/src/tui/light_unix.go +110 -0
  83. data/lib/helpers/fzf/src/tui/light_windows.go +145 -0
  84. data/lib/helpers/fzf/src/tui/tcell.go +721 -0
  85. data/lib/helpers/fzf/src/tui/tcell_test.go +392 -0
  86. data/lib/helpers/fzf/src/tui/ttyname_unix.go +47 -0
  87. data/lib/helpers/fzf/src/tui/ttyname_windows.go +14 -0
  88. data/lib/helpers/fzf/src/tui/tui.go +625 -0
  89. data/lib/helpers/fzf/src/tui/tui_test.go +20 -0
  90. data/lib/helpers/fzf/src/util/atomicbool.go +34 -0
  91. data/lib/helpers/fzf/src/util/atomicbool_test.go +17 -0
  92. data/lib/helpers/fzf/src/util/chars.go +198 -0
  93. data/lib/helpers/fzf/src/util/chars_test.go +46 -0
  94. data/lib/helpers/fzf/src/util/eventbox.go +96 -0
  95. data/lib/helpers/fzf/src/util/eventbox_test.go +61 -0
  96. data/lib/helpers/fzf/src/util/slab.go +12 -0
  97. data/lib/helpers/fzf/src/util/util.go +138 -0
  98. data/lib/helpers/fzf/src/util/util_test.go +40 -0
  99. data/lib/helpers/fzf/src/util/util_unix.go +47 -0
  100. data/lib/helpers/fzf/src/util/util_windows.go +83 -0
  101. data/lib/helpers/fzf/test/fzf.vader +175 -0
  102. data/lib/helpers/fzf/test/test_go.rb +2626 -0
  103. data/lib/helpers/fzf/uninstall +117 -0
  104. data/scripts/generate_bash_completions.rb +6 -12
  105. data/scripts/generate_fish_completions.rb +7 -16
  106. data/scripts/generate_zsh_completions.rb +6 -15
  107. metadata +144 -9
@@ -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
+ }