doing 2.0.20 → 2.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,201 @@
1
+ package fzf
2
+
3
+ import (
4
+ "bufio"
5
+ "context"
6
+ "io"
7
+ "os"
8
+ "os/exec"
9
+ "path/filepath"
10
+ "sync"
11
+ "sync/atomic"
12
+ "time"
13
+
14
+ "github.com/junegunn/fzf/src/util"
15
+ "github.com/saracen/walker"
16
+ )
17
+
18
+ // Reader reads from command or standard input
19
+ type Reader struct {
20
+ pusher func([]byte) bool
21
+ eventBox *util.EventBox
22
+ delimNil bool
23
+ event int32
24
+ finChan chan bool
25
+ mutex sync.Mutex
26
+ exec *exec.Cmd
27
+ command *string
28
+ killed bool
29
+ wait bool
30
+ }
31
+
32
+ // NewReader returns new Reader object
33
+ func NewReader(pusher func([]byte) bool, eventBox *util.EventBox, delimNil bool, wait bool) *Reader {
34
+ return &Reader{pusher, eventBox, delimNil, int32(EvtReady), make(chan bool, 1), sync.Mutex{}, nil, nil, false, wait}
35
+ }
36
+
37
+ func (r *Reader) startEventPoller() {
38
+ go func() {
39
+ ptr := &r.event
40
+ pollInterval := readerPollIntervalMin
41
+ for {
42
+ if atomic.CompareAndSwapInt32(ptr, int32(EvtReadNew), int32(EvtReady)) {
43
+ r.eventBox.Set(EvtReadNew, (*string)(nil))
44
+ pollInterval = readerPollIntervalMin
45
+ } else if atomic.LoadInt32(ptr) == int32(EvtReadFin) {
46
+ if r.wait {
47
+ r.finChan <- true
48
+ }
49
+ return
50
+ } else {
51
+ pollInterval += readerPollIntervalStep
52
+ if pollInterval > readerPollIntervalMax {
53
+ pollInterval = readerPollIntervalMax
54
+ }
55
+ }
56
+ time.Sleep(pollInterval)
57
+ }
58
+ }()
59
+ }
60
+
61
+ func (r *Reader) fin(success bool) {
62
+ atomic.StoreInt32(&r.event, int32(EvtReadFin))
63
+ if r.wait {
64
+ <-r.finChan
65
+ }
66
+
67
+ r.mutex.Lock()
68
+ ret := r.command
69
+ if success || r.killed {
70
+ ret = nil
71
+ }
72
+ r.mutex.Unlock()
73
+
74
+ r.eventBox.Set(EvtReadFin, ret)
75
+ }
76
+
77
+ func (r *Reader) terminate() {
78
+ r.mutex.Lock()
79
+ defer func() { r.mutex.Unlock() }()
80
+
81
+ r.killed = true
82
+ if r.exec != nil && r.exec.Process != nil {
83
+ util.KillCommand(r.exec)
84
+ } else if defaultCommand != "" {
85
+ os.Stdin.Close()
86
+ }
87
+ }
88
+
89
+ func (r *Reader) restart(command string) {
90
+ r.event = int32(EvtReady)
91
+ r.startEventPoller()
92
+ success := r.readFromCommand(nil, command)
93
+ r.fin(success)
94
+ }
95
+
96
+ // ReadSource reads data from the default command or from standard input
97
+ func (r *Reader) ReadSource() {
98
+ r.startEventPoller()
99
+ var success bool
100
+ if util.IsTty() {
101
+ // The default command for *nix requires bash
102
+ shell := "bash"
103
+ cmd := os.Getenv("FZF_DEFAULT_COMMAND")
104
+ if len(cmd) == 0 {
105
+ if defaultCommand != "" {
106
+ success = r.readFromCommand(&shell, defaultCommand)
107
+ } else {
108
+ success = r.readFiles()
109
+ }
110
+ } else {
111
+ success = r.readFromCommand(nil, cmd)
112
+ }
113
+ } else {
114
+ success = r.readFromStdin()
115
+ }
116
+ r.fin(success)
117
+ }
118
+
119
+ func (r *Reader) feed(src io.Reader) {
120
+ delim := byte('\n')
121
+ if r.delimNil {
122
+ delim = '\000'
123
+ }
124
+ reader := bufio.NewReaderSize(src, readerBufferSize)
125
+ for {
126
+ // ReadBytes returns err != nil if and only if the returned data does not
127
+ // end in delim.
128
+ bytea, err := reader.ReadBytes(delim)
129
+ byteaLen := len(bytea)
130
+ if byteaLen > 0 {
131
+ if err == nil {
132
+ // get rid of carriage return if under Windows:
133
+ if util.IsWindows() && byteaLen >= 2 && bytea[byteaLen-2] == byte('\r') {
134
+ bytea = bytea[:byteaLen-2]
135
+ } else {
136
+ bytea = bytea[:byteaLen-1]
137
+ }
138
+ }
139
+ if r.pusher(bytea) {
140
+ atomic.StoreInt32(&r.event, int32(EvtReadNew))
141
+ }
142
+ }
143
+ if err != nil {
144
+ break
145
+ }
146
+ }
147
+ }
148
+
149
+ func (r *Reader) readFromStdin() bool {
150
+ r.feed(os.Stdin)
151
+ return true
152
+ }
153
+
154
+ func (r *Reader) readFiles() bool {
155
+ r.killed = false
156
+ fn := func(path string, mode os.FileInfo) error {
157
+ path = filepath.Clean(path)
158
+ if path != "." {
159
+ isDir := mode.Mode().IsDir()
160
+ if isDir && filepath.Base(path)[0] == '.' {
161
+ return filepath.SkipDir
162
+ }
163
+ if !isDir && r.pusher([]byte(path)) {
164
+ atomic.StoreInt32(&r.event, int32(EvtReadNew))
165
+ }
166
+ }
167
+ r.mutex.Lock()
168
+ defer r.mutex.Unlock()
169
+ if r.killed {
170
+ return context.Canceled
171
+ }
172
+ return nil
173
+ }
174
+ cb := walker.WithErrorCallback(func(pathname string, err error) error {
175
+ return nil
176
+ })
177
+ return walker.Walk(".", fn, cb) == nil
178
+ }
179
+
180
+ func (r *Reader) readFromCommand(shell *string, command string) bool {
181
+ r.mutex.Lock()
182
+ r.killed = false
183
+ r.command = &command
184
+ if shell != nil {
185
+ r.exec = util.ExecCommandWith(*shell, command, true)
186
+ } else {
187
+ r.exec = util.ExecCommand(command, true)
188
+ }
189
+ out, err := r.exec.StdoutPipe()
190
+ if err != nil {
191
+ r.mutex.Unlock()
192
+ return false
193
+ }
194
+ err = r.exec.Start()
195
+ r.mutex.Unlock()
196
+ if err != nil {
197
+ return false
198
+ }
199
+ r.feed(out)
200
+ return r.exec.Wait() == nil
201
+ }
@@ -0,0 +1,63 @@
1
+ package fzf
2
+
3
+ import (
4
+ "testing"
5
+ "time"
6
+
7
+ "github.com/junegunn/fzf/src/util"
8
+ )
9
+
10
+ func TestReadFromCommand(t *testing.T) {
11
+ strs := []string{}
12
+ eb := util.NewEventBox()
13
+ reader := NewReader(
14
+ func(s []byte) bool { strs = append(strs, string(s)); return true },
15
+ eb, false, true)
16
+
17
+ reader.startEventPoller()
18
+
19
+ // Check EventBox
20
+ if eb.Peek(EvtReadNew) {
21
+ t.Error("EvtReadNew should not be set yet")
22
+ }
23
+
24
+ // Normal command
25
+ reader.fin(reader.readFromCommand(nil, `echo abc&&echo def`))
26
+ if len(strs) != 2 || strs[0] != "abc" || strs[1] != "def" {
27
+ t.Errorf("%s", strs)
28
+ }
29
+
30
+ // Check EventBox again
31
+ eb.WaitFor(EvtReadFin)
32
+
33
+ // Wait should return immediately
34
+ eb.Wait(func(events *util.Events) {
35
+ events.Clear()
36
+ })
37
+
38
+ // EventBox is cleared
39
+ if eb.Peek(EvtReadNew) {
40
+ t.Error("EvtReadNew should not be set yet")
41
+ }
42
+
43
+ // Make sure that event poller is finished
44
+ time.Sleep(readerPollIntervalMax)
45
+
46
+ // Restart event poller
47
+ reader.startEventPoller()
48
+
49
+ // Failing command
50
+ reader.fin(reader.readFromCommand(nil, `no-such-command`))
51
+ strs = []string{}
52
+ if len(strs) > 0 {
53
+ t.Errorf("%s", strs)
54
+ }
55
+
56
+ // Check EventBox again
57
+ if eb.Peek(EvtReadNew) {
58
+ t.Error("Command failed. EvtReadNew should not be set")
59
+ }
60
+ if !eb.Peek(EvtReadFin) {
61
+ t.Error("EvtReadFin should be set")
62
+ }
63
+ }
@@ -0,0 +1,243 @@
1
+ package fzf
2
+
3
+ import (
4
+ "math"
5
+ "sort"
6
+ "unicode"
7
+
8
+ "github.com/junegunn/fzf/src/tui"
9
+ "github.com/junegunn/fzf/src/util"
10
+ )
11
+
12
+ // Offset holds two 32-bit integers denoting the offsets of a matched substring
13
+ type Offset [2]int32
14
+
15
+ type colorOffset struct {
16
+ offset [2]int32
17
+ color tui.ColorPair
18
+ }
19
+
20
+ type Result struct {
21
+ item *Item
22
+ points [4]uint16
23
+ }
24
+
25
+ func buildResult(item *Item, offsets []Offset, score int) Result {
26
+ if len(offsets) > 1 {
27
+ sort.Sort(ByOrder(offsets))
28
+ }
29
+
30
+ result := Result{item: item}
31
+ numChars := item.text.Length()
32
+ minBegin := math.MaxUint16
33
+ minEnd := math.MaxUint16
34
+ maxEnd := 0
35
+ validOffsetFound := false
36
+ for _, offset := range offsets {
37
+ b, e := int(offset[0]), int(offset[1])
38
+ if b < e {
39
+ minBegin = util.Min(b, minBegin)
40
+ minEnd = util.Min(e, minEnd)
41
+ maxEnd = util.Max(e, maxEnd)
42
+ validOffsetFound = true
43
+ }
44
+ }
45
+
46
+ for idx, criterion := range sortCriteria {
47
+ val := uint16(math.MaxUint16)
48
+ switch criterion {
49
+ case byScore:
50
+ // Higher is better
51
+ val = math.MaxUint16 - util.AsUint16(score)
52
+ case byLength:
53
+ val = item.TrimLength()
54
+ case byBegin, byEnd:
55
+ if validOffsetFound {
56
+ whitePrefixLen := 0
57
+ for idx := 0; idx < numChars; idx++ {
58
+ r := item.text.Get(idx)
59
+ whitePrefixLen = idx
60
+ if idx == minBegin || !unicode.IsSpace(r) {
61
+ break
62
+ }
63
+ }
64
+ if criterion == byBegin {
65
+ val = util.AsUint16(minEnd - whitePrefixLen)
66
+ } else {
67
+ val = util.AsUint16(math.MaxUint16 - math.MaxUint16*(maxEnd-whitePrefixLen)/int(item.TrimLength()))
68
+ }
69
+ }
70
+ }
71
+ result.points[3-idx] = val
72
+ }
73
+
74
+ return result
75
+ }
76
+
77
+ // Sort criteria to use. Never changes once fzf is started.
78
+ var sortCriteria []criterion
79
+
80
+ // Index returns ordinal index of the Item
81
+ func (result *Result) Index() int32 {
82
+ return result.item.Index()
83
+ }
84
+
85
+ func minRank() Result {
86
+ return Result{item: &minItem, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
87
+ }
88
+
89
+ func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme, colBase tui.ColorPair, colMatch tui.ColorPair, current bool) []colorOffset {
90
+ itemColors := result.item.Colors()
91
+
92
+ // No ANSI codes
93
+ if len(itemColors) == 0 {
94
+ var offsets []colorOffset
95
+ for _, off := range matchOffsets {
96
+ offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: colMatch})
97
+ }
98
+ return offsets
99
+ }
100
+
101
+ // Find max column
102
+ var maxCol int32
103
+ for _, off := range matchOffsets {
104
+ if off[1] > maxCol {
105
+ maxCol = off[1]
106
+ }
107
+ }
108
+ for _, ansi := range itemColors {
109
+ if ansi.offset[1] > maxCol {
110
+ maxCol = ansi.offset[1]
111
+ }
112
+ }
113
+
114
+ cols := make([]int, maxCol)
115
+ for colorIndex, ansi := range itemColors {
116
+ for i := ansi.offset[0]; i < ansi.offset[1]; i++ {
117
+ cols[i] = colorIndex + 1 // 1-based index of itemColors
118
+ }
119
+ }
120
+
121
+ for _, off := range matchOffsets {
122
+ for i := off[0]; i < off[1]; i++ {
123
+ // Negative of 1-based index of itemColors
124
+ // - The extra -1 means highlighted
125
+ cols[i] = cols[i]*-1 - 1
126
+ }
127
+ }
128
+
129
+ // sort.Sort(ByOrder(offsets))
130
+
131
+ // Merge offsets
132
+ // ------------ ---- -- ----
133
+ // ++++++++ ++++++++++
134
+ // --++++++++-- --++++++++++---
135
+ curr := 0
136
+ start := 0
137
+ ansiToColorPair := func(ansi ansiOffset, base tui.ColorPair) tui.ColorPair {
138
+ fg := ansi.color.fg
139
+ bg := ansi.color.bg
140
+ if fg == -1 {
141
+ if current {
142
+ fg = theme.Current.Color
143
+ } else {
144
+ fg = theme.Fg.Color
145
+ }
146
+ }
147
+ if bg == -1 {
148
+ if current {
149
+ bg = theme.DarkBg.Color
150
+ } else {
151
+ bg = theme.Bg.Color
152
+ }
153
+ }
154
+ return tui.NewColorPair(fg, bg, ansi.color.attr).MergeAttr(base)
155
+ }
156
+ var colors []colorOffset
157
+ add := func(idx int) {
158
+ if curr != 0 && idx > start {
159
+ if curr < 0 {
160
+ color := colMatch
161
+ if curr < -1 && theme.Colored {
162
+ origColor := ansiToColorPair(itemColors[-curr-2], colMatch)
163
+ // hl or hl+ only sets the foreground color, so colMatch is the
164
+ // combination of either [hl and bg] or [hl+ and bg+].
165
+ //
166
+ // If the original text already has background color, and the
167
+ // foreground color of colMatch is -1, we shouldn't only apply the
168
+ // background color of colMatch.
169
+ // e.g. echo -e "\x1b[32;7mfoo\x1b[mbar" | fzf --ansi --color bg+:1,hl+:-1:underline
170
+ // echo -e "\x1b[42mfoo\x1b[mbar" | fzf --ansi --color bg+:1,hl+:-1:underline
171
+ if color.Fg().IsDefault() && origColor.HasBg() {
172
+ color = origColor
173
+ } else {
174
+ color = origColor.MergeNonDefault(color)
175
+ }
176
+ }
177
+ colors = append(colors, colorOffset{
178
+ offset: [2]int32{int32(start), int32(idx)}, color: color})
179
+ } else {
180
+ ansi := itemColors[curr-1]
181
+ colors = append(colors, colorOffset{
182
+ offset: [2]int32{int32(start), int32(idx)},
183
+ color: ansiToColorPair(ansi, colBase)})
184
+ }
185
+ }
186
+ }
187
+ for idx, col := range cols {
188
+ if col != curr {
189
+ add(idx)
190
+ start = idx
191
+ curr = col
192
+ }
193
+ }
194
+ add(int(maxCol))
195
+ return colors
196
+ }
197
+
198
+ // ByOrder is for sorting substring offsets
199
+ type ByOrder []Offset
200
+
201
+ func (a ByOrder) Len() int {
202
+ return len(a)
203
+ }
204
+
205
+ func (a ByOrder) Swap(i, j int) {
206
+ a[i], a[j] = a[j], a[i]
207
+ }
208
+
209
+ func (a ByOrder) Less(i, j int) bool {
210
+ ioff := a[i]
211
+ joff := a[j]
212
+ return (ioff[0] < joff[0]) || (ioff[0] == joff[0]) && (ioff[1] <= joff[1])
213
+ }
214
+
215
+ // ByRelevance is for sorting Items
216
+ type ByRelevance []Result
217
+
218
+ func (a ByRelevance) Len() int {
219
+ return len(a)
220
+ }
221
+
222
+ func (a ByRelevance) Swap(i, j int) {
223
+ a[i], a[j] = a[j], a[i]
224
+ }
225
+
226
+ func (a ByRelevance) Less(i, j int) bool {
227
+ return compareRanks(a[i], a[j], false)
228
+ }
229
+
230
+ // ByRelevanceTac is for sorting Items
231
+ type ByRelevanceTac []Result
232
+
233
+ func (a ByRelevanceTac) Len() int {
234
+ return len(a)
235
+ }
236
+
237
+ func (a ByRelevanceTac) Swap(i, j int) {
238
+ a[i], a[j] = a[j], a[i]
239
+ }
240
+
241
+ func (a ByRelevanceTac) Less(i, j int) bool {
242
+ return compareRanks(a[i], a[j], true)
243
+ }
@@ -0,0 +1,16 @@
1
+ // +build !386,!amd64
2
+
3
+ package fzf
4
+
5
+ func compareRanks(irank Result, jrank Result, tac bool) bool {
6
+ for idx := 3; idx >= 0; idx-- {
7
+ left := irank.points[idx]
8
+ right := jrank.points[idx]
9
+ if left < right {
10
+ return true
11
+ } else if left > right {
12
+ return false
13
+ }
14
+ }
15
+ return (irank.item.Index() <= jrank.item.Index()) != tac
16
+ }
@@ -0,0 +1,159 @@
1
+ // +build !tcell
2
+
3
+ package fzf
4
+
5
+ import (
6
+ "math"
7
+ "sort"
8
+ "testing"
9
+
10
+ "github.com/junegunn/fzf/src/tui"
11
+ "github.com/junegunn/fzf/src/util"
12
+ )
13
+
14
+ func withIndex(i *Item, index int) *Item {
15
+ (*i).text.Index = int32(index)
16
+ return i
17
+ }
18
+
19
+ func TestOffsetSort(t *testing.T) {
20
+ offsets := []Offset{
21
+ {3, 5}, {2, 7},
22
+ {1, 3}, {2, 9}}
23
+ sort.Sort(ByOrder(offsets))
24
+
25
+ if offsets[0][0] != 1 || offsets[0][1] != 3 ||
26
+ offsets[1][0] != 2 || offsets[1][1] != 7 ||
27
+ offsets[2][0] != 2 || offsets[2][1] != 9 ||
28
+ offsets[3][0] != 3 || offsets[3][1] != 5 {
29
+ t.Error("Invalid order:", offsets)
30
+ }
31
+ }
32
+
33
+ func TestRankComparison(t *testing.T) {
34
+ rank := func(vals ...uint16) Result {
35
+ return Result{
36
+ points: [4]uint16{vals[0], vals[1], vals[2], vals[3]},
37
+ item: &Item{text: util.Chars{Index: int32(vals[4])}}}
38
+ }
39
+ if compareRanks(rank(3, 0, 0, 0, 5), rank(2, 0, 0, 0, 7), false) ||
40
+ !compareRanks(rank(3, 0, 0, 0, 5), rank(3, 0, 0, 0, 6), false) ||
41
+ !compareRanks(rank(1, 2, 0, 0, 3), rank(1, 3, 0, 0, 2), false) ||
42
+ !compareRanks(rank(0, 0, 0, 0, 0), rank(0, 0, 0, 0, 0), false) {
43
+ t.Error("Invalid order")
44
+ }
45
+
46
+ if compareRanks(rank(3, 0, 0, 0, 5), rank(2, 0, 0, 0, 7), true) ||
47
+ !compareRanks(rank(3, 0, 0, 0, 5), rank(3, 0, 0, 0, 6), false) ||
48
+ !compareRanks(rank(1, 2, 0, 0, 3), rank(1, 3, 0, 0, 2), true) ||
49
+ !compareRanks(rank(0, 0, 0, 0, 0), rank(0, 0, 0, 0, 0), false) {
50
+ t.Error("Invalid order (tac)")
51
+ }
52
+ }
53
+
54
+ // Match length, string length, index
55
+ func TestResultRank(t *testing.T) {
56
+ // FIXME global
57
+ sortCriteria = []criterion{byScore, byLength}
58
+
59
+ strs := [][]rune{[]rune("foo"), []rune("foobar"), []rune("bar"), []rune("baz")}
60
+ item1 := buildResult(
61
+ withIndex(&Item{text: util.RunesToChars(strs[0])}, 1), []Offset{}, 2)
62
+ if item1.points[3] != math.MaxUint16-2 || // Bonus
63
+ item1.points[2] != 3 || // Length
64
+ item1.points[1] != 0 || // Unused
65
+ item1.points[0] != 0 || // Unused
66
+ item1.item.Index() != 1 {
67
+ t.Error(item1)
68
+ }
69
+ // Only differ in index
70
+ item2 := buildResult(&Item{text: util.RunesToChars(strs[0])}, []Offset{}, 2)
71
+
72
+ items := []Result{item1, item2}
73
+ sort.Sort(ByRelevance(items))
74
+ if items[0] != item2 || items[1] != item1 {
75
+ t.Error(items)
76
+ }
77
+
78
+ items = []Result{item2, item1, item1, item2}
79
+ sort.Sort(ByRelevance(items))
80
+ if items[0] != item2 || items[1] != item2 ||
81
+ items[2] != item1 || items[3] != item1 {
82
+ t.Error(items, item1, item1.item.Index(), item2, item2.item.Index())
83
+ }
84
+
85
+ // Sort by relevance
86
+ item3 := buildResult(
87
+ withIndex(&Item{}, 2), []Offset{{1, 3}, {5, 7}}, 3)
88
+ item4 := buildResult(
89
+ withIndex(&Item{}, 2), []Offset{{1, 2}, {6, 7}}, 4)
90
+ item5 := buildResult(
91
+ withIndex(&Item{}, 2), []Offset{{1, 3}, {5, 7}}, 5)
92
+ item6 := buildResult(
93
+ withIndex(&Item{}, 2), []Offset{{1, 2}, {6, 7}}, 6)
94
+ items = []Result{item1, item2, item3, item4, item5, item6}
95
+ sort.Sort(ByRelevance(items))
96
+ if !(items[0] == item6 && items[1] == item5 &&
97
+ items[2] == item4 && items[3] == item3 &&
98
+ items[4] == item2 && items[5] == item1) {
99
+ t.Error(items, item1, item2, item3, item4, item5, item6)
100
+ }
101
+ }
102
+
103
+ func TestColorOffset(t *testing.T) {
104
+ // ------------ 20 ---- -- ----
105
+ // ++++++++ ++++++++++
106
+ // --++++++++-- --++++++++++---
107
+
108
+ offsets := []Offset{{5, 15}, {25, 35}}
109
+ item := Result{
110
+ item: &Item{
111
+ colors: &[]ansiOffset{
112
+ {[2]int32{0, 20}, ansiState{1, 5, 0, -1}},
113
+ {[2]int32{22, 27}, ansiState{2, 6, tui.Bold, -1}},
114
+ {[2]int32{30, 32}, ansiState{3, 7, 0, -1}},
115
+ {[2]int32{33, 40}, ansiState{4, 8, tui.Bold, -1}}}}}
116
+
117
+ colBase := tui.NewColorPair(89, 189, tui.AttrUndefined)
118
+ colMatch := tui.NewColorPair(99, 199, tui.AttrUndefined)
119
+ colors := item.colorOffsets(offsets, tui.Dark256, colBase, colMatch, true)
120
+ assert := func(idx int, b int32, e int32, c tui.ColorPair) {
121
+ o := colors[idx]
122
+ if o.offset[0] != b || o.offset[1] != e || o.color != c {
123
+ t.Error(o, b, e, c)
124
+ }
125
+ }
126
+ // [{[0 5] {1 5 0}} {[5 15] {99 199 0}} {[15 20] {1 5 0}}
127
+ // {[22 25] {2 6 1}} {[25 27] {99 199 1}} {[27 30] {99 199 0}}
128
+ // {[30 32] {99 199 0}} {[32 33] {99 199 0}} {[33 35] {99 199 1}}
129
+ // {[35 40] {4 8 1}}]
130
+ assert(0, 0, 5, tui.NewColorPair(1, 5, tui.AttrUndefined))
131
+ assert(1, 5, 15, colMatch)
132
+ assert(2, 15, 20, tui.NewColorPair(1, 5, tui.AttrUndefined))
133
+ assert(3, 22, 25, tui.NewColorPair(2, 6, tui.Bold))
134
+ assert(4, 25, 27, colMatch.WithAttr(tui.Bold))
135
+ assert(5, 27, 30, colMatch)
136
+ assert(6, 30, 32, colMatch)
137
+ assert(7, 32, 33, colMatch) // TODO: Should we merge consecutive blocks?
138
+ assert(8, 33, 35, colMatch.WithAttr(tui.Bold))
139
+ assert(9, 35, 40, tui.NewColorPair(4, 8, tui.Bold))
140
+
141
+ colRegular := tui.NewColorPair(-1, -1, tui.AttrUndefined)
142
+ colUnderline := tui.NewColorPair(-1, -1, tui.Underline)
143
+ colors = item.colorOffsets(offsets, tui.Dark256, colRegular, colUnderline, true)
144
+
145
+ // [{[0 5] {1 5 0}} {[5 15] {1 5 8}} {[15 20] {1 5 0}}
146
+ // {[22 25] {2 6 1}} {[25 27] {2 6 9}} {[27 30] {-1 -1 8}}
147
+ // {[30 32] {3 7 8}} {[32 33] {-1 -1 8}} {[33 35] {4 8 9}}
148
+ // {[35 40] {4 8 1}}]
149
+ assert(0, 0, 5, tui.NewColorPair(1, 5, tui.AttrUndefined))
150
+ assert(1, 5, 15, tui.NewColorPair(1, 5, tui.Underline))
151
+ assert(2, 15, 20, tui.NewColorPair(1, 5, tui.AttrUndefined))
152
+ assert(3, 22, 25, tui.NewColorPair(2, 6, tui.Bold))
153
+ assert(4, 25, 27, tui.NewColorPair(2, 6, tui.Bold|tui.Underline))
154
+ assert(5, 27, 30, colUnderline)
155
+ assert(6, 30, 32, tui.NewColorPair(3, 7, tui.Underline))
156
+ assert(7, 32, 33, colUnderline)
157
+ assert(8, 33, 35, tui.NewColorPair(4, 8, tui.Bold|tui.Underline))
158
+ assert(9, 35, 40, tui.NewColorPair(4, 8, tui.Bold))
159
+ }
@@ -0,0 +1,16 @@
1
+ // +build 386 amd64
2
+
3
+ package fzf
4
+
5
+ import "unsafe"
6
+
7
+ func compareRanks(irank Result, jrank Result, tac bool) bool {
8
+ left := *(*uint64)(unsafe.Pointer(&irank.points[0]))
9
+ right := *(*uint64)(unsafe.Pointer(&jrank.points[0]))
10
+ if left < right {
11
+ return true
12
+ } else if left > right {
13
+ return false
14
+ }
15
+ return (irank.item.Index() <= jrank.item.Index()) != tac
16
+ }